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.

175 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { BumpArgs } from "@g1t/contracts";
5
6import { bumpEnv, bumpProblem, bumpSandboxName, isBranchName, isSystem, systemActor } from "./bump.ts";
7
8const PREFIX = "g1t/security/";
9
10const args: BumpArgs = {
11 repo: { namespace: "Acme", name: "site" },
12 ecosystem: "npm",
13 package: "@babel/traverse",
14 version: "7.23.2",
15 lockfiles: ["package-lock.json", "web/package-lock.json"],
16 branch: "g1t/security/babel-traverse-7.23.2",
17 message: "Update @babel/traverse to 7.23.2",
18};
19
20test("a well-formed update can start", () => {
21 assert.equal(bumpProblem(args, PREFIX), null);
22 for (const ecosystem of ["crates.io", "Go", "PyPI"]) {
23 assert.equal(bumpProblem({ ...args, ecosystem }, PREFIX), null, ecosystem);
24 }
25});
26
27test("the branch must be a security update's", () => {
28 for (const branch of [
29 "main",
30 "g1t/security/",
31 "feature/g1t/security/x",
32 "g1t/security/a..b",
33 "g1t/security/a b",
34 "g1t/security/a:b",
35 "g1t/security/x.lock",
36 ]) {
37 assert.match(bumpProblem({ ...args, branch }, PREFIX) ?? "", /starts with g1t\/security\//, branch);
38 }
39});
40
41test("names, versions and lockfiles are checked before anything starts", () => {
42 assert.match(bumpProblem({ ...args, ecosystem: "RubyGems" }, PREFIX) ?? "", /cannot update RubyGems/);
43 assert.match(bumpProblem({ ...args, package: "--registry=evil" }, PREFIX) ?? "", /package's name/);
44 assert.match(bumpProblem({ ...args, version: "1.0; rm -rf /" }, PREFIX) ?? "", /version/);
45 assert.match(bumpProblem({ ...args, lockfiles: [] }, PREFIX) ?? "", /between 1 and/);
46 assert.match(bumpProblem({ ...args, lockfiles: ["../Cargo.lock"] }, PREFIX) ?? "", /not a path inside/);
47 assert.match(bumpProblem({ ...args, lockfiles: ["/etc/Cargo.lock"] }, PREFIX) ?? "", /not a path inside/);
48 assert.match(bumpProblem({ ...args, repo: { namespace: "", name: "site" } }, PREFIX) ?? "", /repository/);
49 assert.match(bumpProblem(null, PREFIX) ?? "", /arguments/);
50});
51
52test("g1t acts as itself, a member of the workspace", () => {
53 const actor = systemActor("Acme");
54 assert.deepEqual(actor, { id: "g1t", username: "g1t", kind: "system", verified: true, workspaces: [{ slug: "acme", role: "member" }] });
55 assert.equal(isSystem(actor), true);
56 assert.equal(isSystem({ id: "usr_1", username: "ada" }), false);
57 assert.equal(isSystem(null), false);
58});
59
60test("the sandbox is given what bump mode reads", () => {
61 const env = bumpEnv(args, "main", "g1t_token");
62 assert.deepEqual(env, {
63 MODE: "bump",
64 G1T_USER: "acme",
65 G1T_TOKEN: "g1t_token",
66 GIT_REMOTE: "https://g1t.sh/Acme/site.git",
67 GIT_BRANCH_BASE: "main",
68 GIT_BRANCH: "g1t/security/babel-traverse-7.23.2",
69 BUMP_KIND: "security",
70 BUMP_ECOSYSTEM: "npm",
71 BUMP_PACKAGE: "@babel/traverse",
72 BUMP_VERSION: "7.23.2",
73 BUMP_PACKAGES: '[{"package":"@babel/traverse","version":"7.23.2"}]',
74 BUMP_STRATEGY: "increase",
75 BUMP_FORCE: "0",
76 BUMP_REGISTRIES: "[]",
77 BUMP_LOCKFILES: '["package-lock.json","web/package-lock.json"]',
78 COMMIT_MESSAGE: "Update @babel/traverse to 7.23.2",
79 });
80 assert.equal(bumpEnv({ ...args, message: " " }, "main", "t").COMMIT_MESSAGE, "Update @babel/traverse to 7.23.2");
81 assert.equal(bumpSandboxName(args), "bump:acme/site:g1t/security/babel-traverse-7.23.2");
82});
83
84const update: BumpArgs = {
85 ...args,
86 kind: "version",
87 package: "lodash",
88 version: "4.17.21",
89 packages: [
90 { package: "lodash", version: "4.17.21" },
91 { package: " vitest ", version: "1.6.0" },
92 ],
93 branch: "deps/npm/lodash",
94 message: "Bump lodash and vitest",
95 strategy: "widen",
96 force: true,
97 registries: [{ type: "npm-registry", url: "https://npm.acme.dev/", token: "s3cret", scopes: ["@acme"] }],
98};
99
100test("a version update names any branch git takes", () => {
101 assert.equal(bumpProblem(update, PREFIX), null);
102 for (const branch of ["main", "dependabot/npm/lodash-4.17.21", "deps"]) {
103 assert.equal(bumpProblem({ ...update, branch }, PREFIX), null, branch);
104 }
105 for (const branch of [
106 "",
107 "a b",
108 "a..b",
109 "a~1",
110 "a^",
111 "a:b",
112 "a?",
113 "a*",
114 "a[b",
115 "a\\b",
116 "a@{1}",
117 "-x",
118 "/x",
119 "refs/heads/x",
120 "x/",
121 "x.lock",
122 "x".repeat(201),
123 ]) {
124 assert.match(bumpProblem({ ...update, branch }, PREFIX) ?? "", /not a branch name/, branch);
125 }
126 assert.equal(isBranchName("deps/npm/lodash"), true);
127 assert.equal(isBranchName(undefined), false);
128});
129
130test("a version update's packages, strategy and registries are checked", () => {
131 assert.match(bumpProblem({ ...update, kind: "major" as "version" }, PREFIX) ?? "", /cannot make a major update/);
132 assert.match(bumpProblem({ ...update, package: "" }, PREFIX) ?? "", /A version update needs the package's name/);
133 assert.match(bumpProblem({ ...update, packages: [{ package: "--evil", version: "1" }] }, PREFIX) ?? "", /each package's name/);
134 assert.match(bumpProblem({ ...update, packages: [{ package: "lodash", version: "1 2" }] }, PREFIX) ?? "", /raise lodash to/);
135 const many = Array.from({ length: 51 }, (_, i) => ({ package: `p${i}`, version: "1.0.0" }));
136 assert.match(bumpProblem({ ...update, packages: many }, PREFIX) ?? "", /at most 50 packages/);
137 for (const strategy of ["increase", "increase-if-necessary", "widen", "lockfile-only"]) {
138 assert.equal(bumpProblem({ ...update, strategy }, PREFIX), null, strategy);
139 }
140 assert.match(bumpProblem({ ...update, strategy: "auto" }, PREFIX) ?? "", /not a versioning strategy/);
141 const registry = update.registries![0]!;
142 assert.match(bumpProblem({ ...update, registries: [{ ...registry, type: "maven-repository" }] }, PREFIX) ?? "", /cannot read a maven-repository registry/);
143 for (const url of ["http://npm.acme.dev", "npm.acme.dev", "https://", "https:// x"]) {
144 assert.match(bumpProblem({ ...update, registries: [{ ...registry, url }] }, PREFIX) ?? "", /starts with https/, url);
145 }
146 assert.match(bumpProblem({ ...update, registries: [{ ...registry, token: "x".repeat(2001) }] }, PREFIX) ?? "", /credentials/);
147 assert.match(bumpProblem({ ...update, registries: [{ ...registry, scopes: ["acme"] }] }, PREFIX) ?? "", /not an npm scope/);
148 assert.match(bumpProblem({ ...update, registries: Array.from({ length: 21 }, () => registry) }, PREFIX) ?? "", /at most 20 private registries/);
149 for (const type of ["cargo-registry", "python-index", "goproxy-server"]) {
150 assert.equal(
151 bumpProblem({ ...update, registries: [{ type, url: "https://r.acme.dev/x", username: "u", password: "p", replacesBase: true }] }, PREFIX),
152 null,
153 type,
154 );
155 }
156});
157
158test("a version update's sandbox is told what to raise, how and from where", () => {
159 const env = bumpEnv(update, "main", "t");
160 assert.equal(env.BUMP_KIND, "version");
161 assert.equal(env.GIT_BRANCH, "deps/npm/lodash");
162 assert.equal(env.BUMP_PACKAGE, "lodash");
163 assert.equal(env.BUMP_VERSION, "4.17.21");
164 assert.equal(env.BUMP_PACKAGES, '[{"package":"lodash","version":"4.17.21"},{"package":"vitest","version":"1.6.0"}]');
165 assert.equal(env.BUMP_STRATEGY, "widen");
166 assert.equal(env.BUMP_FORCE, "1");
167 assert.deepEqual(JSON.parse(env.BUMP_REGISTRIES!), update.registries);
168 assert.equal(env.COMMIT_MESSAGE, "Bump lodash and vitest");
169 assert.equal(bumpEnv({ ...update, message: "" }, "main", "t").COMMIT_MESSAGE, "Update lodash and 1 more");
170 const one = bumpEnv({ ...update, packages: [], strategy: undefined, force: undefined, registries: undefined }, "main", "t");
171 assert.equal(one.BUMP_PACKAGES, '[{"package":"lodash","version":"4.17.21"}]');
172 assert.equal(one.BUMP_STRATEGY, "increase");
173 assert.equal(one.BUMP_FORCE, "0");
174 assert.equal(one.BUMP_REGISTRIES, "[]");
175});