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.

54 lines · 3.0 KBRaw History
1import { createHash } from "node:crypto";
2import * as vscode from "vscode";
3import { compareVersions } from "./util";
4
5const DAY_MS = 86_400_000;
6const LAST_CHECK = "diffinery.lastUpdateCheck";
7
8interface Latest {
9 version: string;
10 url: string;
11 sha256: string;
12}
13
14/**
15 * The extension is installed from the Diffinery server rather than a marketplace, so it updates itself from there:
16 * once a day (or when asked) it reads /-/vscode/version.json and offers to install a newer build, checked against
17 * the digest the server published.
18 */
19export async function checkForUpdates(ctx: vscode.ExtensionContext, server: string, current: string, log: vscode.LogOutputChannel, manual: boolean): Promise<void> {
20 if (!manual) {
21 if (!vscode.workspace.getConfiguration("diffinery").get<boolean>("checkForUpdates", true)) return;
22 const last = ctx.globalState.get<number>(LAST_CHECK, 0);
23 if (Date.now() - last < DAY_MS) return;
24 }
25 await ctx.globalState.update(LAST_CHECK, Date.now());
26 let latest: Latest;
27 try {
28 const res = await fetch(`${server}/-/vscode/version.json`, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(15_000) });
29 if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
30 latest = (await res.json()) as Latest;
31 } catch (e) {
32 log.warn("checking for a newer Diffinery extension failed", e);
33 if (manual) void vscode.window.showWarningMessage(`Couldn't check ${server} for a newer Diffinery extension: ${e instanceof Error ? e.message : e}`);
34 return;
35 }
36 if (compareVersions(latest.version, current) <= 0) {
37 if (manual) void vscode.window.showInformationMessage(`The Diffinery extension is up to date (${current}).`);
38 return;
39 }
40 const pick = await vscode.window.showInformationMessage(`Diffinery extension ${latest.version} is available (you have ${current}).`, "Install", "Later");
41 if (pick !== "Install") return;
42 await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: `Installing Diffinery extension ${latest.version}…` }, async () => {
43 const res = await fetch(new URL(latest.url, server), { signal: AbortSignal.timeout(60_000) });
44 if (!res.ok) throw new Error(`Downloading the extension failed: ${res.status} ${res.statusText}`);
45 const bytes = new Uint8Array(await res.arrayBuffer());
46 if (createHash("sha256").update(bytes).digest("hex") !== latest.sha256) throw new Error("The downloaded extension didn't match the published digest; nothing was installed.");
47 const file = vscode.Uri.joinPath(ctx.globalStorageUri, `diffinery-${latest.version}.vsix`);
48 await vscode.workspace.fs.createDirectory(ctx.globalStorageUri);
49 await vscode.workspace.fs.writeFile(file, bytes);
50 await vscode.commands.executeCommand("workbench.extensions.installExtension", file);
51 });
52 const reload = await vscode.window.showInformationMessage(`Diffinery extension ${latest.version} is installed. Reload the window to use it.`, "Reload Window");
53 if (reload) void vscode.commands.executeCommand("workbench.action.reloadWindow");
54}