@codyswann/lisa 2.265.0 → 2.266.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/core/lisa-plugin-selection.d.ts +11 -0
- package/dist/core/lisa-plugin-selection.d.ts.map +1 -1
- package/dist/core/lisa-plugin-selection.js +11 -1
- package/dist/core/lisa-plugin-selection.js.map +1 -1
- package/dist/core/lisa.d.ts +0 -41
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +23 -104
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/template-ownership.d.ts +46 -0
- package/dist/core/template-ownership.d.ts.map +1 -0
- package/dist/core/template-ownership.js +123 -0
- package/dist/core/template-ownership.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +20 -1
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/health/deadline.d.ts +25 -0
- package/dist/health/deadline.d.ts.map +1 -0
- package/dist/health/deadline.js +50 -0
- package/dist/health/deadline.js.map +1 -0
- package/dist/health/deterministic.d.ts +34 -0
- package/dist/health/deterministic.d.ts.map +1 -0
- package/dist/health/deterministic.js +206 -0
- package/dist/health/deterministic.js.map +1 -0
- package/dist/health/finding-utils.d.ts +17 -0
- package/dist/health/finding-utils.d.ts.map +1 -0
- package/dist/health/finding-utils.js +25 -0
- package/dist/health/finding-utils.js.map +1 -0
- package/dist/health/governance-probes.d.ts +51 -0
- package/dist/health/governance-probes.d.ts.map +1 -0
- package/dist/health/governance-probes.js +94 -0
- package/dist/health/governance-probes.js.map +1 -0
- package/dist/health/hook-inspection.d.ts +35 -0
- package/dist/health/hook-inspection.d.ts.map +1 -0
- package/dist/health/hook-inspection.js +123 -0
- package/dist/health/hook-inspection.js.map +1 -0
- package/dist/health/index.d.ts +1 -0
- package/dist/health/index.d.ts.map +1 -1
- package/dist/health/index.js +1 -0
- package/dist/health/index.js.map +1 -1
- package/dist/health/plugin-inspection.d.ts +37 -0
- package/dist/health/plugin-inspection.d.ts.map +1 -0
- package/dist/health/plugin-inspection.js +128 -0
- package/dist/health/plugin-inspection.js.map +1 -0
- package/dist/health/project-probes.d.ts +34 -0
- package/dist/health/project-probes.d.ts.map +1 -0
- package/dist/health/project-probes.js +104 -0
- package/dist/health/project-probes.js.map +1 -0
- package/dist/health/read-only-fs.d.ts +54 -0
- package/dist/health/read-only-fs.d.ts.map +1 -0
- package/dist/health/read-only-fs.js +165 -0
- package/dist/health/read-only-fs.js.map +1 -0
- package/dist/health/ruleset-inspection.d.ts +57 -0
- package/dist/health/ruleset-inspection.d.ts.map +1 -0
- package/dist/health/ruleset-inspection.js +315 -0
- package/dist/health/ruleset-inspection.js.map +1 -0
- package/dist/health/template-inspection.d.ts +67 -0
- package/dist/health/template-inspection.d.ts.map +1 -0
- package/dist/health/template-inspection.js +469 -0
- package/dist/health/template-inspection.js.map +1 -0
- package/dist/strategies/copy-contents.d.ts +7 -25
- package/dist/strategies/copy-contents.d.ts.map +1 -1
- package/dist/strategies/copy-contents.js +32 -63
- package/dist/strategies/copy-contents.js.map +1 -1
- package/dist/strategies/merge.d.ts +7 -0
- package/dist/strategies/merge.d.ts.map +1 -1
- package/dist/strategies/merge.js +10 -1
- package/dist/strategies/merge.js.map +1 -1
- package/dist/strategies/package-lisa.d.ts +10 -1
- package/dist/strategies/package-lisa.d.ts.map +1 -1
- package/dist/strategies/package-lisa.js +18 -20
- package/dist/strategies/package-lisa.js.map +1 -1
- package/dist/strategies/tagged-merge.d.ts +7 -0
- package/dist/strategies/tagged-merge.d.ts.map +1 -1
- package/dist/strategies/tagged-merge.js +10 -1
- package/dist/strategies/tagged-merge.js.map +1 -1
- package/dist/sync/config-sync.d.ts +7 -0
- package/dist/sync/config-sync.d.ts.map +1 -1
- package/dist/sync/config-sync.js +23 -16
- package/dist/sync/config-sync.js.map +1 -1
- package/dist/utils/ignore-patterns.d.ts +18 -0
- package/dist/utils/ignore-patterns.d.ts.map +1 -1
- package/dist/utils/ignore-patterns.js +2 -2
- package/dist/utils/ignore-patterns.js.map +1 -1
- package/package.json +3 -2
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -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-cursor/.claude-plugin/plugin.json +1 -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/scripts/detect-stale-workflow-inputs.mjs +21 -148
- package/scripts/lib/reusable-workflow-contract.d.mts +24 -0
- package/scripts/lib/reusable-workflow-contract.mjs +123 -0
- package/scripts/verify-health-deterministic-built.mjs +499 -0
|
@@ -42,11 +42,26 @@ import fs from "node:fs";
|
|
|
42
42
|
import path from "node:path";
|
|
43
43
|
import process from "node:process";
|
|
44
44
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
45
|
+
import {
|
|
46
|
+
diffStaleKeys,
|
|
47
|
+
extractCallerJobs,
|
|
48
|
+
extractDeclaredInputs,
|
|
49
|
+
parseReusableReference,
|
|
50
|
+
} from "./lib/reusable-workflow-contract.mjs";
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
diffStaleKeys,
|
|
54
|
+
extractCallerJobs,
|
|
55
|
+
extractDeclaredInputs,
|
|
56
|
+
parseReusableReference,
|
|
57
|
+
};
|
|
45
58
|
|
|
46
59
|
const REPO_ROOT = path.resolve(
|
|
47
60
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
48
61
|
".."
|
|
49
62
|
);
|
|
63
|
+
const LISA_OWNER = "codyswanngt";
|
|
64
|
+
const LISA_REPO = "lisa";
|
|
50
65
|
|
|
51
66
|
/**
|
|
52
67
|
* Usage error — thrown by `parseArgs` for an invalid invocation so `main` can
|
|
@@ -68,153 +83,6 @@ function isDirectory(target) {
|
|
|
68
83
|
}
|
|
69
84
|
}
|
|
70
85
|
|
|
71
|
-
/**
|
|
72
|
-
* Leading-whitespace width of a line (its indentation level).
|
|
73
|
-
*
|
|
74
|
-
* @param {string} line - a single source line.
|
|
75
|
-
* @returns {number} number of leading whitespace characters.
|
|
76
|
-
*/
|
|
77
|
-
function indentOf(line) {
|
|
78
|
-
return line.length - line.trimStart().length;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Collect the immediate child `key:` names nested directly under a block
|
|
83
|
-
* opener line, given the opener's own indentation. Stops at the first line
|
|
84
|
-
* whose indentation is `<= parentIndent` (blank lines are skipped).
|
|
85
|
-
*
|
|
86
|
-
* @param {readonly string[]} lines - full file, split into lines.
|
|
87
|
-
* @param {number} openerIndex - index of the block-opener line (e.g. `with:`).
|
|
88
|
-
* @param {number} parentIndent - indentation width of the opener line itself.
|
|
89
|
-
* @returns {string[]} the immediate child key names, in file order.
|
|
90
|
-
*/
|
|
91
|
-
function collectImmediateChildKeys(lines, openerIndex, parentIndent) {
|
|
92
|
-
const keys = [];
|
|
93
|
-
for (let k = openerIndex + 1; k < lines.length; k++) {
|
|
94
|
-
const line = lines[k];
|
|
95
|
-
if (line.trim() === "") {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const lineIndent = indentOf(line);
|
|
99
|
-
if (lineIndent <= parentIndent) {
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
const match = /^\s*([A-Za-z0-9_-]+):/.exec(line);
|
|
103
|
-
if (match && lineIndent === parentIndent + 2) {
|
|
104
|
-
keys.push(match[1]);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return keys;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Parse a `uses:` value referencing a reusable workflow into its basename and
|
|
112
|
-
* ref, or `null` if it doesn't match the `.../.github/workflows/<file>@<ref>`
|
|
113
|
-
* shape (e.g. a plain action reference like `actions/checkout@v6`).
|
|
114
|
-
*
|
|
115
|
-
* @param {string} usesValue - the raw value after `uses:`.
|
|
116
|
-
* @returns {{ file: string, ref: string } | null} the parsed reference.
|
|
117
|
-
*/
|
|
118
|
-
export function parseReusableReference(usesValue) {
|
|
119
|
-
const match = /\.github\/workflows\/([\w.-]+\.ya?ml)@(\S+)$/.exec(
|
|
120
|
-
usesValue.trim()
|
|
121
|
-
);
|
|
122
|
-
if (!match) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
return { file: match[1], ref: match[2] };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Scan a caller workflow file's contents for every job that invokes a
|
|
130
|
-
* reusable workflow, returning the `with:` keys each job passes.
|
|
131
|
-
*
|
|
132
|
-
* @param {string} content - full caller workflow file contents.
|
|
133
|
-
* @returns {{ reusableFile: string, ref: string, withKeys: string[] }[]}
|
|
134
|
-
* one entry per job that calls a reusable workflow (in file order).
|
|
135
|
-
*/
|
|
136
|
-
export function extractCallerJobs(content) {
|
|
137
|
-
const lines = content.split(/\r?\n/);
|
|
138
|
-
const jobs = [];
|
|
139
|
-
for (let i = 0; i < lines.length; i++) {
|
|
140
|
-
const usesMatch = /^(\s*)uses:\s*(\S+)\s*$/.exec(lines[i]);
|
|
141
|
-
if (!usesMatch) {
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const [, indentStr, usesValue] = usesMatch;
|
|
145
|
-
const ref = parseReusableReference(usesValue);
|
|
146
|
-
if (!ref) {
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
const indent = indentStr.length;
|
|
150
|
-
let withKeys = [];
|
|
151
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
152
|
-
const line = lines[j];
|
|
153
|
-
if (line.trim() === "") {
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
const lineIndent = indentOf(line);
|
|
157
|
-
if (lineIndent < indent) {
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
if (lineIndent === indent && /^\s*with:\s*$/.test(line)) {
|
|
161
|
-
withKeys = collectImmediateChildKeys(lines, j, indent).sort();
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
jobs.push({ reusableFile: ref.file, ref: ref.ref, withKeys });
|
|
166
|
-
}
|
|
167
|
-
return jobs;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Extract the declared `on.workflow_call.inputs` key names from a reusable
|
|
172
|
-
* workflow's contents.
|
|
173
|
-
*
|
|
174
|
-
* @param {string} content - full reusable workflow file contents.
|
|
175
|
-
* @returns {string[] | null} declared input names (sorted), an empty array when
|
|
176
|
-
* the reusable workflow declares no inputs, or `null` if the file has no
|
|
177
|
-
* `workflow_call:` block.
|
|
178
|
-
*/
|
|
179
|
-
export function extractDeclaredInputs(content) {
|
|
180
|
-
const lines = content.split(/\r?\n/);
|
|
181
|
-
for (let i = 0; i < lines.length; i++) {
|
|
182
|
-
if (!/^\s*workflow_call:\s*$/.test(lines[i])) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
const wcIndent = indentOf(lines[i]);
|
|
186
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
187
|
-
const line = lines[j];
|
|
188
|
-
if (line.trim() === "") {
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
const lineIndent = indentOf(line);
|
|
192
|
-
if (lineIndent <= wcIndent) {
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
if (lineIndent === wcIndent + 2 && /^\s*inputs:\s*$/.test(line)) {
|
|
196
|
-
return collectImmediateChildKeys(lines, j, lineIndent).sort();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Keys present in `used` but absent from `declared` — i.e. inputs a caller
|
|
206
|
-
* passes that the reusable workflow's contract no longer accepts.
|
|
207
|
-
*
|
|
208
|
-
* @param {readonly string[]} used - `with:` keys a caller passes.
|
|
209
|
-
* @param {readonly string[]} declared - `workflow_call.inputs` keys the
|
|
210
|
-
* reusable workflow declares.
|
|
211
|
-
* @returns {string[]} the stale keys, sorted.
|
|
212
|
-
*/
|
|
213
|
-
export function diffStaleKeys(used, declared) {
|
|
214
|
-
const declaredSet = new Set(declared);
|
|
215
|
-
return used.filter(key => !declaredSet.has(key)).sort();
|
|
216
|
-
}
|
|
217
|
-
|
|
218
86
|
/**
|
|
219
87
|
* Resolve a reusable workflow's declared inputs from the contracts root.
|
|
220
88
|
*
|
|
@@ -245,7 +113,12 @@ function resolveContractInputs(contractsRoot, reusableFile) {
|
|
|
245
113
|
function scanCallerFile(filePath, contractsRoot) {
|
|
246
114
|
const jobs = extractCallerJobs(fs.readFileSync(filePath, "utf8"));
|
|
247
115
|
return jobs.map(job => {
|
|
248
|
-
const
|
|
116
|
+
const isLisaContract =
|
|
117
|
+
job.owner?.toLowerCase() === LISA_OWNER &&
|
|
118
|
+
job.repo?.toLowerCase() === LISA_REPO;
|
|
119
|
+
const declared = isLisaContract
|
|
120
|
+
? resolveContractInputs(contractsRoot, job.reusableFile)
|
|
121
|
+
: null;
|
|
249
122
|
if (declared === null) {
|
|
250
123
|
return { ...job, staleInputs: [], status: "unknown-contract" };
|
|
251
124
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ReusableCallerJob {
|
|
2
|
+
readonly reusableFile: string;
|
|
3
|
+
readonly ref: string;
|
|
4
|
+
readonly owner: string | null;
|
|
5
|
+
readonly repo: string | null;
|
|
6
|
+
readonly withKeys: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function parseReusableReference(usesValue: string): {
|
|
10
|
+
readonly file: string;
|
|
11
|
+
readonly ref: string;
|
|
12
|
+
readonly owner: string | null;
|
|
13
|
+
readonly repo: string | null;
|
|
14
|
+
} | null;
|
|
15
|
+
export function extractCallerJobs(
|
|
16
|
+
content: string
|
|
17
|
+
): readonly ReusableCallerJob[];
|
|
18
|
+
export function extractDeclaredInputs(
|
|
19
|
+
content: string
|
|
20
|
+
): readonly string[] | null;
|
|
21
|
+
export function diffStaleKeys(
|
|
22
|
+
used: readonly string[],
|
|
23
|
+
declared: readonly string[]
|
|
24
|
+
): readonly string[];
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** Pure reusable-workflow caller/contract comparison shared by health and CLI. */
|
|
2
|
+
|
|
3
|
+
/** @param {string} line @returns {number} */
|
|
4
|
+
function indentOf(line) {
|
|
5
|
+
return line.length - line.trimStart().length;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {readonly string[]} lines
|
|
10
|
+
* @param {number} openerIndex
|
|
11
|
+
* @param {number} parentIndent
|
|
12
|
+
* @returns {string[]}
|
|
13
|
+
*/
|
|
14
|
+
function collectImmediateChildKeys(lines, openerIndex, parentIndent) {
|
|
15
|
+
const keys = [];
|
|
16
|
+
for (let index = openerIndex + 1; index < lines.length; index++) {
|
|
17
|
+
const line = lines[index];
|
|
18
|
+
if (line.trim() === "") continue;
|
|
19
|
+
const indentation = indentOf(line);
|
|
20
|
+
if (indentation <= parentIndent) break;
|
|
21
|
+
const match = /^\s*([A-Za-z0-9_-]+):/.exec(line);
|
|
22
|
+
if (match && indentation === parentIndent + 2) keys.push(match[1]);
|
|
23
|
+
}
|
|
24
|
+
return keys;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} usesValue
|
|
29
|
+
* @returns {{ file: string, ref: string, owner: string | null, repo: string | null } | null}
|
|
30
|
+
*/
|
|
31
|
+
export function parseReusableReference(usesValue) {
|
|
32
|
+
const value = usesValue.trim();
|
|
33
|
+
const local = /^\.\/\.github\/workflows\/([\w.-]+\.ya?ml)$/.exec(value);
|
|
34
|
+
if (local) {
|
|
35
|
+
return { file: local[1], ref: "local", owner: null, repo: null };
|
|
36
|
+
}
|
|
37
|
+
const remote =
|
|
38
|
+
/^([^/\s]+)\/([^/\s]+)\/\.github\/workflows\/([\w.-]+\.ya?ml)@(\S+)$/.exec(
|
|
39
|
+
value
|
|
40
|
+
);
|
|
41
|
+
return remote
|
|
42
|
+
? {
|
|
43
|
+
file: remote[3],
|
|
44
|
+
ref: remote[4],
|
|
45
|
+
owner: remote[1],
|
|
46
|
+
repo: remote[2],
|
|
47
|
+
}
|
|
48
|
+
: null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {string} content
|
|
53
|
+
* @returns {{ reusableFile: string, ref: string, owner: string | null, repo: string | null, withKeys: string[] }[]}
|
|
54
|
+
*/
|
|
55
|
+
export function extractCallerJobs(content) {
|
|
56
|
+
const lines = content.split(/\r?\n/);
|
|
57
|
+
const jobs = [];
|
|
58
|
+
for (let index = 0; index < lines.length; index++) {
|
|
59
|
+
const usesMatch = /^(\s*)uses:\s*(\S+)\s*$/.exec(lines[index]);
|
|
60
|
+
if (!usesMatch) continue;
|
|
61
|
+
const reference = parseReusableReference(usesMatch[2]);
|
|
62
|
+
if (!reference) continue;
|
|
63
|
+
const indentation = usesMatch[1].length;
|
|
64
|
+
let withKeys = [];
|
|
65
|
+
for (let child = index + 1; child < lines.length; child++) {
|
|
66
|
+
const line = lines[child];
|
|
67
|
+
if (line.trim() === "") continue;
|
|
68
|
+
const childIndentation = indentOf(line);
|
|
69
|
+
if (childIndentation < indentation) break;
|
|
70
|
+
if (childIndentation === indentation && /^\s*with:\s*$/.test(line)) {
|
|
71
|
+
withKeys = collectImmediateChildKeys(lines, child, indentation).sort(
|
|
72
|
+
(left, right) => left.localeCompare(right)
|
|
73
|
+
);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
jobs.push({
|
|
78
|
+
reusableFile: reference.file,
|
|
79
|
+
ref: reference.ref,
|
|
80
|
+
owner: reference.owner,
|
|
81
|
+
repo: reference.repo,
|
|
82
|
+
withKeys,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return jobs;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** @param {string} content @returns {string[] | null} */
|
|
89
|
+
export function extractDeclaredInputs(content) {
|
|
90
|
+
const lines = content.split(/\r?\n/);
|
|
91
|
+
for (let index = 0; index < lines.length; index++) {
|
|
92
|
+
if (!/^\s*workflow_call:\s*$/.test(lines[index])) continue;
|
|
93
|
+
const indentation = indentOf(lines[index]);
|
|
94
|
+
for (let child = index + 1; child < lines.length; child++) {
|
|
95
|
+
const line = lines[child];
|
|
96
|
+
if (line.trim() === "") continue;
|
|
97
|
+
const childIndentation = indentOf(line);
|
|
98
|
+
if (childIndentation <= indentation) break;
|
|
99
|
+
if (
|
|
100
|
+
childIndentation === indentation + 2 &&
|
|
101
|
+
/^\s*inputs:\s*$/.test(line)
|
|
102
|
+
) {
|
|
103
|
+
return collectImmediateChildKeys(lines, child, childIndentation).sort(
|
|
104
|
+
(left, right) => left.localeCompare(right)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {readonly string[]} used
|
|
115
|
+
* @param {readonly string[]} declared
|
|
116
|
+
* @returns {string[]}
|
|
117
|
+
*/
|
|
118
|
+
export function diffStaleKeys(used, declared) {
|
|
119
|
+
const declaredSet = new Set(declared);
|
|
120
|
+
return used
|
|
121
|
+
.filter(key => !declaredSet.has(key))
|
|
122
|
+
.sort((left, right) => left.localeCompare(right));
|
|
123
|
+
}
|