test(release): ignore shell comments when matching workflow commands

Matching a step's whole run body meant a commented-out command satisfied
the ordering assertion, so commenting out the prerelease notes check left
the test green while the workflow no longer validated anything.

Match executable lines only, and fold the version argument into the same
match so dropping it also fails.
This commit is contained in:
sudacode
2026-08-23 03:57:21 -07:00
parent 615a785703
commit 94643ffceb
2 changed files with 18 additions and 6 deletions
+5 -6
View File
@@ -5,6 +5,7 @@ import { resolve } from 'node:path';
import { import {
jobSteps, jobSteps,
readWorkflow, readWorkflow,
stepRunsCommand,
stepsMissingEnvDeclaration, stepsMissingEnvDeclaration,
templateExpressionsInRunBodies, templateExpressionsInRunBodies,
} from './workflow-test-helpers'; } from './workflow-test-helpers';
@@ -136,20 +137,18 @@ test('prerelease workflow rejects committed notes generated for a different beta
'bun run scripts/build-changelog.ts check-prerelease-notes', 'bun run scripts/build-changelog.ts check-prerelease-notes',
); );
// Matched against executable lines only, so commenting the check out fails the
// test rather than silently satisfying it.
const steps = jobSteps(parsedPrereleaseWorkflow, 'release'); const steps = jobSteps(parsedPrereleaseWorkflow, 'release');
const checkIndex = steps.findIndex((step) => const checkIndex = steps.findIndex((step) =>
step.run?.includes('changelog:check-prerelease-notes'), stepRunsCommand(step, /changelog:check-prerelease-notes --version "\$RELEASE_VERSION"/),
); );
const publishIndex = steps.findIndex((step) => /gh release (create|edit)/.test(step.run ?? '')); const publishIndex = steps.findIndex((step) => stepRunsCommand(step, /gh release (create|edit)/));
assert.notEqual(checkIndex, -1); assert.notEqual(checkIndex, -1);
assert.notEqual(publishIndex, -1); assert.notEqual(publishIndex, -1);
// Stale notes are already published if the check runs after the release. // Stale notes are already published if the check runs after the release.
assert.ok(checkIndex < publishIndex); assert.ok(checkIndex < publishIndex);
assert.match(
steps[checkIndex]!.run!,
/changelog:check-prerelease-notes --version "\$RELEASE_VERSION"/,
);
}); });
test('prerelease workflow keeps tag-derived values out of shell bodies', () => { test('prerelease workflow keeps tag-derived values out of shell bodies', () => {
+13
View File
@@ -42,6 +42,19 @@ function allSteps(workflow: ParsedWorkflow): Array<{ job: string; step: Workflow
); );
} }
// Lines of a step's shell body that actually execute. Comments are dropped so a
// commented-out command cannot satisfy a "this step runs X" assertion.
export function executableRunLines(step: WorkflowStep): string[] {
return (typeof step.run === 'string' ? step.run.split('\n') : [])
.map((line) => line.trim())
.filter((line) => line.length > 0 && !line.startsWith('#'));
}
// Whether a step actually executes a command matching the pattern.
export function stepRunsCommand(step: WorkflowStep, pattern: RegExp): boolean {
return executableRunLines(step).some((line) => pattern.test(line));
}
// GitHub substitutes ${{ }} into a run script before the shell parses it, so any // GitHub substitutes ${{ }} into a run script before the shell parses it, so any
// value used that way is executed as script rather than read as data. Reporting // value used that way is executed as script rather than read as data. Reporting
// every expression (rather than allow-listing known-safe ones) also covers // every expression (rather than allow-listing known-safe ones) also covers