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.

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