Skip to content
167 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.1import assert from "node:assert/strict";
2import { test } from "node:test";
3
Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page.4import { HEARTBEAT_MS, type DesktopRunnerView, backendOf, diagnosisOf, jobHref, reportDue, reportKey, reportOf } from "./desktop-runner.ts";
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.5
6function view(over: Partial<DesktopRunnerView> = {}): DesktopRunnerView {
7 return {
8 diagnosis: { code: "no_docker", tone: "problem", title: "Not running: Docker isn't running", cause: "Docker isn't running.", fix: "Start Docker Desktop.", actions: [{ id: "open_docker", label: "Open Docker" }] },
9 closed: { code: "off", tone: "off", title: "g1t is closed on build-box", cause: null, fix: "Open g1t on build-box.", actions: [] },
10 running: false,
11 version: "0.2.3",
12 backend: "docker",
13 workspace: "acme",
14 workspaces: [{ slug: "acme", name: "Acme" }],
15 enabled: true,
16 paused: false,
17 link: "https://g1t.sh/acme/-/runners",
18 machine: "build-box",
19 platform: "windows",
20 log: ["2026-10-12T08:00:00Z a line"],
21 ...over,
22 };
23}
24
25test("a report carries the app's words, without its buttons or log", () => {
26 const report = reportOf(view());
27 assert.deepEqual(report, {
28 workspace: "acme",
29 machine: "build-box",
30 backend: "docker",
31 version: "0.2.3",
32 diagnosis: { code: "no_docker", tone: "problem", title: "Not running: Docker isn't running", cause: "Docker isn't running.", fix: "Start Docker Desktop." },
33 closed: { code: "off", tone: "off", title: "g1t is closed on build-box", cause: null, fix: "Open g1t on build-box." },
34 });
35 assert.equal(reportOf(view({ workspace: null })), null);
36});
37
38test("a report goes when what the page shows changes, and as a heartbeat", () => {
39 const report = reportOf(view())!;
40 const at = 1_000_000;
41 assert.equal(reportDue(null, report, at), true);
42 const last = { key: reportKey(report), at };
43 assert.equal(reportDue(last, report, at + 1000), false);
44 assert.equal(reportDue(last, report, at + HEARTBEAT_MS), true);
45 const busy = reportOf(view({ diagnosis: { ...view().diagnosis, code: "busy", tone: "working", title: "Running Build (acme/web)" } }))!;
46 assert.equal(reportDue(last, busy, at + 1000), true);
47});
48
Merge the desktop app's sandbox: its default runner now starts g1t-runner's sandbox on Windows, macOS and Linux, and says per kind of job (Linux, Windows) what it is doing or why it cannot and how to fix it.49test("a sandboxed desktop reports each kind of job, without its buttons", () => {
50 const linux = { id: "linux", label: "Linux jobs", tone: "ok" as const, title: "Waiting for jobs", cause: null, fix: null, actions: [] };
51 const windows = {
52 id: "windows",
53 label: "Windows jobs",
54 tone: "off" as const,
55 title: "Not available",
56 cause: "Windows Sandbox is off, or this edition of Windows has none: Home doesn't.",
57 fix: "Linux jobs still run.",
58 actions: [{ id: "windows_features", label: "Open Windows features" }],
59 };
60 const sandboxed = view({ backend: "sandboxed", diagnosis: { code: "idle", tone: "ok", title: "Waiting for jobs", cause: null, fix: null, actions: [], capabilities: [linux, windows] } });
61 const report = reportOf(sandboxed)!;
62 assert.deepEqual(report.diagnosis.capabilities, [
63 { id: "linux", label: "Linux jobs", tone: "ok", title: "Waiting for jobs", cause: null, fix: null },
64 { id: "windows", label: "Windows jobs", tone: "off", title: "Not available", cause: windows.cause, fix: "Linux jobs still run." },
65 ]);
66 // One kind of job changing is a report due.
67 const last = { key: reportKey(report), at: 0 };
68 const running = reportOf(view({ ...sandboxed, diagnosis: { ...sandboxed.diagnosis, capabilities: [linux, { ...windows, tone: "ok", title: "Waiting for jobs", cause: null, fix: null }] } }))!;
69 assert.equal(reportDue(last, running, 1000), true);
70 // What the server keeps: only kinds of job that read, and none for a desktop without any.
71 const kept = diagnosisOf({ ...report.diagnosis, capabilities: [...report.diagnosis.capabilities!, { id: "x", tone: "loud" }, "linux"] })!;
72 assert.equal(kept.capabilities?.length, 2);
73 assert.equal(diagnosisOf({ code: "idle", tone: "ok", title: "Waiting for jobs" })!.capabilities, undefined);
74 assert.equal(reportOf(view())!.diagnosis.capabilities, undefined);
75});
76
Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page.77test("each kind of job reports its state, whether the person stopped it, and its job, without what only works here", () => {
78 const linux = {
79 id: "linux",
80 label: "Linux jobs",
81 tone: "working" as const,
82 title: "Running a job",
83 cause: null,
84 fix: null,
85 state: "busy" as const,
86 about: "A Linux VM of its own, each job in a fresh container",
87 enabled: true,
88 job: { kind: "workflow", name: "build", repo: "acme/web", link: "https://g1t.sh/acme/web/actions" },
89 runner: "build-box-linux",
90 isolation: "nested-container",
91 version: "0.3.0",
92 available: true,
93 can_toggle: true,
94 progress: null,
95 actions: [],
96 };
97 const windows = {
98 id: "windows",
99 label: "Windows jobs",
100 tone: "off" as const,
101 title: "Stopped",
102 cause: "You stopped Windows jobs on this computer. They stay stopped until you start them.",
103 fix: null,
104 state: "stopped" as const,
105 enabled: false,
106 can_toggle: true,
107 actions: [],
108 };
109 const sandboxed = view({
110 backend: "sandboxed",
111 diagnosis: { code: "busy", tone: "working", title: "Running build (acme/web)", cause: null, fix: null, actions: [], capabilities: [linux, windows], in_capabilities: true },
112 });
113 const report = reportOf(sandboxed)!;
114 assert.equal(report.diagnosis.in_capabilities, true);
115 assert.deepEqual(report.diagnosis.capabilities![0], {
116 id: "linux",
117 label: "Linux jobs",
118 tone: "working",
119 title: "Running a job",
120 cause: null,
121 fix: null,
122 state: "busy",
123 about: "A Linux VM of its own, each job in a fresh container",
124 enabled: true,
125 job: { kind: "workflow", name: "build", repo: "acme/web" },
126 runner: "build-box-linux",
127 isolation: "nested-container",
128 version: "0.3.0",
129 });
130 assert.equal(report.diagnosis.capabilities![1].enabled, false);
131 // Starting Windows jobs again is a report due.
132 const last = { key: reportKey(report), at: 0 };
133 const started = reportOf(view({ ...sandboxed, diagnosis: { ...sandboxed.diagnosis, capabilities: [linux, { ...windows, enabled: true, state: "starting", title: "Starting", tone: "working" }] } }))!;
134 assert.equal(reportDue(last, started, 1000), true);
135 // What the server keeps reads back the same, and drops what isn't one of the app's.
136 const kept = diagnosisOf(JSON.parse(JSON.stringify(report.diagnosis)))!;
137 assert.deepEqual(kept, report.diagnosis);
138 const odd = diagnosisOf({ ...report.diagnosis, capabilities: [{ ...report.diagnosis.capabilities![0], state: "dancing", enabled: "yes", job: { name: "x" } }] })!;
139 assert.equal(odd.capabilities![0].state, undefined);
140 assert.equal(odd.capabilities![0].enabled, undefined);
141 assert.equal(odd.capabilities![0].job, undefined);
142 // Only a diagnosis with kinds of job says they say it all.
143 assert.equal(diagnosisOf({ code: "idle", tone: "ok", title: "Waiting for jobs", in_capabilities: true })!.in_capabilities, undefined);
144});
145
146test("a kind of job's work links to its run when the runner says which", () => {
147 const capability = { runner: "build-box-linux", job: { kind: "workflow", name: "build", repo: "acme/web" } };
148 const runners = [{ name: "build-box-linux", work: { kind: "workflow" as const, id: "job_1", name: "build", repo: "acme/web", runId: "run_9", startedAt: null } }];
149 assert.equal(jobHref(capability, runners), "/acme/web/actions/runs/run_9");
150 assert.equal(jobHref(capability, []), "/acme/web/actions");
151 assert.equal(jobHref({ runner: null, job: { kind: "agent", name: "fix", repo: "acme/web" } }, runners), "/acme/web/agents");
152 assert.equal(jobHref({ runner: "x", job: null }, runners), null);
153});
154
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.155test("only a diagnosis and a backend the app makes are kept", () => {
156 assert.deepEqual(diagnosisOf({ code: "idle", tone: "ok", title: "Waiting for jobs", cause: "Running sandboxed.", extra: 1 }), {
157 code: "idle",
158 tone: "ok",
159 title: "Waiting for jobs",
160 cause: "Running sandboxed.",
161 fix: null,
162 });
163 assert.equal(diagnosisOf({ code: "idle", tone: "loud", title: "x" }), null);
164 assert.equal(diagnosisOf("idle"), null);
165 assert.equal(backendOf("host"), "host");
166 assert.equal(backendOf("vm"), null);
167});

This file's history is long; its oldest lines are credited to the oldest commit read.