Skip to content
78 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.

Put the stylesheet at the top of the head, before React Router's preloads1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { detectStacks, freeBranch, starterDescription, starterWorkflow } from "./starter-workflow.ts";
5
6test("each stack is found by what is at the root", () => {
7 const cases: [string[], string[]][] = [
8 [["package.json", "package-lock.json"], ["npm"]],
9 [["package.json", "pnpm-lock.yaml"], ["pnpm"]],
10 [["package.json", "yarn.lock"], ["yarn"]],
11 [["package.json", "bun.lock"], ["bun"]],
12 [["Cargo.toml", "Cargo.lock", "src"], ["rust"]],
13 [["go.mod", "main.go"], ["go"]],
14 [["pyproject.toml", "uv.lock"], ["uv"]],
15 [["requirements.txt"], ["python"]],
16 [["Gemfile", "Rakefile"], ["ruby"]],
17 [["pom.xml"], ["maven"]],
18 [["build.gradle.kts", "gradlew"], ["gradle"]],
19 [["App.sln"], ["dotnet"]],
20 [["Makefile", "main.c"], ["make"]],
21 [["README.md"], []],
22 // A monorepo: Node and Rust, each with a job.
23 [["package.json", "package-lock.json", "Cargo.toml", "Makefile"], ["npm", "rust"]],
24 ];
25 for (const [names, stacks] of cases) assert.deepEqual(detectStacks({ names }), stacks, names.join(" "));
26});
27
28test("a Node project's workflow runs the scripts it has", () => {
29 const { yaml, stacks } = starterWorkflow(
30 {
31 names: ["package.json", "package-lock.json"],
32 packageJson: JSON.stringify({ scripts: { test: "node --test", build: "tsc", start: "node ." } }),
33 },
34 "main",
35 );
36 assert.deepEqual(stacks, ["npm"]);
37 assert.match(yaml, /^name: CI$/m);
38 assert.match(yaml, /^ {2}pull_request:$/m);
39 assert.match(yaml, /^ {4}branches: \["main"\]$/m);
40 assert.match(yaml, /^ {2}merge_group:$/m);
41 assert.match(yaml, /- run: npm ci/);
42 assert.match(yaml, /- run: npm run build/);
43 assert.match(yaml, /- run: npm run test/);
44 assert.doesNotMatch(yaml, /npm run lint/);
45 assert.match(yaml, / {8}with:\n {10}node-version: 24\n {10}cache: npm/);
46});
47
48test("an unreadable package.json runs the usual scripts only if present", () => {
49 const { yaml } = starterWorkflow({ names: ["package.json"], packageJson: "{ not json" }, "trunk");
50 assert.match(yaml, /- run: npm install/);
51 assert.match(yaml, /- run: npm run test --if-present/);
52 assert.match(yaml, /branches: \["trunk"\]/);
53});
54
55test("Rust, Go and an unknown project each get a workflow", () => {
56 assert.match(starterWorkflow({ names: ["Cargo.toml"] }, "main").yaml, /cargo test --all-targets/);
57 assert.match(starterWorkflow({ names: ["go.mod"] }, "main").yaml, /go-version-file: go.mod[\s\S]*go test \.\/\.\.\./);
58 const unknown = starterWorkflow({ names: ["README.md"] }, "main");
59 assert.deepEqual(unknown.stacks, []);
60 assert.match(unknown.yaml, /Add the commands that build and test this project/);
61 assert.match(starterDescription([], "main"), /placeholder/);
62 assert.match(starterDescription(["rust"], "main"), /found Rust/);
63});
64
65test("the workflow is well-formed: one job per stack, steps under each", () => {
66 const { yaml } = starterWorkflow({ names: ["package.json", "pnpm-lock.yaml", "Cargo.toml"], packageJson: '{"scripts":{"lint":"x"}}' }, "main");
67 const jobs = yaml.split("\n").filter((line) => /^ {2}[a-z]+:$/.test(line) && !line.includes("pull_request") && !line.includes("push") && !line.includes("merge_group"));
68 assert.deepEqual(jobs, [" node:", " rust:"]);
69 // Every line is indented by an even number of spaces, as YAML here wants.
70 for (const line of yaml.split("\n")) assert.equal((line.match(/^ */)?.[0].length ?? 0) % 2, 0, line);
71 assert.match(yaml, /pnpm\/action-setup@v4[\s\S]*pnpm install --frozen-lockfile[\s\S]*pnpm run lint/);
72});
73
74test("a branch name is found that is free", () => {
75 assert.equal(freeBranch([]), "add-ci");
76 assert.equal(freeBranch(["main", "add-ci"]), "add-ci-2");
77 assert.equal(freeBranch(["add-ci", "add-ci-2"]), "add-ci-3");
78});

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