simulci 0.1.0 → 0.2.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.js +436 -250
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import { basename, dirname, isAbsolute, join,
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
//#region \0rolldown/runtime.js
|
|
@@ -1122,7 +1122,7 @@ var Parser = class {
|
|
|
1122
1122
|
}
|
|
1123
1123
|
};
|
|
1124
1124
|
//#endregion
|
|
1125
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
1125
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/expressions.ts
|
|
1126
1126
|
/** Every named context the GitHub Actions runtime knows about. */
|
|
1127
1127
|
const KNOWN_CONTEXTS = [
|
|
1128
1128
|
"github",
|
|
@@ -1667,7 +1667,7 @@ function evaluateTemplate(text, resolve, options = {}) {
|
|
|
1667
1667
|
return known$2(out, mergeTaint(...parts));
|
|
1668
1668
|
}
|
|
1669
1669
|
//#endregion
|
|
1670
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
1670
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/ir.ts
|
|
1671
1671
|
/** Case-insensitive lookup of an own key, matching how GitHub resolves context property names. */
|
|
1672
1672
|
function lookup(rec, key) {
|
|
1673
1673
|
if (Object.hasOwn(rec, key)) return rec[key];
|
|
@@ -1896,7 +1896,7 @@ function matrixResolver(combo) {
|
|
|
1896
1896
|
};
|
|
1897
1897
|
}
|
|
1898
1898
|
//#endregion
|
|
1899
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
1899
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/checks.ts
|
|
1900
1900
|
/**
|
|
1901
1901
|
* Check-run names of jobs, as GitHub reports them (and as branch protection requires them).
|
|
1902
1902
|
*
|
|
@@ -2029,7 +2029,7 @@ function skippedCheckName(job) {
|
|
|
2029
2029
|
return kind.kind === "static" ? kind.text || job.id : kind.template.trim();
|
|
2030
2030
|
}
|
|
2031
2031
|
//#endregion
|
|
2032
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
2032
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/text.ts
|
|
2033
2033
|
/**
|
|
2034
2034
|
* `s` without runs of `ch` at its end (and at its start with `start: true`). A loop rather than `/x+$/`, which
|
|
2035
2035
|
* backtracks quadratically on long runs that are not at the end (CodeQL js/polynomial-redos).
|
|
@@ -2066,7 +2066,7 @@ function didYouMean(name, candidates) {
|
|
|
2066
2066
|
return best.d <= Math.max(2, Math.floor(name.length / 3)) ? best.c : void 0;
|
|
2067
2067
|
}
|
|
2068
2068
|
//#endregion
|
|
2069
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
2069
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/glob.ts
|
|
2070
2070
|
/** Minimal glob: `*` matches within a segment, `**` across segments; plain strings match as path prefixes. */
|
|
2071
2071
|
function matchesPattern(path, pattern) {
|
|
2072
2072
|
if (!pattern.includes("*")) return path === pattern || path.startsWith(pattern.endsWith("/") ? pattern : `${pattern}/`);
|
|
@@ -2074,7 +2074,7 @@ function matchesPattern(path, pattern) {
|
|
|
2074
2074
|
}
|
|
2075
2075
|
const escapeRe = (s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
2076
2076
|
//#endregion
|
|
2077
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
2077
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/queries.ts
|
|
2078
2078
|
/** Every reference in a unit's expressions, with the site it is in. */
|
|
2079
2079
|
function* refsOf(unit) {
|
|
2080
2080
|
for (const site of unit.sites) for (const seg of site.segments) for (const ref of seg.refs) yield {
|
|
@@ -2085,7 +2085,7 @@ function* refsOf(unit) {
|
|
|
2085
2085
|
/** Secrets every workflow can read without declaring them (lowercase): the automatic `GITHUB_TOKEN`. */
|
|
2086
2086
|
const BUILTIN_SECRETS = /* @__PURE__ */ new Set(["github_token"]);
|
|
2087
2087
|
//#endregion
|
|
2088
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
2088
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/graph.ts
|
|
2089
2089
|
/** A path segment that names no key: the end of the path, a `*` filter or a `?` computed index. */
|
|
2090
2090
|
const computed = (s) => s === void 0 || s === "*" || s === "?";
|
|
2091
2091
|
/** Whether a path segment names the property `key` (lowercase): GitHub looks properties up ignoring case. */
|
|
@@ -9491,7 +9491,7 @@ var require_public_api = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
9491
9491
|
exports.stringify = stringify;
|
|
9492
9492
|
}));
|
|
9493
9493
|
//#endregion
|
|
9494
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
9494
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/source.ts
|
|
9495
9495
|
var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
9496
9496
|
var composer = require_composer();
|
|
9497
9497
|
var Document = require_Document();
|
|
@@ -9566,7 +9566,7 @@ function formatLoc(loc) {
|
|
|
9566
9566
|
return `${loc.file}:${loc.line}:${loc.column}`;
|
|
9567
9567
|
}
|
|
9568
9568
|
//#endregion
|
|
9569
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
9569
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/logger.ts
|
|
9570
9570
|
const ORDER = {
|
|
9571
9571
|
silent: 0,
|
|
9572
9572
|
error: 1,
|
|
@@ -9631,7 +9631,45 @@ function createLogger(options = {}) {
|
|
|
9631
9631
|
}
|
|
9632
9632
|
const silentLogger = createLogger({ level: "silent" });
|
|
9633
9633
|
//#endregion
|
|
9634
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
9634
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/posix.ts
|
|
9635
|
+
/**
|
|
9636
|
+
* POSIX path arithmetic for repository-relative paths, with Node's `path.posix` semantics for what the parser and the
|
|
9637
|
+
* loader need, so the browser entry carries no `node:path`.
|
|
9638
|
+
*/
|
|
9639
|
+
const posixIsAbsolute = (path) => path.startsWith("/");
|
|
9640
|
+
/** `a/./b/../c` → `a/c`, `` → `.`, `/..` → `/`; a trailing slash is kept, as `path.posix.normalize` keeps it. */
|
|
9641
|
+
function posixNormalize(path) {
|
|
9642
|
+
if (path === "") return ".";
|
|
9643
|
+
const absolute = path.startsWith("/");
|
|
9644
|
+
const trailing = path.endsWith("/");
|
|
9645
|
+
const out = [];
|
|
9646
|
+
for (const segment of path.split("/")) step(out, segment, absolute);
|
|
9647
|
+
if (out.length === 0) return absolute ? "/" : trailing ? "./" : ".";
|
|
9648
|
+
return `${absolute ? "/" : ""}${out.join("/")}${trailing ? "/" : ""}`;
|
|
9649
|
+
}
|
|
9650
|
+
/** One segment onto the normalized path: `.` and empty ones vanish, `..` pops (or stays, when relative and nothing is above). */
|
|
9651
|
+
function step(out, segment, absolute) {
|
|
9652
|
+
if (segment === "" || segment === ".") return;
|
|
9653
|
+
if (segment !== "..") {
|
|
9654
|
+
out.push(segment);
|
|
9655
|
+
return;
|
|
9656
|
+
}
|
|
9657
|
+
if (out.length && out.at(-1) !== "..") out.pop();
|
|
9658
|
+
else if (!absolute) out.push("..");
|
|
9659
|
+
}
|
|
9660
|
+
/** The non-empty parts joined and normalized; `.` when there is nothing. */
|
|
9661
|
+
function posixJoin(...parts) {
|
|
9662
|
+
const joined = parts.filter((p) => p !== "").join("/");
|
|
9663
|
+
return joined === "" ? "." : posixNormalize(joined);
|
|
9664
|
+
}
|
|
9665
|
+
/** The last segment, trailing slashes ignored; `` for `/` and ``. */
|
|
9666
|
+
function posixBasename(path) {
|
|
9667
|
+
const trimmed = path.replace(/\/+$/, "");
|
|
9668
|
+
const i = trimmed.lastIndexOf("/");
|
|
9669
|
+
return i === -1 ? trimmed : trimmed.slice(i + 1);
|
|
9670
|
+
}
|
|
9671
|
+
//#endregion
|
|
9672
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/parse.ts
|
|
9635
9673
|
function createParseContext(repository) {
|
|
9636
9674
|
let id = 0;
|
|
9637
9675
|
return {
|
|
@@ -10249,7 +10287,7 @@ function outputDecls(unit, node, base, valueKey) {
|
|
|
10249
10287
|
const WORKFLOW_FILE = /^\.github\/workflows\/[^/]+\.ya?ml$/;
|
|
10250
10288
|
/** `a/./b/` → `a/b`, `` → `.`. Anything escaping the root (`../x`) keeps its `..` and is never read. */
|
|
10251
10289
|
function normalizeRelative(path) {
|
|
10252
|
-
return trimChar(
|
|
10290
|
+
return trimChar(posixNormalize(trimChar(path, "/") || "."), "/") || ".";
|
|
10253
10291
|
}
|
|
10254
10292
|
function classifyUses(raw, at, loc, ctx) {
|
|
10255
10293
|
const value = raw.trim();
|
|
@@ -15117,7 +15155,9 @@ function parseWorkflow(entryFile, contextOrTrace) {
|
|
|
15117
15155
|
value: readTemplate(context, WORKFLOW_ROOT, reader, fileId)
|
|
15118
15156
|
};
|
|
15119
15157
|
}
|
|
15120
|
-
|
|
15158
|
+
//#endregion
|
|
15159
|
+
//#region ../../node_modules/.pnpm/cronstrue@2.59.0/node_modules/cronstrue/dist/cronstrue.js
|
|
15160
|
+
var require_cronstrue = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
15121
15161
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
15122
15162
|
if (typeof exports === "object" && typeof module === "object") module.exports = factory();
|
|
15123
15163
|
else if (typeof define === "function" && define.amd) define("cronstrue", [], factory);
|
|
@@ -15977,7 +16017,9 @@ function parseWorkflow(entryFile, contextOrTrace) {
|
|
|
15977
16017
|
return __webpack_exports__;
|
|
15978
16018
|
})();
|
|
15979
16019
|
});
|
|
15980
|
-
}))
|
|
16020
|
+
}));
|
|
16021
|
+
//#endregion
|
|
16022
|
+
//#region ../../node_modules/.pnpm/@actions+workflow-parser@0.3.61/node_modules/@actions/workflow-parser/dist/model/converter/cron-constants.js
|
|
15981
16023
|
const MONTHS = {
|
|
15982
16024
|
jan: 1,
|
|
15983
16025
|
feb: 2,
|
|
@@ -16023,8 +16065,7 @@ const DOW_RANGE = {
|
|
|
16023
16065
|
max: 6,
|
|
16024
16066
|
names: DAYS
|
|
16025
16067
|
};
|
|
16026
|
-
|
|
16027
|
-
//#region ../../node_modules/.pnpm/@actions+workflow-parser@0.3.61/node_modules/@actions/workflow-parser/dist/model/converter/cron.js
|
|
16068
|
+
require_cronstrue();
|
|
16028
16069
|
function isValidCron(cron) {
|
|
16029
16070
|
const parts = cron.split(/ +/);
|
|
16030
16071
|
if (parts.length != 5) return false;
|
|
@@ -17181,7 +17222,7 @@ function parseAction(entryFile, contextOrTrace) {
|
|
|
17181
17222
|
};
|
|
17182
17223
|
}
|
|
17183
17224
|
//#endregion
|
|
17184
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
17225
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/schema.ts
|
|
17185
17226
|
/** Root definitions @actions/workflow-parser validates against (the strict ones its editor tooling uses). */
|
|
17186
17227
|
const SCHEMA_ROOT = {
|
|
17187
17228
|
workflow: "workflow-root-strict",
|
|
@@ -17277,7 +17318,7 @@ function mappingsAt(schema, root, path) {
|
|
|
17277
17318
|
return [...out];
|
|
17278
17319
|
}
|
|
17279
17320
|
//#endregion
|
|
17280
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
17321
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/validate.ts
|
|
17281
17322
|
/**
|
|
17282
17323
|
* The parser's errors for an invalid `${{ }}`: those of the expression lexer and parser (@actions/expressions, which
|
|
17283
17324
|
* the sites' own expression parse uses too), and the template reader's for a `${{` without `}}`.
|
|
@@ -17673,7 +17714,7 @@ function mergeFix(key, where, whole) {
|
|
|
17673
17714
|
return whole && single ? `${repeat}, or alias ${whole} instead.` : `${repeat}.`;
|
|
17674
17715
|
}
|
|
17675
17716
|
//#endregion
|
|
17676
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
17717
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/script-writes.ts
|
|
17677
17718
|
const escapes$1 = (path) => path === ".." || path.startsWith("../");
|
|
17678
17719
|
/** `cmd` or `sudo -E cmd`: words that run the next word as the command. */
|
|
17679
17720
|
const PREFIXES = /* @__PURE__ */ new Set([
|
|
@@ -17841,10 +17882,10 @@ function resolveWord(word, cwd) {
|
|
|
17841
17882
|
}
|
|
17842
17883
|
if (base === void 0 || w === "" || /[$`*?[\]{}~]/.test(w) || w.startsWith("/")) return void 0;
|
|
17843
17884
|
if (/^[A-Za-z][\w+.-]*:/.test(w)) return void 0;
|
|
17844
|
-
const path = normalizeRelative(
|
|
17885
|
+
const path = normalizeRelative(posixJoin(base, w));
|
|
17845
17886
|
return escapes$1(path) ? void 0 : path;
|
|
17846
17887
|
}
|
|
17847
|
-
const basename$1 = (word) =>
|
|
17888
|
+
const basename$1 = (word) => posixBasename(word.replaceAll("\\", "/").replace(/\/+$/, ""));
|
|
17848
17889
|
/**
|
|
17849
17890
|
* Paths an inline script writes to: destinations of copies, moves, links and `mkdir`, clones and worktrees,
|
|
17850
17891
|
* extractions (`tar -C`, `unzip -d`), downloads (`-o`, `--output`) and redirections. Variables other than
|
|
@@ -17971,7 +18012,7 @@ function writesTo(write, path, isRepoDir) {
|
|
|
17971
18012
|
return write.path !== "." && path.startsWith(`${write.path}/`) && !isRepoDir(write.path);
|
|
17972
18013
|
}
|
|
17973
18014
|
//#endregion
|
|
17974
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
18015
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/workspace.ts
|
|
17975
18016
|
/**
|
|
17976
18017
|
* Before any checkout, assume this repository at the workspace root: most jobs check it out there, possibly through
|
|
17977
18018
|
* a step flowpact cannot see into, and a `./` action is unusable otherwise.
|
|
@@ -18346,106 +18387,10 @@ function workspaceResolver(opts) {
|
|
|
18346
18387
|
};
|
|
18347
18388
|
}
|
|
18348
18389
|
//#endregion
|
|
18349
|
-
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+
|
|
18390
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/project.ts
|
|
18350
18391
|
const WORKFLOWS_DIR = ".github/workflows";
|
|
18351
|
-
/** How many links one path may go through (the OS's ELOOP limit), and how many linked directories one walk expands. */
|
|
18352
|
-
const MAX_LINKS = 40;
|
|
18353
|
-
const MAX_LINKED_DIRS = 1e3;
|
|
18354
18392
|
const ACTIONS_DIR = ".github/actions";
|
|
18355
18393
|
const toPosix = (p) => p.split("\\").join("/");
|
|
18356
|
-
/**
|
|
18357
|
-
* Whether `abs` (after following symlinks) is inside the repository at `root` and outside its `.git` directory. A
|
|
18358
|
-
* checkout can contain symlinks; this keeps a caller from reading e.g. /etc or the persisted git credentials, and from
|
|
18359
|
-
* writing through a link. A path that does not exist yet is judged by its nearest existing parent.
|
|
18360
|
-
*/
|
|
18361
|
-
function insideRepository(root, abs) {
|
|
18362
|
-
let realRoot;
|
|
18363
|
-
try {
|
|
18364
|
-
realRoot = realpathSync(root);
|
|
18365
|
-
} catch {
|
|
18366
|
-
realRoot = resolve(root);
|
|
18367
|
-
}
|
|
18368
|
-
let target = resolve(abs);
|
|
18369
|
-
let rest = "";
|
|
18370
|
-
for (;;) try {
|
|
18371
|
-
target = join(realpathSync(target), rest);
|
|
18372
|
-
break;
|
|
18373
|
-
} catch {
|
|
18374
|
-
const parent = dirname(target);
|
|
18375
|
-
if (parent === target) return false;
|
|
18376
|
-
rest = join(basename(target), rest);
|
|
18377
|
-
target = parent;
|
|
18378
|
-
}
|
|
18379
|
-
const rel = toPosix(relative(realRoot, target));
|
|
18380
|
-
if (rel === "") return true;
|
|
18381
|
-
if (escapesRoot(rel)) return false;
|
|
18382
|
-
return rel !== ".git" && !rel.startsWith(".git/");
|
|
18383
|
-
}
|
|
18384
|
-
function nodeFileSystem(root) {
|
|
18385
|
-
const abs = (p) => join(root, p);
|
|
18386
|
-
const listings = /* @__PURE__ */ new Map();
|
|
18387
|
-
/** On case-insensitive file systems, `.GitHub/x.YML` opens `.github/x.yml`; GitHub (Linux) would not find it. */
|
|
18388
|
-
const exactCase = (p) => {
|
|
18389
|
-
let dir = root;
|
|
18390
|
-
for (const part of toPosix(p).split("/").filter((x) => x && x !== ".")) {
|
|
18391
|
-
if (part === "..") return false;
|
|
18392
|
-
let names = listings.get(dir);
|
|
18393
|
-
if (!names) {
|
|
18394
|
-
try {
|
|
18395
|
-
names = new Set(readdirSync(dir));
|
|
18396
|
-
} catch {
|
|
18397
|
-
return false;
|
|
18398
|
-
}
|
|
18399
|
-
listings.set(dir, names);
|
|
18400
|
-
}
|
|
18401
|
-
if (!names.has(part)) return false;
|
|
18402
|
-
dir = join(dir, part);
|
|
18403
|
-
}
|
|
18404
|
-
return true;
|
|
18405
|
-
};
|
|
18406
|
-
/** Follows symlinks and accepts only existing targets inside the repository (see insideRepository). */
|
|
18407
|
-
const inside = (p) => existsSync(abs(p)) && exactCase(p) && insideRepository(root, abs(p));
|
|
18408
|
-
const isFile = (p) => inside(p) && statSync(abs(p)).isFile();
|
|
18409
|
-
return {
|
|
18410
|
-
read: (p) => isFile(p) ? readFileSync(abs(p), "utf8") : void 0,
|
|
18411
|
-
list: (dir) => {
|
|
18412
|
-
if (!inside(dir) || !statSync(abs(dir)).isDirectory()) return [];
|
|
18413
|
-
return readdirSync(abs(dir)).map((name) => toPosix(join(dir, name))).filter((f) => isFile(f));
|
|
18414
|
-
},
|
|
18415
|
-
walk: (dir) => {
|
|
18416
|
-
if (!inside(dir) || !statSync(abs(dir)).isDirectory()) return [];
|
|
18417
|
-
const out = [];
|
|
18418
|
-
let linked = 0;
|
|
18419
|
-
const queue = [{
|
|
18420
|
-
dir: toPosix(dir),
|
|
18421
|
-
above: /* @__PURE__ */ new Set(),
|
|
18422
|
-
links: 0
|
|
18423
|
-
}];
|
|
18424
|
-
for (let i = 0; i < queue.length; i++) {
|
|
18425
|
-
const next = queue[i];
|
|
18426
|
-
const real = realpathSync(abs(next.dir));
|
|
18427
|
-
if (next.above.has(real)) continue;
|
|
18428
|
-
const above = new Set(next.above).add(real);
|
|
18429
|
-
for (const name of readdirSync(abs(next.dir))) {
|
|
18430
|
-
if (name === "node_modules" || name === ".git") continue;
|
|
18431
|
-
const p = toPosix(join(next.dir, name));
|
|
18432
|
-
if (isFile(p)) out.push(p);
|
|
18433
|
-
else if (inside(p) && statSync(abs(p)).isDirectory()) {
|
|
18434
|
-
const link = lstatSync(abs(p)).isSymbolicLink();
|
|
18435
|
-
if (link && (next.links >= MAX_LINKS || linked++ >= MAX_LINKED_DIRS)) continue;
|
|
18436
|
-
queue.push({
|
|
18437
|
-
dir: p,
|
|
18438
|
-
above,
|
|
18439
|
-
links: next.links + (link ? 1 : 0)
|
|
18440
|
-
});
|
|
18441
|
-
}
|
|
18442
|
-
}
|
|
18443
|
-
}
|
|
18444
|
-
return out;
|
|
18445
|
-
},
|
|
18446
|
-
isDir: (p) => inside(p) && statSync(abs(p)).isDirectory()
|
|
18447
|
-
};
|
|
18448
|
-
}
|
|
18449
18394
|
/** The prefix of the paths below `dir`: `a/` for `a`, `a/`, `./a` or `./a/`, and `` for the root (`.`, `./`, ``). */
|
|
18450
18395
|
const dirPrefix = (dir) => {
|
|
18451
18396
|
const d = toPosix(dir).replace(/^\.(\/|$)/, "").replace(/\/$/, "");
|
|
@@ -18471,10 +18416,12 @@ const isWorkflowFile = (p) => /^\.github\/workflows\/[^/]+\.ya?ml$/i.test(p);
|
|
|
18471
18416
|
/** GitHub matches the directory of a called workflow exactly, whatever the file system's case sensitivity. */
|
|
18472
18417
|
const isCallableWorkflowPath = (p) => /^\.github\/workflows\/[^/]+\.ya?ml$/.test(p.replace(/\.ya?ml$/i, (e) => e.toLowerCase()));
|
|
18473
18418
|
/** Local `uses:` targets must stay inside the repository. */
|
|
18474
|
-
const escapesRoot = (p) => p === ".." || p.startsWith("../") ||
|
|
18475
|
-
|
|
18419
|
+
const escapesRoot = (p) => p === ".." || p.startsWith("../") || posixIsAbsolute(p);
|
|
18420
|
+
/** Loads a project through a host (`loadProject` of the Node and browser entries wire one in). */
|
|
18421
|
+
function loadProjectWith(opts, host) {
|
|
18476
18422
|
const logger = (opts.logger ?? silentLogger).child("load");
|
|
18477
|
-
const fs = opts.fs ??
|
|
18423
|
+
const fs = opts.fs ?? host.defaultFs?.(opts.root);
|
|
18424
|
+
if (!fs) throw new Error("loadProject needs a file system here: pass fs (memoryFileSystem(files))");
|
|
18478
18425
|
const ctx = createParseContext(opts.repository);
|
|
18479
18426
|
const workflows = /* @__PURE__ */ new Map();
|
|
18480
18427
|
const actions = /* @__PURE__ */ new Map();
|
|
@@ -18540,7 +18487,7 @@ function loadProject(opts) {
|
|
|
18540
18487
|
let wholeRepository = false;
|
|
18541
18488
|
const ignoredTargets = [];
|
|
18542
18489
|
for (const p of opts.paths ?? []) {
|
|
18543
|
-
const rel =
|
|
18490
|
+
const rel = host.relativeTo(opts.root, p) || ".";
|
|
18544
18491
|
if (rel === "." || rel === "") {
|
|
18545
18492
|
wholeRepository = true;
|
|
18546
18493
|
continue;
|
|
@@ -18649,6 +18596,119 @@ function externalUnits(remotes = [], ctx) {
|
|
|
18649
18596
|
}
|
|
18650
18597
|
return { external: out };
|
|
18651
18598
|
}
|
|
18599
|
+
//#endregion
|
|
18600
|
+
//#region ../../node_modules/.pnpm/@ciatlas+core@https+++codeload.github.com+ciatlas+core+tar.gz+cf55cd8373233ba8a821439697c996db4188ecc3/node_modules/@ciatlas/core/src/node-fs.ts
|
|
18601
|
+
/** The project loader on a Node.js host: the working tree on disk, and `owner/repo` from the environment or git. */
|
|
18602
|
+
/** How many links one path may go through (the OS's ELOOP limit), and how many linked directories one walk expands. */
|
|
18603
|
+
const MAX_LINKS = 40;
|
|
18604
|
+
const MAX_LINKED_DIRS = 1e3;
|
|
18605
|
+
/**
|
|
18606
|
+
* Whether `abs` (after following symlinks) is inside the repository at `root` and outside its `.git` directory. A
|
|
18607
|
+
* checkout can contain symlinks; this keeps a caller from reading e.g. /etc or the persisted git credentials, and from
|
|
18608
|
+
* writing through a link. A path that does not exist yet is judged by its nearest existing parent.
|
|
18609
|
+
*/
|
|
18610
|
+
function insideRepository(root, abs) {
|
|
18611
|
+
let realRoot;
|
|
18612
|
+
try {
|
|
18613
|
+
realRoot = realpathSync(root);
|
|
18614
|
+
} catch {
|
|
18615
|
+
realRoot = resolve(root);
|
|
18616
|
+
}
|
|
18617
|
+
let target = resolve(abs);
|
|
18618
|
+
let rest = "";
|
|
18619
|
+
for (;;) try {
|
|
18620
|
+
target = join(realpathSync(target), rest);
|
|
18621
|
+
break;
|
|
18622
|
+
} catch {
|
|
18623
|
+
const parent = dirname(target);
|
|
18624
|
+
if (parent === target) return false;
|
|
18625
|
+
rest = join(basename(target), rest);
|
|
18626
|
+
target = parent;
|
|
18627
|
+
}
|
|
18628
|
+
const rel = toPosix(relative(realRoot, target));
|
|
18629
|
+
if (rel === "") return true;
|
|
18630
|
+
if (rel === ".." || rel.startsWith("../")) return false;
|
|
18631
|
+
return rel !== ".git" && !rel.startsWith(".git/");
|
|
18632
|
+
}
|
|
18633
|
+
function nodeFileSystem(root) {
|
|
18634
|
+
const abs = (p) => join(root, p);
|
|
18635
|
+
const listings = /* @__PURE__ */ new Map();
|
|
18636
|
+
/** On case-insensitive file systems, `.GitHub/x.YML` opens `.github/x.yml`; GitHub (Linux) would not find it. */
|
|
18637
|
+
const exactCase = (p) => {
|
|
18638
|
+
let dir = root;
|
|
18639
|
+
for (const part of toPosix(p).split("/").filter((x) => x && x !== ".")) {
|
|
18640
|
+
if (part === "..") return false;
|
|
18641
|
+
let names = listings.get(dir);
|
|
18642
|
+
if (!names) {
|
|
18643
|
+
try {
|
|
18644
|
+
names = new Set(readdirSync(dir));
|
|
18645
|
+
} catch {
|
|
18646
|
+
return false;
|
|
18647
|
+
}
|
|
18648
|
+
listings.set(dir, names);
|
|
18649
|
+
}
|
|
18650
|
+
if (!names.has(part)) return false;
|
|
18651
|
+
dir = join(dir, part);
|
|
18652
|
+
}
|
|
18653
|
+
return true;
|
|
18654
|
+
};
|
|
18655
|
+
/** Follows symlinks and accepts only existing targets inside the repository (see insideRepository). */
|
|
18656
|
+
const inside = (p) => existsSync(abs(p)) && exactCase(p) && insideRepository(root, abs(p));
|
|
18657
|
+
const isFile = (p) => inside(p) && statSync(abs(p)).isFile();
|
|
18658
|
+
/** One directory of the walk: its files to `out`, its subdirectories to `queue`. */
|
|
18659
|
+
const expand = (next, out, queue, budget) => {
|
|
18660
|
+
const real = realpathSync(abs(next.dir));
|
|
18661
|
+
if (next.above.has(real)) return;
|
|
18662
|
+
const above = new Set(next.above).add(real);
|
|
18663
|
+
for (const name of readdirSync(abs(next.dir))) {
|
|
18664
|
+
if (name === "node_modules" || name === ".git") continue;
|
|
18665
|
+
const p = toPosix(join(next.dir, name));
|
|
18666
|
+
if (isFile(p)) out.push(p);
|
|
18667
|
+
else if (inside(p) && statSync(abs(p)).isDirectory()) enqueue(p, next, above, queue, budget);
|
|
18668
|
+
}
|
|
18669
|
+
};
|
|
18670
|
+
/** A subdirectory onto the queue, unless it is a link past the depth or count the walk allows. */
|
|
18671
|
+
const enqueue = (p, next, above, queue, budget) => {
|
|
18672
|
+
const link = lstatSync(abs(p)).isSymbolicLink();
|
|
18673
|
+
if (link && (next.links >= MAX_LINKS || budget.linked++ >= MAX_LINKED_DIRS)) return;
|
|
18674
|
+
queue.push({
|
|
18675
|
+
dir: p,
|
|
18676
|
+
above,
|
|
18677
|
+
links: next.links + (link ? 1 : 0)
|
|
18678
|
+
});
|
|
18679
|
+
};
|
|
18680
|
+
return {
|
|
18681
|
+
read: (p) => isFile(p) ? readFileSync(abs(p), "utf8") : void 0,
|
|
18682
|
+
list: (dir) => {
|
|
18683
|
+
if (!inside(dir) || !statSync(abs(dir)).isDirectory()) return [];
|
|
18684
|
+
return readdirSync(abs(dir)).map((name) => toPosix(join(dir, name))).filter((f) => isFile(f));
|
|
18685
|
+
},
|
|
18686
|
+
walk: (dir) => {
|
|
18687
|
+
if (!inside(dir) || !statSync(abs(dir)).isDirectory()) return [];
|
|
18688
|
+
const out = [];
|
|
18689
|
+
const queue = [{
|
|
18690
|
+
dir: toPosix(dir),
|
|
18691
|
+
above: /* @__PURE__ */ new Set(),
|
|
18692
|
+
links: 0
|
|
18693
|
+
}];
|
|
18694
|
+
const budget = { linked: 0 };
|
|
18695
|
+
for (let i = 0; i < queue.length; i++) expand(queue[i], out, queue, budget);
|
|
18696
|
+
return out;
|
|
18697
|
+
},
|
|
18698
|
+
isDir: (p) => inside(p) && statSync(abs(p)).isDirectory()
|
|
18699
|
+
};
|
|
18700
|
+
}
|
|
18701
|
+
const nodeHost = {
|
|
18702
|
+
defaultFs: (root) => nodeFileSystem(root),
|
|
18703
|
+
relativeTo: (root, p) => {
|
|
18704
|
+
const rel = relative(root, resolve(root, p));
|
|
18705
|
+
return isAbsolute(rel) ? ".." : toPosix(rel);
|
|
18706
|
+
}
|
|
18707
|
+
};
|
|
18708
|
+
/** Loads the project at `root`: from disk unless `fs` says otherwise. */
|
|
18709
|
+
function loadProject(opts) {
|
|
18710
|
+
return loadProjectWith(opts, nodeHost);
|
|
18711
|
+
}
|
|
18652
18712
|
new Set(CONTEXT_FUNCTIONS.map((f) => f.name.toLowerCase()));
|
|
18653
18713
|
//#endregion
|
|
18654
18714
|
//#region ../engine/src/plan/values.ts
|
|
@@ -18701,6 +18761,12 @@ function* product(lists) {
|
|
|
18701
18761
|
const [first, ...rest] = lists;
|
|
18702
18762
|
for (const value of first) for (const tail of product(rest)) yield [value, ...tail];
|
|
18703
18763
|
}
|
|
18764
|
+
/** A value in GitHub's string form: `null` is empty, objects are JSON, the rest `String()`. */
|
|
18765
|
+
function stringOf(value) {
|
|
18766
|
+
if (value === null) return "";
|
|
18767
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
18768
|
+
return String(value);
|
|
18769
|
+
}
|
|
18704
18770
|
//#endregion
|
|
18705
18771
|
//#region ../engine/src/contexts.ts
|
|
18706
18772
|
/** The value of a context reference, with the unknown that explains a missing value. */
|
|
@@ -19045,28 +19111,24 @@ function literalValue(text) {
|
|
|
19045
19111
|
function evaluateCondition(text, state, status) {
|
|
19046
19112
|
const parsed = parseExpression(text);
|
|
19047
19113
|
const reads = parsed.refs.map((ref) => ({
|
|
19048
|
-
ref:
|
|
19114
|
+
ref: refName(ref),
|
|
19049
19115
|
value: ref.dynamic ? unknown$1([`${ref.context}.${ref.path.join(".")}`]) : lookupContext(state, ref)
|
|
19050
19116
|
}));
|
|
19051
|
-
|
|
19052
|
-
value:
|
|
19117
|
+
return {
|
|
19118
|
+
value: conditionValue(parsed, contextResolver(state), status, reads),
|
|
19053
19119
|
reads
|
|
19054
19120
|
};
|
|
19121
|
+
}
|
|
19122
|
+
/** A reference as a read names it: `needs.build.result`, `github`. */
|
|
19123
|
+
const refName = (ref) => `${ref.context}${ref.path.length ? `.${ref.path.join(".")}` : ""}`;
|
|
19124
|
+
/** Known true, known false, or unknown because of the reads and status answers it could not decide over. */
|
|
19125
|
+
function conditionValue(parsed, resolve, status, reads) {
|
|
19126
|
+
if (!parsed.ast) return unknown$1([`expression:${parsed.source}`], [true, false]);
|
|
19055
19127
|
const options = { runtime: (name) => statusAnswer(status, name) };
|
|
19056
|
-
|
|
19057
|
-
if (
|
|
19058
|
-
value: known$1(true),
|
|
19059
|
-
reads
|
|
19060
|
-
};
|
|
19061
|
-
if (definitelyFalsy(parsed.ast, resolve, options)) return {
|
|
19062
|
-
value: known$1(false),
|
|
19063
|
-
reads
|
|
19064
|
-
};
|
|
19128
|
+
if (definitelyTruthy(parsed.ast, resolve, options)) return known$1(true);
|
|
19129
|
+
if (definitelyFalsy(parsed.ast, resolve, options)) return known$1(false);
|
|
19065
19130
|
const unknowns = [...reads.flatMap((r) => r.value.known ? [] : r.value.unknowns), ...Object.values(status).flatMap((s) => s.known ? [] : s.unknowns)];
|
|
19066
|
-
return {
|
|
19067
|
-
value: unknown$1(unknowns.length ? unknowns : [`expression:${text}`], [true, false]),
|
|
19068
|
-
reads
|
|
19069
|
-
};
|
|
19131
|
+
return unknown$1(unknowns.length ? unknowns : [`expression:${parsed.source}`], [true, false]);
|
|
19070
19132
|
}
|
|
19071
19133
|
/** The status functions as core's evaluator asks for them; an unknown answer leaves the call unknown. */
|
|
19072
19134
|
function statusAnswer(status, name) {
|
|
@@ -19095,11 +19157,6 @@ function evaluateValue(text, state, status) {
|
|
|
19095
19157
|
return unknown$1(unknowns.length ? unknowns : [`expression:${text}`]);
|
|
19096
19158
|
}
|
|
19097
19159
|
/** A value as GitHub writes it into a string: `null` is empty, objects are JSON. */
|
|
19098
|
-
function stringOf(value) {
|
|
19099
|
-
if (value === null) return "";
|
|
19100
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
19101
|
-
return String(value);
|
|
19102
|
-
}
|
|
19103
19160
|
/**
|
|
19104
19161
|
* A script with its `${{ }}` segments replaced by their values, for folding what it writes; a segment the plan cannot
|
|
19105
19162
|
* evaluate becomes `UNKNOWN_MARKER`, which taints any write that includes it.
|
|
@@ -19218,7 +19275,7 @@ function pathsMatch(changed, filter) {
|
|
|
19218
19275
|
}
|
|
19219
19276
|
//#endregion
|
|
19220
19277
|
//#region ../engine/src/paths.ts
|
|
19221
|
-
const ID = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
19278
|
+
const ID$1 = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
19222
19279
|
var UnitPathError = class extends Error {
|
|
19223
19280
|
name = "UnitPathError";
|
|
19224
19281
|
};
|
|
@@ -19242,7 +19299,7 @@ function parseJobSegment(part, text) {
|
|
|
19242
19299
|
const m = /^([^[.]+)(?:\[([^\]]*)\])?(?:\.(.+))?$/.exec(part);
|
|
19243
19300
|
if (!m) throw new UnitPathError(`cannot read '${part}' in '${text}'`);
|
|
19244
19301
|
const [, job, leg, step] = m;
|
|
19245
|
-
if (!ID.test(job)) throw new UnitPathError(`'${job}' is not a job id in '${text}'`);
|
|
19302
|
+
if (!ID$1.test(job)) throw new UnitPathError(`'${job}' is not a job id in '${text}'`);
|
|
19246
19303
|
const segment = { job };
|
|
19247
19304
|
if (leg !== void 0) segment.leg = parseLeg(leg, text);
|
|
19248
19305
|
if (step !== void 0) segment.step = parseStep(step, text);
|
|
@@ -19263,9 +19320,18 @@ function parseStep(step, text) {
|
|
|
19263
19320
|
if (!Number.isInteger(n) || n < 1) throw new UnitPathError(`'${step}' is not #<position> in '${text}'`);
|
|
19264
19321
|
return { index: n };
|
|
19265
19322
|
}
|
|
19266
|
-
if (!ID.test(step)) throw new UnitPathError(`'${step}' is not a step id in '${text}'`);
|
|
19323
|
+
if (!ID$1.test(step)) throw new UnitPathError(`'${step}' is not a step id in '${text}'`);
|
|
19267
19324
|
return { id: step };
|
|
19268
19325
|
}
|
|
19326
|
+
function formatUnitPath(path) {
|
|
19327
|
+
const body = path.segments.map((s) => {
|
|
19328
|
+
const leg = s.leg ? `[${Object.entries(s.leg).map(([k, v]) => `${k}=${v}`).join(",")}]` : "";
|
|
19329
|
+
const step = s.step ? s.step.id !== void 0 ? s.step.id : `#${s.step.index}` : "";
|
|
19330
|
+
if (s.job === void 0) return step;
|
|
19331
|
+
return `${s.job}${leg}${step ? `.${step}` : ""}`;
|
|
19332
|
+
}).join("/");
|
|
19333
|
+
return path.workflow === void 0 ? body : `${path.workflow}:${body}`;
|
|
19334
|
+
}
|
|
19269
19335
|
/** Whether a path names the location: same depth, same jobs and steps, leg values a subset of the location's. */
|
|
19270
19336
|
function pathMatches(path, location) {
|
|
19271
19337
|
if (path.workflow !== void 0 && !workflowMatches(path.workflow, location.workflow)) return false;
|
|
@@ -27637,6 +27703,107 @@ function unitPathOfId(id, paths) {
|
|
|
27637
27703
|
return best === void 0 ? id : `${paths.get(best)}${id.slice(best.length)}`;
|
|
27638
27704
|
}
|
|
27639
27705
|
//#endregion
|
|
27706
|
+
//#region ../engine/src/plan/pins.ts
|
|
27707
|
+
const RACE = "race:fail-fast:";
|
|
27708
|
+
const ID = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
27709
|
+
/** Sets `producer` and an assume `pin` on every unknown about a planned unit's field. */
|
|
27710
|
+
function assumePins(plan, units) {
|
|
27711
|
+
for (const u of plan.unknowns) {
|
|
27712
|
+
const ref = unitRef(u.id, units);
|
|
27713
|
+
if (!ref) continue;
|
|
27714
|
+
u.producer = ref.node;
|
|
27715
|
+
const pin = pinOf(plan, ref.node, units.get(ref.node), ref.about);
|
|
27716
|
+
if (pin) u.pin = pin;
|
|
27717
|
+
}
|
|
27718
|
+
}
|
|
27719
|
+
/** The unit an unknown is about and what of it: the longest unit id followed by `.outputs.`, `.env.` or `.outcome`. */
|
|
27720
|
+
function unitRef(id, units) {
|
|
27721
|
+
if (id.startsWith(RACE)) {
|
|
27722
|
+
const node = id.slice(15);
|
|
27723
|
+
return units.get(node)?.kind === "job" ? {
|
|
27724
|
+
node,
|
|
27725
|
+
about: { field: "cancelled" }
|
|
27726
|
+
} : void 0;
|
|
27727
|
+
}
|
|
27728
|
+
if (id.startsWith("race:") || id.startsWith("conflict:")) return void 0;
|
|
27729
|
+
for (let dot = id.lastIndexOf("."); dot > 0; dot = id.lastIndexOf(".", dot - 1)) {
|
|
27730
|
+
const node = id.slice(0, dot);
|
|
27731
|
+
if (!units.has(node)) continue;
|
|
27732
|
+
const about = aboutOf(id.slice(dot + 1));
|
|
27733
|
+
return about ? {
|
|
27734
|
+
node,
|
|
27735
|
+
about
|
|
27736
|
+
} : void 0;
|
|
27737
|
+
}
|
|
27738
|
+
}
|
|
27739
|
+
function aboutOf(rest) {
|
|
27740
|
+
if (rest === "outcome") return { field: "outcome" };
|
|
27741
|
+
for (const field of ["outputs", "env"]) if (rest.startsWith(`${field}.`) && rest.length > field.length + 1) return {
|
|
27742
|
+
field,
|
|
27743
|
+
name: rest.slice(field.length + 1)
|
|
27744
|
+
};
|
|
27745
|
+
}
|
|
27746
|
+
function pinOf(plan, node, site, about) {
|
|
27747
|
+
if (about.field === "env" && site.kind === "job") return void 0;
|
|
27748
|
+
const named = namedBy(site.location);
|
|
27749
|
+
if (!named) return void 0;
|
|
27750
|
+
const entry = {};
|
|
27751
|
+
if (sharesJobId(plan, node, site.location)) entry.workflow = site.location.workflow;
|
|
27752
|
+
entry[site.kind] = named.path;
|
|
27753
|
+
if (named.matrix) entry.matrix = named.matrix;
|
|
27754
|
+
if (about.field === "cancelled" || about.field === "outcome") entry.outcome = about.field === "cancelled" ? "cancelled" : "failure";
|
|
27755
|
+
else entry[about.field] = { [about.name]: "" };
|
|
27756
|
+
return {
|
|
27757
|
+
layer: "assume",
|
|
27758
|
+
fragment: entry,
|
|
27759
|
+
at: ["assume"]
|
|
27760
|
+
};
|
|
27761
|
+
}
|
|
27762
|
+
/**
|
|
27763
|
+
* The unit path of a location, with the leg of its last segment taken out as `matrix:` values; undefined when a leg
|
|
27764
|
+
* further up cannot be written in the path, or an id is not one a path holds.
|
|
27765
|
+
*/
|
|
27766
|
+
function namedBy(location) {
|
|
27767
|
+
const segments = segmentsOf(location);
|
|
27768
|
+
if (!segments) return void 0;
|
|
27769
|
+
const last = segments.at(-1);
|
|
27770
|
+
const own = last.job === void 0 ? void 0 : last.leg;
|
|
27771
|
+
if (own) delete last.leg;
|
|
27772
|
+
if (!segments.every((s) => s.leg === void 0 || legInPath(s.leg))) return void 0;
|
|
27773
|
+
const path = formatUnitPath({ segments });
|
|
27774
|
+
return own && Object.keys(own).length ? {
|
|
27775
|
+
path,
|
|
27776
|
+
matrix: own
|
|
27777
|
+
} : { path };
|
|
27778
|
+
}
|
|
27779
|
+
/** The chain as path segments: a job opens one, a step after a job's step opens one inside its composite action. */
|
|
27780
|
+
function segmentsOf(location) {
|
|
27781
|
+
const segments = [];
|
|
27782
|
+
for (const link of location.chain) if (link.kind === "step") addStep(segments, link);
|
|
27783
|
+
else if (ID.test(link.id)) segments.push(link.leg ? {
|
|
27784
|
+
job: link.id,
|
|
27785
|
+
leg: { ...link.leg }
|
|
27786
|
+
} : { job: link.id });
|
|
27787
|
+
else return void 0;
|
|
27788
|
+
return segments.length ? segments : void 0;
|
|
27789
|
+
}
|
|
27790
|
+
/** A step joins its job's segment, or opens one inside the composite action of the step before it. */
|
|
27791
|
+
function addStep(segments, link) {
|
|
27792
|
+
const step = link.id !== void 0 && ID.test(link.id) ? { id: link.id } : { index: link.index };
|
|
27793
|
+
const last = segments.at(-1);
|
|
27794
|
+
if (last?.job !== void 0 && last.step === void 0) last.step = step;
|
|
27795
|
+
else segments.push({ step });
|
|
27796
|
+
}
|
|
27797
|
+
/** Whether `[k=v,…]` reads back as these values: no `/`, `,` or `]`, no surrounding space, no `=` in a key. */
|
|
27798
|
+
const legInPath = (leg) => Object.entries(leg).every(([k, v]) => plainCell(k) && !k.includes("=") && plainCell(v));
|
|
27799
|
+
const plainCell = (s) => s.trim() === s && !/[/,\]]/.test(s);
|
|
27800
|
+
/** Whether another workflow of the plan has a job of the same id as the unit's first job. */
|
|
27801
|
+
function sharesJobId(plan, node, location) {
|
|
27802
|
+
const first = location.chain[0];
|
|
27803
|
+
if (first?.kind !== "job") return false;
|
|
27804
|
+
return plan.workflows.some((w) => w.depth === 0 && !node.startsWith(`${w.id}#`) && w.jobs.some((j) => j.jobId === first.id));
|
|
27805
|
+
}
|
|
27806
|
+
//#endregion
|
|
27640
27807
|
//#region ../engine/src/scenario.ts
|
|
27641
27808
|
/**
|
|
27642
27809
|
* A scenario: the facts about one run that the engine simulates. Authors write the input shape (in a test's `when:`,
|
|
@@ -28630,7 +28797,7 @@ function verdictOf(gates) {
|
|
|
28630
28797
|
//#endregion
|
|
28631
28798
|
//#region ../engine/src/version.ts
|
|
28632
28799
|
/** The simulci version. release-please updates it with every release. */
|
|
28633
|
-
const VERSION = "0.
|
|
28800
|
+
const VERSION = "0.2.0";
|
|
28634
28801
|
/** The schema version of every JSON document the engine emits or reads. A breaking change bumps it. */
|
|
28635
28802
|
const SCHEMA_VERSIONS = {
|
|
28636
28803
|
scenario: 1,
|
|
@@ -28685,6 +28852,8 @@ var Planner = class {
|
|
|
28685
28852
|
diagnostics = [];
|
|
28686
28853
|
applied = [];
|
|
28687
28854
|
models = /* @__PURE__ */ new Map();
|
|
28855
|
+
/** Every planned step and leg by id, with the location an assume entry is matched against. */
|
|
28856
|
+
units = /* @__PURE__ */ new Map();
|
|
28688
28857
|
index;
|
|
28689
28858
|
constructor(loaded, scenario, detail, maxDepth, mocks, shipped) {
|
|
28690
28859
|
this.loaded = loaded;
|
|
@@ -28700,7 +28869,7 @@ var Planner = class {
|
|
|
28700
28869
|
depth: 0,
|
|
28701
28870
|
checkPrefix: ""
|
|
28702
28871
|
}));
|
|
28703
|
-
|
|
28872
|
+
const plan = {
|
|
28704
28873
|
$schema: schemaUrl("runPlan"),
|
|
28705
28874
|
schemaVersion: 1,
|
|
28706
28875
|
engine: {
|
|
@@ -28718,6 +28887,8 @@ var Planner = class {
|
|
|
28718
28887
|
})).sort((a, b) => a.id.localeCompare(b.id)),
|
|
28719
28888
|
diagnostics: this.finishDiagnostics()
|
|
28720
28889
|
};
|
|
28890
|
+
assumePins(plan, this.units);
|
|
28891
|
+
return plan;
|
|
28721
28892
|
}
|
|
28722
28893
|
/** The mocks' own diagnostics, plus the entries that named nothing in this plan (SC102). */
|
|
28723
28894
|
finishDiagnostics() {
|
|
@@ -28865,74 +29036,65 @@ var Planner = class {
|
|
|
28865
29036
|
total: exp.combos.length || 1
|
|
28866
29037
|
};
|
|
28867
29038
|
plan.legs = this.legsOf(job, exp, segments, legCtx);
|
|
28868
|
-
|
|
29039
|
+
for (const leg of plan.legs) {
|
|
29040
|
+
const location = {
|
|
29041
|
+
workflow: ctx.wf.path,
|
|
29042
|
+
chain: [...ctx.chain, legLinkOf(job, leg)]
|
|
29043
|
+
};
|
|
29044
|
+
this.units.set(leg.id, {
|
|
29045
|
+
location,
|
|
29046
|
+
kind: "job"
|
|
29047
|
+
});
|
|
29048
|
+
this.applyLegFacts(job, leg, jobNode, location);
|
|
29049
|
+
}
|
|
29050
|
+
this.failFast(plan);
|
|
28869
29051
|
plan.result = this.jobResultOf(runs, cond, continueOnError, plan.legs);
|
|
28870
|
-
plan.outputs = this.jobOutputs(
|
|
28871
|
-
this.applyJobFacts(job, plan, continueOnError, {
|
|
28872
|
-
workflow: ctx.wf.path,
|
|
28873
|
-
chain: [...ctx.chain, {
|
|
28874
|
-
kind: "job",
|
|
28875
|
-
id: job.id
|
|
28876
|
-
}]
|
|
28877
|
-
});
|
|
29052
|
+
plan.outputs = this.jobOutputs(plan, jobNode);
|
|
28878
29053
|
return plan;
|
|
28879
29054
|
}
|
|
28880
|
-
/**
|
|
28881
|
-
|
|
28882
|
-
|
|
29055
|
+
/**
|
|
29056
|
+
* A mock on the job or one of its legs (`test[node=22]`, or `test` with `matrix:`): its outcome is the leg's result
|
|
29057
|
+
* (the steps stay planned), its outputs overlay the leg's. The job's result and outputs are then read from its legs.
|
|
29058
|
+
*/
|
|
29059
|
+
applyLegFacts(job, leg, jobNode, location) {
|
|
29060
|
+
const facts = this.mocks.factsFor(location, leg.id);
|
|
28883
29061
|
if (facts.outcome) {
|
|
28884
|
-
|
|
28885
|
-
|
|
28886
|
-
|
|
28887
|
-
legResults: [facts.outcome.value]
|
|
28888
|
-
});
|
|
28889
|
-
plan.result = plan.runs.verdict === "maybe" ? unknown$1(plan.result.known ? [] : plan.result.unknowns, ["skipped", ...candidates(result) ?? []]) : result;
|
|
28890
|
-
this.record(facts.outcome.by, plan.id, "outcome");
|
|
28891
|
-
if (job.steps.length) this.diagnostics.push({
|
|
29062
|
+
leg.result = facts.outcome.value;
|
|
29063
|
+
this.record(facts.outcome.by, leg.id, "outcome");
|
|
29064
|
+
if (job.steps.length) this.diagnoseOnce({
|
|
28892
29065
|
severity: "info",
|
|
28893
29066
|
code: "SC108",
|
|
28894
29067
|
message: `${facts.outcome.by} sets the outcome of job ${job.id}; its steps are planned as written`,
|
|
28895
|
-
node:
|
|
29068
|
+
node: jobNode,
|
|
28896
29069
|
mock: facts.outcome.by
|
|
28897
29070
|
});
|
|
28898
29071
|
}
|
|
28899
|
-
const declared = job.uses ? Object.keys(
|
|
28900
|
-
this.overlayOutputs(
|
|
29072
|
+
const declared = job.uses ? Object.keys(leg.outputs) : Object.keys(job.outputs);
|
|
29073
|
+
this.overlayOutputs(leg.outputs, facts, leg.id, {
|
|
28901
29074
|
closed: true,
|
|
28902
29075
|
names: declared,
|
|
28903
29076
|
what: `job ${job.id}`
|
|
28904
29077
|
});
|
|
28905
29078
|
}
|
|
29079
|
+
/** A diagnostic said once: every leg of a matrix that meets the same mock says the same thing. */
|
|
29080
|
+
diagnoseOnce(d) {
|
|
29081
|
+
if (!this.diagnostics.some((x) => x.code === d.code && x.mock === d.mock && x.message === d.message)) this.diagnostics.push(d);
|
|
29082
|
+
}
|
|
28906
29083
|
/** Fail-fast: once a leg fails, the other legs are cancelled or finish first; `needs` reads the job as failed either way. */
|
|
28907
|
-
failFast(plan
|
|
29084
|
+
failFast(plan) {
|
|
28908
29085
|
if (!plan.matrix?.failFast.known || !plan.matrix.failFast.value) return;
|
|
28909
29086
|
if (!plan.legs.some((l) => l.result.known && l.result.value === "failure")) return;
|
|
28910
29087
|
for (const leg of plan.legs) {
|
|
28911
29088
|
if (!leg.result.known || leg.result.value !== "success") continue;
|
|
28912
29089
|
const id = `race:fail-fast:${leg.id}`;
|
|
28913
|
-
|
|
28914
|
-
this.register({
|
|
28915
|
-
id,
|
|
28916
|
-
kind: "race",
|
|
28917
|
-
reads: [],
|
|
28918
|
-
affects: [],
|
|
28919
|
-
pin: {
|
|
28920
|
-
layer: "assume",
|
|
28921
|
-
fragment: {
|
|
28922
|
-
job: job.id,
|
|
28923
|
-
matrix,
|
|
28924
|
-
outcome: "cancelled"
|
|
28925
|
-
},
|
|
28926
|
-
at: ["assume"]
|
|
28927
|
-
}
|
|
28928
|
-
}, void 0);
|
|
29090
|
+
this.note([id]);
|
|
28929
29091
|
leg.result = unknown$1([id], ["success", "cancelled"]);
|
|
28930
29092
|
}
|
|
28931
29093
|
}
|
|
28932
29094
|
/** Mocked outputs over the unit's own; a name a closed interface does not declare is an error (SC101). */
|
|
28933
29095
|
overlayOutputs(outputs, facts, node, iface) {
|
|
28934
29096
|
for (const [name, fact] of Object.entries(facts.outputs)) {
|
|
28935
|
-
if (iface.closed && !iface.names.includes(name)) this.
|
|
29097
|
+
if (iface.closed && !iface.names.includes(name)) this.diagnoseOnce({
|
|
28936
29098
|
severity: "error",
|
|
28937
29099
|
code: "SC101",
|
|
28938
29100
|
message: `${fact.by} sets output '${name}', which ${iface.what} does not declare${iface.names.length ? ` (it declares ${iface.names.join(", ")})` : ""}`,
|
|
@@ -28970,13 +29132,25 @@ var Planner = class {
|
|
|
28970
29132
|
node: ctx.wfNode,
|
|
28971
29133
|
verdict: ctx.triggered.verdict
|
|
28972
29134
|
};
|
|
28973
|
-
const runs = decide(ctx.triggered, cond.value, [parent, ifReason]);
|
|
29135
|
+
const runs = this.nestingLimit(job, ctx, decide(ctx.triggered, cond.value, [parent, ifReason]));
|
|
28974
29136
|
if (runs.verdict === "maybe") this.noteValue(cond.value, jobNode);
|
|
28975
29137
|
return {
|
|
28976
29138
|
runs,
|
|
28977
29139
|
cond
|
|
28978
29140
|
};
|
|
28979
29141
|
}
|
|
29142
|
+
/** A call past the nesting limit does not run, whatever its `if:` says: the job stops, not only its legs. */
|
|
29143
|
+
nestingLimit(job, ctx, runs) {
|
|
29144
|
+
if (runs.verdict === "no" || !job.uses || ctx.depth + 1 < this.maxDepth) return runs;
|
|
29145
|
+
if (!this.index.calleeOf(job)) return runs;
|
|
29146
|
+
return {
|
|
29147
|
+
verdict: "no",
|
|
29148
|
+
chain: [...runs.chain, {
|
|
29149
|
+
kind: "limit",
|
|
29150
|
+
limit: "nesting"
|
|
29151
|
+
}]
|
|
29152
|
+
};
|
|
29153
|
+
}
|
|
28980
29154
|
matrixPlan(job, exp, ctx, jobNode) {
|
|
28981
29155
|
const matrix = {
|
|
28982
29156
|
keys: exp.keys,
|
|
@@ -29025,25 +29199,29 @@ var Planner = class {
|
|
|
29025
29199
|
};
|
|
29026
29200
|
return [leg];
|
|
29027
29201
|
}
|
|
29202
|
+
/**
|
|
29203
|
+
* The result `needs` reads. A reader runs only where the workflow does, so a job `maybe` only because its workflow
|
|
29204
|
+
* is reads as its legs did; an `if:` the plan cannot decide adds `skipped`.
|
|
29205
|
+
*/
|
|
29028
29206
|
jobResultOf(runs, cond, continueOnError, legs) {
|
|
29029
29207
|
const result = jobResult({
|
|
29030
29208
|
skipped: false,
|
|
29031
29209
|
continueOnError,
|
|
29032
29210
|
legResults: legs.map((l) => l.result)
|
|
29033
29211
|
});
|
|
29034
|
-
|
|
29035
|
-
return unknown$1(cond.value.known ? [] : cond.value.unknowns, ["skipped", ...candidates(result) ?? []]);
|
|
29212
|
+
return runs.verdict === "maybe" ? orSkipped(result, cond.value, "first") : result;
|
|
29036
29213
|
}
|
|
29037
29214
|
/** A job's outputs as readers see them: the legs' values, where an empty value never wins and the last to finish does (lab 8). */
|
|
29038
|
-
jobOutputs(
|
|
29215
|
+
jobOutputs(plan, jobNode) {
|
|
29039
29216
|
const out = {};
|
|
29040
|
-
const names =
|
|
29217
|
+
const names = [...new Set(plan.legs.flatMap((l) => Object.keys(l.outputs)))];
|
|
29041
29218
|
for (const name of names) {
|
|
29042
29219
|
const values = plan.legs.map((l) => l.outputs[name] ?? known$1(""));
|
|
29043
29220
|
const ids = values.flatMap((v) => v.known ? [] : v.unknowns);
|
|
29044
29221
|
const nonEmpty = [...new Set(values.flatMap((v) => v.known && v.value !== "" ? [v.value] : []))];
|
|
29222
|
+
const chosen = nonEmpty[0] ?? "";
|
|
29045
29223
|
if (ids.length) out[name] = unknown$1(ids);
|
|
29046
|
-
else if (nonEmpty.length <= 1) out[name] =
|
|
29224
|
+
else if (nonEmpty.length <= 1) out[name] = values.find((v) => v.known && v.value === chosen);
|
|
29047
29225
|
else {
|
|
29048
29226
|
const race = `race:matrix-outputs:${jobNode}.${name}`;
|
|
29049
29227
|
this.note([race]);
|
|
@@ -29077,23 +29255,18 @@ var Planner = class {
|
|
|
29077
29255
|
if (combo) leg.origin = combo.origin;
|
|
29078
29256
|
if (job.runsOn) leg.runsOn = this.runsOn(job, ctx.wf, state, legId);
|
|
29079
29257
|
if (job.environment) leg.environment = this.environment(job, ctx.wf, state, legId);
|
|
29258
|
+
const location = {
|
|
29259
|
+
workflow: ctx.wf.path,
|
|
29260
|
+
chain: [...ctx.chain, legLinkOf(job, leg)]
|
|
29261
|
+
};
|
|
29080
29262
|
if (job.uses) {
|
|
29081
|
-
this.planCall(leg, state, seg, ctx);
|
|
29263
|
+
this.planCall(leg, state, seg, ctx, location);
|
|
29082
29264
|
return leg;
|
|
29083
29265
|
}
|
|
29084
29266
|
leg.check = {
|
|
29085
29267
|
name: `${ctx.checkPrefix}${seg.name}`,
|
|
29086
29268
|
certain: seg.certain
|
|
29087
29269
|
};
|
|
29088
|
-
const legLink = {
|
|
29089
|
-
kind: "job",
|
|
29090
|
-
id: job.id
|
|
29091
|
-
};
|
|
29092
|
-
if (combo) legLink.leg = Object.fromEntries(Object.entries(leg.values).map(([k, v]) => [k, v.known ? stringOf(v.value) : "?"]));
|
|
29093
|
-
const location = {
|
|
29094
|
-
workflow: ctx.wf.path,
|
|
29095
|
-
chain: [...ctx.chain, legLink]
|
|
29096
|
-
};
|
|
29097
29270
|
const steps = this.planSteps(job.steps, ctx.settings.steps, legId, runs, state, void 0, location);
|
|
29098
29271
|
if (this.detail === "steps") leg.steps = steps.plans;
|
|
29099
29272
|
leg.result = legResult(steps.conclusions);
|
|
@@ -29148,34 +29321,18 @@ var Planner = class {
|
|
|
29148
29321
|
return v;
|
|
29149
29322
|
}
|
|
29150
29323
|
/** A reusable workflow called by the leg: nested with the caller's inputs and secrets, its result from its jobs. */
|
|
29151
|
-
planCall(leg, state, seg, ctx) {
|
|
29324
|
+
planCall(leg, state, seg, ctx, location) {
|
|
29152
29325
|
const { job } = ctx;
|
|
29153
29326
|
const callee = this.index.calleeOf(job);
|
|
29154
29327
|
if (!callee) {
|
|
29155
29328
|
this.unsupportedCall(leg, job);
|
|
29156
29329
|
return;
|
|
29157
29330
|
}
|
|
29158
|
-
if (ctx.depth + 1 >= this.maxDepth) {
|
|
29159
|
-
leg.runs = {
|
|
29160
|
-
verdict: "no",
|
|
29161
|
-
chain: [...leg.runs.chain, {
|
|
29162
|
-
kind: "limit",
|
|
29163
|
-
limit: "nesting"
|
|
29164
|
-
}]
|
|
29165
|
-
};
|
|
29166
|
-
leg.result = known$1("skipped");
|
|
29167
|
-
return;
|
|
29168
|
-
}
|
|
29169
29331
|
const inputs = callInputs(job, callee, state);
|
|
29170
29332
|
for (const v of Object.values(inputs)) this.noteValue(v);
|
|
29171
|
-
const callLink = {
|
|
29172
|
-
kind: "job",
|
|
29173
|
-
id: job.id
|
|
29174
|
-
};
|
|
29175
|
-
if (Object.keys(leg.values).length) callLink.leg = Object.fromEntries(Object.entries(leg.values).map(([k, v]) => [k, v.known ? stringOf(v.value) : "?"]));
|
|
29176
29333
|
leg.call = this.planWorkflow(callee, {
|
|
29177
29334
|
depth: ctx.depth + 1,
|
|
29178
|
-
chain:
|
|
29335
|
+
chain: location.chain,
|
|
29179
29336
|
caller: leg.id,
|
|
29180
29337
|
triggered: {
|
|
29181
29338
|
verdict: leg.runs.verdict,
|
|
@@ -29190,12 +29347,12 @@ var Planner = class {
|
|
|
29190
29347
|
checkPrefix: `${ctx.checkPrefix}${seg.name} / `
|
|
29191
29348
|
});
|
|
29192
29349
|
const legResults = leg.call.jobs.map((j) => j.result);
|
|
29193
|
-
leg.result =
|
|
29350
|
+
leg.result = jobResult({
|
|
29194
29351
|
skipped: false,
|
|
29195
29352
|
continueOnError: known$1(false),
|
|
29196
29353
|
legResults
|
|
29197
29354
|
});
|
|
29198
|
-
leg.outputs = leg.call.outputs;
|
|
29355
|
+
leg.outputs = { ...leg.call.outputs };
|
|
29199
29356
|
}
|
|
29200
29357
|
unsupportedCall(leg, job) {
|
|
29201
29358
|
const why = job.uses?.kind === "remote-workflow" ? "a remote reusable workflow is not loaded" : "the called workflow was not found or cannot be called";
|
|
@@ -29261,6 +29418,10 @@ var Planner = class {
|
|
|
29261
29418
|
return out;
|
|
29262
29419
|
}
|
|
29263
29420
|
planStep(step, st, sid, parentId, parentRuns, state, status, location) {
|
|
29421
|
+
this.units.set(sid, {
|
|
29422
|
+
location,
|
|
29423
|
+
kind: "step"
|
|
29424
|
+
});
|
|
29264
29425
|
const evaluated = st?.if ?? impliedIf(step);
|
|
29265
29426
|
const cond = evaluateCondition(evaluated, state, stepStatusFunctions(status));
|
|
29266
29427
|
const parent = {
|
|
@@ -29314,10 +29475,7 @@ var Planner = class {
|
|
|
29314
29475
|
envWrites: {}
|
|
29315
29476
|
};
|
|
29316
29477
|
const facts = this.mocks.factsFor(location, sid, step.uses?.raw, target.model);
|
|
29317
|
-
const envWrites =
|
|
29318
|
-
...this.scriptEnv(step, sid, state),
|
|
29319
|
-
...this.envWritesOf(facts, sid)
|
|
29320
|
-
};
|
|
29478
|
+
const envWrites = this.envWrites(step, sid, state, facts);
|
|
29321
29479
|
const { produced, base } = this.producedOutcome(facts, runs, cond, sid);
|
|
29322
29480
|
if (target.action?.using === "composite") return {
|
|
29323
29481
|
...this.compositeProduct(target.action, step, sid, state, runs, location, facts, produced, base),
|
|
@@ -29336,13 +29494,26 @@ var Planner = class {
|
|
|
29336
29494
|
envWrites
|
|
29337
29495
|
};
|
|
29338
29496
|
}
|
|
29339
|
-
/**
|
|
29497
|
+
/**
|
|
29498
|
+
* The outputs in interface order, then the names only a mock added; a name nothing valued stays unknown. Only what
|
|
29499
|
+
* is unknown after the mocks is an unknown of the plan: a pinned output is not.
|
|
29500
|
+
*/
|
|
29340
29501
|
finishOutputs(opaque, sid) {
|
|
29341
29502
|
const out = {};
|
|
29342
|
-
for (const name of opaque.names) out[name] = opaque.outputs[name] ??
|
|
29503
|
+
for (const name of opaque.names) out[name] = opaque.outputs[name] ?? outputUnknown(sid, name);
|
|
29343
29504
|
for (const [name, v] of Object.entries(opaque.outputs)) out[name] ??= v;
|
|
29505
|
+
for (const v of Object.values(out)) this.noteValue(v);
|
|
29344
29506
|
return out;
|
|
29345
29507
|
}
|
|
29508
|
+
/** The `$GITHUB_ENV` writes the steps after this one read: the script's, under the mocked ones. */
|
|
29509
|
+
envWrites(step, sid, state, facts) {
|
|
29510
|
+
const writes = {
|
|
29511
|
+
...this.scriptEnv(step, sid, state),
|
|
29512
|
+
...this.envWritesOf(facts, sid)
|
|
29513
|
+
};
|
|
29514
|
+
for (const v of Object.values(writes)) this.noteValue(v);
|
|
29515
|
+
return writes;
|
|
29516
|
+
}
|
|
29346
29517
|
envWritesOf(facts, sid) {
|
|
29347
29518
|
const envWrites = {};
|
|
29348
29519
|
for (const [name, fact] of Object.entries(facts.env)) {
|
|
@@ -29351,13 +29522,13 @@ var Planner = class {
|
|
|
29351
29522
|
}
|
|
29352
29523
|
return envWrites;
|
|
29353
29524
|
}
|
|
29354
|
-
/** The outcome as mocked, else success; a `
|
|
29525
|
+
/** The outcome as mocked, else success; a step whose own `if:` is unknown may also have been skipped. */
|
|
29355
29526
|
producedOutcome(facts, runs, cond, sid) {
|
|
29356
29527
|
const produced = facts.outcome ? facts.outcome.value : known$1("success");
|
|
29357
29528
|
if (facts.outcome) this.record(facts.outcome.by, sid, "outcome");
|
|
29358
29529
|
return {
|
|
29359
29530
|
produced,
|
|
29360
|
-
base: runs.verdict === "yes" ? produced :
|
|
29531
|
+
base: runs.verdict === "yes" ? produced : orSkipped(produced, cond.value, "last")
|
|
29361
29532
|
};
|
|
29362
29533
|
}
|
|
29363
29534
|
/** A composite action under its step: a mocked outcome makes it opaque, otherwise its inner steps decide. */
|
|
@@ -29407,11 +29578,7 @@ var Planner = class {
|
|
|
29407
29578
|
scriptOutputs(step, sid, state) {
|
|
29408
29579
|
const outputs = {};
|
|
29409
29580
|
const folded = foldWrites(substituteTemplates(step.run ?? "", state), { unknownId: (n) => `${sid}.outputs.${n}` });
|
|
29410
|
-
for (const name of /* @__PURE__ */ new Set([...step.writesOutputs.names, ...Object.keys(folded)]))
|
|
29411
|
-
const v = folded[name] ?? this.unknownOutput(sid, name);
|
|
29412
|
-
this.noteValue(v);
|
|
29413
|
-
outputs[name] = v;
|
|
29414
|
-
}
|
|
29581
|
+
for (const name of /* @__PURE__ */ new Set([...step.writesOutputs.names, ...Object.keys(folded)])) outputs[name] = folded[name] ?? outputUnknown(sid, name);
|
|
29415
29582
|
const open = !(step.writesOutputs.mentions && !step.writesOutputs.dynamic);
|
|
29416
29583
|
return {
|
|
29417
29584
|
outputs,
|
|
@@ -29423,17 +29590,10 @@ var Planner = class {
|
|
|
29423
29590
|
/** The `$GITHUB_ENV` writes of a script, as the steps after it read them. */
|
|
29424
29591
|
scriptEnv(step, sid, state) {
|
|
29425
29592
|
if (step.run === void 0) return {};
|
|
29426
|
-
|
|
29593
|
+
return foldWrites(substituteTemplates(step.run, state), {
|
|
29427
29594
|
file: "GITHUB_ENV",
|
|
29428
29595
|
unknownId: (n) => `${sid}.env.${n}`
|
|
29429
29596
|
});
|
|
29430
|
-
for (const v of Object.values(folded)) this.noteValue(v);
|
|
29431
|
-
return folded;
|
|
29432
|
-
}
|
|
29433
|
-
unknownOutput(sid, name) {
|
|
29434
|
-
const id = `${sid}.outputs.${name}`;
|
|
29435
|
-
this.note([id]);
|
|
29436
|
-
return unknown$1([id]);
|
|
29437
29597
|
}
|
|
29438
29598
|
planComposite(action, step, stepNode, state, runs, location) {
|
|
29439
29599
|
const id = compositeNodeId(stepNode, action.path);
|
|
@@ -29626,6 +29786,27 @@ function legValues(combo, legId) {
|
|
|
29626
29786
|
if (!combo) return {};
|
|
29627
29787
|
return Object.fromEntries(Object.entries(combo.values).map(([k, c]) => [k, c.known ? known$1(c.value) : unknown$1([`${legId}.matrix.${k}`])]));
|
|
29628
29788
|
}
|
|
29789
|
+
/**
|
|
29790
|
+
* A result when the unit's own `if:` may leave it skipped: `skipped` joins its candidates (a job has listed it first, a
|
|
29791
|
+
* step last). A parent's `maybe` adds nothing: the unit's readers (`needs`, `steps`) run only where its parent does.
|
|
29792
|
+
*/
|
|
29793
|
+
function orSkipped(result, cond, at) {
|
|
29794
|
+
if (cond.known) return result;
|
|
29795
|
+
const ids = [...cond.unknowns, ...result.known ? [] : result.unknowns];
|
|
29796
|
+
const options = candidates(result);
|
|
29797
|
+
if (!options) return unknown$1(ids);
|
|
29798
|
+
return unknown$1(ids, at === "first" ? ["skipped", ...options] : [...options, "skipped"]);
|
|
29799
|
+
}
|
|
29800
|
+
const outputUnknown = (sid, name) => unknown$1([`${sid}.outputs.${name}`]);
|
|
29801
|
+
/** The link of a leg in a location: its job, with the leg's values (`?` for an unknown one) when it has a matrix. */
|
|
29802
|
+
function legLinkOf(job, leg) {
|
|
29803
|
+
const link = {
|
|
29804
|
+
kind: "job",
|
|
29805
|
+
id: job.id
|
|
29806
|
+
};
|
|
29807
|
+
if (Object.keys(leg.values).length) link.leg = Object.fromEntries(Object.entries(leg.values).map(([k, v]) => [k, v.known ? stringOf(v.value) : "?"]));
|
|
29808
|
+
return link;
|
|
29809
|
+
}
|
|
29629
29810
|
/** The verdict under a parent and an `if:` value: the first `no` wins, an unknown makes it `maybe`. */
|
|
29630
29811
|
function decide(parent, value, chain) {
|
|
29631
29812
|
if (parent.verdict === "no") return {
|
|
@@ -29746,6 +29927,7 @@ function topoOrder(wf) {
|
|
|
29746
29927
|
return order;
|
|
29747
29928
|
}
|
|
29748
29929
|
const KINDS = [
|
|
29930
|
+
[/^conflict:/, "conflict"],
|
|
29749
29931
|
[/^vars\./, "var"],
|
|
29750
29932
|
[/^inputs\./, "input"],
|
|
29751
29933
|
[/^secrets\./, "secret"],
|
|
@@ -29755,6 +29937,7 @@ const KINDS = [
|
|
|
29755
29937
|
[/^race:/, "race"],
|
|
29756
29938
|
[/\.outputs\./, "step-output"],
|
|
29757
29939
|
[/\.outcome$/, "outcome"],
|
|
29940
|
+
[/\.env\./, "env"],
|
|
29758
29941
|
[/matrix/, "matrix"],
|
|
29759
29942
|
[/hashfiles/i, "hashfiles"]
|
|
29760
29943
|
];
|
|
@@ -29768,7 +29951,6 @@ function kindOf(id) {
|
|
|
29768
29951
|
return KINDS.find(([re]) => re.test(id))?.[1] ?? "unsupported";
|
|
29769
29952
|
}
|
|
29770
29953
|
const SCENARIO_PINS = [
|
|
29771
|
-
[/^vars\.(.+)$/, (name) => ({ vars: { [name]: "" } })],
|
|
29772
29954
|
[/^inputs\.((?!.*\.default$).+)$/, (name) => ({ inputs: { [name]: "" } })],
|
|
29773
29955
|
[/^github\.(sha)$/, () => ({ sha: "" })],
|
|
29774
29956
|
[/^github\.(actor|triggering_actor)$/, () => ({ actor: { login: "" } })],
|
|
@@ -29776,7 +29958,10 @@ const SCENARIO_PINS = [
|
|
|
29776
29958
|
[/^github\.(ref|ref_name|ref_type)$/, () => ({ tag: "" })],
|
|
29777
29959
|
[/^scenario\.changed$/, () => ({ changed: ["path/to/file"] })]
|
|
29778
29960
|
];
|
|
29779
|
-
/**
|
|
29961
|
+
/**
|
|
29962
|
+
* The scenario field that would pin an unknown read, when there is one. A step's or job's output, outcome or env
|
|
29963
|
+
* write is pinned by an assume entry, set over the finished plan (`assumePins`).
|
|
29964
|
+
*/
|
|
29780
29965
|
function pinFor(id) {
|
|
29781
29966
|
for (const [re, fragment] of SCENARIO_PINS) {
|
|
29782
29967
|
const m = re.exec(id);
|
|
@@ -31138,7 +31323,8 @@ const REF_GROUP = [
|
|
|
31138
31323
|
const MERGED = /* @__PURE__ */ new Set([
|
|
31139
31324
|
"inputs",
|
|
31140
31325
|
"vars",
|
|
31141
|
-
"actor"
|
|
31326
|
+
"actor",
|
|
31327
|
+
"workflow_run"
|
|
31142
31328
|
]);
|
|
31143
31329
|
/** The case's scenario over the file's: records merge key by key, the ref group is replaced whole, the rest replaced. */
|
|
31144
31330
|
function mergeScenario(given, when) {
|