diff --git a/src/prerelease-workflow.test.ts b/src/prerelease-workflow.test.ts index 371be2c2..ce162f11 100644 --- a/src/prerelease-workflow.test.ts +++ b/src/prerelease-workflow.test.ts @@ -5,6 +5,7 @@ import { resolve } from 'node:path'; import { jobSteps, readWorkflow, + stepRunsCommand, stepsMissingEnvDeclaration, templateExpressionsInRunBodies, } 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', ); + // Matched against executable lines only, so commenting the check out fails the + // test rather than silently satisfying it. const steps = jobSteps(parsedPrereleaseWorkflow, 'release'); 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(publishIndex, -1); // Stale notes are already published if the check runs after the release. 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', () => { diff --git a/src/workflow-test-helpers.ts b/src/workflow-test-helpers.ts index 6771988c..7d93b196 100644 --- a/src/workflow-test-helpers.ts +++ b/src/workflow-test-helpers.ts @@ -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 // 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