@codyswann/lisa 4.35.4 → 4.36.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/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +554 -0
- package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +296 -3
- package/dist/core/downstream-references.d.ts.map +1 -1
- package/dist/core/downstream-references.js +4 -0
- package/dist/core/downstream-references.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +16 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/third-party-action-pins.d.ts +84 -0
- package/dist/core/third-party-action-pins.d.ts.map +1 -0
- package/dist/core/third-party-action-pins.js +153 -0
- package/dist/core/third-party-action-pins.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +15 -7
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/ensure-third-party-action-pins.d.ts +123 -0
- package/dist/migrations/ensure-third-party-action-pins.d.ts.map +1 -0
- package/dist/migrations/ensure-third-party-action-pins.js +238 -0
- package/dist/migrations/ensure-third-party-action-pins.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -0
- package/dist/migrations/index.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
- package/plugins/lisa/rules/reference/measurement-integrity.md +10 -0
- package/plugins/lisa/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/rules/reference/measurement-integrity.md +10 -0
- package/plugins/lisa-copilot/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/measurement-integrity-reference.mdc +10 -0
- package/plugins/lisa-cursor/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/rules/reference/measurement-integrity.md +10 -0
- package/plugins/src/base/skills/lisa-jsdoc-best-practices/SKILL.md +60 -1
- package/scripts/check-third-party-action-pins.mjs +111 -14
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/.prettierignore +1 -0
- package/typescript/package-lisa/package.lisa.json +1 -0
|
@@ -33,6 +33,16 @@ An entire corpus sits outside the query's scope and nothing says so. `gh search
|
|
|
33
33
|
|
|
34
34
|
**Catch it:** validate the detector against known-true items before believing any count.
|
|
35
35
|
|
|
36
|
+
#### A second domain, which is what makes this a category rather than a quirk
|
|
37
|
+
|
|
38
|
+
The same mode, on a filesystem rather than a tracker. A survey of agent worktrees enumerating `.claude/worktrees/` and `.worktrees/` returns a well-formed list — and omits every worktree created outside the repository tree, under the per-user temporary directory. Four such worktrees existed, three of them with uncommitted changes; none appeared.
|
|
39
|
+
|
|
40
|
+
Run it through the membership test and it qualifies on all three counts: the list is **well formed** (no error, no warning, no partial marker); the omission is **invisible from the list itself**, which looks exactly like a complete survey of a machine with fewer worktrees on it; and **a mechanical step would have caught it** — the same one A1 already prescribes, validating the enumeration against a worktree you know exists.
|
|
41
|
+
|
|
42
|
+
This example is here rather than folded into the paragraph above because **a second domain tests whether the category is real or just a description of one tool's behaviour.** A mode observed only through `gh search` could be a fact about GitHub's search semantics; the same shape in a `find` over directories says the failure belongs to *enumeration with an unstated boundary*, whichever substrate is enumerating. When extending this document, a new domain for an existing mode is worth as much as a new mode.
|
|
43
|
+
|
|
44
|
+
**The consequence in that instance was not academic.** Uncommitted work outside the repository tree is invisible to every query anyone would run before deciding what to clean up, and looks identical to abandoned scratch — so a survey feeding a deletion decision under-reports precisely the thing that must not be deleted. See the worktree-and-binding hazards for the separate question of a mitigation whose cost is invisibility; the measurement half is this mode.
|
|
45
|
+
|
|
36
46
|
### A2. Truncation — under, enumeration
|
|
37
47
|
|
|
38
48
|
Real members past the first page are dropped with no truncation signal. `gh issue list` and `gh pr list` default to 30 and cap silently at whatever `--limit` is passed: one audit reported **37 items where the answer was 513**, a 14× undercount. A paging API returning **249 of 845 rows with `hasNextPage: false`** is the same mode on another substrate.
|
|
@@ -348,7 +348,66 @@ In `@example` blocks, use fenced code blocks and escape `@` as `\@`:
|
|
|
348
348
|
*/
|
|
349
349
|
```
|
|
350
350
|
|
|
351
|
-
###
|
|
351
|
+
### Path Examples Must Not Contain the Block Terminator
|
|
352
|
+
|
|
353
|
+
A path written as an example inside a doc comment **ends the comment** if it
|
|
354
|
+
contains `*/` — a glob such as `sandbox-*/`, or any real directory whose
|
|
355
|
+
name ends in an asterisk. Everything below that point is then parsed as source:
|
|
356
|
+
path segments become identifiers, and the separator between them becomes
|
|
357
|
+
division.
|
|
358
|
+
|
|
359
|
+
**Write the wildcard as a placeholder, not a glob:**
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
// WRONG — the comment ends inside the path, mid-line
|
|
363
|
+
/**
|
|
364
|
+
* Debris lands at `.stryker-tmp/bite-guard-intact/sandbox-*/rails/scripts/foo.sh`
|
|
365
|
+
* and this line is no longer a comment.
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
// RIGHT — a placeholder cannot terminate the block
|
|
369
|
+
/**
|
|
370
|
+
* Debris lands at `.stryker-tmp/bite-guard-intact/sandbox-<id>/rails/scripts/foo.sh`
|
|
371
|
+
* and this line is still a comment.
|
|
372
|
+
*/
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
`<id>`, `<name>`, `<n>` — the same placeholder convention used everywhere else
|
|
376
|
+
in Lisa's docs. Escaping (`*\/`) also works but produces an example nobody can
|
|
377
|
+
copy and paste, which defeats the point of writing one.
|
|
378
|
+
|
|
379
|
+
### What it looks like when you get it wrong
|
|
380
|
+
|
|
381
|
+
The build goes red immediately — `bun run typecheck`, `bun run lint` and the
|
|
382
|
+
test run all fail — but **none of them mentions a comment**, and every line
|
|
383
|
+
they report sits *below* the real cause. Measured, with the true cause on
|
|
384
|
+
line 89:
|
|
385
|
+
|
|
386
|
+
```text
|
|
387
|
+
src/configs/repo-scan.ts(90,9): error TS1005: ';' expected.
|
|
388
|
+
src/configs/repo-scan.ts(90,20): error TS1443: Module declaration names may only use ' or " quoted strings.
|
|
389
|
+
src/configs/repo-scan.ts(94,1): error TS1160: Unterminated template literal.
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Errors naming identifiers you never wrote — `Cannot find name 'mutation'`,
|
|
393
|
+
`Cannot find name 'src'` — are the giveaway: those are backtick contents from
|
|
394
|
+
inside the comment, now being read as code. **Look upward for a doc comment
|
|
395
|
+
before looking for a missing import.**
|
|
396
|
+
|
|
397
|
+
Lisa's gate runner says this for you. When a gate fails, it scans the files the
|
|
398
|
+
failure named and reports the closed comment by name:
|
|
399
|
+
|
|
400
|
+
```text
|
|
401
|
+
a doc comment in src/configs/repo-scan.ts ends early, on line 89 — every error
|
|
402
|
+
reported below that line is downstream of it, not a fault of its own
|
|
403
|
+
↳ src/configs/repo-scan.ts:89 — the block opened on line 87 ends at the
|
|
404
|
+
terminator INSIDE this line, not on line 92
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
It is a text scan rather than a lint rule on purpose: the file does not parse,
|
|
408
|
+
so ESLint reports `Parsing error` and runs zero rules on it.
|
|
409
|
+
|
|
410
|
+
## Quick Reference for Escaping
|
|
352
411
|
|
|
353
412
|
| Context | Approach | Example |
|
|
354
413
|
|---------|----------|---------|
|
|
@@ -69,16 +69,41 @@
|
|
|
69
69
|
* - the file list comes from `git ls-files`, so the gate sees exactly what a
|
|
70
70
|
* release would carry.
|
|
71
71
|
*
|
|
72
|
+
* ## `--warn`: how this arrives in a consumer without breaking it
|
|
73
|
+
*
|
|
74
|
+
* This file also ships to consumers through `all/copy-overwrite/scripts/`,
|
|
75
|
+
* which — unlike `create-only` — reaches every repository that already exists,
|
|
76
|
+
* on its next update. That reach is exactly why it must not arrive blocking.
|
|
77
|
+
*
|
|
78
|
+
* A consumer's seeded workflows carry mutable refs *today*: that is the defect
|
|
79
|
+
* #3588 exists to migrate away. A detector that hard-failed on arrival would
|
|
80
|
+
* redden the entire installed base at once, for a condition those repositories
|
|
81
|
+
* did not introduce and could not have fixed yet, since the migration that
|
|
82
|
+
* fixes it lands in the same update. That is not hypothetical — a guard added
|
|
83
|
+
* to a workflow consumed at `@main` failed closed on a pre-existing consumer
|
|
84
|
+
* misconfiguration and took four repositories' releases down for five hours;
|
|
85
|
+
* the repair was to keep it loud and stop it blocking (#3755, #3757).
|
|
86
|
+
*
|
|
87
|
+
* So `--warn` reports every finding and exits 0, and the shipped consumer
|
|
88
|
+
* script entry uses it. Tightening to blocking is a separate, deliberate
|
|
89
|
+
* decision for once the installed base has had a chance to migrate — one flag
|
|
90
|
+
* on one line — not something to "finish" while consumers are still mutable.
|
|
91
|
+
*
|
|
92
|
+
* `--warn` also softens the empty-scan usage error, because a consumer with no
|
|
93
|
+
* workflow files is an ordinary shape rather than a broken invocation. A
|
|
94
|
+
* misspelled flag stays a hard error in every mode: that one is the caller's.
|
|
95
|
+
*
|
|
72
96
|
* CLI:
|
|
73
|
-
* node scripts/check-third-party-action-pins.mjs [--root <dir>] [--json]
|
|
97
|
+
* node scripts/check-third-party-action-pins.mjs [--root <dir>] [--json] [--warn]
|
|
74
98
|
*
|
|
75
99
|
* Exit codes (mirroring the sibling gates in this directory):
|
|
76
|
-
* 0 — every third-party reference is a 40-hex SHA carrying a version comment
|
|
100
|
+
* 0 — every third-party reference is a 40-hex SHA carrying a version comment;
|
|
101
|
+
* or `--warn` was passed and the findings are being reported, not enforced.
|
|
77
102
|
* 1 — >=1 reference is mutable, or pinned without a version comment.
|
|
78
103
|
* 2 — operational/usage error: unknown flag, a flag missing its value,
|
|
79
104
|
* `--root` absent or not a git repository, git unavailable, or zero
|
|
80
105
|
* workflow files discovered. Finding nothing to check is a broken
|
|
81
|
-
* invocation, not conformance.
|
|
106
|
+
* invocation, not conformance — except under `--warn`, see above.
|
|
82
107
|
*
|
|
83
108
|
* @module scripts/check-third-party-action-pins
|
|
84
109
|
*/
|
|
@@ -134,14 +159,60 @@ const NO_VERSION = "missing-version-comment";
|
|
|
134
159
|
* a trailing `# v1.2.3` can be read. Local (`./path`) and container
|
|
135
160
|
* (`docker://`) references have no owner and never match.
|
|
136
161
|
*/
|
|
137
|
-
const USES_RE =
|
|
138
|
-
|
|
162
|
+
const USES_RE = /^[ \t]*(?:-[ \t]*)?uses:[ \t]*(?<value>[^\s"'#]+)/;
|
|
163
|
+
|
|
164
|
+
/** A well-formed action owner. Anchored, so matching is linear. */
|
|
165
|
+
const OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
166
|
+
|
|
167
|
+
/** A well-formed action path below the owner. Anchored, so linear. */
|
|
168
|
+
const ACTION_PATH_RE = /^[A-Za-z0-9_.][A-Za-z0-9_./-]*$/;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Split a `uses:` value into owner, action path, and ref.
|
|
172
|
+
*
|
|
173
|
+
* The shape is validated with anchored parts rather than described as one
|
|
174
|
+
* pattern. Spelling `owner/repo/path@ref` as adjacent character classes gives
|
|
175
|
+
* the engine several ways to divide the same text, which is super-linear on a
|
|
176
|
+
* long line; splitting first and validating each piece has one.
|
|
177
|
+
*
|
|
178
|
+
* @param {string} value - the raw value of a `uses:` line.
|
|
179
|
+
* @returns {{ owner: string, rest: string, ref: string } | null} parts, or null
|
|
180
|
+
* when this is not an `owner/repo@ref` reference (a local `./path` or a
|
|
181
|
+
* `docker://` image never is).
|
|
182
|
+
*/
|
|
183
|
+
function parseUsesValue(value) {
|
|
184
|
+
const at = value.lastIndexOf("@");
|
|
185
|
+
if (at <= 0) return null;
|
|
186
|
+
const action = value.slice(0, at);
|
|
187
|
+
const ref = value.slice(at + 1);
|
|
188
|
+
const slash = action.indexOf("/");
|
|
189
|
+
if (ref === "" || slash <= 0 || action.includes("://")) return null;
|
|
190
|
+
const owner = action.slice(0, slash);
|
|
191
|
+
const rest = action.slice(slash + 1);
|
|
192
|
+
if (!OWNER_RE.test(owner) || !ACTION_PATH_RE.test(rest)) return null;
|
|
193
|
+
return { owner, rest, ref };
|
|
194
|
+
}
|
|
139
195
|
|
|
140
196
|
/** A full commit SHA. Abbreviated SHAs are ambiguous and do not count. */
|
|
141
197
|
const FULL_SHA_RE = /^[0-9a-f]{40}$/;
|
|
142
198
|
|
|
143
|
-
/**
|
|
144
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Read the version out of a line's trailing `# ...` comment.
|
|
201
|
+
*
|
|
202
|
+
* Deliberately not a regex. Every spelling of "text after a hash to end of
|
|
203
|
+
* line" is either a backtracking shape or an unanchored `.*$` the engine
|
|
204
|
+
* retries from each position, and neither is worth it for a one-character
|
|
205
|
+
* search.
|
|
206
|
+
*
|
|
207
|
+
* @param {string} trailer - everything on the line after the `uses:` value.
|
|
208
|
+
* @returns {string | null} the comment text, or null when there is none.
|
|
209
|
+
*/
|
|
210
|
+
function versionComment(trailer) {
|
|
211
|
+
const hash = trailer.indexOf("#");
|
|
212
|
+
if (hash < 0) return null;
|
|
213
|
+
const version = trailer.slice(hash + 1).trim();
|
|
214
|
+
return version === "" ? null : version;
|
|
215
|
+
}
|
|
145
216
|
|
|
146
217
|
/** Max bytes of `git ls-files` output (7k+ tracked paths is ~0.3 MB today). */
|
|
147
218
|
const MAX_GIT_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
@@ -182,15 +253,19 @@ export function findActionRefs(content) {
|
|
|
182
253
|
for (let index = 0; index < lines.length; index++) {
|
|
183
254
|
const match = USES_RE.exec(lines[index]);
|
|
184
255
|
if (match === null) continue;
|
|
185
|
-
const
|
|
186
|
-
|
|
256
|
+
const parsed = parseUsesValue(match.groups.value);
|
|
257
|
+
if (parsed === null) continue;
|
|
258
|
+
const { owner, ref, rest } = parsed;
|
|
187
259
|
refs.push({
|
|
188
260
|
action: `${owner}/${rest}`,
|
|
189
261
|
kind: classifyOwner(owner),
|
|
190
262
|
line: index + 1,
|
|
191
263
|
owner,
|
|
192
264
|
ref,
|
|
193
|
-
|
|
265
|
+
// The rest of the line is sliced rather than captured: a trailing `.*`
|
|
266
|
+
// after the value's character class gives the engine two ways to divide
|
|
267
|
+
// the same text, which is the super-linear shape.
|
|
268
|
+
version: versionComment(lines[index].slice(match[0].length)),
|
|
194
269
|
});
|
|
195
270
|
}
|
|
196
271
|
return refs;
|
|
@@ -350,10 +425,13 @@ function humanReport(report) {
|
|
|
350
425
|
export function parseArgs(argv) {
|
|
351
426
|
let root = null;
|
|
352
427
|
let json = false;
|
|
428
|
+
let warn = false;
|
|
353
429
|
for (let i = 0; i < argv.length; i++) {
|
|
354
430
|
const arg = argv[i];
|
|
355
431
|
if (arg === "--json") {
|
|
356
432
|
json = true;
|
|
433
|
+
} else if (arg === "--warn") {
|
|
434
|
+
warn = true;
|
|
357
435
|
} else if (arg === "--root") {
|
|
358
436
|
const next = argv[i + 1];
|
|
359
437
|
if (next === undefined || next.startsWith("--")) {
|
|
@@ -365,7 +443,7 @@ export function parseArgs(argv) {
|
|
|
365
443
|
throw new UsageError(`unknown argument: ${arg}`);
|
|
366
444
|
}
|
|
367
445
|
}
|
|
368
|
-
return { json, root: path.resolve(root ?? REPO_ROOT) };
|
|
446
|
+
return { json, warn, root: path.resolve(root ?? REPO_ROOT) };
|
|
369
447
|
}
|
|
370
448
|
|
|
371
449
|
/**
|
|
@@ -406,14 +484,22 @@ export function main(argv, io = {}) {
|
|
|
406
484
|
const out = io.stdout ?? process.stdout;
|
|
407
485
|
const err = io.stderr ?? process.stderr;
|
|
408
486
|
let opts;
|
|
409
|
-
let scan;
|
|
410
487
|
try {
|
|
411
488
|
opts = parseArgs(argv);
|
|
412
|
-
scan = collectWorkflows(opts.root);
|
|
413
489
|
} catch (error) {
|
|
490
|
+
// A misspelled flag is the caller's mistake and stays loud in every mode.
|
|
414
491
|
err.write(`error: ${error.message}\n`);
|
|
415
492
|
return 2;
|
|
416
493
|
}
|
|
494
|
+
let scan;
|
|
495
|
+
try {
|
|
496
|
+
scan = collectWorkflows(opts.root);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
// Under --warn this is a repository with nothing to scan, which for a
|
|
499
|
+
// consumer is an ordinary shape rather than a broken invocation.
|
|
500
|
+
err.write(`error: ${error.message}\n`);
|
|
501
|
+
return opts.warn ? 0 : 2;
|
|
502
|
+
}
|
|
417
503
|
|
|
418
504
|
let checked = 0;
|
|
419
505
|
let thirdParty = 0;
|
|
@@ -445,7 +531,18 @@ export function main(argv, io = {}) {
|
|
|
445
531
|
out.write(
|
|
446
532
|
`${opts.json ? JSON.stringify(report, null, 2) : humanReport(report)}\n`
|
|
447
533
|
);
|
|
448
|
-
|
|
534
|
+
if (report.findings.length === 0) {
|
|
535
|
+
return 0;
|
|
536
|
+
}
|
|
537
|
+
if (opts.warn) {
|
|
538
|
+
out.write(
|
|
539
|
+
"\nReporting only: this check is not blocking here. Nothing above stops " +
|
|
540
|
+
"a build.\nRun `npx @codyswann/lisa@latest .` to pin these " +
|
|
541
|
+
"references, then re-run.\n"
|
|
542
|
+
);
|
|
543
|
+
return 0;
|
|
544
|
+
}
|
|
545
|
+
return 1;
|
|
449
546
|
}
|
|
450
547
|
|
|
451
548
|
if (invokedAsScript(import.meta.url)) {
|
|
@@ -73,6 +73,7 @@ scripts/check-npm-publish-landed.mjs
|
|
|
73
73
|
scripts/check-release-package-identity.mjs
|
|
74
74
|
scripts/check-skipped-required-checks.mjs
|
|
75
75
|
scripts/check-state-classification.mjs
|
|
76
|
+
scripts/check-third-party-action-pins.mjs
|
|
76
77
|
scripts/check-third-party-review-evidence.mjs
|
|
77
78
|
scripts/check-threshold-ratchet.mjs
|
|
78
79
|
scripts/check-verification-coverage.mjs
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"test:node": "LISA_TEST_SCRATCH_PREFIXES='[\"node-\"]' lisa-test-run --profile typescript --adapter direct -- node scripts/lisa-test-node.mjs",
|
|
27
27
|
"check:work-item": "node scripts/lisa-work-item.mjs validate-pr",
|
|
28
28
|
"check:conflict-markers": "node scripts/check-conflict-markers.mjs",
|
|
29
|
+
"check:third-party-action-pins": "node scripts/check-third-party-action-pins.mjs --warn",
|
|
29
30
|
"check:work-item:push": "node scripts/lisa-work-item.mjs validate-push",
|
|
30
31
|
"check:verification": "node scripts/check-verification-coverage.mjs"
|
|
31
32
|
},
|