Skip to content

fix(release-script): guard the list that stages the showcase, and put back what the post-release pass rewrote - #703

Merged
DemchaAV merged 1 commit into
developfrom
fix/release-staging-guard
Sep 21, 2026
Merged

DemchaAV merged 1 commit into
developfrom
fix/release-staging-guard

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

Cutting v2.4.1 exposed two holes around web/showcase, and the second one is what
made the first survive a green CI.

The release commit lost every showcase file. Step 6 passed one git add a list
that mixed the generated-page globs with the showcase assets. Those globs carry
:(exclude)web/showcase, so a file planted under the showcase cannot pass for a card
page — and git applies an exclude to the whole invocation, not to the pathspecs
standing beside it. Measured on the tree at the time: the script's own list staged
0 paths under web/showcase, the same list without the exclusion staged 133.
Release v2.4.1 therefore carried none of the files its own Step 4c had just
rewritten, and git status --short after the cut showed them as churn. Caught before
the tag was pushed; the commit was rebuilt by hand and the tag moved onto it.

The check that owns those pathspecs could not see it. release-script-check.yml
lifts Get-GeneratedPagePathspecs by AST and dry-runs its result alone, where the
exclusion behaves exactly as written. It tests the function, never the list the
function's result is merged into.

-PostReleaseOnly leaves the tree dirty every release. Its Step 2 regenerates the
catalogue through Sync-ShowcaseSite, which re-mirrors examples/target/generated-pdfs
over web/showcase — and its commit list deliberately omits those previews, because
they belong to the tag. Nothing restored them, so the documented last step of a release
ends with ~125 modified files in the tree: the exact signal the runbook teaches you to
read as a bumped-but-unstaged file.

What changed

  • Get-ReleaseCommitPathspecs — the release commit's staging list, lifted out of Step 6
    so a guard can run the real thing rather than a transcription. Same 47 paths, same
    order.
  • A new check in release-script-check.yml lifts that function and runs it: it fails
    on any :(exclude) in the list, on a list that stops naming web/showcase, and on a
    git add --dry-run that leaves a rewritten showcase file out.
  • Restore-CommittedShowcase — the post-release pass puts the previews back. Tracked
    files only, so a preview for an example added since the tag survives untouched.
  • The post-release add is split the same way as the release one. Its list holds nothing
    under the showcase today, so the exclusion cancels nothing there — but a path added
    later would be dropped in silence, which is the defect above.
  • ReleaseAssetStepGuardTest compared the source offset of the knowledge staging
    literal against Step 5c. That literal now sits in the extracted function, above the
    flow, so the comparison moved to the call that builds the list — where the staging
    happens.

Verification

  • The new guard fails closed, proven on three sabotages rather than asserted:

    sabotage caught by
    :(exclude)web/showcase back in the list the exclude pattern
    the entry narrowed to web/showcase/pdf the name check
    :(exclude,glob)web/showcase/** — a form the pattern does not match the git add --dry-run arm

    Each was reverted after the run; the guard passes on the fixed script.

  • ./mvnw test -pl :graph-compose-core -Dtest='ReleaseAssetStepGuardTest,ReleaseKnowledgeGateGuardTest,ReleaseScriptGitExitCodeGuardTest,ReleaseScriptInstallListGuardTest,ReleaseScriptRoadmapGuardTest,BinaryCompatibilityGateGuardTest,VersionConsistencyGuardTest,CiGuardListGuardTest' — 53 tests, 0 failures.

  • cut-release.ps1 -Version 2.4.2 -DryRun -SkipVerify prints the two adds separately, the
    first ending in web/showcase, and leaves the tree untouched.

  • cut-release.ps1 -PostReleaseOnly -DryRun reaches Restore-CommittedShowcase and
    reports what it would put back.

Scope

Release tooling only. Nothing here is compiled into a published module.

The restore was measured before it was written, across all 125 files the v2.4.1
post-release pass had rewritten — not a sample: the 117 PDFs differ from the tagged
renders only in the two /ID values of the trailer, and the 8 PPTX are archives whose
every entry matches on CRC32 and size, differing only in packaging timestamps.

… back what the post-release pass rewrote

Two holes around web/showcase, both surfaced by cutting v2.4.1.

The release commit's staging list is now built by Get-ReleaseCommitPathspecs
rather than inline, and release-script-check.yml lifts it by AST and runs it for
real: a list carrying any :(exclude), one that stops naming web/showcase, or one
whose `git add --dry-run` leaves a rewritten showcase file out now fails the
check. The step that already owned these pathspecs runs Get-GeneratedPagePathspecs
on its own, where the exclusion is correct - which is why it stayed green while
the release commit was losing 133 files. Proven against three sabotages: the
exclusion put back as :(exclude)web/showcase, caught by the pattern; the entry
narrowed to web/showcase/pdf, caught by the name; and the exclusion written
:(exclude,glob)web/showcase/**, which the pattern does not match and the dry run
catches.

-PostReleaseOnly rebuilt every preview through Sync-ShowcaseSite and committed
none of them - those belong to the tag - but nothing put them back, so the
documented last step of every release left ~125 modified files behind, which the
runbook reads as the signature of a bumped-but-unstaged file. Restore-CommittedShowcase
restores them, tracked files only, so a preview for an example added since the tag
survives.

The post-release `add` is split the same way as the release one. Nothing in its
list stands under the showcase today, so its exclusion cancels nothing - but a
path added there later would be dropped in silence, which is the defect above.

ReleaseAssetStepGuardTest compared the source offset of the `knowledge` staging
literal against Step 5c. That literal now sits in the extracted function, above the
flow, so the comparison is against the call that builds the list - where the
staging actually happens.
@DemchaAV
DemchaAV merged commit ee43637 into develop Sep 21, 2026
14 checks passed
@DemchaAV
DemchaAV deleted the fix/release-staging-guard branch September 21, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant