Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

95 lines · 4.9 KBRaw History
1import * as vscode from "vscode";
2import { AUTH_ID, DiffineryAuth, SCOPES } from "./auth";
3import { GitCredentials, Publisher, syncCredentialStore } from "./git";
4import { RemoteSources } from "./remoteSources";
5import { offerInstallerServer, switchServer } from "./servers";
6import type { GitAPI, GitBaseAPI, GitBaseExtension, GitExtension } from "./typings/git";
7import { checkForUpdates } from "./updates";
8
9export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
10 const version = String(ctx.extension.packageJSON.version ?? "0.0.0");
11 const log = vscode.window.createOutputChannel("Diffinery", { log: true });
12 const auth = new DiffineryAuth(ctx, log, version);
13 ctx.subscriptions.push(
14 log,
15 auth,
16 vscode.authentication.registerAuthenticationProvider(AUTH_ID, "Diffinery", auth, { supportsMultipleAccounts: false }),
17 vscode.window.registerUriHandler({ handleUri: (uri) => void auth.handleUri(uri) }),
18 );
19
20 const report = (what: string) => (e: unknown) => {
21 if (e instanceof vscode.CancellationError) return;
22 log.error(what, e);
23 void vscode.window.showErrorMessage(`${what}: ${e instanceof Error ? e.message : String(e)}`);
24 };
25 const command = (id: string, run: () => Promise<unknown>, what: string) => ctx.subscriptions.push(vscode.commands.registerCommand(id, () => run().catch(report(what))));
26
27 command("diffinery.signIn", async () => vscode.authentication.getSession(AUTH_ID, SCOPES, { createIfNone: true }), "Signing in to Diffinery failed");
28 command("diffinery.signInWithToken", () => auth.signInWithToken(), "Signing in to Diffinery failed");
29 command("diffinery.signOut", () => auth.signOut(), "Signing out of Diffinery failed");
30 command("diffinery.useServer", () => switchServer(ctx.globalState, () => auth.login()), "Changing the Diffinery server failed");
31 command("diffinery.checkForUpdates", () => checkForUpdates(ctx, auth.server(), version, log, true), "Checking for updates failed");
32
33 // ---------------------------------------------------------------- the built-in Git extensions
34 let gitBase: GitBaseAPI | undefined;
35 let git: GitAPI | undefined;
36 const remoteSources = new RemoteSources(auth);
37 const gitDisposables: vscode.Disposable[] = [];
38 ctx.subscriptions.push({ dispose: () => gitDisposables.forEach((d) => d.dispose()) });
39 let credentials: vscode.Disposable | undefined;
40 const updateCredentials = () => {
41 const on = !!git && vscode.workspace.getConfiguration("diffinery").get<boolean>("gitAuthentication", true);
42 if (on && !credentials) credentials = git!.registerCredentialsProvider(new GitCredentials(auth));
43 if (!on && credentials) {
44 credentials.dispose();
45 credentials = undefined;
46 }
47 };
48 ctx.subscriptions.push(
49 { dispose: () => credentials?.dispose() },
50 vscode.workspace.onDidChangeConfiguration((e) => e.affectsConfiguration("diffinery.gitAuthentication") && updateCredentials()),
51 syncCredentialStore(auth, () => git, log),
52 );
53
54 const baseExt = vscode.extensions.getExtension<GitBaseExtension>("vscode.git-base");
55 const gitExt = vscode.extensions.getExtension<GitExtension>("vscode.git");
56 const connect = async () => {
57 try {
58 const base = baseExt && (baseExt.isActive ? baseExt.exports : await baseExt.activate());
59 if (base?.enabled && !gitBase) {
60 gitBase = base.getAPI(1);
61 gitDisposables.push(gitBase.registerRemoteSourceProvider(remoteSources));
62 }
63 const g = gitExt && (gitExt.isActive ? gitExt.exports : await gitExt.activate());
64 if (g?.enabled && !git) {
65 git = g.getAPI(1);
66 gitDisposables.push(git.registerRemoteSourcePublisher(new Publisher(auth)));
67 updateCredentials();
68 }
69 return { base, g };
70 } catch (e) {
71 log.error("connecting to the built-in Git extension failed", e);
72 return {};
73 }
74 };
75 const { base, g } = await connect();
76 if (base && !base.enabled) ctx.subscriptions.push(base.onDidChangeEnablement((on) => on && void connect()));
77 if (g && !g.enabled) ctx.subscriptions.push(g.onDidChangeEnablement((on) => on && void connect()));
78 if (!baseExt || !gitExt) log.warn("The editor's built-in Git extension isn't available, so Diffinery can't add itself to the clone picker.");
79
80 command(
81 "diffinery.clone",
82 async () => {
83 if (!gitBase) throw new Error("The editor's Git support is turned off (git.enabled). Turn it on to clone.");
84 const picked = await gitBase.pickRemoteSource({ providerName: remoteSources.name, title: "Clone from Diffinery", urlLabel: "Clone from URL" });
85 const url = typeof picked === "string" ? picked : picked?.url;
86 if (url) await vscode.commands.executeCommand("git.clone", url);
87 },
88 "Cloning from Diffinery failed",
89 );
90
91 void offerInstallerServer(ctx.globalState, () => auth.login(), log).catch(report("Changing the Diffinery server failed"));
92 void checkForUpdates(ctx, auth.server(), version, log, false).catch((e) => log.warn("update check failed", e));
93}
94
95export function deactivate(): void {}