From 604b09c701dc06e1b65f2651fddf02ef5e8e9d75 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 5 Nov 2024 22:07:59 -0500 Subject: [PATCH 1/3] Update the defaults / docs on the use-flakehub and use-gha-cache options --- action.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/action.yml b/action.yml index 8de381d..d413066 100644 --- a/action.yml +++ b/action.yml @@ -5,8 +5,12 @@ branding: description: "Free, no-configuration Nix cache. Cut CI time by 50% or more by caching to GitHub Actions' cache." inputs: use-gha-cache: - description: "Whether to upload build results to the GitHub Actions cache." - default: true + description: | + Whether to upload build results to the Github Actions cache. + Set to "no-preference" or null to have the GitHub Actions cache turn on if it is available, and FlakeHub Cache is not available (default). + Set to "enabled" or true to explicitly request the GitHub Actions Cache. + Set to "disabled" or false to explicitly disable the GitHub Actions Cache. + default: null required: false listen: description: The host and port to listen on. @@ -18,8 +22,12 @@ inputs: description: "Diagnostic endpoint url where diagnostics and performance data is sent. To disable set this to an empty string." default: "-" use-flakehub: - description: "Whether to upload build results to FlakeHub Cache." - default: true + description: | + Whether to upload build results to FlakeHub Cache. + Set to "no-preference" or null to have FlakeHub Cache turn on opportunistically (default). + Set to "enabled" or true to explicitly request FlakeHub Cache. + Set to "disabled" or false to explicitly disable FlakeHub Cache. + default: null required: false flakehub-cache-server: description: "The FlakeHub binary cache server." From c388adb64db7957db4722512df57d2a389598cae Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 5 Nov 2024 22:25:18 -0500 Subject: [PATCH 2/3] Support the MNC trinary, to allow GHA cache to turn off if FHC is enabled --- dist/index.js | 292 ++++++++++++++++------------------------------ dist/index.js.map | 2 +- src/helpers.ts | 27 +++++ src/index.ts | 16 +-- 4 files changed, 137 insertions(+), 200 deletions(-) diff --git a/dist/index.js b/dist/index.js index 4f3885e..61a6c6b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -33210,14 +33210,14 @@ module.exports["default"] = deferToConnect; /***/ }), -/***/ 5542: +/***/ 3601: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const validator = __nccwpck_require__(1876); -const XMLParser = __nccwpck_require__(4120); -const XMLBuilder = __nccwpck_require__(534); +const validator = __nccwpck_require__(8128); +const XMLParser = __nccwpck_require__(897); +const XMLBuilder = __nccwpck_require__(6762); module.exports = { XMLParser: XMLParser, @@ -33227,33 +33227,7 @@ module.exports = { /***/ }), -/***/ 3628: -/***/ ((module) => { - -function getIgnoreAttributesFn(ignoreAttributes) { - if (typeof ignoreAttributes === 'function') { - return ignoreAttributes - } - if (Array.isArray(ignoreAttributes)) { - return (attrName) => { - for (const pattern of ignoreAttributes) { - if (typeof pattern === 'string' && attrName === pattern) { - return true - } - if (pattern instanceof RegExp && pattern.test(attrName)) { - return true - } - } - } - } - return () => false -} - -module.exports = getIgnoreAttributesFn - -/***/ }), - -/***/ 4340: +/***/ 3480: /***/ ((__unused_webpack_module, exports) => { @@ -33332,12 +33306,12 @@ exports.nameRegexp = nameRegexp; /***/ }), -/***/ 1876: +/***/ 8128: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const util = __nccwpck_require__(4340); +const util = __nccwpck_require__(3480); const defaultOptions = { allowBooleanAttributes: false, //A tag can have attributes without any value @@ -33764,13 +33738,12 @@ function getPositionFromMatch(match) { /***/ }), -/***/ 534: +/***/ 6762: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { //parse Empty Node as self closing node -const buildFromOrderedJs = __nccwpck_require__(4354); -const getIgnoreAttributesFn = __nccwpck_require__(3628) +const buildFromOrderedJs = __nccwpck_require__(9603); const defaultOptions = { attributeNamePrefix: '@_', @@ -33808,12 +33781,11 @@ const defaultOptions = { function Builder(options) { this.options = Object.assign({}, defaultOptions, options); - if (this.options.ignoreAttributes === true || this.options.attributesGroupName) { + if (this.options.ignoreAttributes || this.options.attributesGroupName) { this.isAttribute = function(/*a*/) { return false; }; } else { - this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes) this.attrPrefixLen = this.options.attributeNamePrefix.length; this.isAttribute = isAttribute; } @@ -33842,14 +33814,13 @@ Builder.prototype.build = function(jObj) { [this.options.arrayNodeName] : jObj } } - return this.j2x(jObj, 0, []).val; + return this.j2x(jObj, 0).val; } }; -Builder.prototype.j2x = function(jObj, level, ajPath) { +Builder.prototype.j2x = function(jObj, level) { let attrStr = ''; let val = ''; - const jPath = ajPath.join('.') for (let key in jObj) { if(!Object.prototype.hasOwnProperty.call(jObj, key)) continue; if (typeof jObj[key] === 'undefined') { @@ -33872,9 +33843,9 @@ Builder.prototype.j2x = function(jObj, level, ajPath) { } else if (typeof jObj[key] !== 'object') { //premitive type const attr = this.isAttribute(key); - if (attr && !this.ignoreAttributesFn(attr, jPath)) { + if (attr) { attrStr += this.buildAttrPairStr(attr, '' + jObj[key]); - } else if (!attr) { + }else { //tag value if (key === this.options.textNodeName) { let newval = this.options.tagValueProcessor(key, '' + jObj[key]); @@ -33898,13 +33869,13 @@ Builder.prototype.j2x = function(jObj, level, ajPath) { // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar; } else if (typeof item === 'object') { if(this.options.oneListGroup){ - const result = this.j2x(item, level + 1, ajPath.concat(key)); + const result = this.j2x(item, level + 1); listTagVal += result.val; if (this.options.attributesGroupName && item.hasOwnProperty(this.options.attributesGroupName)) { listTagAttr += result.attrStr } }else{ - listTagVal += this.processTextOrObjNode(item, key, level, ajPath) + listTagVal += this.processTextOrObjNode(item, key, level) } } else { if (this.options.oneListGroup) { @@ -33929,7 +33900,7 @@ Builder.prototype.j2x = function(jObj, level, ajPath) { attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]); } } else { - val += this.processTextOrObjNode(jObj[key], key, level, ajPath) + val += this.processTextOrObjNode(jObj[key], key, level) } } } @@ -33944,8 +33915,8 @@ Builder.prototype.buildAttrPairStr = function(attrName, val){ } else return ' ' + attrName + '="' + val + '"'; } -function processTextOrObjNode (object, key, level, ajPath) { - const result = this.j2x(object, level + 1, ajPath.concat(key)); +function processTextOrObjNode (object, key, level) { + const result = this.j2x(object, level + 1); if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) { return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level); } else { @@ -34055,7 +34026,7 @@ module.exports = Builder; /***/ }), -/***/ 4354: +/***/ 9603: /***/ ((module) => { const EOL = "\n"; @@ -34197,10 +34168,10 @@ module.exports = toXml; /***/ }), -/***/ 7573: +/***/ 8778: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const util = __nccwpck_require__(4340); +const util = __nccwpck_require__(3480); //TODO: handle comments function readDocType(xmlData, i){ @@ -34356,7 +34327,7 @@ module.exports = readDocType; /***/ }), -/***/ 1405: +/***/ 5922: /***/ ((__unused_webpack_module, exports) => { @@ -34410,17 +34381,16 @@ exports.defaultOptions = defaultOptions; /***/ }), -/***/ 3028: +/***/ 1076: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { ///@ts-check -const util = __nccwpck_require__(4340); -const xmlNode = __nccwpck_require__(3555); -const readDocType = __nccwpck_require__(7573); +const util = __nccwpck_require__(3480); +const xmlNode = __nccwpck_require__(308); +const readDocType = __nccwpck_require__(8778); const toNumber = __nccwpck_require__(9578); -const getIgnoreAttributesFn = __nccwpck_require__(3628) // const regx = // '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)' @@ -34469,7 +34439,6 @@ class OrderedObjParser{ this.readStopNodeData = readStopNodeData; this.saveTextToParentTag = saveTextToParentTag; this.addChild = addChild; - this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes) } } @@ -34542,7 +34511,7 @@ function resolveNameSpace(tagname) { const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm'); function buildAttributesMap(attrStr, jPath, tagName) { - if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') { + if (!this.options.ignoreAttributes && typeof attrStr === 'string') { // attrStr = attrStr.replace(/\r?\n/g, ' '); //attrStr = attrStr || attrStr.trim(); @@ -34551,9 +34520,6 @@ function buildAttributesMap(attrStr, jPath, tagName) { const attrs = {}; for (let i = 0; i < len; i++) { const attrName = this.resolveNameSpace(matches[i][1]); - if (this.ignoreAttributesFn(attrName, jPath)) { - continue - } let oldVal = matches[i][4]; let aName = this.options.attributeNamePrefix + attrName; if (attrName.length) { @@ -35023,13 +34989,13 @@ module.exports = OrderedObjParser; /***/ }), -/***/ 4120: +/***/ 897: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const { buildOptions} = __nccwpck_require__(1405); -const OrderedObjParser = __nccwpck_require__(3028); -const { prettify} = __nccwpck_require__(4036); -const validator = __nccwpck_require__(1876); +const { buildOptions} = __nccwpck_require__(5922); +const OrderedObjParser = __nccwpck_require__(1076); +const { prettify} = __nccwpck_require__(9872); +const validator = __nccwpck_require__(8128); class XMLParser{ @@ -35087,7 +35053,7 @@ module.exports = XMLParser; /***/ }), -/***/ 4036: +/***/ 9872: /***/ ((__unused_webpack_module, exports) => { @@ -35207,7 +35173,7 @@ exports.prettify = prettify; /***/ }), -/***/ 3555: +/***/ 308: /***/ ((module) => { @@ -75236,7 +75202,7 @@ exports.XML_CHARKEY = "_"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.stringifyXML = stringifyXML; exports.parseXML = parseXML; -const fast_xml_parser_1 = __nccwpck_require__(5542); +const fast_xml_parser_1 = __nccwpck_require__(3601); const xml_common_js_1 = __nccwpck_require__(8917); function getCommonOptions(options) { var _a; @@ -84288,7 +84254,9 @@ const external_node_dns_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequ var cache = __nccwpck_require__(6878); ;// CONCATENATED MODULE: external "node:child_process" const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:child_process"); -;// CONCATENATED MODULE: ./node_modules/.pnpm/github.com+DeterminateSystems+detsys-ts@65dd73c562ac60a068340f8e0c040bdcf2c59afe_eek3lsas7notlem5iqjfyrzcca/node_modules/detsys-ts/dist/index.js +;// 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@cf1897a891edc164a8240f469cd56d14364e6be1_fq5hfjh622jf54cg4vepqdc2u4/node_modules/detsys-ts/dist/index.js var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) @@ -84518,16 +84486,16 @@ function stringifyError(e) { -async function collectBacktraces(prefixes, startTimestampMs) { +async function collectBacktraces(prefixes) { if (isMacOS) { - return await collectBacktracesMacOS(prefixes, startTimestampMs); + return await collectBacktracesMacOS(prefixes); } if (isLinux) { - return await collectBacktracesSystemd(prefixes, startTimestampMs); + return await collectBacktracesSystemd(prefixes); } return /* @__PURE__ */ new Map(); } -async function collectBacktracesMacOS(prefixes, startTimestampMs) { +async function collectBacktracesMacOS(prefixes) { const backtraces = /* @__PURE__ */ new Map(); try { const { stdout: logJson } = await exec.getExecOutput( @@ -84569,20 +84537,16 @@ async function collectBacktracesMacOS(prefixes, startTimestampMs) { for (const [source, dir] of dirs) { const fileNames = (await (0,promises_namespaceObject.readdir)(dir)).filter((fileName) => { return prefixes.some((prefix) => fileName.startsWith(prefix)); - }).filter((fileName) => { - return !fileName.endsWith(".diag"); }); const doGzip = (0,external_node_util_.promisify)(external_node_zlib_.gzip); for (const fileName of fileNames) { try { - if ((await (0,promises_namespaceObject.stat)(`${dir}/${fileName}`)).ctimeMs >= startTimestampMs) { - const logText = await (0,promises_namespaceObject.readFile)(`${dir}/${fileName}`); - const buf = await doGzip(logText); - backtraces.set( - `backtrace_value_${source}_${fileName}`, - buf.toString("base64") - ); - } + const logText = await (0,promises_namespaceObject.readFile)(`${dir}/${fileName}`); + const buf = await doGzip(logText); + backtraces.set( + `backtrace_value_${source}_${fileName}`, + buf.toString("base64") + ); } catch (innerError) { backtraces.set( `backtrace_failure_${source}_${fileName}`, @@ -84593,14 +84557,13 @@ async function collectBacktracesMacOS(prefixes, startTimestampMs) { } return backtraces; } -async function collectBacktracesSystemd(prefixes, startTimestampMs) { - const sinceSeconds = Math.ceil((Date.now() - startTimestampMs) / 1e3); +async function collectBacktracesSystemd(prefixes) { const backtraces = /* @__PURE__ */ new Map(); const coredumps = []; try { const { stdout: coredumpjson } = await exec.getExecOutput( "coredumpctl", - ["--json=pretty", "list", "--since", `${sinceSeconds} seconds ago`], + ["--json=pretty", "list", "--since", "1 hour ago"], { silent: true } @@ -84762,7 +84725,7 @@ var ALLOWED_SUFFIXES = [ ]; var DEFAULT_IDS_HOST = "https://install.determinate.systems"; var LOOKUP = process.env["IDS_LOOKUP"] ?? DEFAULT_LOOKUP; -var DEFAULT_TIMEOUT = 1e4; +var DEFAULT_TIMEOUT = 3e4; var IdsHost = class { constructor(idsProjectName, diagnosticsSuffix, runtimeDiagnosticsUrl) { this.idsProjectName = idsProjectName; @@ -84777,7 +84740,7 @@ var IdsHost = class { request: DEFAULT_TIMEOUT }, retry: { - limit: Math.max((await this.getUrlsByPreference()).length, 3), + limit: (await this.getUrlsByPreference()).length, methods: ["GET", "HEAD"] }, hooks: { @@ -84787,7 +84750,7 @@ var IdsHost = class { this.markCurrentHostBroken(); const nextUrl = await this.getRootUrl(); if (recordFailoverCallback !== void 0) { - recordFailoverCallback(error3, prevUrl, nextUrl); + recordFailoverCallback(prevUrl, nextUrl); } core.info( `Retrying after error ${error3.code}, retry #: ${retryCount}` @@ -85171,10 +85134,8 @@ var FACT_NIX_STORE_CHECK_ERROR = "nix_store_check_error"; var STATE_KEY_EXECUTION_PHASE = "detsys_action_execution_phase"; var STATE_KEY_NIX_NOT_FOUND = "detsys_action_nix_not_found"; var STATE_NOT_FOUND = "not-found"; -var STATE_KEY_CROSS_PHASE_ID = "detsys_cross_phase_id"; -var STATE_BACKTRACE_START_TIMESTAMP = "detsys_backtrace_start_timestamp"; -var DIAGNOSTIC_ENDPOINT_TIMEOUT_MS = 1e4; -var CHECK_IN_ENDPOINT_TIMEOUT_MS = 1e3; +var DIAGNOSTIC_ENDPOINT_TIMEOUT_MS = 3e4; +var CHECK_IN_ENDPOINT_TIMEOUT_MS = 5e3; var DetSysAction = class { determineExecutionPhase() { const currentPhase = core.getState(STATE_KEY_EXECUTION_PHASE); @@ -85200,8 +85161,6 @@ var DetSysAction = class { this.features = {}; this.featureEventMetadata = {}; this.events = []; - this.getCrossPhaseId(); - this.collectBacktraceSetup(); this.facts = { $lib: "idslib", $lib_version: version, @@ -85291,15 +85250,6 @@ var DetSysAction = class { getUniqueId() { return this.identity.run_differentiator || process.env.RUNNER_TRACKING_ID || (0,external_node_crypto_namespaceObject.randomUUID)(); } - // This ID will be saved in the action's state, to be persisted across phase steps - getCrossPhaseId() { - let crossPhaseId = core.getState(STATE_KEY_CROSS_PHASE_ID); - if (crossPhaseId === "") { - crossPhaseId = (0,external_node_crypto_namespaceObject.randomUUID)(); - core.saveState(STATE_KEY_CROSS_PHASE_ID, crossPhaseId); - } - return crossPhaseId; - } getCorrelationHashes() { return this.identity; } @@ -85398,15 +85348,12 @@ var DetSysAction = class { } } async getClient() { - return await this.idsHost.getGot( - (incitingError, prevUrl, nextUrl) => { - this.recordPlausibleTimeout(incitingError); - this.recordEvent("ids-failover", { - previousUrl: prevUrl.toString(), - nextUrl: nextUrl.toString() - }); - } - ); + return await this.idsHost.getGot((prevUrl, nextUrl) => { + this.recordEvent("ids-failover", { + previousUrl: prevUrl.toString(), + nextUrl: nextUrl.toString() + }); + }); } async checkIn() { const checkin = await this.requestCheckIn(); @@ -85490,27 +85437,12 @@ var DetSysAction = class { } }).json(); } catch (e) { - this.recordPlausibleTimeout(e); core.debug(`Error checking in: ${stringifyError2(e)}`); this.idsHost.markCurrentHostBroken(); } } return void 0; } - recordPlausibleTimeout(e) { - if (e instanceof TimeoutError && "timings" in e && "request" in e) { - const reportContext = { - url: e.request.requestUrl?.toString(), - retry_count: e.request.retryCount - }; - for (const [key, value] of Object.entries(e.timings.phases)) { - if (Number.isFinite(value)) { - reportContext[`timing_phase_${key}`] = value; - } - } - this.recordEvent("timeout", reportContext); - } - } /** * Fetch an artifact, such as a tarball, from the location determined by the * `source-*` inputs. If `source-binary` is specified, this will return a path @@ -85554,9 +85486,13 @@ var DetSysAction = class { `No match from the cache, re-fetching from the redirect: ${versionCheckup.url}` ); const destFile = this.getTemporaryName(); - const fetchStream = await this.downloadFile( - new URL(versionCheckup.url), - destFile + const fetchStream = (await this.getClient()).stream(versionCheckup.url); + await (0,external_node_stream_promises_namespaceObject.pipeline)( + fetchStream, + (0,external_node_fs_namespaceObject.createWriteStream)(destFile, { + encoding: "binary", + mode: 493 + }) ); if (fetchStream.response?.headers.etag) { const v = fetchStream.response.headers.etag; @@ -85567,9 +85503,6 @@ var DetSysAction = class { } } return destFile; - } catch (e) { - this.recordPlausibleTimeout(e); - throw e; } finally { core.endGroup(); } @@ -85583,36 +85516,6 @@ var DetSysAction = class { core.setFailed(`strict mode failure: ${msg}`); } } - async downloadFile(url, destination) { - const client = await this.getClient(); - return new Promise((resolve, reject) => { - let writeStream; - let failed = false; - const retry = (stream) => { - if (writeStream) { - writeStream.destroy(); - } - writeStream = (0,external_node_fs_namespaceObject.createWriteStream)(destination, { - encoding: "binary", - mode: 493 - }); - writeStream.once("error", (error3) => { - failed = true; - reject(error3); - }); - writeStream.on("finish", () => { - if (!failed) { - resolve(stream); - } - }); - stream.once("retry", (_count, _error, createRetryStream) => { - retry(createRetryStream()); - }); - stream.pipe(writeStream); - }; - retry(client.stream(url)); - }); - } async complete() { this.recordEvent(`complete_${this.executionPhase}`); await this.submitEvents(); @@ -85700,23 +85603,10 @@ var DetSysAction = class { process.chdir(startCwd); } } - collectBacktraceSetup() { - if (process.env.DETSYS_BACKTRACE_COLLECTOR === "") { - core.exportVariable( - "DETSYS_BACKTRACE_COLLECTOR", - this.getCrossPhaseId() - ); - core.saveState(STATE_BACKTRACE_START_TIMESTAMP, Date.now()); - } - } async collectBacktraces() { try { - if (process.env.DETSYS_BACKTRACE_COLLECTOR !== this.getCrossPhaseId()) { - return; - } const backtraces = await collectBacktraces( - this.actionOptions.binaryNamePrefixes, - parseInt(core.getState(STATE_BACKTRACE_START_TIMESTAMP)) + this.actionOptions.binaryNamePrefixes ); core.debug(`Backtraces identified: ${backtraces.size}`); if (backtraces.size > 0) { @@ -85836,7 +85726,6 @@ var DetSysAction = class { } }); } catch (err) { - this.recordPlausibleTimeout(err); core.debug( `Error submitting diagnostics event to ${diagnosticsUrl}: ${stringifyError2(err)}` ); @@ -85856,11 +85745,7 @@ function makeOptionsConfident(actionOptions) { fetchStyle: actionOptions.fetchStyle, legacySourcePrefix: actionOptions.legacySourcePrefix, requireNix: actionOptions.requireNix, - binaryNamePrefixes: actionOptions.binaryNamePrefixes ?? [ - "nix", - "determinate-nixd", - actionOptions.name - ] + binaryNamePrefixes: actionOptions.binaryNamePrefixes || ["nix"] }; core.debug("idslib options:"); core.debug(JSON.stringify(finalOpts, void 0, 2)); @@ -85887,6 +85772,26 @@ var external_http_ = __nccwpck_require__(3685); +function getTrinaryInput(name) { + const trueValue = ["true", "True", "TRUE", "enabled"]; + const falseValue = ["false", "False", "FALSE", "disabled"]; + const noPreferenceValue = ["", "null", "no-preference"]; + const val = core.getInput(name); + if (trueValue.includes(val)) { + return "enabled"; + } + if (falseValue.includes(val)) { + return "disabled"; + } + if (noPreferenceValue.includes(val)) { + return "no-preference"; + } + const possibleValues = trueValue.concat(falseValue).concat(noPreferenceValue).join(" | "); + throw new TypeError( + `Input ${name} does not look like a trinary, which requires one of: +${possibleValues}` + ); +} function tailLog(daemonDir) { const log = new tail/* Tail */.x(external_node_path_namespaceObject.join(daemonDir, "daemon.log")); core.debug(`tailing daemon.log...`); @@ -86095,11 +86000,11 @@ var MagicNixCacheAction = class extends DetSysAction { const nixConfPath = `${process.env["HOME"]}/.config/nix/nix.conf`; const upstreamCache = inputs_exports.getString("upstream-cache"); const diagnosticEndpoint = inputs_exports.getString("diagnostic-endpoint"); - const useFlakeHub = inputs_exports.getBool("use-flakehub"); + const useFlakeHub = getTrinaryInput("use-flakehub"); const flakeHubCacheServer = inputs_exports.getString("flakehub-cache-server"); const flakeHubApiServer = inputs_exports.getString("flakehub-api-server"); const flakeHubFlakeName = inputs_exports.getString("flakehub-flake-name"); - const useGhaCache = inputs_exports.getBool("use-gha-cache"); + const useGhaCache = getTrinaryInput("use-gha-cache"); const daemonCliFlags = [ "--startup-notification-url", `http://127.0.0.1:${notifyPort}`, @@ -86110,10 +86015,13 @@ var MagicNixCacheAction = class extends DetSysAction { "--diagnostic-endpoint", diagnosticEndpoint, "--nix-conf", - nixConfPath + nixConfPath, + "--use-gha-cache", + useGhaCache, + "--use-flakehub", + useFlakeHub ].concat(this.diffStore ? ["--diff-store"] : []).concat( - useFlakeHub ? [ - "--use-flakehub", + useFlakeHub !== "disabled" ? [ "--flakehub-cache-server", flakeHubCacheServer, "--flakehub-api-server", @@ -86123,7 +86031,7 @@ var MagicNixCacheAction = class extends DetSysAction { "--flakehub-flake-name", flakeHubFlakeName ] : [] - ).concat(useGhaCache ? ["--use-gha-cache"] : []); + ); const opts = { stdio: ["ignore", output, output], env: runEnv, diff --git a/dist/index.js.map b/dist/index.js.map index bfb587f..6ceb5b0 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(\n \"FlakeHub Cache is disabled due to missing or invalid token\",\n );\n actionsCore.info(\n `If you're signed up for FlakeHub Cache, make sure that your Actions config has a \\`permissions\\` block with \\`id-token\\` set to \"write\" and \\`contents\\` set to \"read\"`,\n );\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 { DetSysAction, inputs, stringifyError } from \"detsys-ts\";\nimport got, { Got, Response } from \"got\";\nimport * as http from \"http\";\nimport { SpawnOptions, spawn } from \"node:child_process\";\nimport { mkdirSync, openSync, readFileSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\n// The ENV_DAEMON_DIR is intended to determine if we \"own\" the daemon or not,\n// in the case that a user has put the magic nix cache into their workflow\n// twice.\nconst ENV_DAEMON_DIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\n\nconst FACT_ENV_VARS_PRESENT = \"required_env_vars_present\";\nconst FACT_DIFF_STORE_ENABLED = \"diff_store\";\nconst FACT_ALREADY_RUNNING = \"noop_mode\";\n\nconst STATE_DAEMONDIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\nconst STATE_ERROR_IN_MAIN = \"ERROR_IN_MAIN\";\nconst STATE_STARTED = \"MAGIC_NIX_CACHE_STARTED\";\nconst STARTED_HINT = \"true\";\n\nconst TEXT_ALREADY_RUNNING =\n \"Magic Nix Cache is already running, this workflow job is in noop mode. Is the Magic Nix Cache in the workflow twice?\";\nconst TEXT_TRUST_UNTRUSTED =\n \"The Nix daemon does not consider the user running this workflow to be trusted. Magic Nix Cache is disabled.\";\nconst TEXT_TRUST_UNKNOWN =\n \"The Nix daemon may not consider the user running this workflow to be trusted. Magic Nix Cache may not start correctly.\";\n\nclass MagicNixCacheAction extends DetSysAction {\n private hostAndPort: string;\n private diffStore: boolean;\n private httpClient: Got;\n private daemonDir: string;\n private daemonStarted: boolean;\n\n // This is set to `true` if the MNC is already running, in which case the\n // workflow will use the existing process rather than starting a new one.\n private alreadyRunning: boolean;\n\n constructor() {\n super({\n name: \"magic-nix-cache\",\n fetchStyle: \"gh-env-style\",\n idsProjectName: \"magic-nix-cache-closure\",\n requireNix: \"warn\",\n diagnosticsSuffix: \"perf\",\n });\n\n this.hostAndPort = inputs.getString(\"listen\");\n this.diffStore = inputs.getBool(\"diff-store\");\n\n this.addFact(FACT_DIFF_STORE_ENABLED, this.diffStore);\n\n this.httpClient = 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 this.daemonStarted = actionsCore.getState(STATE_STARTED) === STARTED_HINT;\n\n if (actionsCore.getState(STATE_DAEMONDIR) !== \"\") {\n this.daemonDir = actionsCore.getState(STATE_DAEMONDIR);\n } else {\n this.daemonDir = this.getTemporaryName();\n mkdirSync(this.daemonDir);\n actionsCore.saveState(STATE_DAEMONDIR, this.daemonDir);\n }\n\n if (process.env[ENV_DAEMON_DIR] === undefined) {\n this.alreadyRunning = false;\n actionsCore.exportVariable(ENV_DAEMON_DIR, this.daemonDir);\n } else {\n this.alreadyRunning = process.env[ENV_DAEMON_DIR] !== this.daemonDir;\n }\n this.addFact(FACT_ALREADY_RUNNING, this.alreadyRunning);\n\n this.stapleFile(\"daemon.log\", path.join(this.daemonDir, \"daemon.log\"));\n }\n\n async main(): Promise {\n if (this.alreadyRunning) {\n actionsCore.warning(TEXT_ALREADY_RUNNING);\n return;\n }\n\n if (this.nixStoreTrust === \"untrusted\") {\n actionsCore.warning(TEXT_TRUST_UNTRUSTED);\n return;\n } else if (this.nixStoreTrust === \"unknown\") {\n actionsCore.info(TEXT_TRUST_UNKNOWN);\n }\n\n await this.setUpAutoCache();\n await this.notifyAutoCache();\n }\n\n async post(): Promise {\n // If strict mode is off and there was an error in main, such as the daemon not starting,\n // then the post phase is skipped with a warning.\n if (!this.strictMode && this.errorInMain) {\n actionsCore.warning(\n `skipping post phase due to error in main phase: ${this.errorInMain}`,\n );\n return;\n }\n\n if (this.alreadyRunning) {\n actionsCore.debug(TEXT_ALREADY_RUNNING);\n return;\n }\n\n if (this.nixStoreTrust === \"untrusted\") {\n actionsCore.debug(TEXT_TRUST_UNTRUSTED);\n return;\n } else if (this.nixStoreTrust === \"unknown\") {\n actionsCore.debug(TEXT_TRUST_UNKNOWN);\n }\n\n await this.tearDownAutoCache();\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 this.addFact(FACT_ENV_VARS_PRESENT, !anyMissing);\n if (anyMissing) {\n return;\n }\n\n if (this.daemonStarted) {\n actionsCore.debug(\"Already started.\");\n return;\n }\n\n actionsCore.debug(\n `GitHub Action Cache URL: ${process.env[\"ACTIONS_CACHE_URL\"]}`,\n );\n\n const daemonBin = await this.unpackClosure(\"magic-nix-cache\");\n\n let runEnv;\n if (actionsCore.isDebug()) {\n runEnv = {\n RUST_LOG: \"debug,magic_nix_cache=trace,gha_cache=trace\",\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 = `${this.daemonDir}/daemon.log`;\n const output = openSync(outputPath, \"a\");\n const log = tailLog(this.daemonDir);\n const netrc = await netrcPath();\n const nixConfPath = `${process.env[\"HOME\"]}/.config/nix/nix.conf`;\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 this.hostAndPort,\n \"--upstream\",\n upstreamCache,\n \"--diagnostic-endpoint\",\n diagnosticEndpoint,\n \"--nix-conf\",\n nixConfPath,\n ]\n .concat(this.diffStore ? [\"--diff-store\"] : [])\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 this.daemonStarted = true;\n actionsCore.saveState(STATE_STARTED, STARTED_HINT);\n\n const pidFile = path.join(this.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) => {\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((e: unknown) => {\n this.exitMain(`Error in notifyPromise: ${stringifyError(e)}`);\n });\n\n daemon.on(\"exit\", async (code, signal) => {\n let msg: string;\n if (signal) {\n msg = `Daemon was killed by signal ${signal}`;\n } else if (code) {\n msg = `Daemon exited with code ${code}`;\n } else {\n msg = \"Daemon unexpectedly exited\";\n }\n\n this.exitMain(msg);\n });\n });\n\n daemon.unref();\n\n actionsCore.info(\"Launched Magic Nix Cache\");\n\n log.unwatch();\n }\n\n private async notifyAutoCache(): Promise {\n if (!this.daemonStarted) {\n actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n return;\n }\n\n try {\n actionsCore.debug(`Indicating workflow start`);\n const res: Response = await this.httpClient.post(\n `http://${this.hostAndPort}/api/workflow-start`,\n );\n\n actionsCore.debug(\n `Response from POST to /api/workflow-start: (status: ${res.statusCode}, body: ${res.body})`,\n );\n\n if (res.statusCode !== 200) {\n throw new Error(\n `Failed to trigger workflow start hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n );\n }\n\n actionsCore.debug(`back from post: ${res.body}`);\n } catch (e: unknown) {\n this.exitMain(`Error starting the Magic Nix Cache: ${stringifyError(e)}`);\n }\n }\n\n async tearDownAutoCache(): Promise {\n if (!this.daemonStarted) {\n actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n return;\n }\n\n const pidFile = path.join(this.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(this.daemonDir);\n\n try {\n actionsCore.debug(`about to post to localhost`);\n const res: Response = await this.httpClient.post(\n `http://${this.hostAndPort}/api/workflow-finish`,\n );\n\n actionsCore.debug(\n `Response from POST to /api/workflow-finish: (status: ${res.statusCode}, body: ${res.body})`,\n );\n\n if (res.statusCode !== 200) {\n throw new Error(\n `Failed to trigger workflow finish hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n );\n }\n } finally {\n actionsCore.debug(`unwatching the daemon log`);\n log.unwatch();\n }\n\n actionsCore.debug(`killing daemon process ${pid}`);\n\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (e: unknown) {\n if (typeof e === \"object\" && e && \"code\" in e && e.code !== \"ESRCH\") {\n // Throw an error only in strict mode, otherwise ignore because\n // we're in the post phase and shutting down after this anyway\n if (this.strictMode) {\n throw e;\n }\n }\n } finally {\n if (actionsCore.isDebug()) {\n actionsCore.info(\"Entire log:\");\n const entireLog = readFileSync(path.join(this.daemonDir, \"daemon.log\"));\n actionsCore.info(entireLog.toString());\n }\n }\n }\n\n // Exit the workflow during the main phase. If strict mode is set, fail; if not, save the error\n // message to the workflow's state and exit successfully.\n private exitMain(msg: string): void {\n if (this.strictMode) {\n actionsCore.setFailed(msg);\n } else {\n actionsCore.saveState(STATE_ERROR_IN_MAIN, msg);\n process.exit(0);\n }\n }\n\n // If the main phase threw an error (not in strict mode), this will be a non-empty\n // string available in the post phase.\n private get errorInMain(): string | undefined {\n const state = actionsCore.getState(STATE_ERROR_IN_MAIN);\n return state !== \"\" ? state : undefined;\n }\n}\n\nfunction main(): void {\n new MagicNixCacheAction().execute();\n}\n\nmain();\n"],"mappings":"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","names":["actionsCore","fs","path"]} \ 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 getTrinaryInput(\n name: string,\n): \"enabled\" | \"disabled\" | \"no-preference\" {\n const trueValue = [\"true\", \"True\", \"TRUE\", \"enabled\"];\n const falseValue = [\"false\", \"False\", \"FALSE\", \"disabled\"];\n const noPreferenceValue = [\"\", \"null\", \"no-preference\"];\n\n const val = actionsCore.getInput(name);\n if (trueValue.includes(val)) {\n return \"enabled\";\n }\n if (falseValue.includes(val)) {\n return \"disabled\";\n }\n if (noPreferenceValue.includes(val)) {\n return \"no-preference\";\n }\n\n const possibleValues = trueValue\n .concat(falseValue)\n .concat(noPreferenceValue)\n .join(\" | \");\n throw new TypeError(\n `Input ${name} does not look like a trinary, which requires one of:\\n${possibleValues}`,\n );\n}\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(\n \"FlakeHub Cache is disabled due to missing or invalid token\",\n );\n actionsCore.info(\n `If you're signed up for FlakeHub Cache, make sure that your Actions config has a \\`permissions\\` block with \\`id-token\\` set to \"write\" and \\`contents\\` set to \"read\"`,\n );\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 { getTrinaryInput, netrcPath, tailLog } from \"./helpers.js\";\nimport * as actionsCore from \"@actions/core\";\nimport { DetSysAction, inputs, stringifyError } from \"detsys-ts\";\nimport got, { Got, Response } from \"got\";\nimport * as http from \"http\";\nimport { SpawnOptions, spawn } from \"node:child_process\";\nimport { mkdirSync, openSync, readFileSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\n// The ENV_DAEMON_DIR is intended to determine if we \"own\" the daemon or not,\n// in the case that a user has put the magic nix cache into their workflow\n// twice.\nconst ENV_DAEMON_DIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\n\nconst FACT_ENV_VARS_PRESENT = \"required_env_vars_present\";\nconst FACT_DIFF_STORE_ENABLED = \"diff_store\";\nconst FACT_ALREADY_RUNNING = \"noop_mode\";\n\nconst STATE_DAEMONDIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\nconst STATE_ERROR_IN_MAIN = \"ERROR_IN_MAIN\";\nconst STATE_STARTED = \"MAGIC_NIX_CACHE_STARTED\";\nconst STARTED_HINT = \"true\";\n\nconst TEXT_ALREADY_RUNNING =\n \"Magic Nix Cache is already running, this workflow job is in noop mode. Is the Magic Nix Cache in the workflow twice?\";\nconst TEXT_TRUST_UNTRUSTED =\n \"The Nix daemon does not consider the user running this workflow to be trusted. Magic Nix Cache is disabled.\";\nconst TEXT_TRUST_UNKNOWN =\n \"The Nix daemon may not consider the user running this workflow to be trusted. Magic Nix Cache may not start correctly.\";\n\nclass MagicNixCacheAction extends DetSysAction {\n private hostAndPort: string;\n private diffStore: boolean;\n private httpClient: Got;\n private daemonDir: string;\n private daemonStarted: boolean;\n\n // This is set to `true` if the MNC is already running, in which case the\n // workflow will use the existing process rather than starting a new one.\n private alreadyRunning: boolean;\n\n constructor() {\n super({\n name: \"magic-nix-cache\",\n fetchStyle: \"gh-env-style\",\n idsProjectName: \"magic-nix-cache-closure\",\n requireNix: \"warn\",\n diagnosticsSuffix: \"perf\",\n });\n\n this.hostAndPort = inputs.getString(\"listen\");\n this.diffStore = inputs.getBool(\"diff-store\");\n\n this.addFact(FACT_DIFF_STORE_ENABLED, this.diffStore);\n\n this.httpClient = 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 this.daemonStarted = actionsCore.getState(STATE_STARTED) === STARTED_HINT;\n\n if (actionsCore.getState(STATE_DAEMONDIR) !== \"\") {\n this.daemonDir = actionsCore.getState(STATE_DAEMONDIR);\n } else {\n this.daemonDir = this.getTemporaryName();\n mkdirSync(this.daemonDir);\n actionsCore.saveState(STATE_DAEMONDIR, this.daemonDir);\n }\n\n if (process.env[ENV_DAEMON_DIR] === undefined) {\n this.alreadyRunning = false;\n actionsCore.exportVariable(ENV_DAEMON_DIR, this.daemonDir);\n } else {\n this.alreadyRunning = process.env[ENV_DAEMON_DIR] !== this.daemonDir;\n }\n this.addFact(FACT_ALREADY_RUNNING, this.alreadyRunning);\n\n this.stapleFile(\"daemon.log\", path.join(this.daemonDir, \"daemon.log\"));\n }\n\n async main(): Promise {\n if (this.alreadyRunning) {\n actionsCore.warning(TEXT_ALREADY_RUNNING);\n return;\n }\n\n if (this.nixStoreTrust === \"untrusted\") {\n actionsCore.warning(TEXT_TRUST_UNTRUSTED);\n return;\n } else if (this.nixStoreTrust === \"unknown\") {\n actionsCore.info(TEXT_TRUST_UNKNOWN);\n }\n\n await this.setUpAutoCache();\n await this.notifyAutoCache();\n }\n\n async post(): Promise {\n // If strict mode is off and there was an error in main, such as the daemon not starting,\n // then the post phase is skipped with a warning.\n if (!this.strictMode && this.errorInMain) {\n actionsCore.warning(\n `skipping post phase due to error in main phase: ${this.errorInMain}`,\n );\n return;\n }\n\n if (this.alreadyRunning) {\n actionsCore.debug(TEXT_ALREADY_RUNNING);\n return;\n }\n\n if (this.nixStoreTrust === \"untrusted\") {\n actionsCore.debug(TEXT_TRUST_UNTRUSTED);\n return;\n } else if (this.nixStoreTrust === \"unknown\") {\n actionsCore.debug(TEXT_TRUST_UNKNOWN);\n }\n\n await this.tearDownAutoCache();\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 this.addFact(FACT_ENV_VARS_PRESENT, !anyMissing);\n if (anyMissing) {\n return;\n }\n\n if (this.daemonStarted) {\n actionsCore.debug(\"Already started.\");\n return;\n }\n\n actionsCore.debug(\n `GitHub Action Cache URL: ${process.env[\"ACTIONS_CACHE_URL\"]}`,\n );\n\n const daemonBin = await this.unpackClosure(\"magic-nix-cache\");\n\n let runEnv;\n if (actionsCore.isDebug()) {\n runEnv = {\n RUST_LOG: \"debug,magic_nix_cache=trace,gha_cache=trace\",\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 = `${this.daemonDir}/daemon.log`;\n const output = openSync(outputPath, \"a\");\n const log = tailLog(this.daemonDir);\n const netrc = await netrcPath();\n const nixConfPath = `${process.env[\"HOME\"]}/.config/nix/nix.conf`;\n const upstreamCache = inputs.getString(\"upstream-cache\");\n const diagnosticEndpoint = inputs.getString(\"diagnostic-endpoint\");\n const useFlakeHub = getTrinaryInput(\"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 = getTrinaryInput(\"use-gha-cache\");\n\n const daemonCliFlags: string[] = [\n \"--startup-notification-url\",\n `http://127.0.0.1:${notifyPort}`,\n \"--listen\",\n this.hostAndPort,\n \"--upstream\",\n upstreamCache,\n \"--diagnostic-endpoint\",\n diagnosticEndpoint,\n \"--nix-conf\",\n nixConfPath,\n \"--use-gha-cache\",\n useGhaCache,\n \"--use-flakehub\",\n useFlakeHub,\n ]\n .concat(this.diffStore ? [\"--diff-store\"] : [])\n .concat(\n useFlakeHub !== \"disabled\"\n ? [\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\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 this.daemonStarted = true;\n actionsCore.saveState(STATE_STARTED, STARTED_HINT);\n\n const pidFile = path.join(this.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) => {\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((e: unknown) => {\n this.exitMain(`Error in notifyPromise: ${stringifyError(e)}`);\n });\n\n daemon.on(\"exit\", async (code, signal) => {\n let msg: string;\n if (signal) {\n msg = `Daemon was killed by signal ${signal}`;\n } else if (code) {\n msg = `Daemon exited with code ${code}`;\n } else {\n msg = \"Daemon unexpectedly exited\";\n }\n\n this.exitMain(msg);\n });\n });\n\n daemon.unref();\n\n actionsCore.info(\"Launched Magic Nix Cache\");\n\n log.unwatch();\n }\n\n private async notifyAutoCache(): Promise {\n if (!this.daemonStarted) {\n actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n return;\n }\n\n try {\n actionsCore.debug(`Indicating workflow start`);\n const res: Response = await this.httpClient.post(\n `http://${this.hostAndPort}/api/workflow-start`,\n );\n\n actionsCore.debug(\n `Response from POST to /api/workflow-start: (status: ${res.statusCode}, body: ${res.body})`,\n );\n\n if (res.statusCode !== 200) {\n throw new Error(\n `Failed to trigger workflow start hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n );\n }\n\n actionsCore.debug(`back from post: ${res.body}`);\n } catch (e: unknown) {\n this.exitMain(`Error starting the Magic Nix Cache: ${stringifyError(e)}`);\n }\n }\n\n async tearDownAutoCache(): Promise {\n if (!this.daemonStarted) {\n actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n return;\n }\n\n const pidFile = path.join(this.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(this.daemonDir);\n\n try {\n actionsCore.debug(`about to post to localhost`);\n const res: Response = await this.httpClient.post(\n `http://${this.hostAndPort}/api/workflow-finish`,\n );\n\n actionsCore.debug(\n `Response from POST to /api/workflow-finish: (status: ${res.statusCode}, body: ${res.body})`,\n );\n\n if (res.statusCode !== 200) {\n throw new Error(\n `Failed to trigger workflow finish hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n );\n }\n } finally {\n actionsCore.debug(`unwatching the daemon log`);\n log.unwatch();\n }\n\n actionsCore.debug(`killing daemon process ${pid}`);\n\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (e: unknown) {\n if (typeof e === \"object\" && e && \"code\" in e && e.code !== \"ESRCH\") {\n // Throw an error only in strict mode, otherwise ignore because\n // we're in the post phase and shutting down after this anyway\n if (this.strictMode) {\n throw e;\n }\n }\n } finally {\n if (actionsCore.isDebug()) {\n actionsCore.info(\"Entire log:\");\n const entireLog = readFileSync(path.join(this.daemonDir, \"daemon.log\"));\n actionsCore.info(entireLog.toString());\n }\n }\n }\n\n // Exit the workflow during the main phase. If strict mode is set, fail; if not, save the error\n // message to the workflow's state and exit successfully.\n private exitMain(msg: string): void {\n if (this.strictMode) {\n actionsCore.setFailed(msg);\n } else {\n actionsCore.saveState(STATE_ERROR_IN_MAIN, msg);\n process.exit(0);\n }\n }\n\n // If the main phase threw an error (not in strict mode), this will be a non-empty\n // string available in the post phase.\n private get errorInMain(): string | undefined {\n const state = actionsCore.getState(STATE_ERROR_IN_MAIN);\n return state !== \"\" ? state : undefined;\n }\n}\n\nfunction main(): void {\n new MagicNixCacheAction().execute();\n}\n\nmain();\n"],"mappings":"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","names":["actionsCore","fs","path"]} \ No newline at end of file diff --git a/src/helpers.ts b/src/helpers.ts index c2a7596..0525980 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -4,6 +4,33 @@ import * as os from "node:os"; import path from "node:path"; import { Tail } from "tail"; +export function getTrinaryInput( + name: string, +): "enabled" | "disabled" | "no-preference" { + const trueValue = ["true", "True", "TRUE", "enabled"]; + const falseValue = ["false", "False", "FALSE", "disabled"]; + const noPreferenceValue = ["", "null", "no-preference"]; + + const val = actionsCore.getInput(name); + if (trueValue.includes(val)) { + return "enabled"; + } + if (falseValue.includes(val)) { + return "disabled"; + } + if (noPreferenceValue.includes(val)) { + return "no-preference"; + } + + const possibleValues = trueValue + .concat(falseValue) + .concat(noPreferenceValue) + .join(" | "); + throw new TypeError( + `Input ${name} does not look like a trinary, which requires one of:\n${possibleValues}`, + ); +} + export function tailLog(daemonDir: string): Tail { const log = new Tail(path.join(daemonDir, "daemon.log")); actionsCore.debug(`tailing daemon.log...`); diff --git a/src/index.ts b/src/index.ts index 7ade870..de03946 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { netrcPath, tailLog } from "./helpers.js"; +import { getTrinaryInput, netrcPath, tailLog } from "./helpers.js"; import * as actionsCore from "@actions/core"; import { DetSysAction, inputs, stringifyError } from "detsys-ts"; import got, { Got, Response } from "got"; @@ -207,11 +207,11 @@ class MagicNixCacheAction extends DetSysAction { const nixConfPath = `${process.env["HOME"]}/.config/nix/nix.conf`; const upstreamCache = inputs.getString("upstream-cache"); const diagnosticEndpoint = inputs.getString("diagnostic-endpoint"); - const useFlakeHub = inputs.getBool("use-flakehub"); + const useFlakeHub = getTrinaryInput("use-flakehub"); const flakeHubCacheServer = inputs.getString("flakehub-cache-server"); const flakeHubApiServer = inputs.getString("flakehub-api-server"); const flakeHubFlakeName = inputs.getString("flakehub-flake-name"); - const useGhaCache = inputs.getBool("use-gha-cache"); + const useGhaCache = getTrinaryInput("use-gha-cache"); const daemonCliFlags: string[] = [ "--startup-notification-url", @@ -224,12 +224,15 @@ class MagicNixCacheAction extends DetSysAction { diagnosticEndpoint, "--nix-conf", nixConfPath, + "--use-gha-cache", + useGhaCache, + "--use-flakehub", + useFlakeHub, ] .concat(this.diffStore ? ["--diff-store"] : []) .concat( - useFlakeHub + useFlakeHub !== "disabled" ? [ - "--use-flakehub", "--flakehub-cache-server", flakeHubCacheServer, "--flakehub-api-server", @@ -240,8 +243,7 @@ class MagicNixCacheAction extends DetSysAction { flakeHubFlakeName, ] : [], - ) - .concat(useGhaCache ? ["--use-gha-cache"] : []); + ); const opts: SpawnOptions = { stdio: ["ignore", output, output], From 5629079d68ce2f9709767cc5d9c46df97aba5e55 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 5 Nov 2024 22:33:14 -0500 Subject: [PATCH 3/3] derp --- dist/index.js | 257 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 186 insertions(+), 71 deletions(-) diff --git a/dist/index.js b/dist/index.js index 61a6c6b..873fa82 100644 --- a/dist/index.js +++ b/dist/index.js @@ -33210,14 +33210,14 @@ module.exports["default"] = deferToConnect; /***/ }), -/***/ 3601: +/***/ 5542: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const validator = __nccwpck_require__(8128); -const XMLParser = __nccwpck_require__(897); -const XMLBuilder = __nccwpck_require__(6762); +const validator = __nccwpck_require__(1876); +const XMLParser = __nccwpck_require__(4120); +const XMLBuilder = __nccwpck_require__(534); module.exports = { XMLParser: XMLParser, @@ -33227,7 +33227,33 @@ module.exports = { /***/ }), -/***/ 3480: +/***/ 3628: +/***/ ((module) => { + +function getIgnoreAttributesFn(ignoreAttributes) { + if (typeof ignoreAttributes === 'function') { + return ignoreAttributes + } + if (Array.isArray(ignoreAttributes)) { + return (attrName) => { + for (const pattern of ignoreAttributes) { + if (typeof pattern === 'string' && attrName === pattern) { + return true + } + if (pattern instanceof RegExp && pattern.test(attrName)) { + return true + } + } + } + } + return () => false +} + +module.exports = getIgnoreAttributesFn + +/***/ }), + +/***/ 4340: /***/ ((__unused_webpack_module, exports) => { @@ -33306,12 +33332,12 @@ exports.nameRegexp = nameRegexp; /***/ }), -/***/ 8128: +/***/ 1876: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -const util = __nccwpck_require__(3480); +const util = __nccwpck_require__(4340); const defaultOptions = { allowBooleanAttributes: false, //A tag can have attributes without any value @@ -33738,12 +33764,13 @@ function getPositionFromMatch(match) { /***/ }), -/***/ 6762: +/***/ 534: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { //parse Empty Node as self closing node -const buildFromOrderedJs = __nccwpck_require__(9603); +const buildFromOrderedJs = __nccwpck_require__(4354); +const getIgnoreAttributesFn = __nccwpck_require__(3628) const defaultOptions = { attributeNamePrefix: '@_', @@ -33781,11 +33808,12 @@ const defaultOptions = { function Builder(options) { this.options = Object.assign({}, defaultOptions, options); - if (this.options.ignoreAttributes || this.options.attributesGroupName) { + if (this.options.ignoreAttributes === true || this.options.attributesGroupName) { this.isAttribute = function(/*a*/) { return false; }; } else { + this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes) this.attrPrefixLen = this.options.attributeNamePrefix.length; this.isAttribute = isAttribute; } @@ -33814,13 +33842,14 @@ Builder.prototype.build = function(jObj) { [this.options.arrayNodeName] : jObj } } - return this.j2x(jObj, 0).val; + return this.j2x(jObj, 0, []).val; } }; -Builder.prototype.j2x = function(jObj, level) { +Builder.prototype.j2x = function(jObj, level, ajPath) { let attrStr = ''; let val = ''; + const jPath = ajPath.join('.') for (let key in jObj) { if(!Object.prototype.hasOwnProperty.call(jObj, key)) continue; if (typeof jObj[key] === 'undefined') { @@ -33843,9 +33872,9 @@ Builder.prototype.j2x = function(jObj, level) { } else if (typeof jObj[key] !== 'object') { //premitive type const attr = this.isAttribute(key); - if (attr) { + if (attr && !this.ignoreAttributesFn(attr, jPath)) { attrStr += this.buildAttrPairStr(attr, '' + jObj[key]); - }else { + } else if (!attr) { //tag value if (key === this.options.textNodeName) { let newval = this.options.tagValueProcessor(key, '' + jObj[key]); @@ -33869,13 +33898,13 @@ Builder.prototype.j2x = function(jObj, level) { // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar; } else if (typeof item === 'object') { if(this.options.oneListGroup){ - const result = this.j2x(item, level + 1); + const result = this.j2x(item, level + 1, ajPath.concat(key)); listTagVal += result.val; if (this.options.attributesGroupName && item.hasOwnProperty(this.options.attributesGroupName)) { listTagAttr += result.attrStr } }else{ - listTagVal += this.processTextOrObjNode(item, key, level) + listTagVal += this.processTextOrObjNode(item, key, level, ajPath) } } else { if (this.options.oneListGroup) { @@ -33900,7 +33929,7 @@ Builder.prototype.j2x = function(jObj, level) { attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]); } } else { - val += this.processTextOrObjNode(jObj[key], key, level) + val += this.processTextOrObjNode(jObj[key], key, level, ajPath) } } } @@ -33915,8 +33944,8 @@ Builder.prototype.buildAttrPairStr = function(attrName, val){ } else return ' ' + attrName + '="' + val + '"'; } -function processTextOrObjNode (object, key, level) { - const result = this.j2x(object, level + 1); +function processTextOrObjNode (object, key, level, ajPath) { + const result = this.j2x(object, level + 1, ajPath.concat(key)); if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) { return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level); } else { @@ -34026,7 +34055,7 @@ module.exports = Builder; /***/ }), -/***/ 9603: +/***/ 4354: /***/ ((module) => { const EOL = "\n"; @@ -34168,10 +34197,10 @@ module.exports = toXml; /***/ }), -/***/ 8778: +/***/ 7573: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const util = __nccwpck_require__(3480); +const util = __nccwpck_require__(4340); //TODO: handle comments function readDocType(xmlData, i){ @@ -34327,7 +34356,7 @@ module.exports = readDocType; /***/ }), -/***/ 5922: +/***/ 1405: /***/ ((__unused_webpack_module, exports) => { @@ -34381,16 +34410,17 @@ exports.defaultOptions = defaultOptions; /***/ }), -/***/ 1076: +/***/ 3028: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { ///@ts-check -const util = __nccwpck_require__(3480); -const xmlNode = __nccwpck_require__(308); -const readDocType = __nccwpck_require__(8778); +const util = __nccwpck_require__(4340); +const xmlNode = __nccwpck_require__(3555); +const readDocType = __nccwpck_require__(7573); const toNumber = __nccwpck_require__(9578); +const getIgnoreAttributesFn = __nccwpck_require__(3628) // const regx = // '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)' @@ -34439,6 +34469,7 @@ class OrderedObjParser{ this.readStopNodeData = readStopNodeData; this.saveTextToParentTag = saveTextToParentTag; this.addChild = addChild; + this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes) } } @@ -34511,7 +34542,7 @@ function resolveNameSpace(tagname) { const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm'); function buildAttributesMap(attrStr, jPath, tagName) { - if (!this.options.ignoreAttributes && typeof attrStr === 'string') { + if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') { // attrStr = attrStr.replace(/\r?\n/g, ' '); //attrStr = attrStr || attrStr.trim(); @@ -34520,6 +34551,9 @@ function buildAttributesMap(attrStr, jPath, tagName) { const attrs = {}; for (let i = 0; i < len; i++) { const attrName = this.resolveNameSpace(matches[i][1]); + if (this.ignoreAttributesFn(attrName, jPath)) { + continue + } let oldVal = matches[i][4]; let aName = this.options.attributeNamePrefix + attrName; if (attrName.length) { @@ -34989,13 +35023,13 @@ module.exports = OrderedObjParser; /***/ }), -/***/ 897: +/***/ 4120: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const { buildOptions} = __nccwpck_require__(5922); -const OrderedObjParser = __nccwpck_require__(1076); -const { prettify} = __nccwpck_require__(9872); -const validator = __nccwpck_require__(8128); +const { buildOptions} = __nccwpck_require__(1405); +const OrderedObjParser = __nccwpck_require__(3028); +const { prettify} = __nccwpck_require__(4036); +const validator = __nccwpck_require__(1876); class XMLParser{ @@ -35053,7 +35087,7 @@ module.exports = XMLParser; /***/ }), -/***/ 9872: +/***/ 4036: /***/ ((__unused_webpack_module, exports) => { @@ -35173,7 +35207,7 @@ exports.prettify = prettify; /***/ }), -/***/ 308: +/***/ 3555: /***/ ((module) => { @@ -75202,7 +75236,7 @@ exports.XML_CHARKEY = "_"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.stringifyXML = stringifyXML; exports.parseXML = parseXML; -const fast_xml_parser_1 = __nccwpck_require__(3601); +const fast_xml_parser_1 = __nccwpck_require__(5542); const xml_common_js_1 = __nccwpck_require__(8917); function getCommonOptions(options) { var _a; @@ -84254,9 +84288,7 @@ const external_node_dns_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequ var cache = __nccwpck_require__(6878); ;// CONCATENATED MODULE: external "node:child_process" const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:child_process"); -;// 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@cf1897a891edc164a8240f469cd56d14364e6be1_fq5hfjh622jf54cg4vepqdc2u4/node_modules/detsys-ts/dist/index.js +;// CONCATENATED MODULE: ./node_modules/.pnpm/github.com+DeterminateSystems+detsys-ts@65dd73c562ac60a068340f8e0c040bdcf2c59afe_eek3lsas7notlem5iqjfyrzcca/node_modules/detsys-ts/dist/index.js var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) @@ -84486,16 +84518,16 @@ function stringifyError(e) { -async function collectBacktraces(prefixes) { +async function collectBacktraces(prefixes, startTimestampMs) { if (isMacOS) { - return await collectBacktracesMacOS(prefixes); + return await collectBacktracesMacOS(prefixes, startTimestampMs); } if (isLinux) { - return await collectBacktracesSystemd(prefixes); + return await collectBacktracesSystemd(prefixes, startTimestampMs); } return /* @__PURE__ */ new Map(); } -async function collectBacktracesMacOS(prefixes) { +async function collectBacktracesMacOS(prefixes, startTimestampMs) { const backtraces = /* @__PURE__ */ new Map(); try { const { stdout: logJson } = await exec.getExecOutput( @@ -84537,16 +84569,20 @@ async function collectBacktracesMacOS(prefixes) { for (const [source, dir] of dirs) { const fileNames = (await (0,promises_namespaceObject.readdir)(dir)).filter((fileName) => { return prefixes.some((prefix) => fileName.startsWith(prefix)); + }).filter((fileName) => { + return !fileName.endsWith(".diag"); }); const doGzip = (0,external_node_util_.promisify)(external_node_zlib_.gzip); for (const fileName of fileNames) { try { - const logText = await (0,promises_namespaceObject.readFile)(`${dir}/${fileName}`); - const buf = await doGzip(logText); - backtraces.set( - `backtrace_value_${source}_${fileName}`, - buf.toString("base64") - ); + if ((await (0,promises_namespaceObject.stat)(`${dir}/${fileName}`)).ctimeMs >= startTimestampMs) { + const logText = await (0,promises_namespaceObject.readFile)(`${dir}/${fileName}`); + const buf = await doGzip(logText); + backtraces.set( + `backtrace_value_${source}_${fileName}`, + buf.toString("base64") + ); + } } catch (innerError) { backtraces.set( `backtrace_failure_${source}_${fileName}`, @@ -84557,13 +84593,14 @@ async function collectBacktracesMacOS(prefixes) { } return backtraces; } -async function collectBacktracesSystemd(prefixes) { +async function collectBacktracesSystemd(prefixes, startTimestampMs) { + const sinceSeconds = Math.ceil((Date.now() - startTimestampMs) / 1e3); const backtraces = /* @__PURE__ */ new Map(); const coredumps = []; try { const { stdout: coredumpjson } = await exec.getExecOutput( "coredumpctl", - ["--json=pretty", "list", "--since", "1 hour ago"], + ["--json=pretty", "list", "--since", `${sinceSeconds} seconds ago`], { silent: true } @@ -84725,7 +84762,7 @@ var ALLOWED_SUFFIXES = [ ]; var DEFAULT_IDS_HOST = "https://install.determinate.systems"; var LOOKUP = process.env["IDS_LOOKUP"] ?? DEFAULT_LOOKUP; -var DEFAULT_TIMEOUT = 3e4; +var DEFAULT_TIMEOUT = 1e4; var IdsHost = class { constructor(idsProjectName, diagnosticsSuffix, runtimeDiagnosticsUrl) { this.idsProjectName = idsProjectName; @@ -84740,7 +84777,7 @@ var IdsHost = class { request: DEFAULT_TIMEOUT }, retry: { - limit: (await this.getUrlsByPreference()).length, + limit: Math.max((await this.getUrlsByPreference()).length, 3), methods: ["GET", "HEAD"] }, hooks: { @@ -84750,7 +84787,7 @@ var IdsHost = class { this.markCurrentHostBroken(); const nextUrl = await this.getRootUrl(); if (recordFailoverCallback !== void 0) { - recordFailoverCallback(prevUrl, nextUrl); + recordFailoverCallback(error3, prevUrl, nextUrl); } core.info( `Retrying after error ${error3.code}, retry #: ${retryCount}` @@ -85134,8 +85171,10 @@ var FACT_NIX_STORE_CHECK_ERROR = "nix_store_check_error"; var STATE_KEY_EXECUTION_PHASE = "detsys_action_execution_phase"; var STATE_KEY_NIX_NOT_FOUND = "detsys_action_nix_not_found"; var STATE_NOT_FOUND = "not-found"; -var DIAGNOSTIC_ENDPOINT_TIMEOUT_MS = 3e4; -var CHECK_IN_ENDPOINT_TIMEOUT_MS = 5e3; +var STATE_KEY_CROSS_PHASE_ID = "detsys_cross_phase_id"; +var STATE_BACKTRACE_START_TIMESTAMP = "detsys_backtrace_start_timestamp"; +var DIAGNOSTIC_ENDPOINT_TIMEOUT_MS = 1e4; +var CHECK_IN_ENDPOINT_TIMEOUT_MS = 1e3; var DetSysAction = class { determineExecutionPhase() { const currentPhase = core.getState(STATE_KEY_EXECUTION_PHASE); @@ -85161,6 +85200,8 @@ var DetSysAction = class { this.features = {}; this.featureEventMetadata = {}; this.events = []; + this.getCrossPhaseId(); + this.collectBacktraceSetup(); this.facts = { $lib: "idslib", $lib_version: version, @@ -85250,6 +85291,15 @@ var DetSysAction = class { getUniqueId() { return this.identity.run_differentiator || process.env.RUNNER_TRACKING_ID || (0,external_node_crypto_namespaceObject.randomUUID)(); } + // This ID will be saved in the action's state, to be persisted across phase steps + getCrossPhaseId() { + let crossPhaseId = core.getState(STATE_KEY_CROSS_PHASE_ID); + if (crossPhaseId === "") { + crossPhaseId = (0,external_node_crypto_namespaceObject.randomUUID)(); + core.saveState(STATE_KEY_CROSS_PHASE_ID, crossPhaseId); + } + return crossPhaseId; + } getCorrelationHashes() { return this.identity; } @@ -85348,12 +85398,15 @@ var DetSysAction = class { } } async getClient() { - return await this.idsHost.getGot((prevUrl, nextUrl) => { - this.recordEvent("ids-failover", { - previousUrl: prevUrl.toString(), - nextUrl: nextUrl.toString() - }); - }); + return await this.idsHost.getGot( + (incitingError, prevUrl, nextUrl) => { + this.recordPlausibleTimeout(incitingError); + this.recordEvent("ids-failover", { + previousUrl: prevUrl.toString(), + nextUrl: nextUrl.toString() + }); + } + ); } async checkIn() { const checkin = await this.requestCheckIn(); @@ -85437,12 +85490,27 @@ var DetSysAction = class { } }).json(); } catch (e) { + this.recordPlausibleTimeout(e); core.debug(`Error checking in: ${stringifyError2(e)}`); this.idsHost.markCurrentHostBroken(); } } return void 0; } + recordPlausibleTimeout(e) { + if (e instanceof TimeoutError && "timings" in e && "request" in e) { + const reportContext = { + url: e.request.requestUrl?.toString(), + retry_count: e.request.retryCount + }; + for (const [key, value] of Object.entries(e.timings.phases)) { + if (Number.isFinite(value)) { + reportContext[`timing_phase_${key}`] = value; + } + } + this.recordEvent("timeout", reportContext); + } + } /** * Fetch an artifact, such as a tarball, from the location determined by the * `source-*` inputs. If `source-binary` is specified, this will return a path @@ -85486,13 +85554,9 @@ var DetSysAction = class { `No match from the cache, re-fetching from the redirect: ${versionCheckup.url}` ); const destFile = this.getTemporaryName(); - const fetchStream = (await this.getClient()).stream(versionCheckup.url); - await (0,external_node_stream_promises_namespaceObject.pipeline)( - fetchStream, - (0,external_node_fs_namespaceObject.createWriteStream)(destFile, { - encoding: "binary", - mode: 493 - }) + const fetchStream = await this.downloadFile( + new URL(versionCheckup.url), + destFile ); if (fetchStream.response?.headers.etag) { const v = fetchStream.response.headers.etag; @@ -85503,6 +85567,9 @@ var DetSysAction = class { } } return destFile; + } catch (e) { + this.recordPlausibleTimeout(e); + throw e; } finally { core.endGroup(); } @@ -85516,6 +85583,36 @@ var DetSysAction = class { core.setFailed(`strict mode failure: ${msg}`); } } + async downloadFile(url, destination) { + const client = await this.getClient(); + return new Promise((resolve, reject) => { + let writeStream; + let failed = false; + const retry = (stream) => { + if (writeStream) { + writeStream.destroy(); + } + writeStream = (0,external_node_fs_namespaceObject.createWriteStream)(destination, { + encoding: "binary", + mode: 493 + }); + writeStream.once("error", (error3) => { + failed = true; + reject(error3); + }); + writeStream.on("finish", () => { + if (!failed) { + resolve(stream); + } + }); + stream.once("retry", (_count, _error, createRetryStream) => { + retry(createRetryStream()); + }); + stream.pipe(writeStream); + }; + retry(client.stream(url)); + }); + } async complete() { this.recordEvent(`complete_${this.executionPhase}`); await this.submitEvents(); @@ -85603,10 +85700,23 @@ var DetSysAction = class { process.chdir(startCwd); } } + collectBacktraceSetup() { + if (process.env.DETSYS_BACKTRACE_COLLECTOR === "") { + core.exportVariable( + "DETSYS_BACKTRACE_COLLECTOR", + this.getCrossPhaseId() + ); + core.saveState(STATE_BACKTRACE_START_TIMESTAMP, Date.now()); + } + } async collectBacktraces() { try { + if (process.env.DETSYS_BACKTRACE_COLLECTOR !== this.getCrossPhaseId()) { + return; + } const backtraces = await collectBacktraces( - this.actionOptions.binaryNamePrefixes + this.actionOptions.binaryNamePrefixes, + parseInt(core.getState(STATE_BACKTRACE_START_TIMESTAMP)) ); core.debug(`Backtraces identified: ${backtraces.size}`); if (backtraces.size > 0) { @@ -85726,6 +85836,7 @@ var DetSysAction = class { } }); } catch (err) { + this.recordPlausibleTimeout(err); core.debug( `Error submitting diagnostics event to ${diagnosticsUrl}: ${stringifyError2(err)}` ); @@ -85745,7 +85856,11 @@ function makeOptionsConfident(actionOptions) { fetchStyle: actionOptions.fetchStyle, legacySourcePrefix: actionOptions.legacySourcePrefix, requireNix: actionOptions.requireNix, - binaryNamePrefixes: actionOptions.binaryNamePrefixes || ["nix"] + binaryNamePrefixes: actionOptions.binaryNamePrefixes ?? [ + "nix", + "determinate-nixd", + actionOptions.name + ] }; core.debug("idslib options:"); core.debug(JSON.stringify(finalOpts, void 0, 2));