From 99895923b96003d66f851cce1e8caa285db89e24 Mon Sep 17 00:00:00 2001 From: ahmdkaml Date: Tue, 28 Jul 2026 02:23:54 +0300 Subject: [PATCH 1/3] Add Go toolchain detection --- src/detect.js | 36 ++++++++++++++++--- test/detect.test.js | 11 ++++-- .../fixtures/aligned/.github/workflows/ci.yml | 5 +++ test/fixtures/aligned/go.mod | 2 ++ test/fixtures/drift/Dockerfile | 1 + test/fixtures/drift/go.mod | 1 + 6 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/aligned/go.mod create mode 100644 test/fixtures/drift/go.mod diff --git a/src/detect.js b/src/detect.js index 2f5f8fb..3d64f98 100644 --- a/src/detect.js +++ b/src/detect.js @@ -61,6 +61,24 @@ async function detectPyproject(root, output) { if (match) add(output, "python", "pyproject.toml", match[1], index + 1); } } +async function detectGoMod(root, output) { + const content = await read(root, "go.mod"); + if (!content) return; + + const lines = content.split(/\r?\n/); + + for (const [index, line] of lines.entries()) { + const go = line.match(/^\s*go\s+(\S+)/); + if (go) { + add(output, "go", "go.mod", go[1], index + 1); + } + + const toolchain = line.match(/^\s*toolchain\s+go(\S+)/); + if (toolchain) { + add(output, "go", "go.mod", toolchain[1], index + 1); + } + } +} async function detectToolVersions(root, output) { const content = await read(root, ".tool-versions"); @@ -96,8 +114,17 @@ async function detectDockerfiles(root, output) { if (!entry.isFile() || !/^Dockerfile(?:\..+)?$/i.test(entry.name)) continue; const content = await read(root, entry.name); for (const [index, line] of content.split(/\r?\n/).entries()) { - const match = line.match(/^\s*FROM\s+(?:[^/\s]+\/)?(node|python):([^\s@]+)/i); - if (match) add(output, match[1].toLowerCase(), entry.name, match[2], index + 1); + const match = line.match( + /^\s*FROM\s+(?:[^/\s]+\/)?(node|python|golang):([^\s@]+)/i, + ); + if (match) + add( + output, + match[1].toLowerCase() === "golang" ? "go" : match[1].toLowerCase(), + entry.name, + match[2], + index + 1, + ); } } } @@ -124,7 +151,7 @@ async function detectGithubActions(root, output) { let remaining = 0; for (const [index, line] of lines.entries()) { - const action = line.match(/uses:\s*actions\/setup-(node|python)@/i); + const action = line.match(/uses:\s*actions\/setup-(node|python|go)@/i); if (action) { runtime = action[1].toLowerCase(); remaining = 12; @@ -135,7 +162,7 @@ async function detectGithubActions(root, output) { continue; } remaining -= 1; - const declared = line.match(/(?:node-version|python-version):\s*["']?([^"'#\s]+)/i); + const declared = line.match(/(?:node-version|python-version|go-version):\s*["']?([^"'#\s]+)/i); if (runtime && declared && !declared[1].startsWith("${{")) { add(output, runtime, file, declared[1], index + 1); runtime = null; @@ -150,6 +177,7 @@ export async function detectDeclarations(root) { detectVersionFiles(root, output), detectPackageJson(root, output), detectPyproject(root, output), + detectGoMod(root, output), detectToolVersions(root, output), detectMise(root, output), detectDockerfiles(root, output), diff --git a/test/detect.test.js b/test/detect.test.js index 7dec27d..ae848a0 100644 --- a/test/detect.test.js +++ b/test/detect.test.js @@ -11,18 +11,23 @@ test("finds aligned Node.js and Python declarations", async () => { assert.equal(result.ok, true); assert.deepEqual( + result.runtimes.map((runtime) => runtime.runtime), - ["node", "python"], + ["go","node", "python"], ); - assert.equal(result.runtimes[0].declarations.length, 5); + assert.equal(result.runtimes[0].declarations.length, 3); assert.equal(result.runtimes[1].declarations.length, 5); + assert.equal(result.runtimes[2].declarations.length, 5); }); test("finds Docker and project-file drift", async () => { const result = await inspect(path.join(here, "fixtures", "drift")); assert.equal(result.ok, false); - assert.equal(result.runtimes[0].runtime, "node"); + assert.deepEqual( + result.runtimes.map((runtime) => runtime.runtime), + ["go", "node"], +); }); test("returns an empty success for a directory with no declarations", async () => { diff --git a/test/fixtures/aligned/.github/workflows/ci.yml b/test/fixtures/aligned/.github/workflows/ci.yml index a33365f..15e7e7e 100644 --- a/test/fixtures/aligned/.github/workflows/ci.yml +++ b/test/fixtures/aligned/.github/workflows/ci.yml @@ -9,3 +9,8 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.12" + go: + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.24" \ No newline at end of file diff --git a/test/fixtures/aligned/go.mod b/test/fixtures/aligned/go.mod new file mode 100644 index 0000000..d3f9de9 --- /dev/null +++ b/test/fixtures/aligned/go.mod @@ -0,0 +1,2 @@ +go 1.24 +toolchain go1.24.3 \ No newline at end of file diff --git a/test/fixtures/drift/Dockerfile b/test/fixtures/drift/Dockerfile index f0e004a..58abd4a 100644 --- a/test/fixtures/drift/Dockerfile +++ b/test/fixtures/drift/Dockerfile @@ -1 +1,2 @@ FROM node:20-alpine +FROM golang:1.23 \ No newline at end of file diff --git a/test/fixtures/drift/go.mod b/test/fixtures/drift/go.mod new file mode 100644 index 0000000..b506908 --- /dev/null +++ b/test/fixtures/drift/go.mod @@ -0,0 +1 @@ +go 1.24 \ No newline at end of file From d90be13ef57c5d4e2ba36eb930c87f0665f076c7 Mon Sep 17 00:00:00 2001 From: ahmdkaml Date: Tue, 28 Jul 2026 18:31:39 +0300 Subject: [PATCH 2/3] Strengthen Go drift test --- test/detect.test.js | 13 +++++++------ test/fixtures/aligned/.github/workflows/ci.yml | 2 +- test/fixtures/aligned/go.mod | 2 +- test/fixtures/drift/Dockerfile | 2 +- test/fixtures/drift/go.mod | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/test/detect.test.js b/test/detect.test.js index ae848a0..ac50477 100644 --- a/test/detect.test.js +++ b/test/detect.test.js @@ -11,9 +11,8 @@ test("finds aligned Node.js and Python declarations", async () => { assert.equal(result.ok, true); assert.deepEqual( - result.runtimes.map((runtime) => runtime.runtime), - ["go","node", "python"], + ["go", "node", "python"], ); assert.equal(result.runtimes[0].declarations.length, 3); assert.equal(result.runtimes[1].declarations.length, 5); @@ -22,12 +21,14 @@ test("finds aligned Node.js and Python declarations", async () => { test("finds Docker and project-file drift", async () => { const result = await inspect(path.join(here, "fixtures", "drift")); - assert.equal(result.ok, false); assert.deepEqual( - result.runtimes.map((runtime) => runtime.runtime), - ["go", "node"], -); + result.runtimes.map((runtime) => runtime.runtime), + ["go", "node"], + ); + const go = result.runtimes.find((runtime) => runtime.runtime === "go"); + + assert.equal(go.ok, false); }); test("returns an empty success for a directory with no declarations", async () => { diff --git a/test/fixtures/aligned/.github/workflows/ci.yml b/test/fixtures/aligned/.github/workflows/ci.yml index 15e7e7e..d932b85 100644 --- a/test/fixtures/aligned/.github/workflows/ci.yml +++ b/test/fixtures/aligned/.github/workflows/ci.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/setup-go@v5 with: - go-version: "1.24" \ No newline at end of file + go-version: "1.24" diff --git a/test/fixtures/aligned/go.mod b/test/fixtures/aligned/go.mod index d3f9de9..85cfba5 100644 --- a/test/fixtures/aligned/go.mod +++ b/test/fixtures/aligned/go.mod @@ -1,2 +1,2 @@ go 1.24 -toolchain go1.24.3 \ No newline at end of file +toolchain go1.24.3 diff --git a/test/fixtures/drift/Dockerfile b/test/fixtures/drift/Dockerfile index 58abd4a..67a885b 100644 --- a/test/fixtures/drift/Dockerfile +++ b/test/fixtures/drift/Dockerfile @@ -1,2 +1,2 @@ FROM node:20-alpine -FROM golang:1.23 \ No newline at end of file +FROM golang:1.23 diff --git a/test/fixtures/drift/go.mod b/test/fixtures/drift/go.mod index b506908..8e3bc02 100644 --- a/test/fixtures/drift/go.mod +++ b/test/fixtures/drift/go.mod @@ -1 +1 @@ -go 1.24 \ No newline at end of file +go 1.24 From 8ef18732af60c85238c4dd05ab18b38407ca7d9c Mon Sep 17 00:00:00 2001 From: ahmdkaml Date: Tue, 28 Jul 2026 18:40:45 +0300 Subject: [PATCH 3/3] Strengthen Go drift test --- src/format.js | 8 +++++++- test/detect.test.js | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/format.js b/src/format.js index 2bf565e..70a9273 100644 --- a/src/format.js +++ b/src/format.js @@ -2,6 +2,12 @@ function location(declaration) { return declaration.line ? `${declaration.file}:${declaration.line}` : declaration.file; } +const LABELS = { + go: "Go", + node: "Node.js", + python: "Python", +}; + export function formatHuman(result) { if (result.runtimes.length === 0) { return "No supported runtime declarations found."; @@ -14,7 +20,7 @@ export function formatHuman(result) { }); if (!runtime.ok) { - const label = runtime.runtime === "node" ? "Node.js" : "Python"; + const label = LABELS[runtime.runtime] ?? runtime.runtime; rows.push("", ` No ${label} version satisfies every declaration.`); } else if (runtime.declarations.some((declaration) => !declaration.parsed)) { rows.push("", " Some declarations could not be parsed."); diff --git a/test/detect.test.js b/test/detect.test.js index ac50477..ec7ab70 100644 --- a/test/detect.test.js +++ b/test/detect.test.js @@ -28,6 +28,7 @@ test("finds Docker and project-file drift", async () => { ); const go = result.runtimes.find((runtime) => runtime.runtime === "go"); + assert.ok(go); assert.equal(go.ok, false); });