@feigi/fleet-ctl 3.18.10 → 3.18.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/citation-sweep-prose.test.mjs +1 -1
- package/scripts/claim-lifecycle.test.mjs +2 -1
- package/scripts/ledger.mjs +143 -147
- package/scripts/member-record.mjs +51 -53
- package/scripts/reap.test.mjs +19 -13
- package/scripts/release-ticket.test.mjs +9 -7
- package/scripts/worktree-porcelain.mjs +35 -0
- package/scripts/worktree-porcelain.test.mjs +102 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// worktree-porcelain.mjs against real `git worktree list --porcelain` output.
|
|
2
|
+
// Zero deps: `node --test plugin/scripts/worktree-porcelain.test.mjs`.
|
|
3
|
+
|
|
4
|
+
import { test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { hasAttribute, worktreeNames } from "./worktree-porcelain.mjs";
|
|
11
|
+
|
|
12
|
+
const ENV = {
|
|
13
|
+
...process.env,
|
|
14
|
+
GIT_DIR: undefined,
|
|
15
|
+
GIT_WORK_TREE: undefined,
|
|
16
|
+
GIT_AUTHOR_NAME: "t",
|
|
17
|
+
GIT_AUTHOR_EMAIL: "t@example.com",
|
|
18
|
+
GIT_COMMITTER_NAME: "t",
|
|
19
|
+
GIT_COMMITTER_EMAIL: "t@example.com",
|
|
20
|
+
GIT_CONFIG_GLOBAL: "/dev/null",
|
|
21
|
+
GIT_CONFIG_SYSTEM: "/dev/null",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const git = (cwd, ...args) =>
|
|
25
|
+
execFileSync("git", args, { cwd, env: ENV, encoding: "utf8" }).trim();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A checkout on `main` whose parent directories spell the attributes the guards
|
|
29
|
+
* look for (`locked`, `prunable`, `detached`) and some of the names they look
|
|
30
|
+
* for (`feature/merged`, `79-brief`), the way an operator's TMPDIR can.
|
|
31
|
+
*/
|
|
32
|
+
function repoUnder(t) {
|
|
33
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "worktree-porcelain-")));
|
|
34
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
35
|
+
const w = join(root, "locked", "prunable", "detached", "feature", "merged", "79-brief", "w");
|
|
36
|
+
mkdirSync(w, { recursive: true });
|
|
37
|
+
execFileSync("git", ["-c", "init.defaultBranch=main", "init", "-q", w], { env: ENV });
|
|
38
|
+
git(w, "commit", "-q", "--allow-empty", "-m", "root");
|
|
39
|
+
return w;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const listing = (w) => git(w, "worktree", "list", "--porcelain");
|
|
43
|
+
|
|
44
|
+
test("a name or attribute that is only in a worktree's parent directories is not reported", (t) => {
|
|
45
|
+
const w = repoUnder(t);
|
|
46
|
+
git(w, "worktree", "add", "-q", "-b", "fix/9-x", join(w, ".worktrees", "9-x"), "main");
|
|
47
|
+
const l = listing(w);
|
|
48
|
+
for (const word of ["locked", "prunable", "detached", "feature/merged", "79-brief"]) {
|
|
49
|
+
assert.ok(l.includes(word), `fixture: the listing must carry ${word} in a path: ${l}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
assert.deepEqual(worktreeNames(l), ["w", "main", "9-x", "fix/9-x"]);
|
|
53
|
+
for (const attr of ["locked", "prunable", "detached"]) {
|
|
54
|
+
assert.equal(hasAttribute(l, attr), false, `${attr} is only a parent directory here: ${l}`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("a worktree's own basename, branch and attributes are reported", (t) => {
|
|
59
|
+
const w = repoUnder(t);
|
|
60
|
+
const brief = join(w, ".worktrees", "79-brief");
|
|
61
|
+
git(w, "worktree", "add", "-q", "-b", "feature/merged", brief, "main");
|
|
62
|
+
const lone = join(w, ".worktrees", "lone");
|
|
63
|
+
git(w, "worktree", "add", "-q", "--detach", lone, "main");
|
|
64
|
+
|
|
65
|
+
assert.deepEqual(worktreeNames(listing(w)), ["w", "main", "79-brief", "feature/merged", "lone"]);
|
|
66
|
+
assert.equal(hasAttribute(listing(w), "detached"), true, "the bare-word form");
|
|
67
|
+
|
|
68
|
+
git(w, "worktree", "lock", brief, "--reason", "held by a review");
|
|
69
|
+
assert.match(listing(w), /^locked held by a review$/m, "fixture: a reason follows the word on its line");
|
|
70
|
+
assert.equal(hasAttribute(listing(w), "locked"), true, "the word-and-reason form");
|
|
71
|
+
git(w, "worktree", "unlock", brief);
|
|
72
|
+
git(w, "worktree", "lock", brief);
|
|
73
|
+
assert.equal(hasAttribute(listing(w), "locked"), true);
|
|
74
|
+
git(w, "worktree", "unlock", brief);
|
|
75
|
+
assert.equal(hasAttribute(listing(w), "locked"), false);
|
|
76
|
+
|
|
77
|
+
rmSync(lone, { recursive: true, force: true });
|
|
78
|
+
assert.equal(hasAttribute(listing(w), "prunable"), true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("a lock reason that spells a guard's prefix is not a name", (t) => {
|
|
82
|
+
const w = repoUnder(t);
|
|
83
|
+
const a = join(w, ".worktrees", "a");
|
|
84
|
+
git(w, "worktree", "add", "-q", "-b", "fix/a", a, "main");
|
|
85
|
+
git(w, "worktree", "lock", a, "--reason", "worktree on usb");
|
|
86
|
+
const b = join(w, ".worktrees", "b");
|
|
87
|
+
git(w, "worktree", "add", "-q", "-b", "fix/b", b, "main");
|
|
88
|
+
git(w, "worktree", "lock", b, "--reason", "branch refs/heads/held");
|
|
89
|
+
const c = join(w, ".worktrees", "c");
|
|
90
|
+
git(w, "worktree", "add", "-q", "-b", "fix/c", c, "main");
|
|
91
|
+
git(w, "worktree", "lock", c, "--reason", "see refs/heads/held");
|
|
92
|
+
const l = listing(w);
|
|
93
|
+
for (const reason of ["worktree on usb", "branch refs/heads/held", "see refs/heads/held"]) {
|
|
94
|
+
assert.match(l, new RegExp(`^locked ${reason}$`, "m"), `fixture: the reason is on its own line: ${l}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
assert.deepEqual(worktreeNames(l), ["w", "main", "a", "fix/a", "b", "fix/b", "c", "fix/c"]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("an attribute git never prints is refused, not answered false", () => {
|
|
101
|
+
assert.throws(() => hasAttribute("worktree /x\nlocked\n", "lock"), /unknown attribute "lock"/);
|
|
102
|
+
});
|