| 1 | import { existsSync } from "node:fs"; |
| 2 | import path from "node:path"; |
| 3 | import { errMsg, failure } from "../job.js"; |
| 4 | import { maxSatisfying, satisfies, truthyInput } from "../util.js"; |
| 5 | import type { Builtin } from "./index.js"; |
| 6 | import { fetchJson, installTarball, isCached, packageCache, probe, readVersionFile, toolArch, toolDir } from "./tools.js"; |
| 7 | |
| 8 | interface GoRelease { |
| 9 | version: string; // "go1.22.3" |
| 10 | stable: boolean; |
| 11 | files: Array<{ filename: string; os: string; arch: string; kind: string }>; |
| 12 | } |
| 13 | |
| 14 | export const setupGo: Builtin = async (c) => { |
| 15 | let spec = c.input("go-version"); |
| 16 | const file = c.input("go-version-file"); |
| 17 | if (!spec && file) spec = await readVersionFile(c, file, "go"); |
| 18 | spec = spec.trim().replace(/^go/, ""); |
| 19 | const installedRaw = await probe(c, "go", ["version"]); |
| 20 | const installed = /go(\d+\.\d+(?:\.\d+)?)/.exec(installedRaw ?? "")?.[1] ?? null; |
| 21 | let version = installed; |
| 22 | |
| 23 | if (!spec && !installed) throw failure("No go-version specified and Go is not installed on this runner."); |
| 24 | const useInstalled = installed && (!spec || (!/^(stable|oldstable)$/.test(spec) && satisfies(installed, spec))) && !truthyInput(c.input("check-latest"), false); |
| 25 | if (useInstalled) { |
| 26 | c.log(`Using pre-installed Go ${installed}`); |
| 27 | } else { |
| 28 | let releases: GoRelease[]; |
| 29 | try { |
| 30 | releases = await fetchJson<GoRelease[]>(process.env.DIFFINERY_GO_INDEX ?? "https://go.dev/dl/?mode=json&include=all"); |
| 31 | } catch (e) { |
| 32 | throw failure(`Unable to fetch the Go versions index: ${errMsg(e)}`); |
| 33 | } |
| 34 | const arch = toolArch("go"); |
| 35 | const stable = releases.filter((r) => r.stable && r.files.some((f) => f.os === "linux" && f.arch === arch && f.kind === "archive")); |
| 36 | let v: string | null; |
| 37 | if (spec === "stable") v = maxSatisfying(stable.map((r) => r.version.replace(/^go/, "")), "*"); |
| 38 | else if (spec === "oldstable") { |
| 39 | const sorted = stable.map((r) => r.version.replace(/^go/, "")); |
| 40 | const latest = maxSatisfying(sorted, "*"); |
| 41 | const latestMinor = latest ? latest.split(".").slice(0, 2).join(".") : ""; |
| 42 | v = maxSatisfying(sorted.filter((x) => !x.startsWith(latestMinor + ".") && x !== latestMinor), "*"); |
| 43 | } else v = maxSatisfying(stable.map((r) => r.version.replace(/^go/, "")), spec); |
| 44 | if (!v) throw failure(`Unable to find Go version '${spec}' for platform linux and architecture ${arch}.`); |
| 45 | const rel = stable.find((r) => r.version === `go${v}`)!; |
| 46 | const f = rel.files.find((x) => x.os === "linux" && x.arch === arch && x.kind === "archive")!; |
| 47 | const dir = toolDir(c, "go", v, arch); |
| 48 | if (!isCached(dir)) await installTarball(c, `https://go.dev/dl/${f.filename}`, dir); |
| 49 | else c.log(`Found in cache @ ${dir}`); |
| 50 | c.addPath(path.join(dir, "bin")); |
| 51 | version = v; |
| 52 | } |
| 53 | const gopath = await probe(c, "go", ["env", "GOPATH"], c.toolEnv()); |
| 54 | if (gopath) c.addPath(path.join(gopath, "bin")); |
| 55 | if (version) c.setOutput("go-version", version); |
| 56 | |
| 57 | if (truthyInput(c.input("cache"), true)) { |
| 58 | const deps = c.input("cache-dependency-path"); |
| 59 | if (!deps && !existsSync(path.join(c.job.workspace, "go.sum"))) { |
| 60 | c.log("go.sum not found; skipping dependency cache."); |
| 61 | return; |
| 62 | } |
| 63 | const env = c.toolEnv(); |
| 64 | const modcache = await probe(c, "go", ["env", "GOMODCACHE"], env); |
| 65 | const buildcache = await probe(c, "go", ["env", "GOCACHE"], env); |
| 66 | await packageCache(c, { |
| 67 | prefix: `setup-go-Linux-${toolArch("go")}-go-${version}`, |
| 68 | paths: [modcache ?? "", buildcache ?? ""], |
| 69 | lockPatterns: ["**/go.sum"], |
| 70 | what: "go", |
| 71 | }); |
| 72 | } |
| 73 | }; |