@quikit/quikpilot-runner 8.2.3 → 8.2.5
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/README.md +27 -27
- package/dist/index.js +202 -20
- package/dist/index.js.map +4 -4
- package/package.json +26 -26
package/README.md
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
# @quikit/quikpilot-runner
|
|
2
|
-
|
|
3
|
-
QuikPilot's CI runner. Invoked automatically by the `.github/workflows/quikpilot.yml` file QuikPilot
|
|
4
|
-
installs in an onboarded repository — it fetches the run's work packet, runs your setup/lint/test
|
|
5
|
-
commands, reports results to QuikTest, enforces the denied-path guardrail, commits, pushes, and
|
|
6
|
-
reports back to QuikTrack.
|
|
7
|
-
|
|
8
|
-
This package is not meant to be installed or run by hand. It exists so a fix to QuikPilot's runner
|
|
9
|
-
logic ships as a new package version rather than a new pull request into every onboarded repository
|
|
10
|
-
— the workflow file itself lives in your repository (QuikIT cannot update it directly), so it stays a
|
|
11
|
-
thin, ~25-line launcher that invokes an exact, pinned version of this package via `npx`.
|
|
12
|
-
|
|
13
|
-
It carries no secrets and needs none of your credentials: it authenticates to QuikTrack and to GitHub
|
|
14
|
-
using short-lived tokens exchanged at runtime, never a value baked into the workflow file or this
|
|
15
|
-
package.
|
|
16
|
-
|
|
17
|
-
## Versioning and support
|
|
18
|
-
|
|
19
|
-
Every workflow file pins an **exact** version of this package (`npx --yes
|
|
20
|
-
@quikit/quikpilot-runner@<version>`, never a range or `latest`) — a repository never picks up a new
|
|
21
|
-
release on its own. Older published versions are not removed from npm and keep working as long as a
|
|
22
|
-
repository's workflow file keeps pinning them; upgrading to a newer version only happens through a new
|
|
23
|
-
pull request into that repository (QuikTrack's "re-bootstrap" flow), never silently.
|
|
24
|
-
|
|
25
|
-
Releases follow [SemVer](https://semver.org/): a major version bump means the workflow file's own
|
|
26
|
-
shape changed (inputs, permissions) and requires a new pull request regardless of this package's
|
|
27
|
-
version; minor/patch releases are runner-logic fixes safe to pick up on the next re-bootstrap.
|
|
1
|
+
# @quikit/quikpilot-runner
|
|
2
|
+
|
|
3
|
+
QuikPilot's CI runner. Invoked automatically by the `.github/workflows/quikpilot.yml` file QuikPilot
|
|
4
|
+
installs in an onboarded repository — it fetches the run's work packet, runs your setup/lint/test
|
|
5
|
+
commands, reports results to QuikTest, enforces the denied-path guardrail, commits, pushes, and
|
|
6
|
+
reports back to QuikTrack.
|
|
7
|
+
|
|
8
|
+
This package is not meant to be installed or run by hand. It exists so a fix to QuikPilot's runner
|
|
9
|
+
logic ships as a new package version rather than a new pull request into every onboarded repository
|
|
10
|
+
— the workflow file itself lives in your repository (QuikIT cannot update it directly), so it stays a
|
|
11
|
+
thin, ~25-line launcher that invokes an exact, pinned version of this package via `npx`.
|
|
12
|
+
|
|
13
|
+
It carries no secrets and needs none of your credentials: it authenticates to QuikTrack and to GitHub
|
|
14
|
+
using short-lived tokens exchanged at runtime, never a value baked into the workflow file or this
|
|
15
|
+
package.
|
|
16
|
+
|
|
17
|
+
## Versioning and support
|
|
18
|
+
|
|
19
|
+
Every workflow file pins an **exact** version of this package (`npx --yes
|
|
20
|
+
@quikit/quikpilot-runner@<version>`, never a range or `latest`) — a repository never picks up a new
|
|
21
|
+
release on its own. Older published versions are not removed from npm and keep working as long as a
|
|
22
|
+
repository's workflow file keeps pinning them; upgrading to a newer version only happens through a new
|
|
23
|
+
pull request into that repository (QuikTrack's "re-bootstrap" flow), never silently.
|
|
24
|
+
|
|
25
|
+
Releases follow [SemVer](https://semver.org/): a major version bump means the workflow file's own
|
|
26
|
+
shape changed (inputs, permissions) and requires a new pull request regardless of this package's
|
|
27
|
+
version; minor/patch releases are runner-logic fixes safe to pick up on the next re-bootstrap.
|
package/dist/index.js
CHANGED
|
@@ -73,10 +73,11 @@ async function describeQuikTrackFailure(res) {
|
|
|
73
73
|
|
|
74
74
|
// src/callback.ts
|
|
75
75
|
async function reportBack(params) {
|
|
76
|
-
const { callbackUrl, runToken, status, branch, warnings } = params;
|
|
76
|
+
const { callbackUrl, runToken, status, branch, warnings, targeting } = params;
|
|
77
77
|
const body = { status };
|
|
78
78
|
if (branch) body.branch = branch;
|
|
79
79
|
if (warnings && warnings.length > 0) body.warnings = warnings;
|
|
80
|
+
if (targeting) body.targeting = targeting;
|
|
80
81
|
const res = await fetch(callbackUrl, {
|
|
81
82
|
method: "POST",
|
|
82
83
|
headers: { Authorization: `Bearer ${runToken}`, "Content-Type": "application/json" },
|
|
@@ -202,6 +203,26 @@ function commitAndPush(branch, runId, installationToken, targeted, issueKey, iss
|
|
|
202
203
|
var import_node_fs = require("node:fs");
|
|
203
204
|
var import_node_path = require("node:path");
|
|
204
205
|
|
|
206
|
+
// src/deniedPaths.ts
|
|
207
|
+
function globToRegExp(pattern) {
|
|
208
|
+
const escaped = pattern.split("").map((c) => /[.+^${}()|[\]\\]/.test(c) ? `\\${c}` : c).join("");
|
|
209
|
+
const withGlobs = escaped.replace(/\*\*\//g, " SLASH ").replace(/\*\*/g, " STAR ").replace(/\*/g, "[^/]*").replace(/ SLASH /g, "(?:.*/)?").replace(/ STAR /g, ".*");
|
|
210
|
+
return new RegExp(`^${withGlobs}$`);
|
|
211
|
+
}
|
|
212
|
+
function matchesDeniedPath(path, patterns) {
|
|
213
|
+
for (const pattern of patterns) {
|
|
214
|
+
if (globToRegExp(pattern).test(path)) return pattern;
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
function findDeniedContextPath(contextPaths, patterns) {
|
|
219
|
+
for (const path of contextPaths) {
|
|
220
|
+
const pattern = matchesDeniedPath(path, patterns);
|
|
221
|
+
if (pattern) return { path, pattern };
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
205
226
|
// src/envelope.ts
|
|
206
227
|
var HARD_FAILURE_CODES = /* @__PURE__ */ new Set(["SEMANTIC_MISMATCH", "CONTRACT_REJECTED"]);
|
|
207
228
|
function checkHardFailure(envelope, useCase, target) {
|
|
@@ -256,6 +277,37 @@ var CONTEXT_TOO_LARGE_CODE = "CODEGEN_CONTEXT_TOO_LARGE";
|
|
|
256
277
|
var OUTPUT_TOO_LARGE_CODE = "CODEGEN_OUTPUT_TOO_LARGE";
|
|
257
278
|
var NOT_RETRIED_CODES = /* @__PURE__ */ new Set([FILE_TOO_LARGE_CODE, CONTEXT_TOO_LARGE_CODE, OUTPUT_TOO_LARGE_CODE]);
|
|
258
279
|
var JUDGED_BY_TOO_LARGE_CODE = "CODEGEN_JUDGED_BY_TOO_LARGE";
|
|
280
|
+
var MAX_ADDED_FILE_CHARS = 4e4;
|
|
281
|
+
var MAX_ADDED_CONTEXT_CHARS = 6e4;
|
|
282
|
+
function collectRunContext(target, targets, alreadyNamed, deniedPaths, readFile = (p) => (0, import_node_fs.readFileSync)(p, "utf8")) {
|
|
283
|
+
const added = { entries: [], skipped: [] };
|
|
284
|
+
if (!target.automationIds?.length) return added;
|
|
285
|
+
const seen = new Set(alreadyNamed);
|
|
286
|
+
seen.add(target.path);
|
|
287
|
+
let total = 0;
|
|
288
|
+
for (const candidate of targets) {
|
|
289
|
+
if (candidate.kind !== "EXISTING" || candidate.automationIds?.length) continue;
|
|
290
|
+
if (seen.has(candidate.path)) continue;
|
|
291
|
+
seen.add(candidate.path);
|
|
292
|
+
if (matchesDeniedPath(candidate.path, deniedPaths)) continue;
|
|
293
|
+
let content;
|
|
294
|
+
try {
|
|
295
|
+
content = readFile(candidate.path);
|
|
296
|
+
} catch {
|
|
297
|
+
added.skipped.push({ path: candidate.path, reason: "unreadable" });
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (content.length > MAX_ADDED_FILE_CHARS) {
|
|
301
|
+
added.skipped.push({ path: candidate.path, reason: "too large" });
|
|
302
|
+
} else if (total + content.length > MAX_ADDED_CONTEXT_CHARS) {
|
|
303
|
+
added.skipped.push({ path: candidate.path, reason: "over budget" });
|
|
304
|
+
} else {
|
|
305
|
+
total += content.length;
|
|
306
|
+
added.entries.push({ path: candidate.path, content });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return added;
|
|
310
|
+
}
|
|
259
311
|
var SUSPECT_PLACEHOLDER_RE = /rest(?: of the)? (?:file|code) unchanged|remainder unchanged|\/\/\s*\.\.\.\s*(?:rest|remaining)/i;
|
|
260
312
|
var SUSPECT_LENGTH_DROP_RATIO = 0.6;
|
|
261
313
|
var SUSPECT_REMOVED_LINE_RATIO = 0.3;
|
|
@@ -340,13 +392,19 @@ function narrowJudgedByForTarget(pool, targets, codeTargetPath) {
|
|
|
340
392
|
function toWireJudgedBy(pool) {
|
|
341
393
|
return pool.map(({ automationId: _automationId, ...rest }) => rest);
|
|
342
394
|
}
|
|
343
|
-
async function runCodegen(gateway, targets, workspaceListing, requiredTests, deniedPaths, judgedByPool, warnings) {
|
|
395
|
+
async function runCodegen(gateway, targets, workspaceListing, requiredTests, deniedPaths, judgedByPool, warnings, contextLog) {
|
|
344
396
|
const results = [];
|
|
345
397
|
const listing = new Set(workspaceListing);
|
|
346
398
|
for (const target of targets) {
|
|
347
399
|
const isNewFile = target.kind === "NEW_FILE";
|
|
348
400
|
const currentContent = isNewFile ? "" : (0, import_node_fs.readFileSync)(target.path, "utf8");
|
|
349
401
|
const contextPaths = target.contextPaths ?? [];
|
|
402
|
+
const deniedContext = findDeniedContextPath(contextPaths, deniedPaths);
|
|
403
|
+
if (deniedContext) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`pilot.codegen contextPaths cited a denied path (${deniedContext.pattern}): ${deniedContext.path} (target ${target.path})`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
350
408
|
for (const contextPath of contextPaths) {
|
|
351
409
|
if (!listing.has(contextPath)) {
|
|
352
410
|
throw new Error(
|
|
@@ -354,7 +412,20 @@ async function runCodegen(gateway, targets, workspaceListing, requiredTests, den
|
|
|
354
412
|
);
|
|
355
413
|
}
|
|
356
414
|
}
|
|
357
|
-
const
|
|
415
|
+
const namedContents = contextPaths.map((path) => ({ path, content: (0, import_node_fs.readFileSync)(path, "utf8") }));
|
|
416
|
+
const added = collectRunContext(target, targets, new Set(contextPaths), deniedPaths);
|
|
417
|
+
const contextContents = [...namedContents, ...added.entries];
|
|
418
|
+
for (const s of added.skipped) {
|
|
419
|
+
warnings.push(`not shown to the test for ${target.path}: ${s.path} (${s.reason})`);
|
|
420
|
+
}
|
|
421
|
+
const contextRecord = {
|
|
422
|
+
targetPath: target.path,
|
|
423
|
+
contextPaths,
|
|
424
|
+
addedPaths: added.entries.map((e) => e.path),
|
|
425
|
+
skipped: added.skipped
|
|
426
|
+
};
|
|
427
|
+
console.log(JSON.stringify({ event: "codegen.context", ...contextRecord }));
|
|
428
|
+
contextLog?.push(contextRecord);
|
|
358
429
|
const contextData = {
|
|
359
430
|
targetPath: target.path,
|
|
360
431
|
currentContent,
|
|
@@ -409,6 +480,18 @@ async function runCodegen(gateway, targets, workspaceListing, requiredTests, den
|
|
|
409
480
|
envelope = await res.json();
|
|
410
481
|
checkHardFailure(envelope, "pilot.codegen", target.path);
|
|
411
482
|
}
|
|
483
|
+
if (envelope?.errorCode === CONTEXT_TOO_LARGE_CODE && added.entries.length > 0) {
|
|
484
|
+
contextData.contextContents = namedContents;
|
|
485
|
+
warnings.push(`context for ${target.path} was too large with the run's other files; retried without them`);
|
|
486
|
+
contextRecord.addedPaths = [];
|
|
487
|
+
contextRecord.retriedWithoutAdded = true;
|
|
488
|
+
res = await callCodegen();
|
|
489
|
+
if (!res.ok) {
|
|
490
|
+
throw new Error(`Codegen call failed for ${target.path}: ${res.status} ${res.statusText}`);
|
|
491
|
+
}
|
|
492
|
+
envelope = await res.json();
|
|
493
|
+
checkHardFailure(envelope, "pilot.codegen", target.path);
|
|
494
|
+
}
|
|
412
495
|
if (typeof envelope?.errorCode === "string" && NOT_RETRIED_CODES.has(envelope.errorCode)) {
|
|
413
496
|
const detail = envelope.error ? `: ${envelope.error}` : "";
|
|
414
497
|
throw new Error(`Codegen refused ${target.path}: ${envelope.errorCode} (not retried)${detail}`);
|
|
@@ -1121,21 +1204,6 @@ function describeScopeViolations(check) {
|
|
|
1121
1204
|
|
|
1122
1205
|
// src/targeting.ts
|
|
1123
1206
|
var import_node_path4 = require("node:path");
|
|
1124
|
-
|
|
1125
|
-
// src/deniedPaths.ts
|
|
1126
|
-
function globToRegExp(pattern) {
|
|
1127
|
-
const escaped = pattern.split("").map((c) => /[.+^${}()|[\]\\]/.test(c) ? `\\${c}` : c).join("");
|
|
1128
|
-
const withGlobs = escaped.replace(/\*\*\//g, " SLASH ").replace(/\*\*/g, " STAR ").replace(/\*/g, "[^/]*").replace(/ SLASH /g, "(?:.*/)?").replace(/ STAR /g, ".*");
|
|
1129
|
-
return new RegExp(`^${withGlobs}$`);
|
|
1130
|
-
}
|
|
1131
|
-
function matchesDeniedPath(path, patterns) {
|
|
1132
|
-
for (const pattern of patterns) {
|
|
1133
|
-
if (globToRegExp(pattern).test(path)) return pattern;
|
|
1134
|
-
}
|
|
1135
|
-
return null;
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
// src/targeting.ts
|
|
1139
1207
|
var CONTRACT_VERSION2 = "test-targets-1";
|
|
1140
1208
|
function listWorkspaceFiles(workspaceRoot) {
|
|
1141
1209
|
return listFiles(workspaceRoot ?? ".");
|
|
@@ -1147,6 +1215,12 @@ function validateTarget(target, workspaceRoot, workspaceListing, deniedPaths) {
|
|
|
1147
1215
|
`pilot.targeting proposed a target matching a denied path (${denied}): ${target.path}`
|
|
1148
1216
|
);
|
|
1149
1217
|
}
|
|
1218
|
+
const deniedContext = findDeniedContextPath(target.contextPaths ?? [], deniedPaths);
|
|
1219
|
+
if (deniedContext) {
|
|
1220
|
+
throw new Error(
|
|
1221
|
+
`pilot.targeting cited a denied path as context (${deniedContext.pattern}): ${deniedContext.path} (target ${target.path})`
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1150
1224
|
if (target.kind !== "EXISTING" && target.kind !== "NEW_FILE") {
|
|
1151
1225
|
throw new Error(`pilot.targeting returned an unknown kind "${target.kind}" for ${target.path}`);
|
|
1152
1226
|
}
|
|
@@ -1216,6 +1290,108 @@ async function runTargeting(gateway, design, workspaceRoot, workspaceListing, re
|
|
|
1216
1290
|
};
|
|
1217
1291
|
}
|
|
1218
1292
|
|
|
1293
|
+
// src/targetingRecord.ts
|
|
1294
|
+
var MAX_TARGETS = 25;
|
|
1295
|
+
var MAX_IDS_PER_TARGET = 50;
|
|
1296
|
+
var MAX_CONTEXT_PATHS = 20;
|
|
1297
|
+
var MAX_ADDED_PATHS = 20;
|
|
1298
|
+
var MAX_SKIPPED_CONTEXT = 20;
|
|
1299
|
+
var MAX_SKIPPED_CASES = 200;
|
|
1300
|
+
var MAX_CONTEXT_RECORDS = 25;
|
|
1301
|
+
var MAX_PATH_CHARS = 300;
|
|
1302
|
+
var MAX_ID_CHARS = 100;
|
|
1303
|
+
var MAX_REASON_CHARS = 300;
|
|
1304
|
+
function clampList(values, max, maxChars) {
|
|
1305
|
+
let cut = false;
|
|
1306
|
+
const kept = [];
|
|
1307
|
+
for (const raw of values ?? []) {
|
|
1308
|
+
const v = typeof raw === "string" ? raw.trim() : "";
|
|
1309
|
+
if (v.length === 0) continue;
|
|
1310
|
+
if (v.length > maxChars || kept.length >= max) {
|
|
1311
|
+
cut = true;
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
kept.push(v);
|
|
1315
|
+
}
|
|
1316
|
+
return { kept, cut };
|
|
1317
|
+
}
|
|
1318
|
+
function buildTargetingRecord(targeting, contextLog) {
|
|
1319
|
+
let truncated = false;
|
|
1320
|
+
const note = (cut) => {
|
|
1321
|
+
if (cut) truncated = true;
|
|
1322
|
+
};
|
|
1323
|
+
if (targeting.outcome === "NO_TARGET") {
|
|
1324
|
+
const record2 = { outcome: "NO_TARGET", targets: [], skippedCaseIds: [], codegenContext: [] };
|
|
1325
|
+
const reason = (targeting.reason ?? "").trim();
|
|
1326
|
+
if (reason.length > 0) {
|
|
1327
|
+
record2.noTargetReason = reason.slice(0, MAX_REASON_CHARS);
|
|
1328
|
+
if (reason.length > MAX_REASON_CHARS) record2.noTargetReasonTruncated = true;
|
|
1329
|
+
}
|
|
1330
|
+
return record2;
|
|
1331
|
+
}
|
|
1332
|
+
const targets = [];
|
|
1333
|
+
for (const t of targeting.targets) {
|
|
1334
|
+
if (targets.length >= MAX_TARGETS) {
|
|
1335
|
+
truncated = true;
|
|
1336
|
+
break;
|
|
1337
|
+
}
|
|
1338
|
+
const path = typeof t.path === "string" ? t.path.trim() : "";
|
|
1339
|
+
if (path.length === 0 || path.length > MAX_PATH_CHARS) {
|
|
1340
|
+
truncated = true;
|
|
1341
|
+
continue;
|
|
1342
|
+
}
|
|
1343
|
+
const contextPaths = clampList(t.contextPaths, MAX_CONTEXT_PATHS, MAX_PATH_CHARS);
|
|
1344
|
+
const automationIds = clampList(t.automationIds, MAX_IDS_PER_TARGET, MAX_ID_CHARS);
|
|
1345
|
+
note(contextPaths.cut);
|
|
1346
|
+
note(automationIds.cut);
|
|
1347
|
+
targets.push({ path, kind: t.kind, contextPaths: contextPaths.kept, automationIds: automationIds.kept });
|
|
1348
|
+
}
|
|
1349
|
+
const skipped = clampList(
|
|
1350
|
+
(targeting.skippedCases ?? []).map((c) => c.automationId),
|
|
1351
|
+
MAX_SKIPPED_CASES,
|
|
1352
|
+
MAX_ID_CHARS
|
|
1353
|
+
);
|
|
1354
|
+
note(skipped.cut);
|
|
1355
|
+
const codegenContext = [];
|
|
1356
|
+
for (const entry of contextLog) {
|
|
1357
|
+
if (codegenContext.length >= MAX_CONTEXT_RECORDS) {
|
|
1358
|
+
truncated = true;
|
|
1359
|
+
break;
|
|
1360
|
+
}
|
|
1361
|
+
const targetPath = typeof entry.targetPath === "string" ? entry.targetPath.trim() : "";
|
|
1362
|
+
if (targetPath.length === 0 || targetPath.length > MAX_PATH_CHARS) {
|
|
1363
|
+
truncated = true;
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
const contextPaths = clampList(entry.contextPaths, MAX_CONTEXT_PATHS, MAX_PATH_CHARS);
|
|
1367
|
+
const addedPaths = clampList(entry.addedPaths, MAX_ADDED_PATHS, MAX_PATH_CHARS);
|
|
1368
|
+
const skippedPaths = clampList(
|
|
1369
|
+
(entry.skipped ?? []).map((s) => s.path),
|
|
1370
|
+
MAX_SKIPPED_CONTEXT,
|
|
1371
|
+
MAX_PATH_CHARS
|
|
1372
|
+
);
|
|
1373
|
+
note(contextPaths.cut);
|
|
1374
|
+
note(addedPaths.cut);
|
|
1375
|
+
note(skippedPaths.cut);
|
|
1376
|
+
const reasonByPath = new Map((entry.skipped ?? []).map((s) => [s.path.trim(), s.reason]));
|
|
1377
|
+
codegenContext.push({
|
|
1378
|
+
targetPath,
|
|
1379
|
+
contextPaths: contextPaths.kept,
|
|
1380
|
+
addedPaths: addedPaths.kept,
|
|
1381
|
+
skipped: skippedPaths.kept.map((path) => ({ path, reason: String(reasonByPath.get(path) ?? "skipped").slice(0, 40) })),
|
|
1382
|
+
...entry.retriedWithoutAdded ? { retriedWithoutAdded: true } : {}
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
const record = {
|
|
1386
|
+
outcome: "TARGETED",
|
|
1387
|
+
targets,
|
|
1388
|
+
skippedCaseIds: skipped.kept,
|
|
1389
|
+
codegenContext
|
|
1390
|
+
};
|
|
1391
|
+
if (truncated) record.truncated = true;
|
|
1392
|
+
return record;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1219
1395
|
// src/testPlacementCheck.ts
|
|
1220
1396
|
function isDiscovered(targetPath, discoveredFiles) {
|
|
1221
1397
|
const normalized = targetPath.replace(/\\/g, "/");
|
|
@@ -1282,6 +1458,8 @@ async function run(env2) {
|
|
|
1282
1458
|
let warnings = [];
|
|
1283
1459
|
let targeted = false;
|
|
1284
1460
|
let generatedFilePaths = [];
|
|
1461
|
+
let targetingResult;
|
|
1462
|
+
const contextLog = [];
|
|
1285
1463
|
try {
|
|
1286
1464
|
runToken = await exchangeForRunToken(env2.tokenUrl, await fetchGithubOidcToken());
|
|
1287
1465
|
packet = await fetchPacket(env2.packetUrl, runToken, env2.githubRunId);
|
|
@@ -1296,6 +1474,7 @@ async function run(env2) {
|
|
|
1296
1474
|
packet.guardrails.deniedPaths,
|
|
1297
1475
|
packet.testDiscovery
|
|
1298
1476
|
);
|
|
1477
|
+
targetingResult = targeting;
|
|
1299
1478
|
if (targeting.outcome === "NO_TARGET") {
|
|
1300
1479
|
writePhase0Marker(env2.runId);
|
|
1301
1480
|
runCommand(packet.commands.setup, "Setup");
|
|
@@ -1339,7 +1518,8 @@ async function run(env2) {
|
|
|
1339
1518
|
packet.requiredTests,
|
|
1340
1519
|
packet.guardrails.deniedPaths,
|
|
1341
1520
|
judgedByPool,
|
|
1342
|
-
warnings
|
|
1521
|
+
warnings,
|
|
1522
|
+
contextLog
|
|
1343
1523
|
)
|
|
1344
1524
|
]);
|
|
1345
1525
|
const depCheck = checkImportDependencies(
|
|
@@ -1433,7 +1613,9 @@ async function run(env2) {
|
|
|
1433
1613
|
runToken,
|
|
1434
1614
|
status: hardFailure ? "failed" : "completed",
|
|
1435
1615
|
branch,
|
|
1436
|
-
warnings
|
|
1616
|
+
warnings,
|
|
1617
|
+
// Omitted when targeting itself never produced a result (nothing exists to record).
|
|
1618
|
+
targeting: targetingResult ? buildTargetingRecord(targetingResult, contextLog) : void 0
|
|
1437
1619
|
});
|
|
1438
1620
|
return { hardFailure };
|
|
1439
1621
|
}
|