{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { spawn, exec, SpawnOptions } from \"node:child_process\";\nimport { openSync, readFileSync } from \"node:fs\";\nimport { inspect, promisify } from \"node:util\";\nimport * as http from \"http\";\n\nimport * as core from \"@actions/core\";\nimport { Tail } from \"tail\";\nimport got from \"got\";\nimport { IdsToolbox } from \"detsys-ts\";\n\nconst ENV_CACHE_DAEMONDIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\n\nconst gotClient = got.extend({\n retry: {\n limit: 1,\n methods: [\"POST\", \"GET\", \"PUT\", \"HEAD\", \"DELETE\", \"OPTIONS\", \"TRACE\"],\n },\n hooks: {\n beforeRetry: [\n (error, retryCount) => {\n core.info(`Retrying after error ${error.code}, retry #: ${retryCount}`);\n },\n ],\n },\n});\n\nasync function fetchAutoCacher(toolbox: IdsToolbox): Promise {\n const closurePath = await toolbox.fetch();\n toolbox.recordEvent(\"load_closure\");\n const { stdout } = await promisify(exec)(\n `cat \"${closurePath}\" | xz -d | nix-store --import`,\n );\n\n const paths = stdout.split(os.EOL);\n // Since the export is in reverse topologically sorted order, magic-nix-cache is always the penultimate entry in the list (the empty string left by split being the last).\n const last_path = paths.at(-2);\n return `${last_path}/bin/magic-nix-cache`;\n}\n\nfunction tailLog(daemonDir: string): Tail {\n const log = new Tail(path.join(daemonDir, \"daemon.log\"));\n core.debug(`tailing daemon.log...`);\n log.on(\"line\", (line) => {\n core.info(line);\n });\n return log;\n}\n\nasync function setUpAutoCache(toolbox: IdsToolbox): Promise {\n const tmpdir = process.env[\"RUNNER_TEMP\"] || os.tmpdir();\n const required_env = [\n \"ACTIONS_CACHE_URL\",\n \"ACTIONS_RUNTIME_URL\",\n \"ACTIONS_RUNTIME_TOKEN\",\n ];\n\n let anyMissing = false;\n for (const n of required_env) {\n if (!process.env.hasOwnProperty(n)) {\n anyMissing = true;\n core.warning(\n `Disabling automatic caching since required environment ${n} isn't available`,\n );\n }\n }\n\n if (anyMissing) {\n return;\n }\n\n core.debug(`GitHub Action Cache URL: ${process.env[\"ACTIONS_CACHE_URL\"]}`);\n\n const daemonDir = await fs.mkdtemp(path.join(tmpdir, \"magic-nix-cache-\"));\n\n let daemonBin: string;\n if (core.getInput(\"source-binary\")) {\n daemonBin = core.getInput(\"source-binary\");\n } else {\n daemonBin = await fetchAutoCacher(toolbox);\n }\n\n let runEnv;\n if (core.isDebug()) {\n runEnv = {\n RUST_LOG: \"trace,magic_nix_cache=debug,gha_cache=debug\",\n RUST_BACKTRACE: \"full\",\n ...process.env,\n };\n } else {\n runEnv = process.env;\n }\n\n const notifyPort = core.getInput(\"startup-notification-port\");\n\n const notifyPromise = new Promise>((resolveListening) => {\n const promise = new Promise(async (resolveQuit) => {\n const notifyServer = http.createServer((req, res) => {\n if (req.method === \"POST\" && req.url === \"/\") {\n core.debug(`Notify server shutting down.`);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\"{}\");\n notifyServer.close(() => {\n resolveQuit();\n });\n }\n });\n\n notifyServer.listen(notifyPort, () => {\n core.debug(`Notify server running.`);\n resolveListening(promise);\n });\n });\n });\n\n // Start tailing the daemon log.\n const outputPath = `${daemonDir}/daemon.log`;\n const output = openSync(outputPath, \"a\");\n const log = tailLog(daemonDir);\n const netrc = await netrcPath();\n const nixConfPath = `${process.env[\"HOME\"]}/.config/nix/nix.conf`;\n\n const daemonCliFlags: string[] = [\n \"--startup-notification-url\",\n `http://127.0.0.1:${notifyPort}`,\n \"--listen\",\n core.getInput(\"listen\"),\n \"--upstream\",\n core.getInput(\"upstream-cache\"),\n \"--diagnostic-endpoint\",\n core.getInput(\"diagnostic-endpoint\"),\n \"--nix-conf\",\n nixConfPath,\n ]\n .concat(\n core.getBooleanInput(\"use-flakehub\")\n ? [\n \"--use-flakehub\",\n \"--flakehub-cache-server\",\n core.getInput(\"flakehub-cache-server\"),\n \"--flakehub-api-server\",\n core.getInput(\"flakehub-api-server\"),\n \"--flakehub-api-server-netrc\",\n netrc,\n \"--flakehub-flake-name\",\n core.getInput(\"flakehub-flake-name\"),\n ]\n : [],\n )\n .concat(core.getBooleanInput(\"use-gha-cache\") ? [\"--use-gha-cache\"] : []);\n\n const opts: SpawnOptions = {\n stdio: [\"ignore\", output, output],\n env: runEnv,\n detached: true,\n };\n\n // Display the final command for debugging purposes\n core.debug(\"Full daemon start command:\");\n core.debug(`${daemonBin} ${daemonCliFlags.join(\" \")}`);\n\n // Start the server. Once it is ready, it will notify us via the notification server.\n const daemon = spawn(daemonBin, daemonCliFlags, opts);\n\n const pidFile = path.join(daemonDir, \"daemon.pid\");\n await fs.writeFile(pidFile, `${daemon.pid}`);\n\n core.info(\"Waiting for magic-nix-cache to start...\");\n\n await new Promise((resolve, reject) => {\n notifyPromise\n // eslint-disable-next-line github/no-then\n .then((_value) => {\n resolve();\n })\n // eslint-disable-next-line github/no-then\n .catch((err) => {\n reject(new Error(`error in notifyPromise: ${err}`));\n });\n daemon.on(\"exit\", async (code, signal) => {\n if (signal) {\n reject(new Error(`Daemon was killed by signal ${signal}`));\n } else if (code) {\n reject(new Error(`Daemon exited with code ${code}`));\n } else {\n reject(new Error(`Daemon unexpectedly exited`));\n }\n });\n });\n\n daemon.unref();\n\n core.info(\"Launched Magic Nix Cache\");\n core.exportVariable(ENV_CACHE_DAEMONDIR, daemonDir);\n\n log.unwatch();\n}\n\nasync function notifyAutoCache(): Promise {\n const daemonDir = process.env[ENV_CACHE_DAEMONDIR];\n\n if (!daemonDir) {\n return;\n }\n\n try {\n core.debug(`Indicating workflow start`);\n const res: Response = await gotClient\n .post(`http://${core.getInput(\"listen\")}/api/workflow-start`)\n .json();\n core.debug(`back from post: ${res}`);\n } catch (e) {\n core.info(`Error marking the workflow as started:`);\n core.info(inspect(e));\n core.info(`Magic Nix Cache may not be running for this workflow.`);\n }\n}\n\nasync function netrcPath(): Promise {\n const expectedNetrcPath = path.join(\n process.env[\"RUNNER_TEMP\"] || os.tmpdir(),\n \"determinate-nix-installer-netrc\",\n );\n try {\n await fs.access(expectedNetrcPath);\n return expectedNetrcPath;\n } catch {\n // `nix-installer` was not used, the user may be registered with FlakeHub though.\n const destinedNetrcPath = path.join(\n process.env[\"RUNNER_TEMP\"] || os.tmpdir(),\n \"magic-nix-cache-netrc\",\n );\n try {\n await flakehub_login(destinedNetrcPath);\n } catch (e) {\n core.info(\"FlakeHub cache disabled.\");\n core.debug(`Error while logging into FlakeHub: ${e}`);\n }\n return destinedNetrcPath;\n }\n}\n\nasync function flakehub_login(netrc: string): Promise {\n const jwt = await core.getIDToken(\"api.flakehub.com\");\n\n await fs.writeFile(\n netrc,\n [\n `machine api.flakehub.com login flakehub password ${jwt}`,\n `machine flakehub.com login flakehub password ${jwt}`,\n `machine cache.flakehub.com login flakehub password ${jwt}`,\n ].join(\"\\n\"),\n );\n\n core.info(\"Logged in to FlakeHub.\");\n}\n\nasync function tearDownAutoCache(): Promise {\n const daemonDir = process.env[ENV_CACHE_DAEMONDIR];\n\n if (!daemonDir) {\n core.debug(\"magic-nix-cache not started - Skipping\");\n return;\n }\n\n const pidFile = path.join(daemonDir, \"daemon.pid\");\n const pid = parseInt(await fs.readFile(pidFile, { encoding: \"ascii\" }));\n core.debug(`found daemon pid: ${pid}`);\n if (!pid) {\n throw new Error(\"magic-nix-cache did not start successfully\");\n }\n\n const log = tailLog(daemonDir);\n\n try {\n core.debug(`about to post to localhost`);\n const res: Response = await gotClient\n .post(`http://${core.getInput(\"listen\")}/api/workflow-finish`)\n .json();\n core.debug(`back from post: ${res}`);\n } finally {\n core.debug(`unwatching the daemon log`);\n log.unwatch();\n }\n\n core.debug(`killing`);\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (e) {\n if (typeof e === \"object\" && e && \"code\" in e && e.code !== \"ESRCH\") {\n throw e;\n }\n } finally {\n if (core.isDebug()) {\n core.info(\"Entire log:\");\n const entireLog = readFileSync(path.join(daemonDir, \"daemon.log\"));\n core.info(entireLog.toString());\n }\n }\n}\n\nconst idslib = new IdsToolbox({\n name: \"magic-nix-cache\",\n fetchStyle: \"gh-env-style\",\n idsProjectName: \"magic-nix-cache-closure\",\n requireNix: \"warn\",\n});\n\nidslib.onMain(async () => {\n await setUpAutoCache(idslib);\n await notifyAutoCache();\n});\nidslib.onPost(async () => {\n await tearDownAutoCache();\n});\n\nidslib.execute();\n"],"mappings":"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","names":["tmpdir"]}