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.

72 lines · 2.8 KBRaw History
1import * as vscode from "vscode";
2import type { DiffineryAuth } from "./auth";
3import type { RemoteSource, RemoteSourceAction, RemoteSourceProvider } from "./typings/git";
4import { dedupe, matches, repoRef, type RepoJson } from "./util";
5
6/** How long the list of your repositories is reused while you type in the picker. */
7const MINE_TTL_MS = 60_000;
8
9/**
10 * "Clone from Diffinery" in the editor's clone picker (and "Add remote from Diffinery"): your repositories and
11 * your organizations', most recently pushed first, and as you type, any repository you can read that matches.
12 */
13export class RemoteSources implements RemoteSourceProvider {
14 readonly name = "Diffinery";
15 readonly icon = "diffinery-logo";
16 readonly supportsQuery = true;
17 readonly placeholder = "Repository name or owner/name (type to search)";
18
19 private mine: RepoJson[] = [];
20 private mineAt = 0;
21
22 constructor(private readonly auth: DiffineryAuth) {}
23
24 async getRemoteSources(query?: string): Promise<RemoteSource[]> {
25 const api = await this.auth.api(true);
26 if (!api) return [];
27 const q = (query ?? "").trim();
28 if (!q || Date.now() - this.mineAt > MINE_TTL_MS) {
29 this.mine = await api.myRepos();
30 this.mineAt = Date.now();
31 }
32 if (!q) return this.mine.map(source);
33 const ref = repoRef(api.server, q);
34 const [direct, found] = await Promise.all([ref ? api.repo(ref).then((r) => [r], () => []) : Promise.resolve([]), api.search(q).catch(() => [] as RepoJson[])]);
35 return dedupe(direct, this.mine.filter((r) => matches(r, q)), found).map(source);
36 }
37
38 async getBranches(url: string): Promise<string[]> {
39 const api = await this.auth.api(true);
40 const ref = api && repoRef(api.server, url);
41 if (!api || !ref) return [];
42 const [branches, repo] = await Promise.all([api.branches(ref), api.repo(ref)]);
43 const main = repo.default_branch;
44 return branches.sort((a, b) => (a === main ? -1 : b === main ? 1 : 0));
45 }
46
47 getRemoteSourceActions(url: string): RemoteSourceAction[] {
48 let server: string;
49 try {
50 server = this.auth.server();
51 } catch {
52 return [];
53 }
54 const ref = repoRef(server, url);
55 if (!ref || !/^[a-z][a-z0-9+.-]*:\/\//i.test(url)) return [];
56 return [
57 {
58 label: "Open on Diffinery",
59 icon: "link-external",
60 run: (branch: string) => {
61 const base = `${server}/${ref.owner}/${ref.repo}`;
62 void vscode.env.openExternal(vscode.Uri.parse(branch ? `${base}/tree/${branch.split("/").map(encodeURIComponent).join("/")}` : base));
63 },
64 },
65 ];
66 }
67}
68
69function source(r: RepoJson): RemoteSource {
70 const tags = [r.private ? "Private" : "Public", r.archived ? "archived" : "", r.fork ? "fork" : ""].filter(Boolean);
71 return { name: r.full_name, icon: "diffinery-logo", description: tags.join(" · "), detail: r.description || undefined, url: r.clone_url };
72}