Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
143 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 branch 'mirroring' into artifacts-mode1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { HandbackPlan, RepoMirror } from "@g1t/contracts";
5
6import { DEFAULT_MIRROR_SETTINGS, TAKE_OVER_AFTER_MINUTES, mirrorWritable } from "../../../../packages/contracts/src/mirrors.ts";
7
8import {
9 clampMinutes,
10 decisionsFrom,
11 handBackReady,
12 mirrorBadge,
13 mirrorBanner,
14 mirrorReason,
15 mirrorSettingsFrom,
16 shortSha,
17 TAKE_OVER_MINUTES,
18 workflowReason,
19 writable,
20} from "./mirror.ts";
21
22const mirror = (state: RepoMirror["state"]): RepoMirror => ({
23 state,
24 remote: "github.com/acme/web",
25 url: "https://github.com/acme/web",
26 since: "2026-10-08T10:00:00Z",
27});
28const repo = (state: RepoMirror["state"] | null) => ({ namespace: "acme", name: "web", mirror: state ? mirror(state) : null });
29const leader = (reachable: boolean) => ({ repoId: "r", role: "leader" as const, name: "github.com/acme/web", state: "standby" as const, reachable });
30const follower = (name: string) => ({ repoId: "r", role: "follower" as const, name, state: "following" as const, reachable: true });
31
32test("the copies agree with the contracts", () => {
33 for (const state of [null, "standby", "ci", "takeover", "handing_back"] as const) {
34 assert.equal(writable(state ? mirror(state) : null), mirrorWritable(state ? mirror(state) : null));
35 }
36 assert.deepEqual([...TAKE_OVER_MINUTES], [...TAKE_OVER_AFTER_MINUTES]);
37});
38
39test("a read-only mirror says why its buttons are off, and where to take over", () => {
40 assert.equal(
41 mirrorReason(repo("standby")),
42 "acme/web is a mirror of github.com/acme/web. Work happens there until someone takes over in Settings → Mirroring.",
43 );
44 assert.equal(mirrorReason(repo("ci")), mirrorReason(repo("standby")));
45 assert.equal(mirrorReason(repo("handing_back")), "acme/web is handing back to github.com/acme/web; it takes changes again when that is done.");
46 assert.equal(mirrorReason(repo("takeover")), null);
47 assert.equal(mirrorReason(repo(null)), null);
48 assert.equal(mirrorReason(null), null);
49});
50
51test("workflows run in CI failover and takeovers, not while standing by or handing back", () => {
52 assert.equal(workflowReason(repo("ci")), null);
53 assert.equal(workflowReason(repo("takeover")), null);
54 assert.ok(workflowReason(repo("standby")));
55 assert.ok(workflowReason(repo("handing_back")));
56 assert.equal(workflowReason(repo(null)), null);
57});
58
59test("the badge says what the repository is to its remotes", () => {
60 assert.deepEqual(mirrorBadge(mirror("standby"), []), { label: "Mirror of github.com/acme/web", tone: "neutral", more: 0 });
61 assert.equal(mirrorBadge(mirror("ci"), [])?.tone, "info");
62 assert.equal(mirrorBadge(mirror("takeover"), [])?.label, "Taken over from github.com/acme/web");
63 assert.equal(mirrorBadge(mirror("handing_back"), [])?.label, "Handing back to github.com/acme/web");
64 assert.deepEqual(mirrorBadge(null, [follower("github.com/acme/web"), follower("git.example.com/web")]), {
65 label: "Mirrored to github.com/acme/web",
66 tone: "accent",
67 more: 1,
68 });
69 assert.equal(mirrorBadge(null, []), null);
70});
71
72test("a mirror standing by whose remote answers needs no banner", () => {
73 assert.equal(mirrorBanner(mirror("standby"), [leader(true)]), null);
74 assert.equal(mirrorBanner(mirror("standby"), []), null);
75 assert.equal(mirrorBanner(mirror("standby"), [leader(false)]), "unreachable");
76 assert.equal(mirrorBanner(mirror("ci"), [leader(false)]), "unreachable");
77 assert.equal(mirrorBanner(mirror("ci"), [leader(true)]), "ci");
78 assert.equal(mirrorBanner(mirror("takeover"), [leader(false)]), "takeover");
79 assert.equal(mirrorBanner(mirror("handing_back"), []), "handing_back");
80 assert.equal(mirrorBanner(null, [follower("x")]), null);
81});
82
83test("hand-back decisions are read from their fields, and only known ones", () => {
84 const form = new FormData();
85 form.set("intent", "hand-back");
86 form.set("decision:main", "keep_ours");
87 form.set("decision:release/1.x", "pull_request");
88 form.set("decision:dev", "delete_everything");
89 assert.deepEqual(decisionsFrom(form.entries()), { main: "keep_ours", "release/1.x": "pull_request" });
90});
91
92test("a hand-back is ready once the remote answers and every diverged branch has a decision", () => {
93 const plan: HandbackPlan = {
94 reachable: true,
95 ready: false,
96 refs: [
97 { ref: "main", base: "a", ours: "b", theirs: "c", action: "diverged", decision: null },
98 { ref: "dev", base: "a", ours: "b", theirs: "a", action: "push", decision: null },
99 ],
100 };
101 assert.equal(handBackReady(plan, {}), false);
102 assert.equal(handBackReady(plan, { main: "keep_theirs" }), true);
103 assert.equal(handBackReady({ ...plan, reachable: false }, { main: "keep_theirs" }), false);
104});
105
106test("settings come back from the form, keeping what it did not carry", () => {
107 const leaderForm = new FormData();
108 leaderForm.set("kind", "leader");
109 leaderForm.set("notify", "inbox");
110 leaderForm.set("autoTakeOver", "on");
111 leaderForm.set("takeOverAfter", "2");
112 leaderForm.set("githubWorkflows", "on");
113 const current = { ...DEFAULT_MIRROR_SETTINGS, remotePushes: "overwrite" as const };
114 assert.deepEqual(mirrorSettingsFrom(leaderForm, current), {
115 notify: "inbox",
116 takeOverAfter: 5,
117 handBack: "ask",
118 keepCiWarm: false,
119 githubWorkflows: true,
120 holdDeploys: false,
A mirror of a GitHub repository whose workflows g1t runs, in CI failover, during a takeover or with CI kept warm, now shows their results on GitHub: each status g1t sets on a commit, a workflow run's or a deploy's, goes to the commit there through g1t's GitHub App as a commit status named under g1t / (g1t / CI / push; g1t / deploy stays as it is), with its state, its description and a link back to g1t, once each time it changes, and commits made on g1t during a takeover are skipped. It is on by default and turned off with Report checks to GitHub in the mirror's settings, or report_checks through the API. The app needs the Commit statuses permission; until an installation has it, the mirror's settings say g1t can't report checks to GitHub and why, and the line goes once a status gets through. Statuses set by workflow runs, deploys and g1t's other parts are now published as status.created, as ones set through the API were, so webhooks hear them too. The mirroring, actions, GitHub, checks, webhooks and self-hosting guides say so.121 reportChecks: true,
Merge branch 'mirroring' into artifacts-mode122 remotePushes: "overwrite",
123 });
A mirror of a GitHub repository whose workflows g1t runs, in CI failover, during a takeover or with CI kept warm, now shows their results on GitHub: each status g1t sets on a commit, a workflow run's or a deploy's, goes to the commit there through g1t's GitHub App as a commit status named under g1t / (g1t / CI / push; g1t / deploy stays as it is), with its state, its description and a link back to g1t, once each time it changes, and commits made on g1t during a takeover are skipped. It is on by default and turned off with Report checks to GitHub in the mirror's settings, or report_checks through the API. The app needs the Commit statuses permission; until an installation has it, the mirror's settings say g1t can't report checks to GitHub and why, and the line goes once a status gets through. Statuses set by workflow runs, deploys and g1t's other parts are now published as status.created, as ones set through the API were, so webhooks hear them too. The mirroring, actions, GitHub, checks, webhooks and self-hosting guides say so.124 const githubForm = new FormData();
125 githubForm.set("kind", "leader");
126 githubForm.set("provider", "github");
127 assert.equal(mirrorSettingsFrom(githubForm, DEFAULT_MIRROR_SETTINGS).reportChecks, false, "a GitHub mirror's box left clear turns it off");
128 githubForm.set("reportChecks", "on");
129 assert.equal(mirrorSettingsFrom(githubForm, { ...DEFAULT_MIRROR_SETTINGS, reportChecks: false }).reportChecks, true);
Merge branch 'mirroring' into artifacts-mode130 const followerForm = new FormData();
131 followerForm.set("kind", "follower");
132 followerForm.set("remotePushes", "overwrite");
133 assert.deepEqual(mirrorSettingsFrom(followerForm, DEFAULT_MIRROR_SETTINGS), { ...DEFAULT_MIRROR_SETTINGS, remotePushes: "overwrite" });
134});
135
136test("minutes and commits are shown within bounds", () => {
137 assert.equal(clampMinutes(3), 5);
138 assert.equal(clampMinutes(90.4), 90);
139 assert.equal(clampMinutes(99999), 1440);
140 assert.equal(clampMinutes(Number.NaN), 5);
141 assert.equal(shortSha("0123456789abcdef"), "0123456");
142 assert.equal(shortSha(null), "—");
143});

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