@determinate-systems/detsys-ts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1493 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import * as fs$1 from "node:fs";
3
+ import { constants, createReadStream, createWriteStream, readFileSync } from "node:fs";
4
+ import * as os$1 from "node:os";
5
+ import { tmpdir } from "node:os";
6
+ import { promisify } from "node:util";
7
+ import * as actionsCore from "@actions/core";
8
+ import * as exec$1 from "@actions/exec";
9
+ import os from "os";
10
+ import fs, { chmod, copyFile, mkdir, readFile, readdir, stat } from "node:fs/promises";
11
+ import { gzip } from "node:zlib";
12
+ import { createHash, randomUUID } from "node:crypto";
13
+ import got, { TimeoutError } from "got";
14
+ import { resolveSrv } from "node:dns/promises";
15
+ import * as actionsCache from "@actions/cache";
16
+ import { exec } from "node:child_process";
17
+ import * as path from "node:path";
18
+ //#region src/linux-release-info.ts
19
+ /*!
20
+ * linux-release-info
21
+ * Get Linux release info (distribution name, version, arch, release, etc.)
22
+ * from '/etc/os-release' or '/usr/lib/os-release' files and from native os
23
+ * module. On Windows and Darwin platforms it only returns common node os module
24
+ * info (platform, hostname, release, and arch)
25
+ *
26
+ * Licensed under MIT
27
+ * Copyright (c) 2018-2020 [Samuel Carreira]
28
+ */
29
+ const readFileAsync = promisify(fs$1.readFile);
30
+ const linuxReleaseInfoOptionsDefaults = {
31
+ mode: "async",
32
+ customFile: null,
33
+ debug: false
34
+ };
35
+ /**
36
+ * Get OS release info from 'os-release' file and from native os module
37
+ * on Windows or Darwin it only returns common os module info
38
+ * (uses native fs module)
39
+ * @returns {object} info from the current os
40
+ */
41
+ function releaseInfo(infoOptions) {
42
+ const options = {
43
+ ...linuxReleaseInfoOptionsDefaults,
44
+ ...infoOptions
45
+ };
46
+ const searchOsReleaseFileList = osReleaseFileList(options.customFile);
47
+ if (os$1.type() !== "Linux") {
48
+ if (options.mode === "sync") return getOsInfo();
49
+ else return Promise.resolve(getOsInfo());
50
+ }
51
+ if (options.mode === "sync") return readSyncOsreleaseFile(searchOsReleaseFileList, options);
52
+ else return Promise.resolve(readAsyncOsReleaseFile(searchOsReleaseFileList, options));
53
+ }
54
+ /**
55
+ * Format file data: convert data to object keys/values
56
+ *
57
+ * @param {object} sourceData Source object to be appended
58
+ * @param {string} srcParseData Input file data to be parsed
59
+ * @returns {object} Formated object
60
+ */
61
+ function formatFileData(sourceData, srcParseData) {
62
+ const lines = srcParseData.split("\n");
63
+ for (const line of lines) {
64
+ const lineData = line.split("=");
65
+ if (lineData.length === 2) {
66
+ lineData[1] = lineData[1].replace(/["'\r]/gi, "");
67
+ Object.defineProperty(sourceData, lineData[0].toLowerCase(), {
68
+ value: lineData[1],
69
+ writable: true,
70
+ enumerable: true,
71
+ configurable: true
72
+ });
73
+ }
74
+ }
75
+ return sourceData;
76
+ }
77
+ /**
78
+ * Export a list of os-release files
79
+ *
80
+ * @param {string} customFile optional custom complete filepath
81
+ * @returns {array} list of os-release files
82
+ */
83
+ function osReleaseFileList(customFile) {
84
+ const DEFAULT_OS_RELEASE_FILES = ["/etc/os-release", "/usr/lib/os-release"];
85
+ if (!customFile) return DEFAULT_OS_RELEASE_FILES;
86
+ else return Array(customFile);
87
+ }
88
+ /**
89
+ * Get OS Basic Info
90
+ * (uses node 'os' native module)
91
+ *
92
+ * @returns {OsInfo} os basic info
93
+ */
94
+ function getOsInfo() {
95
+ return {
96
+ type: os$1.type(),
97
+ platform: os$1.platform(),
98
+ hostname: os$1.hostname(),
99
+ arch: os$1.arch(),
100
+ release: os$1.release()
101
+ };
102
+ }
103
+ async function readAsyncOsReleaseFile(fileList, options) {
104
+ let fileData = null;
105
+ for (const osReleaseFile of fileList) try {
106
+ if (options.debug) console.log(`Trying to read '${osReleaseFile}'...`);
107
+ fileData = await readFileAsync(osReleaseFile, "binary");
108
+ if (options.debug) console.log(`Read data:\n${fileData}`);
109
+ break;
110
+ } catch (error) {
111
+ if (options.debug) console.error(error);
112
+ }
113
+ if (fileData === null) throw new Error("Cannot read os-release file!");
114
+ return formatFileData(getOsInfo(), fileData);
115
+ }
116
+ function readSyncOsreleaseFile(releaseFileList, options) {
117
+ let fileData = null;
118
+ for (const osReleaseFile of releaseFileList) try {
119
+ if (options.debug) console.log(`Trying to read '${osReleaseFile}'...`);
120
+ fileData = fs$1.readFileSync(osReleaseFile, "binary");
121
+ if (options.debug) console.log(`Read data:\n${fileData}`);
122
+ break;
123
+ } catch (error) {
124
+ if (options.debug) console.error(error);
125
+ }
126
+ if (fileData === null) throw new Error("Cannot read os-release file!");
127
+ return formatFileData(getOsInfo(), fileData);
128
+ }
129
+ //#endregion
130
+ //#region src/actions-core-platform.ts
131
+ /**
132
+ * Get the name and version of the current Windows system.
133
+ */
134
+ const getWindowsInfo = async () => {
135
+ const { stdout: version } = await exec$1.getExecOutput("powershell -command \"(Get-CimInstance -ClassName Win32_OperatingSystem).Version\"", void 0, { silent: true });
136
+ const { stdout: name } = await exec$1.getExecOutput("powershell -command \"(Get-CimInstance -ClassName Win32_OperatingSystem).Caption\"", void 0, { silent: true });
137
+ return {
138
+ name: name.trim(),
139
+ version: version.trim()
140
+ };
141
+ };
142
+ /**
143
+ * Get the name and version of the current macOS system.
144
+ */
145
+ const getMacOsInfo = async () => {
146
+ const { stdout } = await exec$1.getExecOutput("sw_vers", void 0, { silent: true });
147
+ const version = stdout.match(/ProductVersion:\s*(.+)/)?.[1] ?? "";
148
+ return {
149
+ name: stdout.match(/ProductName:\s*(.+)/)?.[1] ?? "",
150
+ version
151
+ };
152
+ };
153
+ /**
154
+ * Get the name and version of the current Linux system.
155
+ */
156
+ const getLinuxInfo = async () => {
157
+ let data = {};
158
+ try {
159
+ data = releaseInfo({ mode: "sync" });
160
+ actionsCore.debug(`Identified release info: ${JSON.stringify(data)}`);
161
+ } catch (e) {
162
+ actionsCore.debug(`Error collecting release info: ${e}`);
163
+ }
164
+ return {
165
+ name: getPropertyViaWithDefault(data, [
166
+ "id",
167
+ "name",
168
+ "pretty_name",
169
+ "id_like"
170
+ ], "unknown"),
171
+ version: getPropertyViaWithDefault(data, [
172
+ "version_id",
173
+ "version",
174
+ "version_codename"
175
+ ], "unknown")
176
+ };
177
+ };
178
+ function getPropertyViaWithDefault(data, names, defaultValue) {
179
+ for (const name of names) {
180
+ const ret = getPropertyWithDefault(data, name, defaultValue);
181
+ if (ret !== defaultValue) return ret;
182
+ }
183
+ return defaultValue;
184
+ }
185
+ function getPropertyWithDefault(data, name, defaultValue) {
186
+ if (!data.hasOwnProperty(name)) return defaultValue;
187
+ const value = data[name];
188
+ if (typeof value !== typeof defaultValue) return defaultValue;
189
+ return value;
190
+ }
191
+ /**
192
+ * The Action runner's platform.
193
+ */
194
+ const platform = os.platform();
195
+ /**
196
+ * The Action runner's architecture.
197
+ */
198
+ const arch = os.arch();
199
+ /**
200
+ * Whether the Action runner is a Windows system.
201
+ */
202
+ const isWindows = platform === "win32";
203
+ /**
204
+ * Whether the Action runner is a macOS system.
205
+ */
206
+ const isMacOS = platform === "darwin";
207
+ /**
208
+ * Whether the Action runner is a Linux system.
209
+ */
210
+ const isLinux = platform === "linux";
211
+ /**
212
+ * Get system-level information about the current host (platform, architecture, etc.).
213
+ */
214
+ async function getDetails() {
215
+ return {
216
+ ...await (isWindows ? getWindowsInfo() : isMacOS ? getMacOsInfo() : getLinuxInfo()),
217
+ platform,
218
+ arch,
219
+ isWindows,
220
+ isMacOS,
221
+ isLinux
222
+ };
223
+ }
224
+ //#endregion
225
+ //#region src/errors.ts
226
+ /**
227
+ * Coerce a value of type `unknown` into a string.
228
+ */
229
+ function stringifyError(e) {
230
+ if (e instanceof Error) return e.message;
231
+ else if (typeof e === "string") return e;
232
+ else return JSON.stringify(e);
233
+ }
234
+ //#endregion
235
+ //#region src/backtrace.ts
236
+ /**
237
+ * @packageDocumentation
238
+ * Collects backtraces for executables for diagnostics
239
+ */
240
+ const START_SLOP_SECONDS = 5;
241
+ async function collectBacktraces(prefixes, programNameDenyList, startTimestampMs) {
242
+ if (isMacOS) return await collectBacktracesMacOS(prefixes, programNameDenyList, startTimestampMs);
243
+ if (isLinux) return await collectBacktracesSystemd(prefixes, programNameDenyList, startTimestampMs);
244
+ return /* @__PURE__ */ new Map();
245
+ }
246
+ async function collectBacktracesMacOS(prefixes, programNameDenyList, startTimestampMs) {
247
+ const backtraces = /* @__PURE__ */ new Map();
248
+ try {
249
+ const { stdout: logJson } = await exec$1.getExecOutput("log", [
250
+ "show",
251
+ "--style",
252
+ "json",
253
+ "--last",
254
+ "1m",
255
+ "--no-info",
256
+ "--predicate",
257
+ "sender = 'ReportCrash'"
258
+ ], { silent: true });
259
+ const sussyArray = JSON.parse(logJson);
260
+ if (!Array.isArray(sussyArray)) throw new Error(`Log json isn't an array: ${logJson}`);
261
+ if (sussyArray.length > 0) {
262
+ actionsCore.info(`Collecting crash data...`);
263
+ const delay = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
264
+ await delay(5e3);
265
+ }
266
+ } catch {
267
+ actionsCore.debug("Failed to check logs for in-progress crash dumps; now proceeding with the assumption that all crash dumps completed.");
268
+ }
269
+ const dirs = [["system", "/Library/Logs/DiagnosticReports/"], ["user", `${process.env["HOME"]}/Library/Logs/DiagnosticReports/`]];
270
+ for (const [source, dir] of dirs) {
271
+ const fileNames = (await readdir(dir)).filter((fileName) => {
272
+ return prefixes.some((prefix) => fileName.startsWith(prefix));
273
+ }).filter((fileName) => {
274
+ return !programNameDenyList.some((programName) => fileName.startsWith(programName));
275
+ }).filter((fileName) => {
276
+ return !fileName.endsWith(".diag");
277
+ });
278
+ const doGzip = promisify(gzip);
279
+ for (const fileName of fileNames) try {
280
+ if ((await stat(`${dir}/${fileName}`)).ctimeMs >= startTimestampMs) {
281
+ const buf = await doGzip(await readFile(`${dir}/${fileName}`));
282
+ backtraces.set(`backtrace_value_${source}_${fileName}`, buf.toString("base64"));
283
+ }
284
+ } catch (innerError) {
285
+ backtraces.set(`backtrace_failure_${source}_${fileName}`, stringifyError(innerError));
286
+ }
287
+ }
288
+ return backtraces;
289
+ }
290
+ async function collectBacktracesSystemd(prefixes, programNameDenyList, startTimestampMs) {
291
+ const sinceSeconds = Math.ceil((Date.now() - startTimestampMs) / 1e3) + START_SLOP_SECONDS;
292
+ const backtraces = /* @__PURE__ */ new Map();
293
+ const coredumps = [];
294
+ try {
295
+ const { stdout: coredumpjson } = await exec$1.getExecOutput("coredumpctl", [
296
+ "--json=pretty",
297
+ "list",
298
+ "--since",
299
+ `${sinceSeconds} seconds ago`
300
+ ], { silent: true });
301
+ const sussyArray = JSON.parse(coredumpjson);
302
+ if (!Array.isArray(sussyArray)) throw new Error(`Coredump isn't an array: ${coredumpjson}`);
303
+ for (const sussyObject of sussyArray) {
304
+ const keys = Object.keys(sussyObject);
305
+ if (keys.includes("exe") && keys.includes("pid")) {
306
+ if (typeof sussyObject.exe == "string" && typeof sussyObject.pid == "number") {
307
+ const execParts = sussyObject.exe.split("/");
308
+ const binaryName = execParts[execParts.length - 1];
309
+ if (prefixes.some((prefix) => binaryName.startsWith(prefix)) && !programNameDenyList.includes(binaryName)) coredumps.push({
310
+ exe: sussyObject.exe,
311
+ pid: sussyObject.pid
312
+ });
313
+ } else actionsCore.debug(`Mysterious coredump entry missing exe string and/or pid number: ${JSON.stringify(sussyObject)}`);
314
+ } else actionsCore.debug(`Mysterious coredump entry missing exe value and/or pid value: ${JSON.stringify(sussyObject)}`);
315
+ }
316
+ } catch (innerError) {
317
+ actionsCore.debug(`Cannot collect backtraces: ${stringifyError(innerError)}`);
318
+ return backtraces;
319
+ }
320
+ const doGzip = promisify(gzip);
321
+ for (const coredump of coredumps) try {
322
+ const { stdout: logText } = await exec$1.getExecOutput("coredumpctl", ["info", `${coredump.pid}`], { silent: true });
323
+ const buf = await doGzip(logText);
324
+ backtraces.set(`backtrace_value_${coredump.pid}`, buf.toString("base64"));
325
+ } catch (innerError) {
326
+ backtraces.set(`backtrace_failure_${coredump.pid}`, stringifyError(innerError));
327
+ }
328
+ return backtraces;
329
+ }
330
+ //#endregion
331
+ //#region src/checksums.ts
332
+ /**
333
+ * @packageDocumentation
334
+ * Parsing and hashing helpers for `shasum`-format checksum files, used to
335
+ * hash-lock downloaded artifacts.
336
+ */
337
+ const HEX_STRING_RE = /^[0-9a-fA-F]+$/;
338
+ /**
339
+ * Parse a `shasum`-format checksums file into a map of filename -> hex digest.
340
+ *
341
+ * Each non-empty line has the shape `<hex-digest><space(s)><filename>`. Lines
342
+ * without a space delimiter are skipped. Invalid hex digests throw, so a
343
+ * malformed file fails loudly rather than silently skipping the entry we
344
+ * care about.
345
+ */
346
+ function parseChecksumsFile(text) {
347
+ const result = /* @__PURE__ */ new Map();
348
+ for (const record of text.split(/\r\n|\n|\r/).filter(Boolean)) {
349
+ const delimIndex = record.indexOf(" ");
350
+ if (delimIndex === -1) continue;
351
+ const digest = record.slice(0, delimIndex);
352
+ if (!HEX_STRING_RE.test(digest)) throw new Error(`Invalid digest in checksums file: ${digest}`);
353
+ const name = record.slice(delimIndex + 1).trim();
354
+ if (name === "") continue;
355
+ result.set(name, digest.toLowerCase());
356
+ }
357
+ return result;
358
+ }
359
+ /**
360
+ * Compute the SHA-256 of a file on disk and return its lowercase hex digest.
361
+ * Streams the file so memory use is constant regardless of size.
362
+ */
363
+ async function sha256OfFile(filePath) {
364
+ return new Promise((resolve, reject) => {
365
+ const hash = createHash("sha256").setEncoding("hex");
366
+ createReadStream(filePath).once("error", reject).pipe(hash).once("finish", () => resolve(hash.read()));
367
+ });
368
+ }
369
+ /**
370
+ * Compute the SHA-256 of an in-memory buffer or string and return its
371
+ * lowercase hex digest.
372
+ */
373
+ function sha256OfBuffer(data) {
374
+ return createHash("sha256").update(data).digest("hex");
375
+ }
376
+ //#endregion
377
+ //#region src/correlation.ts
378
+ const OPTIONAL_VARIABLES = ["INVOCATION_ID"];
379
+ function identify() {
380
+ const repository = hashEnvironmentVariables("GHR", [
381
+ "GITHUB_SERVER_URL",
382
+ "GITHUB_REPOSITORY_OWNER",
383
+ "GITHUB_REPOSITORY_OWNER_ID",
384
+ "GITHUB_REPOSITORY",
385
+ "GITHUB_REPOSITORY_ID"
386
+ ]);
387
+ const run_differentiator = hashEnvironmentVariables("GHWJA", [
388
+ "GITHUB_SERVER_URL",
389
+ "GITHUB_REPOSITORY_OWNER",
390
+ "GITHUB_REPOSITORY_OWNER_ID",
391
+ "GITHUB_REPOSITORY",
392
+ "GITHUB_REPOSITORY_ID",
393
+ "GITHUB_WORKFLOW",
394
+ "GITHUB_JOB",
395
+ "GITHUB_RUN_ID",
396
+ "GITHUB_RUN_NUMBER",
397
+ "GITHUB_RUN_ATTEMPT",
398
+ "INVOCATION_ID"
399
+ ]);
400
+ const ident = {
401
+ $anon_distinct_id: process.env["RUNNER_TRACKING_ID"] || randomUUID(),
402
+ correlation_source: "github-actions",
403
+ github_repository_hash: repository,
404
+ github_workflow_hash: hashEnvironmentVariables("GHW", [
405
+ "GITHUB_SERVER_URL",
406
+ "GITHUB_REPOSITORY_OWNER",
407
+ "GITHUB_REPOSITORY_OWNER_ID",
408
+ "GITHUB_REPOSITORY",
409
+ "GITHUB_REPOSITORY_ID",
410
+ "GITHUB_WORKFLOW"
411
+ ]),
412
+ github_workflow_job_hash: hashEnvironmentVariables("GHWJ", [
413
+ "GITHUB_SERVER_URL",
414
+ "GITHUB_REPOSITORY_OWNER",
415
+ "GITHUB_REPOSITORY_OWNER_ID",
416
+ "GITHUB_REPOSITORY",
417
+ "GITHUB_REPOSITORY_ID",
418
+ "GITHUB_WORKFLOW",
419
+ "GITHUB_JOB"
420
+ ]),
421
+ github_workflow_run_hash: hashEnvironmentVariables("GHWJR", [
422
+ "GITHUB_SERVER_URL",
423
+ "GITHUB_REPOSITORY_OWNER",
424
+ "GITHUB_REPOSITORY_OWNER_ID",
425
+ "GITHUB_REPOSITORY",
426
+ "GITHUB_REPOSITORY_ID",
427
+ "GITHUB_WORKFLOW",
428
+ "GITHUB_JOB",
429
+ "GITHUB_RUN_ID"
430
+ ]),
431
+ github_workflow_run_differentiator_hash: run_differentiator,
432
+ $session_id: run_differentiator,
433
+ $groups: {
434
+ github_repository: repository,
435
+ github_organization: hashEnvironmentVariables("GHO", [
436
+ "GITHUB_SERVER_URL",
437
+ "GITHUB_REPOSITORY_OWNER",
438
+ "GITHUB_REPOSITORY_OWNER_ID"
439
+ ])
440
+ },
441
+ is_ci: true
442
+ };
443
+ actionsCore.debug("Correlation data:");
444
+ actionsCore.debug(JSON.stringify(ident, null, 2));
445
+ return ident;
446
+ }
447
+ function hashEnvironmentVariables(prefix, variables) {
448
+ const hash = createHash("sha256");
449
+ for (const varName of variables) {
450
+ let value = process.env[varName];
451
+ if (value === void 0) {
452
+ if (OPTIONAL_VARIABLES.includes(varName)) {
453
+ actionsCore.debug(`Optional environment variable not set: ${varName} -- substituting with the variable name`);
454
+ value = varName;
455
+ } else {
456
+ actionsCore.debug(`Environment variable not set: ${varName} -- can't generate the requested identity`);
457
+ return;
458
+ }
459
+ }
460
+ hash.update(value);
461
+ hash.update("\0");
462
+ }
463
+ return `${prefix}-${hash.digest("hex")}`;
464
+ }
465
+ //#endregion
466
+ //#region src/ids-host.ts
467
+ /**
468
+ * @packageDocumentation
469
+ * Identifies and discovers backend servers for install.determinate.systems
470
+ */
471
+ const DEFAULT_LOOKUP = "_detsys_ids._tcp.install.determinate.systems.";
472
+ const ALLOWED_SUFFIXES = [".install.determinate.systems", ".install.detsys.dev"];
473
+ const DEFAULT_IDS_HOST = "https://install.determinate.systems";
474
+ const LOOKUP = process.env["IDS_LOOKUP"] ?? DEFAULT_LOOKUP;
475
+ const DEFAULT_TIMEOUT = 1e4;
476
+ /**
477
+ * Host information for install.determinate.systems.
478
+ */
479
+ var IdsHost = class {
480
+ constructor(idsProjectName, diagnosticsSuffix, runtimeDiagnosticsUrl, timeout = DEFAULT_TIMEOUT) {
481
+ this.idsProjectName = idsProjectName;
482
+ this.diagnosticsSuffix = diagnosticsSuffix;
483
+ this.runtimeDiagnosticsUrl = runtimeDiagnosticsUrl;
484
+ this.client = void 0;
485
+ this.timeout = timeout;
486
+ }
487
+ async getGot(recordFailoverCallback) {
488
+ if (this.client === void 0) this.client = got.extend({
489
+ timeout: { request: this.timeout },
490
+ retry: {
491
+ limit: Math.max((await this.getUrlsByPreference()).length, 3),
492
+ methods: ["GET", "HEAD"]
493
+ },
494
+ hooks: {
495
+ beforeRetry: [async (error, retryCount) => {
496
+ const prevUrl = await this.getRootUrl();
497
+ this.markCurrentHostBroken();
498
+ const nextUrl = await this.getRootUrl();
499
+ if (recordFailoverCallback !== void 0) recordFailoverCallback(error, prevUrl, nextUrl);
500
+ actionsCore.info(`Retrying after error ${error.code}, retry #: ${retryCount}`);
501
+ }],
502
+ beforeRequest: [async (options) => {
503
+ const currentUrl = options.url;
504
+ if (this.isUrlSubjectToDynamicUrls(currentUrl)) {
505
+ const newUrl = new URL(currentUrl);
506
+ newUrl.host = (await this.getRootUrl()).host;
507
+ options.url = newUrl;
508
+ actionsCore.debug(`Transmuted ${currentUrl} into ${newUrl}`);
509
+ } else actionsCore.debug(`No transmutations on ${currentUrl}`);
510
+ }]
511
+ }
512
+ });
513
+ return this.client;
514
+ }
515
+ markCurrentHostBroken() {
516
+ this.prioritizedURLs?.shift();
517
+ }
518
+ setPrioritizedUrls(urls) {
519
+ this.prioritizedURLs = urls;
520
+ }
521
+ isUrlSubjectToDynamicUrls(url) {
522
+ if (url.origin === DEFAULT_IDS_HOST) return true;
523
+ for (const suffix of ALLOWED_SUFFIXES) if (url.host.endsWith(suffix)) return true;
524
+ return false;
525
+ }
526
+ async getDynamicRootUrl() {
527
+ const idsHost = process.env["IDS_HOST"];
528
+ if (idsHost !== void 0) try {
529
+ return new URL(idsHost);
530
+ } catch (err) {
531
+ actionsCore.error(`IDS_HOST environment variable is not a valid URL. Ignoring. ${stringifyError(err)}`);
532
+ }
533
+ let url = void 0;
534
+ try {
535
+ url = (await this.getUrlsByPreference())[0];
536
+ } catch (err) {
537
+ actionsCore.error(`Error collecting IDS URLs by preference: ${stringifyError(err)}`);
538
+ }
539
+ if (url === void 0) return;
540
+ else return new URL(url);
541
+ }
542
+ async getRootUrl() {
543
+ const url = await this.getDynamicRootUrl();
544
+ if (url === void 0) return new URL(DEFAULT_IDS_HOST);
545
+ return url;
546
+ }
547
+ async getDiagnosticsUrl() {
548
+ if (this.runtimeDiagnosticsUrl === "") return;
549
+ if (this.runtimeDiagnosticsUrl !== "-" && this.runtimeDiagnosticsUrl !== void 0) try {
550
+ return new URL(this.runtimeDiagnosticsUrl);
551
+ } catch (err) {
552
+ actionsCore.info(`User-provided diagnostic endpoint ignored: not a valid URL: ${stringifyError(err)}`);
553
+ }
554
+ try {
555
+ const diagnosticUrl = await this.getRootUrl();
556
+ diagnosticUrl.pathname += "events/batch";
557
+ return diagnosticUrl;
558
+ } catch (err) {
559
+ actionsCore.info(`Generated diagnostic endpoint ignored, and diagnostics are disabled: not a valid URL: ${stringifyError(err)}`);
560
+ return;
561
+ }
562
+ }
563
+ async getUrlsByPreference() {
564
+ if (this.prioritizedURLs === void 0) this.prioritizedURLs = orderRecordsByPriorityWeight(await discoverServiceRecords()).flatMap((record) => recordToUrl(record) || []);
565
+ return this.prioritizedURLs;
566
+ }
567
+ };
568
+ function recordToUrl(record) {
569
+ const urlStr = `https://${record.name}:${record.port}`;
570
+ try {
571
+ return new URL(urlStr);
572
+ } catch (err) {
573
+ actionsCore.debug(`Record ${JSON.stringify(record)} produced an invalid URL: ${urlStr} (${err})`);
574
+ return;
575
+ }
576
+ }
577
+ async function discoverServiceRecords() {
578
+ return await discoverServicesStub(resolveSrv(LOOKUP), 1e3);
579
+ }
580
+ async function discoverServicesStub(lookup, timeout) {
581
+ const defaultFallback = new Promise((resolve, _reject) => {
582
+ setTimeout(resolve, timeout, []);
583
+ });
584
+ let records;
585
+ try {
586
+ records = await Promise.race([lookup, defaultFallback]);
587
+ } catch (reason) {
588
+ actionsCore.debug(`Error resolving SRV records: ${stringifyError(reason)}`);
589
+ records = [];
590
+ }
591
+ const acceptableRecords = records.filter((record) => {
592
+ for (const suffix of ALLOWED_SUFFIXES) if (record.name.endsWith(suffix)) return true;
593
+ actionsCore.debug(`Unacceptable domain due to an invalid suffix: ${record.name}`);
594
+ return false;
595
+ });
596
+ if (acceptableRecords.length === 0) actionsCore.debug(`No records found for ${LOOKUP}`);
597
+ else actionsCore.debug(`Resolved ${LOOKUP} to ${JSON.stringify(acceptableRecords)}`);
598
+ return acceptableRecords;
599
+ }
600
+ function orderRecordsByPriorityWeight(records) {
601
+ const byPriorityWeight = /* @__PURE__ */ new Map();
602
+ for (const record of records) {
603
+ const existing = byPriorityWeight.get(record.priority);
604
+ if (existing) existing.push(record);
605
+ else byPriorityWeight.set(record.priority, [record]);
606
+ }
607
+ const prioritizedRecords = [];
608
+ const keys = Array.from(byPriorityWeight.keys()).sort((a, b) => a - b);
609
+ for (const priority of keys) {
610
+ const recordsByPrio = byPriorityWeight.get(priority);
611
+ if (recordsByPrio === void 0) continue;
612
+ prioritizedRecords.push(...weightedRandom(recordsByPrio));
613
+ }
614
+ return prioritizedRecords;
615
+ }
616
+ function weightedRandom(records) {
617
+ const scratchRecords = records.slice();
618
+ const result = [];
619
+ while (scratchRecords.length > 0) {
620
+ const weights = [];
621
+ for (let i = 0; i < scratchRecords.length; i++) weights.push(scratchRecords[i].weight + (i > 0 ? scratchRecords[i - 1].weight : 0));
622
+ const point = Math.random() * weights[weights.length - 1];
623
+ for (let selectedIndex = 0; selectedIndex < weights.length; selectedIndex++) if (weights[selectedIndex] > point) {
624
+ result.push(scratchRecords.splice(selectedIndex, 1)[0]);
625
+ break;
626
+ }
627
+ }
628
+ return result;
629
+ }
630
+ //#endregion
631
+ //#region src/inputs.ts
632
+ /**
633
+ * @packageDocumentation
634
+ * Helpers for getting values from an Action's configuration.
635
+ */
636
+ var inputs_exports = /* @__PURE__ */ __exportAll({
637
+ getArrayOfStrings: () => getArrayOfStrings,
638
+ getArrayOfStringsOrNull: () => getArrayOfStringsOrNull,
639
+ getBool: () => getBool,
640
+ getBoolOrUndefined: () => getBoolOrUndefined,
641
+ getMultilineStringOrNull: () => getMultilineStringOrNull,
642
+ getNumberOrNull: () => getNumberOrNull,
643
+ getNumberOrUndefined: () => getNumberOrUndefined,
644
+ getString: () => getString,
645
+ getStringOrNull: () => getStringOrNull,
646
+ getStringOrUndefined: () => getStringOrUndefined,
647
+ handleString: () => handleString
648
+ });
649
+ /**
650
+ * Get a Boolean input from the Action's configuration by name.
651
+ */
652
+ const getBool = (name) => {
653
+ return actionsCore.getBooleanInput(name);
654
+ };
655
+ /**
656
+ * Get a Boolean input from the Action's configuration by name, or undefined if it is unset.
657
+ */
658
+ const getBoolOrUndefined = (name) => {
659
+ if (getStringOrUndefined(name) === void 0) return;
660
+ return actionsCore.getBooleanInput(name);
661
+ };
662
+ /**
663
+ * Convert a comma-separated string input into an array of strings. If `comma` is selected,
664
+ * all whitespace is removed from the string before converting to an array.
665
+ */
666
+ const getArrayOfStrings = (name, separator) => {
667
+ const original = getString(name);
668
+ return handleString(original, separator);
669
+ };
670
+ /**
671
+ * Convert a string input into an array of strings or `null` if no value is set.
672
+ */
673
+ const getArrayOfStringsOrNull = (name, separator) => {
674
+ const original = getStringOrNull(name);
675
+ if (original === null) return null;
676
+ else return handleString(original, separator);
677
+ };
678
+ const handleString = (input, separator) => {
679
+ const sepChar = separator === "comma" ? "," : /\s+/;
680
+ const trimmed = input.trim();
681
+ if (trimmed === "") return [];
682
+ return trimmed.split(sepChar).map((s) => s.trim());
683
+ };
684
+ /**
685
+ * Get a multi-line string input from the Action's configuration by name or return `null` if not set.
686
+ */
687
+ const getMultilineStringOrNull = (name) => {
688
+ const value = actionsCore.getMultilineInput(name);
689
+ if (value.length === 0) return null;
690
+ else return value;
691
+ };
692
+ /**
693
+ * Get a number input from the Action's configuration by name or return `null` if not set.
694
+ */
695
+ const getNumberOrNull = (name) => {
696
+ const value = actionsCore.getInput(name);
697
+ if (value === "") return null;
698
+ else return Number(value);
699
+ };
700
+ /**
701
+ * Get a Number input from the Action's configuration by name, or undefined if it is unset.
702
+ */
703
+ const getNumberOrUndefined = (name) => {
704
+ const value = getStringOrUndefined(name);
705
+ if (value === void 0) return;
706
+ return Number(value);
707
+ };
708
+ /**
709
+ * Get a string input from the Action's configuration.
710
+ */
711
+ const getString = (name) => {
712
+ return actionsCore.getInput(name);
713
+ };
714
+ /**
715
+ * Get a string input from the Action's configuration by name or return `null` if not set.
716
+ */
717
+ const getStringOrNull = (name) => {
718
+ const value = actionsCore.getInput(name);
719
+ if (value === "") return null;
720
+ else return value;
721
+ };
722
+ /**
723
+ * Get a string input from the Action's configuration by name or return `undefined` if not set.
724
+ */
725
+ const getStringOrUndefined = (name) => {
726
+ const value = actionsCore.getInput(name);
727
+ if (value === "") return;
728
+ else return value;
729
+ };
730
+ //#endregion
731
+ //#region src/platform.ts
732
+ /**
733
+ * @packageDocumentation
734
+ * Helpers for determining system attributes of the current runner.
735
+ */
736
+ var platform_exports = /* @__PURE__ */ __exportAll({
737
+ getArchOs: () => getArchOs,
738
+ getNixPlatform: () => getNixPlatform
739
+ });
740
+ /**
741
+ * Get the current architecture plus OS. Examples include `X64-Linux` and `ARM64-macOS`.
742
+ */
743
+ function getArchOs() {
744
+ const envArch = process.env.RUNNER_ARCH;
745
+ const envOs = process.env.RUNNER_OS;
746
+ if (envArch && envOs) return `${envArch}-${envOs}`;
747
+ else {
748
+ actionsCore.error(`Can't identify the platform: RUNNER_ARCH or RUNNER_OS undefined (${envArch}-${envOs})`);
749
+ throw new Error("RUNNER_ARCH and/or RUNNER_OS is not defined");
750
+ }
751
+ }
752
+ /**
753
+ * Get the current Nix system. Examples include `x86_64-linux` and `aarch64-darwin`.
754
+ */
755
+ function getNixPlatform(archOs) {
756
+ const mappedTo = (/* @__PURE__ */ new Map([
757
+ ["X64-macOS", "x86_64-darwin"],
758
+ ["ARM64-macOS", "aarch64-darwin"],
759
+ ["X64-Linux", "x86_64-linux"],
760
+ ["ARM64-Linux", "aarch64-linux"]
761
+ ])).get(archOs);
762
+ if (mappedTo) return mappedTo;
763
+ else {
764
+ actionsCore.error(`ArchOs (${archOs}) doesn't map to a supported Nix platform.`);
765
+ throw new Error(`Cannot convert ArchOs (${archOs}) to a supported Nix platform.`);
766
+ }
767
+ }
768
+ //#endregion
769
+ //#region src/sourcedef.ts
770
+ /**
771
+ * Throw if hash-locking is requested against a source that is not pinned to a
772
+ * fixed version. `source-tag`, `source-revision`, and `source-url` are
773
+ * immutable (or caller-controlled); any other selector resolves to a moving
774
+ * target (`branch`, `pr`, or the `stable` fallback) where the pinned checksum
775
+ * would break the moment a new release is published.
776
+ */
777
+ function assertChecksumSourceIsPinned(source) {
778
+ if (source.url === void 0 && source.tag === void 0 && source.revision === void 0) throw new Error("Hash-locking via `source-checksums-url`/`source-checksums-sha256` requires a pinned source: set `source-tag`, `source-revision`, or `source-url`. Without one the action resolves to a moving target (e.g. `stable`) and the checksum will break the next time a release is published.");
779
+ }
780
+ function constructSourceParameters(legacyPrefix) {
781
+ return {
782
+ path: noisilyGetInput("path", legacyPrefix),
783
+ url: noisilyGetInput("url", legacyPrefix),
784
+ tag: noisilyGetInput("tag", legacyPrefix),
785
+ pr: noisilyGetInput("pr", legacyPrefix),
786
+ branch: noisilyGetInput("branch", legacyPrefix),
787
+ revision: noisilyGetInput("revision", legacyPrefix)
788
+ };
789
+ }
790
+ function noisilyGetInput(suffix, legacyPrefix) {
791
+ const preferredInput = getStringOrUndefined(`source-${suffix}`);
792
+ if (!legacyPrefix) return preferredInput;
793
+ const legacyInput = getStringOrUndefined(`${legacyPrefix}-${suffix}`);
794
+ if (preferredInput && legacyInput) {
795
+ actionsCore.warning(`The supported option source-${suffix} and the legacy option ${legacyPrefix}-${suffix} are both set. Preferring source-${suffix}. Please stop setting ${legacyPrefix}-${suffix}.`);
796
+ return preferredInput;
797
+ } else if (legacyInput) {
798
+ actionsCore.warning(`The legacy option ${legacyPrefix}-${suffix} is set. Please migrate to source-${suffix}.`);
799
+ return legacyInput;
800
+ } else return preferredInput;
801
+ }
802
+ //#endregion
803
+ //#region src/index.ts
804
+ /**
805
+ * @packageDocumentation
806
+ * Determinate Systems' TypeScript library for creating GitHub Actions logic.
807
+ */
808
+ const pkgVersion = "1.0";
809
+ const EVENT_BACKTRACES = "backtrace";
810
+ const EVENT_EXCEPTION = "exception";
811
+ const EVENT_ARTIFACT_CACHE_HIT = "artifact_cache_hit";
812
+ const EVENT_ARTIFACT_CACHE_MISS = "artifact_cache_miss";
813
+ const EVENT_ARTIFACT_CACHE_PERSIST = "artifact_cache_persist";
814
+ const EVENT_PREFLIGHT_REQUIRE_NIX_DENIED = "preflight-require-nix-denied";
815
+ const EVENT_STORE_IDENTITY_FAILED = "store_identity_failed";
816
+ const FACT_ARTIFACT_FETCHED_FROM_CACHE = "artifact_fetched_from_cache";
817
+ const FACT_ENDED_WITH_EXCEPTION = "ended_with_exception";
818
+ const FACT_FINAL_EXCEPTION = "final_exception";
819
+ const FACT_OS = "$os";
820
+ const FACT_OS_VERSION = "$os_version";
821
+ const FACT_SOURCE_URL = "source_url";
822
+ const FACT_SOURCE_URL_ETAG = "source_url_etag";
823
+ const FACT_SOURCE_CHECKSUMS_SHA256 = "source_checksums_sha256";
824
+ const FACT_NIX_VERSION = "nix_version";
825
+ const FACT_NIX_LOCATION = "nix_location";
826
+ const FACT_NIX_STORE_TRUST = "nix_store_trusted";
827
+ const FACT_NIX_STORE_VERSION = "nix_store_version";
828
+ const FACT_NIX_STORE_CHECK_METHOD = "nix_store_check_method";
829
+ const FACT_NIX_STORE_CHECK_ERROR = "nix_store_check_error";
830
+ const STATE_KEY_EXECUTION_PHASE = "detsys_action_execution_phase";
831
+ const STATE_KEY_NIX_NOT_FOUND = "detsys_action_nix_not_found";
832
+ const STATE_NOT_FOUND = "not-found";
833
+ const STATE_KEY_CROSS_PHASE_ID = "detsys_cross_phase_id";
834
+ const STATE_BACKTRACE_START_TIMESTAMP = "detsys_backtrace_start_timestamp";
835
+ const DIAGNOSTIC_ENDPOINT_TIMEOUT_MS = 1e4;
836
+ const CHECK_IN_ENDPOINT_TIMEOUT_MS = 1e3;
837
+ const PROGRAM_NAME_CRASH_DENY_LIST = [
838
+ "nix-expr-tests",
839
+ "nix-store-tests",
840
+ "nix-util-tests"
841
+ ];
842
+ const determinateStateDir = "/var/lib/determinate";
843
+ const determinateIdentityFile = path.join(determinateStateDir, "identity.json");
844
+ const isRoot = typeof process.geteuid === "function" && process.geteuid() === 0;
845
+ /** Create the Determinate state directory by escalating via sudo */
846
+ async function sudoEnsureDeterminateStateDir() {
847
+ const code = await exec$1.exec("sudo", [
848
+ "mkdir",
849
+ "-p",
850
+ determinateStateDir
851
+ ]);
852
+ if (code !== 0) throw new Error(`sudo mkdir -p exit: ${code}`);
853
+ }
854
+ /** Ensures the Determinate state directory exists, escalating if necessary */
855
+ async function ensureDeterminateStateDir() {
856
+ if (isRoot) await mkdir(determinateStateDir, { recursive: true });
857
+ else return sudoEnsureDeterminateStateDir();
858
+ }
859
+ /** Writes correlation hashes to the Determinate state directory by writing to a `sudo tee` pipe */
860
+ async function sudoWriteCorrelationHashes(hashes) {
861
+ const buffer = Buffer.from(hashes);
862
+ const code = await exec$1.exec("sudo", ["tee", determinateIdentityFile], {
863
+ input: buffer,
864
+ outStream: createWriteStream("/dev/null")
865
+ });
866
+ if (code !== 0) throw new Error(`sudo tee exit: ${code}`);
867
+ }
868
+ /** Writes correlation hashes to the Determinate state directory, escalating if necessary */
869
+ async function writeCorrelationHashes(hashes) {
870
+ await ensureDeterminateStateDir();
871
+ if (isRoot) await fs.writeFile(determinateIdentityFile, hashes, "utf-8");
872
+ else return sudoWriteCorrelationHashes(hashes);
873
+ }
874
+ var DetSysAction = class {
875
+ determineExecutionPhase() {
876
+ if (actionsCore.getState(STATE_KEY_EXECUTION_PHASE) === "") {
877
+ actionsCore.saveState(STATE_KEY_EXECUTION_PHASE, "post");
878
+ return "main";
879
+ } else return "post";
880
+ }
881
+ constructor(actionOptions) {
882
+ this.actionOptions = makeOptionsConfident(actionOptions);
883
+ this.idsHost = new IdsHost(this.actionOptions.idsProjectName, actionOptions.diagnosticsSuffix, process.env["INPUT_DIAGNOSTIC-ENDPOINT"], getNumberOrUndefined("timeout-request"));
884
+ this.exceptionAttachments = /* @__PURE__ */ new Map();
885
+ this.nixStoreTrust = "unknown";
886
+ this.strictMode = getBool("_internal-strict-mode");
887
+ if (getBoolOrUndefined("_internal-obliterate-actions-id-token-request-variables") === true) {
888
+ process.env["ACTIONS_ID_TOKEN_REQUEST_URL"] = void 0;
889
+ process.env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = void 0;
890
+ }
891
+ this.features = {};
892
+ this.featureEventMetadata = {};
893
+ this.events = [];
894
+ this.getCrossPhaseId();
895
+ this.collectBacktraceSetup();
896
+ this.facts = {
897
+ $lib: "idslib",
898
+ $lib_version: pkgVersion,
899
+ project: this.actionOptions.name,
900
+ ids_project: this.actionOptions.idsProjectName
901
+ };
902
+ for (const [target, env] of [
903
+ ["github_action_ref", "GITHUB_ACTION_REF"],
904
+ ["github_action_repository", "GITHUB_ACTION_REPOSITORY"],
905
+ ["github_event_name", "GITHUB_EVENT_NAME"],
906
+ ["$os", "RUNNER_OS"],
907
+ ["arch", "RUNNER_ARCH"]
908
+ ]) {
909
+ const value = process.env[env];
910
+ if (value) this.facts[target] = value;
911
+ }
912
+ this.identity = identify();
913
+ this.archOs = getArchOs();
914
+ this.nixSystem = getNixPlatform(this.archOs);
915
+ this.facts.$app_name = `${this.actionOptions.name}/action`;
916
+ this.facts.arch_os = this.archOs;
917
+ this.facts.nix_system = this.nixSystem;
918
+ getDetails().then((details) => {
919
+ if (details.name !== "unknown") this.addFact(FACT_OS, details.name);
920
+ if (details.version !== "unknown") this.addFact(FACT_OS_VERSION, details.version);
921
+ }).catch((e) => {
922
+ actionsCore.debug(`Failure getting platform details: ${stringifyError$1(e)}`);
923
+ });
924
+ this.executionPhase = this.determineExecutionPhase();
925
+ this.facts.execution_phase = this.executionPhase;
926
+ if (this.actionOptions.fetchStyle === "gh-env-style") this.architectureFetchSuffix = this.archOs;
927
+ else if (this.actionOptions.fetchStyle === "nix-style") this.architectureFetchSuffix = this.nixSystem;
928
+ else if (this.actionOptions.fetchStyle === "universal") this.architectureFetchSuffix = "universal";
929
+ else throw new Error(`fetchStyle ${this.actionOptions.fetchStyle} is not a valid style`);
930
+ this.sourceParameters = constructSourceParameters(this.actionOptions.legacySourcePrefix);
931
+ this.recordEvent(`begin_${this.executionPhase}`);
932
+ }
933
+ /**
934
+ * Attach a file to the diagnostics data in error conditions.
935
+ *
936
+ * The file at `location` doesn't need to exist when stapleFile is called.
937
+ *
938
+ * If the file doesn't exist or is unreadable when trying to staple the attachments, the JS error will be stored in a context value at `staple_failure_{name}`.
939
+ * If the file is readable, the file's contents will be stored in a context value at `staple_value_{name}`.
940
+ */
941
+ stapleFile(name, location) {
942
+ this.exceptionAttachments.set(name, location);
943
+ }
944
+ /**
945
+ * Execute the Action as defined.
946
+ */
947
+ execute() {
948
+ this.executeAsync().catch((error) => {
949
+ console.log(error);
950
+ process.exitCode = 1;
951
+ });
952
+ }
953
+ getTemporaryName() {
954
+ const tmpDir = process.env["RUNNER_TEMP"] || tmpdir();
955
+ return path.join(tmpDir, `${this.actionOptions.name}-${randomUUID()}`);
956
+ }
957
+ addFact(key, value) {
958
+ this.facts[key] = value;
959
+ }
960
+ async getDiagnosticsUrl() {
961
+ return await this.idsHost.getDiagnosticsUrl();
962
+ }
963
+ getUniqueId() {
964
+ return this.identity.github_workflow_run_differentiator_hash || process.env.RUNNER_TRACKING_ID || randomUUID();
965
+ }
966
+ getCrossPhaseId() {
967
+ let crossPhaseId = actionsCore.getState(STATE_KEY_CROSS_PHASE_ID);
968
+ if (crossPhaseId === "") {
969
+ crossPhaseId = randomUUID();
970
+ actionsCore.saveState(STATE_KEY_CROSS_PHASE_ID, crossPhaseId);
971
+ }
972
+ return crossPhaseId;
973
+ }
974
+ getCorrelationHashes() {
975
+ return this.identity;
976
+ }
977
+ recordEvent(eventName, context = {}) {
978
+ const prefixedName = eventName === "$feature_flag_called" || eventName === "$groupidentify" ? eventName : `${this.actionOptions.eventPrefix}${eventName}`;
979
+ this.events.push({
980
+ name: prefixedName,
981
+ distinct_id: this.identity.$anon_distinct_id,
982
+ uuid: randomUUID(),
983
+ timestamp: /* @__PURE__ */ new Date(),
984
+ properties: {
985
+ ...context,
986
+ ...this.identity,
987
+ ...this.facts,
988
+ ...Object.fromEntries(Object.entries(this.featureEventMetadata).map(([name, variant]) => [`$feature/${name}`, variant]))
989
+ }
990
+ });
991
+ }
992
+ /**
993
+ * Unpacks the closure returned by `fetchArtifact()`, imports the
994
+ * contents into the Nix store, and returns the path of the executable at
995
+ * `/nix/store/STORE_PATH/bin/${bin}`.
996
+ */
997
+ async unpackClosure(bin) {
998
+ const artifact = await this.fetchArtifact();
999
+ const { stdout } = await promisify(exec)(`cat "${artifact}" | xz -d | nix-store --import`);
1000
+ return `${stdout.split(os$1.EOL).at(-2)}/bin/${bin}`;
1001
+ }
1002
+ /**
1003
+ * Fetches the executable at the URL determined by the `source-*` inputs and
1004
+ * other facts, `chmod`s it, and returns the path to the executable on disk.
1005
+ */
1006
+ async fetchExecutable() {
1007
+ const binaryPath = await this.fetchArtifact();
1008
+ await chmod(binaryPath, constants.S_IXUSR | constants.S_IXGRP);
1009
+ return binaryPath;
1010
+ }
1011
+ get isMain() {
1012
+ return this.executionPhase === "main";
1013
+ }
1014
+ get isPost() {
1015
+ return this.executionPhase === "post";
1016
+ }
1017
+ async executeAsync() {
1018
+ try {
1019
+ await this.checkIn();
1020
+ const correlationHashes = JSON.stringify(this.getCorrelationHashes());
1021
+ process.env.DETSYS_CORRELATION = correlationHashes;
1022
+ try {
1023
+ await writeCorrelationHashes(correlationHashes);
1024
+ } catch (error) {
1025
+ this.recordEvent(EVENT_STORE_IDENTITY_FAILED, { error: String(error) });
1026
+ }
1027
+ if (!await this.preflightRequireNix()) {
1028
+ this.recordEvent(EVENT_PREFLIGHT_REQUIRE_NIX_DENIED);
1029
+ return;
1030
+ } else {
1031
+ await this.preflightNixStoreInfo();
1032
+ await this.preflightNixVersion();
1033
+ this.addFact(FACT_NIX_STORE_TRUST, this.nixStoreTrust);
1034
+ }
1035
+ if (this.isMain) {
1036
+ this.recordGroup();
1037
+ await this.main();
1038
+ await this.preflightNixVersion();
1039
+ } else if (this.isPost) await this.post();
1040
+ this.addFact(FACT_ENDED_WITH_EXCEPTION, false);
1041
+ } catch (e) {
1042
+ this.addFact(FACT_ENDED_WITH_EXCEPTION, true);
1043
+ const reportable = stringifyError$1(e);
1044
+ this.addFact(FACT_FINAL_EXCEPTION, reportable);
1045
+ if (this.isPost) actionsCore.warning(reportable);
1046
+ else actionsCore.setFailed(reportable);
1047
+ const doGzip = promisify(gzip);
1048
+ const exceptionContext = /* @__PURE__ */ new Map();
1049
+ for (const [attachmentLabel, filePath] of this.exceptionAttachments) try {
1050
+ const buf = await doGzip(readFileSync(filePath));
1051
+ exceptionContext.set(`staple_value_${attachmentLabel}`, buf.toString("base64"));
1052
+ } catch (innerError) {
1053
+ exceptionContext.set(`staple_failure_${attachmentLabel}`, stringifyError$1(innerError));
1054
+ }
1055
+ this.recordEvent(EVENT_EXCEPTION, Object.fromEntries(exceptionContext));
1056
+ } finally {
1057
+ if (this.isPost) await this.collectBacktraces();
1058
+ await this.complete();
1059
+ }
1060
+ }
1061
+ async getClient() {
1062
+ return await this.idsHost.getGot((incitingError, prevUrl, nextUrl) => {
1063
+ this.recordPlausibleTimeout(incitingError);
1064
+ this.recordEvent("ids-failover", {
1065
+ previousUrl: prevUrl.toString(),
1066
+ nextUrl: nextUrl.toString()
1067
+ });
1068
+ });
1069
+ }
1070
+ async checkIn() {
1071
+ const checkin = await this.requestCheckIn();
1072
+ if (checkin === void 0) return;
1073
+ this.features = checkin.options;
1074
+ for (const [key, feature] of Object.entries(this.features)) this.featureEventMetadata[key] = feature.variant;
1075
+ const impactSymbol = /* @__PURE__ */ new Map([
1076
+ ["none", "⚪"],
1077
+ ["maintenance", "🛠️"],
1078
+ ["minor", "🟡"],
1079
+ ["major", "🟠"],
1080
+ ["critical", "🔴"]
1081
+ ]);
1082
+ const defaultImpactSymbol = "🔵";
1083
+ if (checkin.status !== null) {
1084
+ const summaries = [];
1085
+ for (const incident of checkin.status.incidents) summaries.push(`${impactSymbol.get(incident.impact) || defaultImpactSymbol} ${incident.status.replace("_", " ")}: ${incident.name} (${incident.shortlink})`);
1086
+ for (const maintenance of checkin.status.scheduled_maintenances) summaries.push(`${impactSymbol.get(maintenance.impact) || defaultImpactSymbol} ${maintenance.status.replace("_", " ")}: ${maintenance.name} (${maintenance.shortlink})`);
1087
+ if (summaries.length > 0) {
1088
+ actionsCore.info(`${checkin.status.page.name} Status`);
1089
+ for (const notice of summaries) actionsCore.info(notice);
1090
+ actionsCore.info(`See: ${checkin.status.page.url}`);
1091
+ actionsCore.info(``);
1092
+ }
1093
+ }
1094
+ }
1095
+ getFeature(name) {
1096
+ if (!this.features.hasOwnProperty(name)) return;
1097
+ const result = this.features[name];
1098
+ if (result === void 0) return;
1099
+ this.recordEvent("$feature_flag_called", {
1100
+ $feature_flag: name,
1101
+ $feature_flag_response: result.variant
1102
+ });
1103
+ return result;
1104
+ }
1105
+ recordGroup() {
1106
+ const ghorg_hash = this.identity.$groups["github_organization"];
1107
+ const ghorg_name = process.env["GITHUB_REPOSITORY_OWNER"];
1108
+ if (ghorg_hash !== void 0 && ghorg_name !== void 0) this.recordEvent("$groupidentify", {
1109
+ $group_type: "github_organization",
1110
+ $group_key: ghorg_hash,
1111
+ $group_set: { name: ghorg_name }
1112
+ });
1113
+ }
1114
+ /**
1115
+ * Check in to install.determinate.systems, to accomplish three things:
1116
+ *
1117
+ * 1. Preflight the server selected from IdsHost, to increase the chances of success.
1118
+ * 2. Fetch any incidents and maintenance events to let users know in case things are weird.
1119
+ * 3. Get feature flag data so we can gently roll out new features.
1120
+ */
1121
+ async requestCheckIn() {
1122
+ for (let attemptsRemaining = 5; attemptsRemaining > 0; attemptsRemaining--) {
1123
+ const checkInUrl = await this.getCheckInUrl();
1124
+ if (checkInUrl === void 0) return;
1125
+ try {
1126
+ actionsCore.debug(`Preflighting via ${checkInUrl}`);
1127
+ const props = {
1128
+ distinct_id: this.identity.$anon_distinct_id,
1129
+ anon_distinct_id: this.identity.$anon_distinct_id,
1130
+ groups: this.identity.$groups,
1131
+ person_properties: {
1132
+ ci: "github",
1133
+ ...this.identity,
1134
+ ...this.facts
1135
+ }
1136
+ };
1137
+ return await (await this.getClient()).post(checkInUrl, {
1138
+ json: props,
1139
+ timeout: { request: CHECK_IN_ENDPOINT_TIMEOUT_MS }
1140
+ }).json();
1141
+ } catch (e) {
1142
+ this.recordPlausibleTimeout(e);
1143
+ actionsCore.debug(`Error checking in: ${stringifyError$1(e)}`);
1144
+ this.idsHost.markCurrentHostBroken();
1145
+ }
1146
+ }
1147
+ }
1148
+ recordPlausibleTimeout(e) {
1149
+ if (e instanceof TimeoutError && "timings" in e && "request" in e) {
1150
+ const reportContext = {
1151
+ url: e.request.requestUrl?.toString(),
1152
+ retry_count: e.request.retryCount
1153
+ };
1154
+ for (const [key, value] of Object.entries(e.timings.phases)) if (Number.isFinite(value)) reportContext[`timing_phase_${key}`] = value;
1155
+ this.recordEvent("timeout", reportContext);
1156
+ }
1157
+ }
1158
+ /**
1159
+ * Fetch an artifact, such as a tarball, from the location determined by the
1160
+ * `source-*` inputs. If `source-binary` is specified, this will return a path
1161
+ * to a binary on disk; otherwise, the artifact will be downloaded from the
1162
+ * URL determined by the other `source-*` inputs (`source-url`, `source-pr`,
1163
+ * etc.).
1164
+ *
1165
+ * When `source-checksums-url` and `source-checksums-sha256` are both set,
1166
+ * the downloaded artifact is verified against the per-arch hash in the
1167
+ * checksums file, which is itself verified against the pinned
1168
+ * `source-checksums-sha256`. Both inputs must be set together.
1169
+ */
1170
+ async fetchArtifact() {
1171
+ const sourceBinary = getStringOrNull("source-binary");
1172
+ if (sourceBinary !== null && sourceBinary !== "") {
1173
+ actionsCore.debug(`Using the provided source binary at ${sourceBinary}`);
1174
+ return sourceBinary;
1175
+ }
1176
+ const expectedArtifactHash = await this.resolveExpectedArtifactHash();
1177
+ actionsCore.startGroup(`Downloading ${this.actionOptions.name} for ${this.architectureFetchSuffix}`);
1178
+ try {
1179
+ actionsCore.info(`Fetching from ${await this.getSourceUrl()}`);
1180
+ const correlatedUrl = await this.getSourceUrl();
1181
+ correlatedUrl.searchParams.set("ci", "github");
1182
+ correlatedUrl.searchParams.set("correlation", JSON.stringify(this.identity));
1183
+ const versionCheckup = await (await this.getClient()).head(correlatedUrl);
1184
+ if (versionCheckup.headers.etag) {
1185
+ const v = versionCheckup.headers.etag;
1186
+ this.addFact(FACT_SOURCE_URL_ETAG, v);
1187
+ actionsCore.debug(`Checking the tool cache for ${await this.getSourceUrl()} at ${v}`);
1188
+ const cached = await this.getCachedVersion(v, expectedArtifactHash);
1189
+ if (cached) {
1190
+ this.facts[FACT_ARTIFACT_FETCHED_FROM_CACHE] = true;
1191
+ actionsCore.debug(`Tool cache hit.`);
1192
+ await this.verifyArtifactHash(cached, expectedArtifactHash);
1193
+ return cached;
1194
+ }
1195
+ }
1196
+ this.facts[FACT_ARTIFACT_FETCHED_FROM_CACHE] = false;
1197
+ actionsCore.debug(`No match from the cache, re-fetching from the redirect: ${versionCheckup.url}`);
1198
+ const destFile = this.getTemporaryName();
1199
+ const fetchStream = await this.downloadFile(new URL(versionCheckup.url), destFile);
1200
+ await this.verifyArtifactHash(destFile, expectedArtifactHash);
1201
+ if (fetchStream.response?.headers.etag) {
1202
+ const v = fetchStream.response.headers.etag;
1203
+ try {
1204
+ await this.saveCachedVersion(v, destFile, expectedArtifactHash);
1205
+ } catch (e) {
1206
+ actionsCore.debug(`Error caching the artifact: ${stringifyError$1(e)}`);
1207
+ }
1208
+ }
1209
+ return destFile;
1210
+ } catch (e) {
1211
+ this.recordPlausibleTimeout(e);
1212
+ throw e;
1213
+ } finally {
1214
+ actionsCore.endGroup();
1215
+ }
1216
+ }
1217
+ /**
1218
+ * Read the `source-checksums-url` and `source-checksums-sha256` inputs and,
1219
+ * if both are set, fetch the checksums file, verify its hash matches the
1220
+ * pin, parse it, and return the expected hash for the artifact matching
1221
+ * this runner's `${name}-${architectureFetchSuffix}`. Returns `null` when
1222
+ * verification is opted out (both inputs unset).
1223
+ */
1224
+ async resolveExpectedArtifactHash() {
1225
+ const checksumsUrl = getStringOrNull("source-checksums-url");
1226
+ const checksumsSha256 = getStringOrNull("source-checksums-sha256");
1227
+ if (checksumsUrl === null && checksumsSha256 === null) return null;
1228
+ if (checksumsUrl === null || checksumsSha256 === null) throw new Error("`source-checksums-url` and `source-checksums-sha256` must be set together");
1229
+ assertChecksumSourceIsPinned(this.sourceParameters);
1230
+ const expectedFileHash = checksumsSha256.toLowerCase();
1231
+ this.addFact(FACT_SOURCE_CHECKSUMS_SHA256, expectedFileHash);
1232
+ const parsedUrl = new URL(checksumsUrl);
1233
+ const safeUrl = parsedUrl.origin + parsedUrl.pathname;
1234
+ actionsCore.info(`Fetching checksums file from ${safeUrl}`);
1235
+ const body = (await (await this.getClient()).get(checksumsUrl)).body;
1236
+ const actualFileHash = sha256OfBuffer(body);
1237
+ if (actualFileHash !== expectedFileHash) throw new Error(`Checksums file hash mismatch at ${safeUrl}: expected ${expectedFileHash}, got ${actualFileHash}`);
1238
+ const wanted = `${this.actionOptions.name}-${this.architectureFetchSuffix}`;
1239
+ const artifactHash = parseChecksumsFile(body).get(wanted);
1240
+ if (artifactHash === void 0) throw new Error(`No entry for ${wanted} in checksums file at ${safeUrl}`);
1241
+ return artifactHash;
1242
+ }
1243
+ /**
1244
+ * Verify a downloaded artifact's SHA-256 matches the expected hash. No-op
1245
+ * when `expected` is `null` (verification disabled).
1246
+ */
1247
+ async verifyArtifactHash(filePath, expected) {
1248
+ if (expected === null) return;
1249
+ const actual = await sha256OfFile(filePath);
1250
+ if (actual !== expected) throw new Error(`Artifact hash mismatch for ${this.architectureFetchSuffix}: expected ${expected}, got ${actual}`);
1251
+ }
1252
+ /**
1253
+ * A helper function for failing on error only if strict mode is enabled.
1254
+ * This is intended only for CI environments testing Actions themselves.
1255
+ */
1256
+ failOnError(msg) {
1257
+ if (this.strictMode) actionsCore.setFailed(`strict mode failure: ${msg}`);
1258
+ }
1259
+ async downloadFile(url, destination) {
1260
+ const client = await this.getClient();
1261
+ return new Promise((resolve, reject) => {
1262
+ let writeStream;
1263
+ let failed = false;
1264
+ const retry = (stream) => {
1265
+ if (writeStream) writeStream.destroy();
1266
+ writeStream = createWriteStream(destination, {
1267
+ encoding: "binary",
1268
+ mode: 493
1269
+ });
1270
+ writeStream.once("error", (error) => {
1271
+ failed = true;
1272
+ reject(error);
1273
+ });
1274
+ writeStream.on("finish", () => {
1275
+ if (!failed) resolve(stream);
1276
+ });
1277
+ stream.once("retry", (_count, _error, createRetryStream) => {
1278
+ retry(createRetryStream());
1279
+ });
1280
+ stream.pipe(writeStream);
1281
+ };
1282
+ retry(client.stream(url));
1283
+ });
1284
+ }
1285
+ async complete() {
1286
+ this.recordEvent(`complete_${this.executionPhase}`);
1287
+ await this.submitEvents();
1288
+ }
1289
+ async getCheckInUrl() {
1290
+ const checkInUrl = await this.idsHost.getDynamicRootUrl();
1291
+ if (checkInUrl === void 0) return;
1292
+ checkInUrl.pathname += "check-in";
1293
+ return checkInUrl;
1294
+ }
1295
+ async getSourceUrl() {
1296
+ const p = this.sourceParameters;
1297
+ if (p.url) {
1298
+ this.addFact(FACT_SOURCE_URL, p.url);
1299
+ return new URL(p.url);
1300
+ }
1301
+ const fetchUrl = await this.idsHost.getRootUrl();
1302
+ fetchUrl.pathname += this.actionOptions.idsProjectName;
1303
+ if (p.tag) fetchUrl.pathname += `/tag/${p.tag}`;
1304
+ else if (p.pr) fetchUrl.pathname += `/pr/${p.pr}`;
1305
+ else if (p.branch) fetchUrl.pathname += `/branch/${p.branch}`;
1306
+ else if (p.revision) fetchUrl.pathname += `/rev/${p.revision}`;
1307
+ else fetchUrl.pathname += `/stable`;
1308
+ fetchUrl.pathname += `/${this.architectureFetchSuffix}`;
1309
+ this.addFact(FACT_SOURCE_URL, fetchUrl.toString());
1310
+ return fetchUrl;
1311
+ }
1312
+ cacheKey(version, expectedHash) {
1313
+ const cleanedVersion = version.replace(/[^a-zA-Z0-9-+.]/g, "");
1314
+ const hashSuffix = expectedHash ? `-h${expectedHash}` : "";
1315
+ return `determinatesystem-${this.actionOptions.name}-${this.architectureFetchSuffix}-${cleanedVersion}${hashSuffix}`;
1316
+ }
1317
+ async getCachedVersion(version, expectedHash) {
1318
+ const startCwd = process.cwd();
1319
+ try {
1320
+ const tempDir = this.getTemporaryName();
1321
+ await mkdir(tempDir);
1322
+ process.chdir(tempDir);
1323
+ process.env.GITHUB_WORKSPACE_BACKUP = process.env.GITHUB_WORKSPACE;
1324
+ delete process.env.GITHUB_WORKSPACE;
1325
+ if (await actionsCache.restoreCache([this.actionOptions.name], this.cacheKey(version, expectedHash), [], void 0, true)) {
1326
+ this.recordEvent(EVENT_ARTIFACT_CACHE_HIT);
1327
+ return `${tempDir}/${this.actionOptions.name}`;
1328
+ }
1329
+ this.recordEvent(EVENT_ARTIFACT_CACHE_MISS);
1330
+ return;
1331
+ } finally {
1332
+ process.env.GITHUB_WORKSPACE = process.env.GITHUB_WORKSPACE_BACKUP;
1333
+ delete process.env.GITHUB_WORKSPACE_BACKUP;
1334
+ process.chdir(startCwd);
1335
+ }
1336
+ }
1337
+ async saveCachedVersion(version, toolPath, expectedHash) {
1338
+ const startCwd = process.cwd();
1339
+ try {
1340
+ const tempDir = this.getTemporaryName();
1341
+ await mkdir(tempDir);
1342
+ process.chdir(tempDir);
1343
+ await copyFile(toolPath, `${tempDir}/${this.actionOptions.name}`);
1344
+ process.env.GITHUB_WORKSPACE_BACKUP = process.env.GITHUB_WORKSPACE;
1345
+ delete process.env.GITHUB_WORKSPACE;
1346
+ await actionsCache.saveCache([this.actionOptions.name], this.cacheKey(version, expectedHash), void 0, true);
1347
+ this.recordEvent(EVENT_ARTIFACT_CACHE_PERSIST);
1348
+ } finally {
1349
+ process.env.GITHUB_WORKSPACE = process.env.GITHUB_WORKSPACE_BACKUP;
1350
+ delete process.env.GITHUB_WORKSPACE_BACKUP;
1351
+ process.chdir(startCwd);
1352
+ }
1353
+ }
1354
+ collectBacktraceSetup() {
1355
+ if (!process.env.DETSYS_BACKTRACE_COLLECTOR) {
1356
+ actionsCore.exportVariable("DETSYS_BACKTRACE_COLLECTOR", this.getCrossPhaseId());
1357
+ actionsCore.saveState(STATE_BACKTRACE_START_TIMESTAMP, Date.now());
1358
+ }
1359
+ }
1360
+ async collectBacktraces() {
1361
+ try {
1362
+ if (process.env.DETSYS_BACKTRACE_COLLECTOR !== this.getCrossPhaseId()) return;
1363
+ const backtraces = await collectBacktraces(this.actionOptions.binaryNamePrefixes, this.actionOptions.binaryNamesDenyList, parseInt(actionsCore.getState(STATE_BACKTRACE_START_TIMESTAMP)));
1364
+ actionsCore.debug(`Backtraces identified: ${backtraces.size}`);
1365
+ if (backtraces.size > 0) this.recordEvent(EVENT_BACKTRACES, Object.fromEntries(backtraces));
1366
+ } catch (innerError) {
1367
+ actionsCore.debug(`Error collecting backtraces: ${stringifyError$1(innerError)}`);
1368
+ }
1369
+ }
1370
+ async preflightRequireNix() {
1371
+ let nixLocation;
1372
+ const pathParts = (process.env["PATH"] || "").split(":");
1373
+ for (const location of pathParts) {
1374
+ const candidateNix = path.join(location, "nix");
1375
+ try {
1376
+ await fs.access(candidateNix, fs.constants.X_OK);
1377
+ actionsCore.debug(`Found Nix at ${candidateNix}`);
1378
+ nixLocation = candidateNix;
1379
+ break;
1380
+ } catch {
1381
+ actionsCore.debug(`Nix not at ${candidateNix}`);
1382
+ }
1383
+ }
1384
+ this.addFact(FACT_NIX_LOCATION, nixLocation || "");
1385
+ if (this.actionOptions.requireNix === "ignore") return true;
1386
+ if (actionsCore.getState(STATE_KEY_NIX_NOT_FOUND) === STATE_NOT_FOUND) return false;
1387
+ if (nixLocation !== void 0) return true;
1388
+ actionsCore.saveState(STATE_KEY_NIX_NOT_FOUND, STATE_NOT_FOUND);
1389
+ switch (this.actionOptions.requireNix) {
1390
+ case "fail":
1391
+ actionsCore.setFailed(["This action can only be used when Nix is installed.", "Add `- uses: DeterminateSystems/determinate-nix-action@v3` earlier in your workflow."].join(" "));
1392
+ break;
1393
+ case "warn": actionsCore.warning(["This action is in no-op mode because Nix is not installed.", "Add `- uses: DeterminateSystems/determinate-nix-action@v3` earlier in your workflow."].join(" "));
1394
+ }
1395
+ return false;
1396
+ }
1397
+ async preflightNixStoreInfo() {
1398
+ let output = "";
1399
+ const options = {};
1400
+ options.silent = true;
1401
+ options.listeners = { stdout: (data) => {
1402
+ output += data.toString();
1403
+ } };
1404
+ try {
1405
+ output = "";
1406
+ await exec$1.exec("nix", [
1407
+ "store",
1408
+ "info",
1409
+ "--json"
1410
+ ], options);
1411
+ this.addFact(FACT_NIX_STORE_CHECK_METHOD, "info");
1412
+ } catch {
1413
+ try {
1414
+ output = "";
1415
+ await exec$1.exec("nix", [
1416
+ "store",
1417
+ "ping",
1418
+ "--json"
1419
+ ], options);
1420
+ this.addFact(FACT_NIX_STORE_CHECK_METHOD, "ping");
1421
+ } catch {
1422
+ this.addFact(FACT_NIX_STORE_CHECK_METHOD, "none");
1423
+ return;
1424
+ }
1425
+ }
1426
+ try {
1427
+ const parsed = JSON.parse(output);
1428
+ if (parsed.trusted === true || parsed.trusted === 1) this.nixStoreTrust = "trusted";
1429
+ else if (parsed.trusted === false || parsed.trusted === 0) this.nixStoreTrust = "untrusted";
1430
+ else if (parsed.trusted !== void 0) this.addFact(FACT_NIX_STORE_CHECK_ERROR, `Mysterious trusted value: ${JSON.stringify(parsed.trusted)}`);
1431
+ this.addFact(FACT_NIX_STORE_VERSION, JSON.stringify(parsed.version));
1432
+ } catch (e) {
1433
+ this.addFact(FACT_NIX_STORE_CHECK_ERROR, stringifyError$1(e));
1434
+ }
1435
+ }
1436
+ async preflightNixVersion() {
1437
+ let output = "unknown";
1438
+ try {
1439
+ ({stdout: output} = await exec$1.getExecOutput("nix", ["--version"], { silent: true }));
1440
+ output = output.trim() || "unknown";
1441
+ } catch {}
1442
+ this.addFact(FACT_NIX_VERSION, output);
1443
+ }
1444
+ async submitEvents() {
1445
+ const diagnosticsUrl = await this.idsHost.getDiagnosticsUrl();
1446
+ if (diagnosticsUrl === void 0) {
1447
+ actionsCore.debug("Diagnostics are disabled. Not sending the following events:");
1448
+ actionsCore.debug(JSON.stringify(this.events, void 0, 2));
1449
+ return;
1450
+ }
1451
+ const batch = {
1452
+ sent_at: /* @__PURE__ */ new Date(),
1453
+ batch: this.events
1454
+ };
1455
+ try {
1456
+ await (await this.getClient()).post(diagnosticsUrl, {
1457
+ json: batch,
1458
+ timeout: { request: DIAGNOSTIC_ENDPOINT_TIMEOUT_MS }
1459
+ });
1460
+ } catch (err) {
1461
+ this.recordPlausibleTimeout(err);
1462
+ actionsCore.debug(`Error submitting diagnostics event to ${diagnosticsUrl}: ${stringifyError$1(err)}`);
1463
+ }
1464
+ this.events = [];
1465
+ }
1466
+ };
1467
+ function stringifyError$1(error) {
1468
+ return error instanceof Error || typeof error == "string" ? error.toString() : JSON.stringify(error);
1469
+ }
1470
+ function makeOptionsConfident(actionOptions) {
1471
+ const idsProjectName = actionOptions.idsProjectName ?? actionOptions.name;
1472
+ const finalOpts = {
1473
+ name: actionOptions.name,
1474
+ idsProjectName,
1475
+ eventPrefix: actionOptions.eventPrefix || "action:",
1476
+ fetchStyle: actionOptions.fetchStyle,
1477
+ legacySourcePrefix: actionOptions.legacySourcePrefix,
1478
+ requireNix: actionOptions.requireNix,
1479
+ binaryNamePrefixes: actionOptions.binaryNamePrefixes ?? [
1480
+ "nix",
1481
+ "determinate-nixd",
1482
+ actionOptions.name
1483
+ ],
1484
+ binaryNamesDenyList: actionOptions.binaryNamesDenyList ?? PROGRAM_NAME_CRASH_DENY_LIST
1485
+ };
1486
+ actionsCore.debug("idslib options:");
1487
+ actionsCore.debug(JSON.stringify(finalOpts, void 0, 2));
1488
+ return finalOpts;
1489
+ }
1490
+ //#endregion
1491
+ export { DetSysAction, IdsHost, inputs_exports as inputs, platform_exports as platform, stringifyError };
1492
+
1493
+ //# sourceMappingURL=index.mjs.map