diff --git a/.github/workflows/drift-comment.yaml b/.github/workflows/drift-comment.yaml
index aadf1d35..df081558 100644
--- a/.github/workflows/drift-comment.yaml
+++ b/.github/workflows/drift-comment.yaml
@@ -138,16 +138,45 @@ jobs:
return edits.size > 0 ? edits : null;
};
- // Last ~30 non-empty lines of a failed job's log.
- const logTail = async (jobId) => {
+ // Extract the failure-relevant slice of a job log for display. A Go
+ // test failure (and its want/got rows) prints in the MIDDLE of the log;
+ // the trailing lines are post-job git-config and credential cleanup that
+ // end at "Cleaning up orphan processes". Anchoring on the first failure
+ // signature and windowing forward keeps the region on the failing test
+ // output instead of that cleanup tail. Falls back to the trailing lines
+ // when no signature is present (a non-test failure).
+ const extractFailRegion = (text) => {
+ const lines = String(text || '').split('\n');
+ const signatures = [
+ '--- FAIL',
+ 'governed uses: refs diverge from internal/generate/action_pins.yaml',
+ 'dependabot anchor refs diverge from action_pins.yaml',
+ '\tFAIL\t',
+ ];
+ let anchor = -1;
+ for (let i = 0; i < lines.length; i += 1) {
+ if (signatures.some((s) => lines[i].includes(s))) { anchor = i; break; }
+ }
+ const nonEmpty = (arr) => arr.filter((l) => l.trim().length > 0);
+ if (anchor < 0) {
+ return nonEmpty(lines).slice(-30).join('\n');
+ }
+ const start = Math.max(0, anchor - 5);
+ return nonEmpty(lines.slice(start)).slice(0, 60).join('\n');
+ };
+
+ // Download a failed job's log once. Returns the full decoded text (for
+ // signature parsing, never rendered) plus a bounded, failure-anchored
+ // region for display.
+ const logFor = async (jobId) => {
try {
const res = await github.rest.actions.downloadJobLogsForWorkflowRun({
owner, repo, job_id: jobId,
});
- const lines = String(res.data || '').split('\n').filter((l) => l.trim().length > 0);
- return lines.slice(-30).join('\n');
+ const full = String(res.data || '');
+ return { full, region: extractFailRegion(full) };
} catch (e) {
- return '';
+ return { full: '', region: '' };
}
};
@@ -242,17 +271,20 @@ jobs:
const failingStep = (job.steps || []).find((s) => s.conclusion === 'failure');
const annText = await annotationsFor(job.id);
- const tail = await logTail(job.id);
- const scan = `${annText}\n${tail}`;
- pinEdits = pinEdits || parsePinGuard(scan);
+ const jobLog = await logFor(job.id);
+ // Parse the pin guard against the FULL log, not the display region:
+ // the guard signature and its want/got rows print in the middle of
+ // the log, so a trailing slice would miss them and drop the manifest
+ // remediation entirely.
+ pinEdits = pinEdits || parsePinGuard(`${annText}\n${jobLog.full}`);
const parts = [`### Job: ${job.name}`];
if (failingStep) { parts.push('', `Failing step: ${failingStep.name}`); }
if (annText) {
parts.push('', 'Annotations:', '', fence(annText));
}
- if (tail) {
- parts.push('', 'log tail
', '', fence(tail), '', ' ');
+ if (jobLog.region) {
+ parts.push('', 'log tail
', '', fence(jobLog.region), '', ' ');
}
if (job.html_url) { parts.push('', `[Full job log](${job.html_url})`); }
sections.push(parts.join('\n'));