build(release): publish the showcase the cut produced, not the one lying in target - #477
Merged
Conversation
…ing in target cut-release.ps1 never ran GenerateAllExamples. Run-ShowcaseSync installed the train siblings, recompiled the examples so ShowcaseMetadata.GH_BASE pointed at the tag, and then synced whatever happened to be sitting in examples/target/generated-pdfs from earlier commands — on a clean checkout, nothing, and the cut aborts mid-flight. Step 4 now regenerates the catalogue first, with clean, so the published site is a function of the tag rather than of the maintainer's shell history. It runs after the pom bump and the GH_BASE rewrite, so version-stamped renders carry the new version. ShowcaseSync copied but never removed, so an example that was renamed or deleted kept its artifact live on GitHub Pages indefinitely — reachable by URL, absent from the manifest, rendered from source that no longer exists. It now clears the three output trees first. Three documents went with it: invoice-custom-theme, whose example was removed, and invoice.pdf / proposal.pdf, which no example has written since June. Only files are deleted, not directories: git tracks no empty directory, the copy recreates what it needs, and removing them is what makes this fail on Windows with AccessDeniedException. Eight generated artifacts had no ShowcaseMetadata entry, so lookup fell back to a filename-derived title, a generic description and a link to the examples root — a placeholder card. Two of them were the flagships 2.1.x exists to show, social-card and linkedin-carousel. The manifest now carries 90 cards and no placeholders. EngineDeckV2Example, which renders the deck the README banner is cut from, was never in GenerateAllExamples despite being linked from the README and covered by a snapshot test; registering it made it the eighth. invoice() and proposal() hardcoded their producing example, so the two v2 entries could not name theirs. Both now take the class like cv(), letter(), feature() and flagship() already do. Five committed previews were materially stale. pdf-chrome.pdf is the visible one: the whole body is drawn at low alpha with the DRAFT watermark bleeding through every panel, where a fresh render is solid. Staleness was decided by rendering both copies and comparing pixels — the five differ by 11.9 to 36.4 mean channel delta, the other twenty-one by less than 0.16, which is the date stamp in the corner and nothing else. Eleven committed binaries totalling 9.01 MiB are referenced by no file in the repository — assets/Overview.png alone is 5.8 MiB. The .gitignore allowlist still whitelisted docs/assets, docs/showcase and site/, three trees with no tracked file between them.
…assert The banner deck never reached the site. GenerateAllExamples called only MavenBannerPptxExample.generate(), which writes flagships/maven-banner.pptx, while the class also exposes generatePdf(). ShowcaseSync walks .pdf files, keys one card per PDF, and copies a .pptx only as that PDF's same-named companion — so a deck with no PDF twin lands in target and stops there, and the metadata entry written for it goes unused. The banner appeared in web/examples.json zero times. Generating the PDF first puts it on the site: the sync now reports 91 PDFs and 8 PPTX twins where it reported 90 and 7. everyGeneratedDeckHasAPdfTwin states that invariant so the next deck cannot repeat it. The smoke test around it asserted nothing — its own comment said "at least one PDF should have been written" above assertThat(true).isTrue(). It now reads the output tree: PDFs exist, every deck has its twin, and the catalogue spans templates, features and flagships rather than one corner of it. ShowcaseSync collected the PDF list after clearing the published trees, so a generation run that succeeded while writing nothing would have deleted the whole site and written an empty manifest, with the failure visible only once the site was live. The list is now collected first and an empty one aborts before anything is removed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
cut-release.ps1never runsGenerateAllExamples.Run-ShowcaseSyncinstalls the train siblings, recompiles the examples soShowcaseMetadata.GH_BASEpoints at the tag, and then runsShowcaseSync— which only copies what is already inexamples/target/generated-pdfs. So the published site is whatever happened to be in the maintainer's target directory from earlier commands. On a clean checkout there is nothing there, and the cut aborts at Step 4 with the tree already version-bumped.ShowcaseSyncalso copies but never deletes. An example that was renamed or removed keeps its artifact live on GitHub Pages indefinitely — reachable by URL, absent from the manifest, rendered from source that no longer exists. Three such documents are published today:invoice-custom-theme(whose example was removed), andinvoice.pdf/proposal.pdf, which no example has written since June — verified by grepping for the exact output names and by their absence from a fresh generation.And eight generated artifacts have no
ShowcaseMetadataentry, solookup(ShowcaseMetadata.java:145) falls back to a filename-derived title, the description "Generated showcase for flagships / default.", and a link to the examples root instead of the example. Two of them aresocial-cardandlinkedin-carousel— the flagships the 2.1 line exists to show. They would have shipped on the 2.1.1 site as placeholder cards.What changed
Regeneration before sync.
Run-ShowcaseSyncgains aclean compile exec:java GenerateAllExamplesahead of the sync.cleanis the point: it drops artifacts left by tests or by examples since renamed, so the published tree is a function of the tag rather than of shell history. It sits after the pom bump and theGH_BASErewrite, so version-stamped renders carry the new version — the ordering ruleRender-ReadmeBanneralready documents.ShowcaseSyncclears before it copies — but collects first. The directory existing is not evidence anything was produced, and clearing before listing meant a generation run that succeeded while writing nothing would delete the whole published site and write an empty manifest, with the failure visible only once the site was live. The list is gathered first and an empty one aborts before anything is removed. The clear itself removes only regular files, not directories: git tracks no empty directory, the copy recreates what it needs, and deleting directories is what makes this fail on Windows — the first version died withAccessDeniedExceptiononweb/showcase/pdf/templates/schedule.The smoke test asserts.
GenerateAllExamplesSmokeTestended inassertThat(true).isTrue()under a comment claiming it checked that at least one PDF was written. It now reads the output tree: PDFs exist, every deck has its twin, and the catalogue spanstemplates,featuresandflagships.A PDF for the Maven banner.
GenerateAllExamplescalled onlyMavenBannerPptxExample.generate(), which writesflagships/maven-banner.pptx, while the class also exposesgeneratePdf().ShowcaseSyncwalks.pdf, keys one card per PDF, and copies a.pptxonly as that PDF's same-named companion — so a deck without a PDF twin lands intargetand stops there, and the metadata entry written for it is never read. The banner appeared inweb/examples.jsonzero times.everyGeneratedDeckHasAPdfTwinnow states that invariant, so the next deck cannot repeat it.Nine metadata entries, including
engine-deck-v2.EngineDeckV2Examplerenders the deck the README banner is cut from and has its own snapshot test, but was never wired intoGenerateAllExamples— registering it made it the eighth artifact needing an entry.invoice()andproposal()hardcoded their producing example, so the two*-modern-v2entries could not name theirs; both helpers now take the class, likecv(),letter(),feature()andflagship()already do.Five committed previews re-rendered, and eleven unreferenced binaries removed (9.01 MiB;
assets/Overview.pngalone is 5.8 MiB). The.gitignoreallowlist still whitelisteddocs/assets,docs/showcaseandsite/— three trees with no tracked file between them.Verification
Reactor gate
BUILD SUCCESS, 13/13 modules, 2:04, zero errors. Examples module 49 tests green.cut-release.ps1parses clean (PowerShell AST, 4549 tokens); thecut-release.ps1 dry-runCI check covers the dry-run branch.GenerateAllExamplesthenShowcaseSyncwere run in the same order the script now uses:Synced 99 documents (91 PDFs, 8 PPTX twins) and 91 preview PNGs. The manifest check is bidirectional, because counting placeholder cards is not enough — a missing card does not look like a placeholder, which is how the Maven banner went unnoticed in the first pass: 91 produced, 91 cards, 0 placeholders, 0 produced without a card.The deck-twin guard was proved in both directions. With the fix: 3 tests green. With
generatePdf()removed:Expecting empty but was: ["flagships/maven-banner.pptx"]. The first attempt at that negative run passed spuriously — Surefire does notclean, so the previous run'smaven-banner.pdfwas still intargetand the guard saw a twin; re-run withclean testit fails as it should.Staleness was measured, not guessed, and the measurement corrected two earlier numbers:
pdf-chrome.pdf, which differs by 2.6%That cliff is the date stamp in the page corner and nothing else, so 21 previews were left alone.
pdf-chrome.pdfis the one worth looking at: the committed copy draws the entire body at low alpha with the DRAFT watermark bleeding through every panel, where a fresh render is solid — a 2.6% size difference and an 18.3/255 pixel difference.The same rule was applied to
web/showcase: a full regeneration touched 111 files, of which 86 differed only by that date stamp. Those were reverted, leaving 25 — six additions, six orphan removals, thirteen material re-renders — instead of a PR carrying ninety binary diffs nobody can review.The eleven deleted assets were each checked twice: a content scan across every tracked text file, then
git grep.GraphComposeLogo_final.svgis not the referencedGraphComposeLogo.png. All remain recoverable from history.Not in scope
Nothing asserts that a produced artifact has a metadata entry —
lookupfalls back rather than failing, which is why nine accumulated silently. Today's nine are fixed by hand and the deck-twin invariant is guarded, but a new PDF without an entry still publishes as a placeholder. That check needsShowcaseMetadata's id set, and the class is package-private; it belongs with the other guards in the next change, along with a guard against the orphan leak returning.The regeneration step itself only executes during a real cut; the dry-run check covers syntax and the dry-run branch. Running both mains manually in the script's order is the closest available proof short of cutting a release.