From 4ae13c51d8d824d64f1cad182c5d54155ee3cf8f Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 6 May 2024 11:33:36 -0400 Subject: [PATCH] Extract out the daemonDir into a class member --- dist/index.js | 87 +++++++++++++++++++++++++++++++++++++++-------- dist/index.js.map | 2 +- package.json | 2 +- pnpm-lock.yaml | 8 ++--- src/index.ts | 40 +++++++++++++++++----- 5 files changed, 110 insertions(+), 29 deletions(-) diff --git a/dist/index.js b/dist/index.js index 734e062..17341cd 100644 --- a/dist/index.js +++ b/dist/index.js @@ -93750,7 +93750,9 @@ const got = source_create(defaults); ;// CONCATENATED MODULE: external "node:stream/promises" const external_node_stream_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:stream/promises"); -;// CONCATENATED MODULE: ./node_modules/.pnpm/github.com+DeterminateSystems+detsys-ts@5abcb239472d24b114a53f70800f0e42fc30819c_ey4bwtjkop43mcem42nicbf3we/node_modules/detsys-ts/dist/index.js +;// CONCATENATED MODULE: external "node:zlib" +const external_node_zlib_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:zlib"); +;// CONCATENATED MODULE: ./node_modules/.pnpm/github.com+DeterminateSystems+detsys-ts@3baa96b315ee9f6b26033191a123eb9015522791_2fnsfzpup2itrpjvndt6evj3qu/node_modules/detsys-ts/dist/index.js var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) @@ -94096,17 +94098,27 @@ function getNixPlatform(archOs) { // src/inputs.ts var inputs_exports = {}; __export(inputs_exports, { + getArrayOfStrings: () => getArrayOfStrings, getBool: () => getBool, getMultilineStringOrNull: () => getMultilineStringOrNull, getNumberOrNull: () => getNumberOrNull, getString: () => getString, getStringOrNull: () => getStringOrNull, - getStringOrUndefined: () => getStringOrUndefined + getStringOrUndefined: () => getStringOrUndefined, + handleString: () => handleString }); var getBool = (name) => { return core.getBooleanInput(name); }; +var getArrayOfStrings = (name, separator) => { + const original = getString(name); + return handleString(original, separator); +}; +var handleString = (input, separator) => { + const sepChar = separator === "comma" ? "," : /\s+/; + return input.trim().split(sepChar).map((s) => s.trim()); +}; var getMultilineStringOrNull = (name) => { const value = core.getMultilineInput(name); if (value.length === 0) { @@ -94186,18 +94198,24 @@ function constructSourceParameters(legacyPrefix) { + + var DEFAULT_IDS_HOST = "https://install.determinate.systems"; var IDS_HOST = process.env["IDS_HOST"] ?? DEFAULT_IDS_HOST; var EVENT_EXCEPTION = "exception"; var EVENT_ARTIFACT_CACHE_HIT = "artifact_cache_hit"; var EVENT_ARTIFACT_CACHE_MISS = "artifact_cache_miss"; +var EVENT_ARTIFACT_CACHE_PERSIST = "artifact_cache_persist"; var FACT_ENDED_WITH_EXCEPTION = "ended_with_exception"; var FACT_FINAL_EXCEPTION = "final_exception"; +var FACT_SOURCE_URL = "source_url"; +var FACT_SOURCE_URL_ETAG = "source_url_etag"; var IdsToolbox = class { constructor(actionOptions) { this.actionOptions = makeOptionsConfident(actionOptions); this.hookMain = void 0; this.hookPost = void 0; + this.exceptionAttachments = /* @__PURE__ */ new Map(); this.events = []; this.client = got_dist_source.extend({ retry: { @@ -94276,6 +94294,10 @@ var IdsToolbox = class { ); this.recordEvent(`begin_${this.executionPhase}`); } + // Attach a file to the diagnostics data in error conditions. + stapleFile(name, location) { + this.exceptionAttachments.set(name, location); + } onMain(callback) { this.hookMain = callback; } @@ -94288,6 +94310,9 @@ var IdsToolbox = class { process.exitCode = 1; }); } + stringifyError(error2) { + return error2 instanceof Error || typeof error2 == "string" ? error2.toString() : JSON.stringify(error2); + } async executeAsync() { try { process.env.DETSYS_CORRELATION = JSON.stringify( @@ -94305,14 +94330,29 @@ var IdsToolbox = class { this.addFact(FACT_ENDED_WITH_EXCEPTION, false); } catch (error2) { this.addFact(FACT_ENDED_WITH_EXCEPTION, true); - const reportable = error2 instanceof Error || typeof error2 == "string" ? error2.toString() : JSON.stringify(error2); + const reportable = this.stringifyError(error2); this.addFact(FACT_FINAL_EXCEPTION, reportable); if (this.executionPhase === "post") { core.warning(reportable); } else { core.setFailed(reportable); } - this.recordEvent(EVENT_EXCEPTION); + const do_deflate = (0,external_node_util_.promisify)(external_node_zlib_namespaceObject.deflate); + const exceptionContext = /* @__PURE__ */ new Map(); + for (const [attachmentLabel, filePath] of this.exceptionAttachments) { + let logText; + try { + logText = (0,external_node_fs_namespaceObject.readFileSync)(filePath); + } catch (e) { + logText = Buffer.from(this.stringifyError(e)); + } + const buf = await do_deflate(logText); + exceptionContext.set( + `staple_${attachmentLabel}`, + buf.toString("base64") + ); + } + this.recordEvent(EVENT_EXCEPTION, Object.fromEntries(exceptionContext)); } finally { await this.complete(); } @@ -94354,6 +94394,7 @@ var IdsToolbox = class { const versionCheckup = await this.client.head(correlatedUrl); if (versionCheckup.headers.etag) { const v = versionCheckup.headers.etag; + this.addFact(FACT_SOURCE_URL_ETAG, v); core.debug( `Checking the tool cache for ${this.getUrl()} at ${v}` ); @@ -94402,6 +94443,7 @@ var IdsToolbox = class { getUrl() { const p = this.sourceParameters; if (p.url) { + this.addFact(FACT_SOURCE_URL, p.url); return new URL(p.url); } const fetchUrl = new URL(IDS_HOST); @@ -94418,6 +94460,7 @@ var IdsToolbox = class { fetchUrl.pathname += `/stable`; } fetchUrl.pathname += `/${this.architectureFetchSuffix}`; + this.addFact(FACT_SOURCE_URL, fetchUrl.toString()); return fetchUrl; } cacheKey(version2) { @@ -94465,7 +94508,7 @@ var IdsToolbox = class { void 0, true ); - this.recordEvent(EVENT_ARTIFACT_CACHE_HIT); + this.recordEvent(EVENT_ARTIFACT_CACHE_PERSIST); } finally { process.env.GITHUB_WORKSPACE = process.env.GITHUB_WORKSPACE_BACKUP; delete process.env.GITHUB_WORKSPACE_BACKUP; @@ -94624,6 +94667,8 @@ function mungeDiagnosticEndpoint(inputUrl) { * Copyright (c) 2018-2020 [Samuel Carreira] */ //# sourceMappingURL=index.js.map +;// CONCATENATED MODULE: external "fs/promises" +const external_fs_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs/promises"); // EXTERNAL MODULE: external "http" var external_http_ = __nccwpck_require__(3685); ;// CONCATENATED MODULE: external "node:child_process" @@ -94689,6 +94734,7 @@ async function flakeHubLogin(netrc) { + var ENV_CACHE_DAEMONDIR = "MAGIC_NIX_CACHE_DAEMONDIR"; var MagicNixCacheAction = class { constructor() { @@ -94713,9 +94759,27 @@ var MagicNixCacheAction = class { ] } }); + if (process.env[ENV_CACHE_DAEMONDIR]) { + this.unsafeDaemonDir = process.env[ENV_CACHE_DAEMONDIR]; + } else { + this.unsafeDaemonDir = this.idslib.getTemporaryName(); + core.exportVariable(ENV_CACHE_DAEMONDIR, this.unsafeDaemonDir); + } + } + async getDaemonDir() { + if (this.daemonDir === void 0) { + await (0,external_fs_promises_namespaceObject.mkdir)(this.unsafeDaemonDir, { recursive: true }); + this.daemonDir = this.unsafeDaemonDir; + return this.unsafeDaemonDir; + } else { + return this.daemonDir; + } + } + async daemonDirExists() { + const statRes = await (0,external_fs_promises_namespaceObject.stat)(this.unsafeDaemonDir); + return statRes.isDirectory(); } async setUpAutoCache() { - const tmpdir3 = process.env["RUNNER_TEMP"] || external_node_os_.tmpdir(); const requiredEnv = [ "ACTIONS_CACHE_URL", "ACTIONS_RUNTIME_URL", @@ -94736,7 +94800,6 @@ var MagicNixCacheAction = class { core.debug( `GitHub Action Cache URL: ${process.env["ACTIONS_CACHE_URL"]}` ); - const daemonDir = await promises_namespaceObject.mkdtemp(external_node_path_namespaceObject.join(tmpdir3, "magic-nix-cache-")); const sourceBinary = inputs_exports.getStringOrNull("source-binary"); const daemonBin = sourceBinary !== null ? sourceBinary : await this.fetchAutoCacher(); let runEnv; @@ -94768,6 +94831,7 @@ var MagicNixCacheAction = class { }); }); }); + const daemonDir = await this.getDaemonDir(); const outputPath = `${daemonDir}/daemon.log`; const output = (0,external_node_fs_namespaceObject.openSync)(outputPath, "a"); const log = tailLog(daemonDir); @@ -94834,7 +94898,6 @@ var MagicNixCacheAction = class { }); daemon.unref(); core.info("Launched Magic Nix Cache"); - core.exportVariable(ENV_CACHE_DAEMONDIR, daemonDir); log.unwatch(); } async fetchAutoCacher() { @@ -94848,10 +94911,6 @@ var MagicNixCacheAction = class { return `${lastPath}/bin/magic-nix-cache`; } async notifyAutoCache() { - const daemonDir = process.env[ENV_CACHE_DAEMONDIR]; - if (!daemonDir) { - return; - } try { core.debug(`Indicating workflow start`); const hostAndPort = inputs_exports.getString("listen"); @@ -94864,11 +94923,11 @@ var MagicNixCacheAction = class { } } async tearDownAutoCache() { - const daemonDir = process.env[ENV_CACHE_DAEMONDIR]; - if (!daemonDir) { + if (await this.daemonDirExists()) { core.debug("magic-nix-cache not started - Skipping"); return; } + const daemonDir = await this.getDaemonDir(); const pidFile = external_node_path_namespaceObject.join(daemonDir, "daemon.pid"); const pid = parseInt(await promises_namespaceObject.readFile(pidFile, { encoding: "ascii" })); core.debug(`found daemon pid: ${pid}`); diff --git a/dist/index.js.map b/dist/index.js.map index 37a53a9..bf8f00d 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/helpers.ts","../src/index.ts"],"sourcesContent":["import * as actionsCore from \"@actions/core\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport path from \"node:path\";\nimport { Tail } from \"tail\";\n\nexport function tailLog(daemonDir: string): Tail {\n const log = new Tail(path.join(daemonDir, \"daemon.log\"));\n actionsCore.debug(`tailing daemon.log...`);\n log.on(\"line\", (line) => {\n actionsCore.info(line);\n });\n return log;\n}\n\nexport async 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 flakeHubLogin(destinedNetrcPath);\n } catch (e) {\n actionsCore.info(\"FlakeHub cache disabled.\");\n actionsCore.debug(`Error while logging into FlakeHub: ${e}`);\n }\n return destinedNetrcPath;\n }\n}\n\nasync function flakeHubLogin(netrc: string): Promise {\n const jwt = await actionsCore.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 actionsCore.info(\"Logged in to FlakeHub.\");\n}\n","import { netrcPath, tailLog } from \"./helpers.js\";\nimport * as actionsCore from \"@actions/core\";\nimport { IdsToolbox, inputs } from \"detsys-ts\";\nimport got, { Got } from \"got\";\nimport * as http from \"http\";\nimport { SpawnOptions, exec, spawn } from \"node:child_process\";\nimport { openSync, readFileSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { inspect, promisify } from \"node:util\";\n\nconst ENV_CACHE_DAEMONDIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\n\nclass MagicNixCacheAction {\n idslib: IdsToolbox;\n private client: Got;\n\n constructor() {\n this.idslib = new IdsToolbox({\n name: \"magic-nix-cache\",\n fetchStyle: \"gh-env-style\",\n idsProjectName: \"magic-nix-cache-closure\",\n requireNix: \"warn\",\n });\n\n this.client = 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 actionsCore.info(\n `Retrying after error ${error.code}, retry #: ${retryCount}`,\n );\n },\n ],\n },\n });\n }\n\n async setUpAutoCache(): Promise {\n const tmpdir = process.env[\"RUNNER_TEMP\"] || os.tmpdir();\n const requiredEnv = [\n \"ACTIONS_CACHE_URL\",\n \"ACTIONS_RUNTIME_URL\",\n \"ACTIONS_RUNTIME_TOKEN\",\n ];\n\n let anyMissing = false;\n for (const n of requiredEnv) {\n if (!process.env.hasOwnProperty(n)) {\n anyMissing = true;\n actionsCore.warning(\n `Disabling automatic caching since required environment ${n} isn't available`,\n );\n }\n }\n\n if (anyMissing) {\n return;\n }\n\n actionsCore.debug(\n `GitHub Action Cache URL: ${process.env[\"ACTIONS_CACHE_URL\"]}`,\n );\n\n const daemonDir = await fs.mkdtemp(path.join(tmpdir, \"magic-nix-cache-\"));\n const sourceBinary = inputs.getStringOrNull(\"source-binary\");\n const daemonBin =\n sourceBinary !== null ? sourceBinary : await this.fetchAutoCacher();\n\n let runEnv;\n if (actionsCore.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 = inputs.getString(\"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 actionsCore.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 actionsCore.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 hostAndPort = inputs.getString(\"listen\");\n const upstreamCache = inputs.getString(\"upstream-cache\");\n const diagnosticEndpoint = inputs.getString(\"diagnostic-endpoint\");\n const useFlakeHub = inputs.getBool(\"use-flakehub\");\n const flakeHubCacheServer = inputs.getString(\"flakehub-cache-server\");\n const flakeHubApiServer = inputs.getString(\"flakehub-api-server\");\n const flakeHubFlakeName = inputs.getString(\"flakehub-flake-name\");\n const useGhaCache = inputs.getBool(\"use-gha-cache\");\n\n const daemonCliFlags: string[] = [\n \"--startup-notification-url\",\n `http://127.0.0.1:${notifyPort}`,\n \"--listen\",\n hostAndPort,\n \"--upstream\",\n upstreamCache,\n \"--diagnostic-endpoint\",\n diagnosticEndpoint,\n \"--nix-conf\",\n nixConfPath,\n ]\n .concat(\n useFlakeHub\n ? [\n \"--use-flakehub\",\n \"--flakehub-cache-server\",\n flakeHubCacheServer,\n \"--flakehub-api-server\",\n flakeHubApiServer,\n \"--flakehub-api-server-netrc\",\n netrc,\n \"--flakehub-flake-name\",\n flakeHubFlakeName,\n ]\n : [],\n )\n .concat(useGhaCache ? [\"--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 actionsCore.debug(\"Full daemon start command:\");\n actionsCore.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 actionsCore.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 actionsCore.info(\"Launched Magic Nix Cache\");\n actionsCore.exportVariable(ENV_CACHE_DAEMONDIR, daemonDir);\n\n log.unwatch();\n }\n\n private async fetchAutoCacher(): Promise {\n const closurePath = await this.idslib.fetch();\n this.idslib.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 lastPath = paths.at(-2);\n return `${lastPath}/bin/magic-nix-cache`;\n }\n\n async notifyAutoCache(): Promise {\n const daemonDir = process.env[ENV_CACHE_DAEMONDIR];\n\n if (!daemonDir) {\n return;\n }\n\n try {\n actionsCore.debug(`Indicating workflow start`);\n const hostAndPort = inputs.getString(\"listen\");\n const res: Response = await this.client\n .post(`http://${hostAndPort}/api/workflow-start`)\n .json();\n actionsCore.debug(`back from post: ${res}`);\n } catch (e) {\n actionsCore.info(`Error marking the workflow as started:`);\n actionsCore.info(inspect(e));\n actionsCore.info(`Magic Nix Cache may not be running for this workflow.`);\n }\n }\n\n async tearDownAutoCache(): Promise {\n const daemonDir = process.env[ENV_CACHE_DAEMONDIR];\n\n if (!daemonDir) {\n actionsCore.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 actionsCore.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 actionsCore.debug(`about to post to localhost`);\n const hostAndPort = inputs.getString(\"listen\");\n const res: Response = await this.client\n .post(`http://${hostAndPort}/api/workflow-finish`)\n .json();\n actionsCore.debug(`back from post: ${res}`);\n } finally {\n actionsCore.debug(`unwatching the daemon log`);\n log.unwatch();\n }\n\n actionsCore.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 (actionsCore.isDebug()) {\n actionsCore.info(\"Entire log:\");\n const entireLog = readFileSync(path.join(daemonDir, \"daemon.log\"));\n actionsCore.info(entireLog.toString());\n }\n }\n }\n}\n\nfunction main(): void {\n const cacheAction = new MagicNixCacheAction();\n\n cacheAction.idslib.onMain(async () => {\n await cacheAction.setUpAutoCache();\n await cacheAction.notifyAutoCache();\n });\n cacheAction.idslib.onPost(async () => {\n await cacheAction.tearDownAutoCache();\n });\n\n cacheAction.idslib.execute();\n}\n\nmain();\n"],"mappings":"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","names":["actionsCore","fs","os","path","tmpdir"]} \ No newline at end of file +{"version":3,"sources":["../src/helpers.ts","../src/index.ts"],"sourcesContent":["import * as actionsCore from \"@actions/core\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport path from \"node:path\";\nimport { Tail } from \"tail\";\n\nexport function tailLog(daemonDir: string): Tail {\n const log = new Tail(path.join(daemonDir, \"daemon.log\"));\n actionsCore.debug(`tailing daemon.log...`);\n log.on(\"line\", (line) => {\n actionsCore.info(line);\n });\n return log;\n}\n\nexport async 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 flakeHubLogin(destinedNetrcPath);\n } catch (e) {\n actionsCore.info(\"FlakeHub cache disabled.\");\n actionsCore.debug(`Error while logging into FlakeHub: ${e}`);\n }\n return destinedNetrcPath;\n }\n}\n\nasync function flakeHubLogin(netrc: string): Promise {\n const jwt = await actionsCore.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 actionsCore.info(\"Logged in to FlakeHub.\");\n}\n","import { netrcPath, tailLog } from \"./helpers.js\";\nimport * as actionsCore from \"@actions/core\";\nimport { IdsToolbox, inputs } from \"detsys-ts\";\nimport { mkdir, stat } from \"fs/promises\";\nimport got, { Got } from \"got\";\nimport * as http from \"http\";\nimport { SpawnOptions, exec, spawn } from \"node:child_process\";\nimport { openSync, readFileSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { inspect, promisify } from \"node:util\";\n\nconst ENV_CACHE_DAEMONDIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\n\nclass MagicNixCacheAction {\n idslib: IdsToolbox;\n private client: Got;\n\n private daemonDir?: string;\n private unsafeDaemonDir: string;\n\n constructor() {\n this.idslib = new IdsToolbox({\n name: \"magic-nix-cache\",\n fetchStyle: \"gh-env-style\",\n idsProjectName: \"magic-nix-cache-closure\",\n requireNix: \"warn\",\n });\n\n this.client = 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 actionsCore.info(\n `Retrying after error ${error.code}, retry #: ${retryCount}`,\n );\n },\n ],\n },\n });\n\n if (process.env[ENV_CACHE_DAEMONDIR]) {\n this.unsafeDaemonDir = process.env[ENV_CACHE_DAEMONDIR];\n } else {\n this.unsafeDaemonDir = this.idslib.getTemporaryName();\n actionsCore.exportVariable(ENV_CACHE_DAEMONDIR, this.unsafeDaemonDir);\n }\n }\n\n async getDaemonDir(): Promise {\n if (this.daemonDir === undefined) {\n await mkdir(this.unsafeDaemonDir, { recursive: true });\n this.daemonDir = this.unsafeDaemonDir;\n return this.unsafeDaemonDir;\n } else {\n return this.daemonDir;\n }\n }\n\n async daemonDirExists(): Promise {\n const statRes = await stat(this.unsafeDaemonDir);\n return statRes.isDirectory();\n }\n\n async setUpAutoCache(): Promise {\n const requiredEnv = [\n \"ACTIONS_CACHE_URL\",\n \"ACTIONS_RUNTIME_URL\",\n \"ACTIONS_RUNTIME_TOKEN\",\n ];\n\n let anyMissing = false;\n for (const n of requiredEnv) {\n if (!process.env.hasOwnProperty(n)) {\n anyMissing = true;\n actionsCore.warning(\n `Disabling automatic caching since required environment ${n} isn't available`,\n );\n }\n }\n\n if (anyMissing) {\n return;\n }\n\n actionsCore.debug(\n `GitHub Action Cache URL: ${process.env[\"ACTIONS_CACHE_URL\"]}`,\n );\n\n const sourceBinary = inputs.getStringOrNull(\"source-binary\");\n const daemonBin =\n sourceBinary !== null ? sourceBinary : await this.fetchAutoCacher();\n\n let runEnv;\n if (actionsCore.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 = inputs.getString(\"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 actionsCore.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 actionsCore.debug(`Notify server running.`);\n resolveListening(promise);\n });\n });\n });\n\n // Start tailing the daemon log.\n const daemonDir = await this.getDaemonDir();\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 hostAndPort = inputs.getString(\"listen\");\n const upstreamCache = inputs.getString(\"upstream-cache\");\n const diagnosticEndpoint = inputs.getString(\"diagnostic-endpoint\");\n const useFlakeHub = inputs.getBool(\"use-flakehub\");\n const flakeHubCacheServer = inputs.getString(\"flakehub-cache-server\");\n const flakeHubApiServer = inputs.getString(\"flakehub-api-server\");\n const flakeHubFlakeName = inputs.getString(\"flakehub-flake-name\");\n const useGhaCache = inputs.getBool(\"use-gha-cache\");\n\n const daemonCliFlags: string[] = [\n \"--startup-notification-url\",\n `http://127.0.0.1:${notifyPort}`,\n \"--listen\",\n hostAndPort,\n \"--upstream\",\n upstreamCache,\n \"--diagnostic-endpoint\",\n diagnosticEndpoint,\n \"--nix-conf\",\n nixConfPath,\n ]\n .concat(\n useFlakeHub\n ? [\n \"--use-flakehub\",\n \"--flakehub-cache-server\",\n flakeHubCacheServer,\n \"--flakehub-api-server\",\n flakeHubApiServer,\n \"--flakehub-api-server-netrc\",\n netrc,\n \"--flakehub-flake-name\",\n flakeHubFlakeName,\n ]\n : [],\n )\n .concat(useGhaCache ? [\"--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 actionsCore.debug(\"Full daemon start command:\");\n actionsCore.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 actionsCore.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 actionsCore.info(\"Launched Magic Nix Cache\");\n\n log.unwatch();\n }\n\n private async fetchAutoCacher(): Promise {\n const closurePath = await this.idslib.fetch();\n this.idslib.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 lastPath = paths.at(-2);\n return `${lastPath}/bin/magic-nix-cache`;\n }\n\n async notifyAutoCache(): Promise {\n try {\n actionsCore.debug(`Indicating workflow start`);\n const hostAndPort = inputs.getString(\"listen\");\n const res: Response = await this.client\n .post(`http://${hostAndPort}/api/workflow-start`)\n .json();\n actionsCore.debug(`back from post: ${res}`);\n } catch (e) {\n actionsCore.info(`Error marking the workflow as started:`);\n actionsCore.info(inspect(e));\n actionsCore.info(`Magic Nix Cache may not be running for this workflow.`);\n }\n }\n\n async tearDownAutoCache(): Promise {\n if (await this.daemonDirExists()) {\n actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n return;\n }\n const daemonDir = await this.getDaemonDir();\n\n const pidFile = path.join(daemonDir, \"daemon.pid\");\n const pid = parseInt(await fs.readFile(pidFile, { encoding: \"ascii\" }));\n actionsCore.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 actionsCore.debug(`about to post to localhost`);\n const hostAndPort = inputs.getString(\"listen\");\n const res: Response = await this.client\n .post(`http://${hostAndPort}/api/workflow-finish`)\n .json();\n actionsCore.debug(`back from post: ${res}`);\n } finally {\n actionsCore.debug(`unwatching the daemon log`);\n log.unwatch();\n }\n\n actionsCore.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 (actionsCore.isDebug()) {\n actionsCore.info(\"Entire log:\");\n const entireLog = readFileSync(path.join(daemonDir, \"daemon.log\"));\n actionsCore.info(entireLog.toString());\n }\n }\n }\n}\n\nfunction main(): void {\n const cacheAction = new MagicNixCacheAction();\n\n cacheAction.idslib.onMain(async () => {\n await cacheAction.setUpAutoCache();\n await cacheAction.notifyAutoCache();\n });\n cacheAction.idslib.onPost(async () => {\n await cacheAction.tearDownAutoCache();\n });\n\n cacheAction.idslib.execute();\n}\n\nmain();\n"],"mappings":";AAAA,YAAY,iBAAiB;AAC7B,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,OAAO,UAAU;AACjB,SAAS,YAAY;AAEd,SAAS,QAAQ,WAAyB;AAC/C,QAAM,MAAM,IAAI,KAAK,KAAK,KAAK,WAAW,YAAY,CAAC;AACvD,EAAY,kBAAM,uBAAuB;AACzC,MAAI,GAAG,QAAQ,CAAC,SAAS;AACvB,IAAY,iBAAK,IAAI;AAAA,EACvB,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,YAA6B;AACjD,QAAM,oBAAoB,KAAK;AAAA,IAC7B,QAAQ,IAAI,aAAa,KAAQ,UAAO;AAAA,IACxC;AAAA,EACF;AACA,MAAI;AACF,UAAS,UAAO,iBAAiB;AACjC,WAAO;AAAA,EACT,QAAQ;AAEN,UAAM,oBAAoB,KAAK;AAAA,MAC7B,QAAQ,IAAI,aAAa,KAAQ,UAAO;AAAA,MACxC;AAAA,IACF;AACA,QAAI;AACF,YAAM,cAAc,iBAAiB;AAAA,IACvC,SAAS,GAAG;AACV,MAAY,iBAAK,0BAA0B;AAC3C,MAAY,kBAAM,sCAAsC,CAAC,EAAE;AAAA,IAC7D;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAe,cAAc,OAA8B;AACzD,QAAM,MAAM,MAAkB,uBAAW,kBAAkB;AAE3D,QAAS;AAAA,IACP;AAAA,IACA;AAAA,MACE,oDAAoD,GAAG;AAAA,MACvD,gDAAgD,GAAG;AAAA,MACnD,sDAAsD,GAAG;AAAA,IAC3D,EAAE,KAAK,IAAI;AAAA,EACb;AAEA,EAAY,iBAAK,wBAAwB;AAC3C;;;ACnDA,YAAYA,kBAAiB;AAC7B,SAAS,YAAY,cAAc;AACnC,SAAS,OAAO,YAAY;AAC5B,OAAO,SAAkB;AACzB,YAAY,UAAU;AACtB,SAAuB,MAAM,aAAa;AAC1C,SAAS,UAAU,oBAAoB;AACvC,YAAYC,SAAQ;AACpB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,SAAS,iBAAiB;AAEnC,IAAM,sBAAsB;AAE5B,IAAM,sBAAN,MAA0B;AAAA,EAOxB,cAAc;AACZ,SAAK,SAAS,IAAI,WAAW;AAAA,MAC3B,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,YAAY;AAAA,IACd,CAAC;AAED,SAAK,SAAS,IAAI,OAAO;AAAA,MACvB,OAAO;AAAA,QACL,OAAO;AAAA,QACP,SAAS,CAAC,QAAQ,OAAO,OAAO,QAAQ,UAAU,WAAW,OAAO;AAAA,MACtE;AAAA,MACA,OAAO;AAAA,QACL,aAAa;AAAA,UACX,CAAC,OAAO,eAAe;AACrB,YAAY;AAAA,cACV,wBAAwB,MAAM,IAAI,cAAc,UAAU;AAAA,YAC5D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,IAAI,mBAAmB,GAAG;AACpC,WAAK,kBAAkB,QAAQ,IAAI,mBAAmB;AAAA,IACxD,OAAO;AACL,WAAK,kBAAkB,KAAK,OAAO,iBAAiB;AACpD,MAAY,4BAAe,qBAAqB,KAAK,eAAe;AAAA,IACtE;AAAA,EACF;AAAA,EAEA,MAAM,eAAgC;AACpC,QAAI,KAAK,cAAc,QAAW;AAChC,YAAM,MAAM,KAAK,iBAAiB,EAAE,WAAW,KAAK,CAAC;AACrD,WAAK,YAAY,KAAK;AACtB,aAAO,KAAK;AAAA,IACd,OAAO;AACL,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,kBAAoC;AACxC,UAAM,UAAU,MAAM,KAAK,KAAK,eAAe;AAC/C,WAAO,QAAQ,YAAY;AAAA,EAC7B;AAAA,EAEA,MAAM,iBAAgC;AACpC,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,aAAa;AACjB,eAAW,KAAK,aAAa;AAC3B,UAAI,CAAC,QAAQ,IAAI,eAAe,CAAC,GAAG;AAClC,qBAAa;AACb,QAAY;AAAA,UACV,0DAA0D,CAAC;AAAA,QAC7D;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY;AACd;AAAA,IACF;AAEA,IAAY;AAAA,MACV,4BAA4B,QAAQ,IAAI,mBAAmB,CAAC;AAAA,IAC9D;AAEA,UAAM,eAAe,OAAO,gBAAgB,eAAe;AAC3D,UAAM,YACJ,iBAAiB,OAAO,eAAe,MAAM,KAAK,gBAAgB;AAEpE,QAAI;AACJ,QAAgB,qBAAQ,GAAG;AACzB,eAAS;AAAA,QACP,UAAU;AAAA,QACV,gBAAgB;AAAA,QAChB,GAAG,QAAQ;AAAA,MACb;AAAA,IACF,OAAO;AACL,eAAS,QAAQ;AAAA,IACnB;AAEA,UAAM,aAAa,OAAO,UAAU,2BAA2B;AAE/D,UAAM,gBAAgB,IAAI,QAAuB,CAAC,qBAAqB;AACrE,YAAM,UAAU,IAAI,QAAc,OAAO,gBAAgB;AACvD,cAAM,eAAoB,kBAAa,CAAC,KAAK,QAAQ;AACnD,cAAI,IAAI,WAAW,UAAU,IAAI,QAAQ,KAAK;AAC5C,YAAY,mBAAM,8BAA8B;AAChD,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,IAAI;AACZ,yBAAa,MAAM,MAAM;AACvB,0BAAY;AAAA,YACd,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAED,qBAAa,OAAO,YAAY,MAAM;AACpC,UAAY,mBAAM,wBAAwB;AAC1C,2BAAiB,OAAO;AAAA,QAC1B,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAGD,UAAM,YAAY,MAAM,KAAK,aAAa;AAC1C,UAAM,aAAa,GAAG,SAAS;AAC/B,UAAM,SAAS,SAAS,YAAY,GAAG;AACvC,UAAM,MAAM,QAAQ,SAAS;AAC7B,UAAM,QAAQ,MAAM,UAAU;AAC9B,UAAM,cAAc,GAAG,QAAQ,IAAI,MAAM,CAAC;AAE1C,UAAM,cAAc,OAAO,UAAU,QAAQ;AAC7C,UAAM,gBAAgB,OAAO,UAAU,gBAAgB;AACvD,UAAM,qBAAqB,OAAO,UAAU,qBAAqB;AACjE,UAAM,cAAc,OAAO,QAAQ,cAAc;AACjD,UAAM,sBAAsB,OAAO,UAAU,uBAAuB;AACpE,UAAM,oBAAoB,OAAO,UAAU,qBAAqB;AAChE,UAAM,oBAAoB,OAAO,UAAU,qBAAqB;AAChE,UAAM,cAAc,OAAO,QAAQ,eAAe;AAElD,UAAM,iBAA2B;AAAA,MAC/B;AAAA,MACA,oBAAoB,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EACG;AAAA,MACC,cACI;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,IACA,CAAC;AAAA,IACP,EACC,OAAO,cAAc,CAAC,iBAAiB,IAAI,CAAC,CAAC;AAEhD,UAAM,OAAqB;AAAA,MACzB,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,MAChC,KAAK;AAAA,MACL,UAAU;AAAA,IACZ;AAGA,IAAY,mBAAM,4BAA4B;AAC9C,IAAY,mBAAM,GAAG,SAAS,IAAI,eAAe,KAAK,GAAG,CAAC,EAAE;AAG5D,UAAM,SAAS,MAAM,WAAW,gBAAgB,IAAI;AAEpD,UAAM,UAAe,WAAK,WAAW,YAAY;AACjD,UAAS,cAAU,SAAS,GAAG,OAAO,GAAG,EAAE;AAE3C,IAAY,kBAAK,yCAAyC;AAE1D,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,oBAEG,KAAK,CAAC,WAAW;AAChB,gBAAQ;AAAA,MACV,CAAC,EAEA,MAAM,CAAC,QAAQ;AACd,eAAO,IAAI,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAAA,MACpD,CAAC;AACH,aAAO,GAAG,QAAQ,OAAO,MAAM,WAAW;AACxC,YAAI,QAAQ;AACV,iBAAO,IAAI,MAAM,+BAA+B,MAAM,EAAE,CAAC;AAAA,QAC3D,WAAW,MAAM;AACf,iBAAO,IAAI,MAAM,2BAA2B,IAAI,EAAE,CAAC;AAAA,QACrD,OAAO;AACL,iBAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,QAChD;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,WAAO,MAAM;AAEb,IAAY,kBAAK,0BAA0B;AAE3C,QAAI,QAAQ;AAAA,EACd;AAAA,EAEA,MAAc,kBAAmC;AAC/C,UAAM,cAAc,MAAM,KAAK,OAAO,MAAM;AAC5C,SAAK,OAAO,YAAY,cAAc;AACtC,UAAM,EAAE,OAAO,IAAI,MAAM,UAAU,IAAI;AAAA,MACrC,QAAQ,WAAW;AAAA,IACrB;AAEA,UAAM,QAAQ,OAAO,MAAS,OAAG;AAEjC,UAAM,WAAW,MAAM,GAAG,EAAE;AAC5B,WAAO,GAAG,QAAQ;AAAA,EACpB;AAAA,EAEA,MAAM,kBAAiC;AACrC,QAAI;AACF,MAAY,mBAAM,2BAA2B;AAC7C,YAAM,cAAc,OAAO,UAAU,QAAQ;AAC7C,YAAM,MAAgB,MAAM,KAAK,OAC9B,KAAK,UAAU,WAAW,qBAAqB,EAC/C,KAAK;AACR,MAAY,mBAAM,mBAAmB,GAAG,EAAE;AAAA,IAC5C,SAAS,GAAG;AACV,MAAY,kBAAK,wCAAwC;AACzD,MAAY,kBAAK,QAAQ,CAAC,CAAC;AAC3B,MAAY,kBAAK,uDAAuD;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,MAAM,oBAAmC;AACvC,QAAI,MAAM,KAAK,gBAAgB,GAAG;AAChC,MAAY,mBAAM,wCAAwC;AAC1D;AAAA,IACF;AACA,UAAM,YAAY,MAAM,KAAK,aAAa;AAE1C,UAAM,UAAe,WAAK,WAAW,YAAY;AACjD,UAAM,MAAM,SAAS,MAAS,aAAS,SAAS,EAAE,UAAU,QAAQ,CAAC,CAAC;AACtE,IAAY,mBAAM,qBAAqB,GAAG,EAAE;AAC5C,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AAEA,UAAM,MAAM,QAAQ,SAAS;AAE7B,QAAI;AACF,MAAY,mBAAM,4BAA4B;AAC9C,YAAM,cAAc,OAAO,UAAU,QAAQ;AAC7C,YAAM,MAAgB,MAAM,KAAK,OAC9B,KAAK,UAAU,WAAW,sBAAsB,EAChD,KAAK;AACR,MAAY,mBAAM,mBAAmB,GAAG,EAAE;AAAA,IAC5C,UAAE;AACA,MAAY,mBAAM,2BAA2B;AAC7C,UAAI,QAAQ;AAAA,IACd;AAEA,IAAY,mBAAM,SAAS;AAC3B,QAAI;AACF,cAAQ,KAAK,KAAK,SAAS;AAAA,IAC7B,SAAS,GAAG;AACV,UAAI,OAAO,MAAM,YAAY,KAAK,UAAU,KAAK,EAAE,SAAS,SAAS;AACnE,cAAM;AAAA,MACR;AAAA,IACF,UAAE;AACA,UAAgB,qBAAQ,GAAG;AACzB,QAAY,kBAAK,aAAa;AAC9B,cAAM,YAAY,aAAkB,WAAK,WAAW,YAAY,CAAC;AACjE,QAAY,kBAAK,UAAU,SAAS,CAAC;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,OAAa;AACpB,QAAM,cAAc,IAAI,oBAAoB;AAE5C,cAAY,OAAO,OAAO,YAAY;AACpC,UAAM,YAAY,eAAe;AACjC,UAAM,YAAY,gBAAgB;AAAA,EACpC,CAAC;AACD,cAAY,OAAO,OAAO,YAAY;AACpC,UAAM,YAAY,kBAAkB;AAAA,EACtC,CAAC;AAED,cAAY,OAAO,QAAQ;AAC7B;AAEA,KAAK;","names":["actionsCore","fs","os","path"]} \ No newline at end of file diff --git a/package.json b/package.json index 0947056..be2ed24 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "dependencies": { "@actions/core": "^1.10.1", "@actions/exec": "^1.1.1", - "detsys-ts": "github:DeterminateSystems/detsys-ts", + "detsys-ts": "github:DeterminateSystems/detsys-ts#error-condition", "got": "^14.2.1", "tail": "^2.2.6" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17782a3..c15c98b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ dependencies: specifier: ^1.1.1 version: 1.1.1 detsys-ts: - specifier: github:DeterminateSystems/detsys-ts - version: github.com/DeterminateSystems/detsys-ts/d872d42fb693faad3027a08c78620639f23168e1 + specifier: github:DeterminateSystems/detsys-ts#error-condition + version: github.com/DeterminateSystems/detsys-ts/3baa96b315ee9f6b26033191a123eb9015522791 got: specifier: ^14.2.1 version: 14.2.1 @@ -3831,8 +3831,8 @@ packages: engines: {node: '>=10'} dev: true - github.com/DeterminateSystems/detsys-ts/d872d42fb693faad3027a08c78620639f23168e1: - resolution: {tarball: https://codeload.github.com/DeterminateSystems/detsys-ts/tar.gz/d872d42fb693faad3027a08c78620639f23168e1} + github.com/DeterminateSystems/detsys-ts/3baa96b315ee9f6b26033191a123eb9015522791: + resolution: {tarball: https://codeload.github.com/DeterminateSystems/detsys-ts/tar.gz/3baa96b315ee9f6b26033191a123eb9015522791} name: detsys-ts version: 1.0.0 dependencies: diff --git a/src/index.ts b/src/index.ts index 2fef45c..3769af4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import { netrcPath, tailLog } from "./helpers.js"; import * as actionsCore from "@actions/core"; import { IdsToolbox, inputs } from "detsys-ts"; +import { mkdir, stat } from "fs/promises"; import got, { Got } from "got"; import * as http from "http"; import { SpawnOptions, exec, spawn } from "node:child_process"; @@ -16,6 +17,9 @@ class MagicNixCacheAction { idslib: IdsToolbox; private client: Got; + private daemonDir?: string; + private unsafeDaemonDir: string; + constructor() { this.idslib = new IdsToolbox({ name: "magic-nix-cache", @@ -39,10 +43,31 @@ class MagicNixCacheAction { ], }, }); + + if (process.env[ENV_CACHE_DAEMONDIR]) { + this.unsafeDaemonDir = process.env[ENV_CACHE_DAEMONDIR]; + } else { + this.unsafeDaemonDir = this.idslib.getTemporaryName(); + actionsCore.exportVariable(ENV_CACHE_DAEMONDIR, this.unsafeDaemonDir); + } + } + + async getDaemonDir(): Promise { + if (this.daemonDir === undefined) { + await mkdir(this.unsafeDaemonDir, { recursive: true }); + this.daemonDir = this.unsafeDaemonDir; + return this.unsafeDaemonDir; + } else { + return this.daemonDir; + } + } + + async daemonDirExists(): Promise { + const statRes = await stat(this.unsafeDaemonDir); + return statRes.isDirectory(); } async setUpAutoCache(): Promise { - const tmpdir = process.env["RUNNER_TEMP"] || os.tmpdir(); const requiredEnv = [ "ACTIONS_CACHE_URL", "ACTIONS_RUNTIME_URL", @@ -67,7 +92,6 @@ class MagicNixCacheAction { `GitHub Action Cache URL: ${process.env["ACTIONS_CACHE_URL"]}`, ); - const daemonDir = await fs.mkdtemp(path.join(tmpdir, "magic-nix-cache-")); const sourceBinary = inputs.getStringOrNull("source-binary"); const daemonBin = sourceBinary !== null ? sourceBinary : await this.fetchAutoCacher(); @@ -106,6 +130,7 @@ class MagicNixCacheAction { }); // Start tailing the daemon log. + const daemonDir = await this.getDaemonDir(); const outputPath = `${daemonDir}/daemon.log`; const output = openSync(outputPath, "a"); const log = tailLog(daemonDir); @@ -192,7 +217,6 @@ class MagicNixCacheAction { daemon.unref(); actionsCore.info("Launched Magic Nix Cache"); - actionsCore.exportVariable(ENV_CACHE_DAEMONDIR, daemonDir); log.unwatch(); } @@ -211,9 +235,8 @@ class MagicNixCacheAction { } async notifyAutoCache(): Promise { - const daemonDir = process.env[ENV_CACHE_DAEMONDIR]; - - if (!daemonDir) { + if (!await this.daemonDirExists()) { + actionsCore.debug("magic-nix-cache not started - Skipping"); return; } @@ -232,12 +255,11 @@ class MagicNixCacheAction { } async tearDownAutoCache(): Promise { - const daemonDir = process.env[ENV_CACHE_DAEMONDIR]; - - if (!daemonDir) { + if (!await this.daemonDirExists()) { actionsCore.debug("magic-nix-cache not started - Skipping"); return; } + const daemonDir = await this.getDaemonDir(); const pidFile = path.join(daemonDir, "daemon.pid"); const pid = parseInt(await fs.readFile(pidFile, { encoding: "ascii" }));