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