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.

170 linesCodeBlameRaw
1import { test } from "node:test";
2import assert from "node:assert/strict";
3
4import { type KindFacts, type RootFiles, detectKind, goFilesToRead, kindSetting, neverRuns, nextSetting, resolveKind, runsSetting } from "./kind.ts";
5
6const root = (text: Record<string, string>, more: Partial<RootFiles> = {}): RootFiles => ({
7 entries: [...Object.keys(text), ...(more.entries ?? [])],
8 text,
9 ...(more.src ? { src: more.src } : {}),
10 ...(more.public ? { public: more.public } : {}),
11});
12
13// psr/log's composer.json, as it is at 3.0.2: no type, an autoload.
14const PSR_LOG = JSON.stringify({
15 name: "psr/log",
16 description: "Common interface for logging libraries",
17 license: "MIT",
18 require: { php: ">=8.0.0" },
19 autoload: { "psr-4": { "Psr\\Log\\": "src" } },
20});
21
22test("a Composer package is a library; a Composer project is an app", () => {
23 assert.deepEqual(detectKind(root({ "composer.json": PSR_LOG }, { entries: ["src/", "README.md"] })), {
24 kind: "library",
25 detail: "composer.json has autoload and no public/index.php.",
26 ecosystem: "composer",
27 });
28 assert.equal(detectKind(root({ "composer.json": '{"type":"library"}' })).kind, "library");
29 assert.equal(detectKind(root({ "composer.json": '{"type":"symfony-bundle"}' })).detail, 'composer.json says "type": "symfony-bundle".');
30 assert.equal(detectKind(root({ "composer.json": '{"type":"project","autoload":{}}' })).kind, "app");
31 // Laravel and the like: no type, but a front controller to serve.
32 const laravel = root({ "composer.json": '{"autoload":{"psr-4":{}}}' }, { entries: ["public/"], public: ["index.php", ".htaccess"] });
33 assert.equal(detectKind(laravel).kind, "app");
34});
35
36test("package.json: an entry point without a start or dev script is a library", () => {
37 assert.equal(detectKind(root({ "package.json": '{"name":"x","main":"dist/index.js","scripts":{"build":"tsc"}}' })).kind, "library");
38 assert.equal(detectKind(root({ "package.json": '{"exports":{".":"./index.js"}}' })).detail, 'package.json has "exports" and no start or dev script.');
39 assert.equal(detectKind(root({ "package.json": '{"main":"index.js","scripts":{"start":"node index.js"}}' })).kind, "app");
40 assert.equal(detectKind(root({ "package.json": '{"main":"index.js","scripts":{"dev":"vite"}}' })).kind, "app");
41 assert.equal(detectKind(root({ "package.json": '{"files":["dist"],"dependencies":{"next":"15"}}' })).detail, "package.json depends on next.");
42 assert.equal(detectKind(root({ "package.json": '{"files":["dist"],"devDependencies":{"@sveltejs/kit":"2"}}' })).kind, "app");
43 // A library built with Vite stays a library; a Vite site has index.html.
44 assert.equal(detectKind(root({ "package.json": '{"main":"dist/x.js","devDependencies":{"vite":"6"}}' })).kind, "library");
45 assert.equal(detectKind(root({ "package.json": '{"main":"x.js","devDependencies":{"vite":"6"}}' }, { entries: ["index.html"] })).kind, "app");
46 // Nothing to tell from.
47 assert.equal(detectKind(root({ "package.json": '{"name":"x"}' })).kind, null);
48 assert.equal(detectKind(root({ "package.json": "not json" })).kind, null);
49});
50
51test("Cargo: a library and no binary is a library", () => {
52 assert.equal(detectKind(root({ "Cargo.toml": '[package]\nname = "x"\n\n[lib]\n' }, { entries: ["src/"], src: ["lib.rs"] })).kind, "library");
53 assert.equal(detectKind(root({ "Cargo.toml": '[package]\nname = "x"\n' }, { entries: ["src/"], src: ["lib.rs"] })).kind, "library");
54 assert.equal(detectKind(root({ "Cargo.toml": "[package]\n[lib]\n" }, { entries: ["src/"], src: ["lib.rs", "main.rs"] })).kind, "app");
55 assert.equal(detectKind(root({ "Cargo.toml": '[package]\n[lib]\n[[bin]]\nname = "x"\n' })).kind, "app");
56});
57
58test("Go: a module with no package main at its root is a library", () => {
59 const lib = root({ "go.mod": "module g1t.sh/acme/log\n", "log.go": "// Package log logs.\npackage log\n" });
60 assert.deepEqual(detectKind(lib), { kind: "library", detail: "go.mod, with no package main at the root.", ecosystem: "go" });
61 const app = root({ "go.mod": "module x\n", "main.go": "package main\n\nfunc main() {}\n" });
62 assert.deepEqual(detectKind(app), { kind: "app", detail: "main.go at the root is package main.", ecosystem: "go" });
63 assert.equal(detectKind(root({ "go.mod": "module x\n" }, { entries: ["cmd/"] })).kind, null);
64 assert.deepEqual(goFilesToRead(["a.go", "a_test.go", "b.go", "c.go", "d.go", "go.mod"]), ["a.go", "b.go", "c.go"]);
65});
66
67test("Python: a build backend and no app framework is a library", () => {
68 const lib = '[build-system]\nrequires = ["hatchling"]\nbuild-backend = "hatchling.build"\n[project]\ndependencies = ["httpx"]\n';
69 assert.equal(detectKind(root({ "pyproject.toml": lib })).kind, "library");
70 const app = '[build-system]\nbuild-backend = "hatchling.build"\n[project]\ndependencies = ["django>=5", "flask-login"]\n';
71 assert.equal(detectKind(root({ "pyproject.toml": app })).detail, "pyproject.toml depends on django.");
72 // A plugin named after a framework is not the framework.
73 assert.equal(detectKind(root({ "pyproject.toml": '[build-system]\nbuild-backend = "x"\ndependencies = ["flask-login"]\n' })).kind, "library");
74});
75
76test("the language's manifest decides before package.json, and a Workers config is always an app", () => {
77 const php = root({ "composer.json": PSR_LOG, "package.json": '{"scripts":{"dev":"vite"}}' });
78 assert.equal(detectKind(php).ecosystem, "composer");
79 assert.equal(detectKind(php).kind, "library");
80 assert.equal(detectKind(root({ "package.json": '{"main":"x.js"}' }, { entries: ["wrangler.jsonc"] })).kind, "app");
81 assert.equal(detectKind(root({}, { entries: ["index.html", "style.css"] })).kind, "app");
82 assert.deepEqual(detectKind(root({}, { entries: ["README.md"] })), { kind: null, detail: "No manifest at the root says what it is.", ecosystem: null });
83});
84
85const auto: KindFacts = { setting: { kind: null, runs: null }, deploymentsOn: false, reportsProduction: false, linkedPackage: null, detected: null };
86
87test("the setting wins over everything", () => {
88 assert.equal(resolveKind({ ...auto, setting: { kind: "app", runs: "g1t" }, linkedPackage: "Composer package psr/log" }).kind, "app");
89 assert.deepEqual(resolveKind({ ...auto, setting: { kind: "library", runs: null }, deploymentsOn: true }), {
90 kind: "library",
91 reason: { by: "set", detail: "Set in its settings: a library." },
92 runs: null,
93 });
94 assert.equal(resolveKind({ ...auto, setting: { kind: "tool", runs: null } }).reason.detail, "Set in its settings: a tool.");
95});
96
97test("where it runs: set, or on g1t while Deployments are on, or nobody has said", () => {
98 // An app deployed by its own CI: no nag to turn on Deployments.
99 const elsewhere = resolveKind({ ...auto, setting: { kind: null, runs: "elsewhere" }, detected: { kind: "library", detail: "" } });
100 assert.deepEqual([elsewhere.kind, elsewhere.runs, elsewhere.reason.by], ["app", "elsewhere", "set"]);
101 assert.equal(resolveKind({ ...auto, deploymentsOn: true }).runs, "g1t");
102 assert.equal(resolveKind({ ...auto, setting: { kind: "app", runs: "g1t" } }).runs, "g1t");
103 // Detected an app, Deployments off: unknown, so the page asks instead of nagging.
104 assert.equal(resolveKind({ ...auto, detected: { kind: "app", detail: "wrangler.jsonc at the root." } }).runs, null);
105 // Docs can be a site; a library, a tool and other never run.
106 assert.equal(resolveKind({ ...auto, setting: { kind: "docs", runs: null }, deploymentsOn: true }).runs, "g1t");
107 for (const kind of ["library", "tool", "other"] as const) {
108 assert.equal(resolveKind({ ...auto, setting: { kind, runs: "g1t" }, deploymentsOn: true }).runs, null);
109 }
110});
111
112test("an app that deploys production itself deploys itself, until something says otherwise", () => {
113 // Its own workflow deployed production: an app that deploys itself, whatever its files say.
114 const itself = resolveKind({ ...auto, reportsProduction: true, detected: { kind: "library", detail: "" } });
115 assert.deepEqual(itself, { kind: "app", reason: { by: "deployments", detail: "It deploys production itself." }, runs: "elsewhere" });
116 // Hosted on g1t.page wins: Deployments being on, or the setting.
117 assert.equal(resolveKind({ ...auto, reportsProduction: true, deploymentsOn: true }).runs, "g1t");
118 assert.equal(resolveKind({ ...auto, reportsProduction: true, setting: { kind: null, runs: "g1t" } }).runs, "g1t");
119 // Set to something that never runs, it doesn't.
120 assert.equal(resolveKind({ ...auto, reportsProduction: true, setting: { kind: "library", runs: null } }).runs, null);
121 // Docs published by its own pipeline deploy themselves too.
122 assert.equal(resolveKind({ ...auto, reportsProduction: true, setting: { kind: "docs", runs: null } }).runs, "elsewhere");
123});
124
125test("a change to the setting keeps it coherent", () => {
126 const none = { kind: null, runs: null };
127 assert.deepEqual(nextSetting(none, { runs: "elsewhere" }), { kind: null, runs: "elsewhere" });
128 assert.deepEqual(nextSetting({ kind: "app", runs: "g1t" }, { kind: "library" }), { kind: "library", runs: null });
129 assert.deepEqual(nextSetting({ kind: "docs", runs: "elsewhere" }, { kind: "docs" }), { kind: "docs", runs: "elsewhere" });
130 assert.deepEqual(nextSetting({ kind: "tool", runs: null }, { runs: "g1t" }), { kind: "app", runs: "g1t" });
131 assert.deepEqual(nextSetting({ kind: "app", runs: "g1t" }, { kind: "auto", runs: "auto" }), none);
132 assert.equal(neverRuns({ kind: "other", runs: null }), true);
133 assert.equal(neverRuns({ kind: "docs", runs: null }), false);
134 assert.equal(neverRuns(none), false);
135});
136
137test("on auto: Deployments on, then packages, then files, then an app", () => {
138 const library = { kind: "library" as const, detail: "composer.json has autoload and no public/index.php." };
139 assert.equal(resolveKind({ ...auto, deploymentsOn: true, linkedPackage: "Composer package psr/log", detected: library }).reason.by, "deployments");
140 assert.deepEqual(resolveKind({ ...auto, linkedPackage: "Composer package psr/log", detected: library }), {
141 kind: "library",
142 reason: { by: "packages", detail: "Its repository publishes the Composer package psr/log." },
143 runs: null,
144 });
145 assert.deepEqual(resolveKind({ ...auto, detected: library }), { kind: "library", reason: { by: "files", detail: library.detail }, runs: null });
146 assert.equal(resolveKind({ ...auto, detected: { kind: "app", detail: "package.json has a start script." } }).reason.by, "files");
147 assert.deepEqual(resolveKind({ ...auto, deploymentsOn: null, detected: { kind: null, detail: "" } }).kind, "app");
148 assert.equal(resolveKind(auto).reason.by, "default");
149});
150
151test("an unknown setting is left to detection", () => {
152 assert.equal(kindSetting("tool"), "tool");
153 assert.equal(kindSetting("auto"), null);
154 assert.equal(kindSetting("yes"), null);
155 assert.equal(runsSetting("elsewhere"), "elsewhere");
156 assert.equal(runsSetting("sometimes"), null);
157});
158
159test("detection says tool, and docs, where the files do", () => {
160 assert.deepEqual(detectKind(root({ "package.json": '{"name":"x","bin":{"x":"cli.js"}}' })), {
161 kind: "tool",
162 detail: 'package.json has "bin" and nothing to import.',
163 ecosystem: "npm",
164 });
165 // Something to import as well as a command: a library.
166 assert.equal(detectKind(root({ "package.json": '{"main":"x.js","bin":"cli.js"}' })).kind, "library");
167 assert.equal(detectKind(root({ "package.json": '{"scripts":{"start":"docusaurus start"}}' }, { entries: ["docusaurus.config.ts"] })).kind, "docs");
168 assert.equal(detectKind(root({}, { entries: ["mkdocs.yml", "docs/"] })).detail, "mkdocs.yml at the root builds documentation.");
169 assert.equal(resolveKind({ ...auto, detected: { kind: "docs", detail: "mkdocs.yml at the root builds documentation." } }).kind, "docs");
170});