feat(release): publish localized changelog metadata - #5518
Closed
PerishCode wants to merge 14 commits into
Closed
Conversation
After an update, the home surface shows a one-time bottom-right card with the release highlights (title, copy, optional image) and a "See what's new" link. Content ships in the release feed metadata.json as an optional whatsNew block sourced from tools/release/whats-new/<baseVersion>.json; versions without highlights fall back to generic copy linking the release notes. The card shows once per version, keyed on a locally stored last-seen version, and never on fresh installs. - packages/contracts: WhatsNewContent/WhatsNewResponse DTOs + analytics props - daemon: GET /api/whats-new (channel feed fetch, 1h cache, stale/null degradation) + od whats-new CLI mirror - web: WhatsNewPopup card (CSS Module, i18n chrome in 19 locales, surface_view/ui_click tracking), lastSeen decision logic in lib/whats-new - release tooling: publish-metadata forwards the optional whatsNew block and fails loudly on malformed files; docs in tools/pack/AGENTS.md and tools/release/whats-new/README.md
… locale overrides
Address review feedback on the post-update what's-new card:
- EntryShell passes `active={view === 'home'}` into WhatsNewPopup, and the
component defers its fetch/show decision (and `page_name: 'home'`
analytics) until Home is the active entry view, so direct routes like
/projects no longer surface the card or emit home-page events.
- The document-level Escape listener now ignores already-handled events
(`defaultPrevented`) and hides the card without marking the version as
seen or firing the dismiss analytics event, so a stray Escape no longer
permanently spends the once-per-version card.
- publish-metadata validates the optional `locales` block at publish time
(plain-object shape, non-empty `title`/`body`, HTTPS `linkUrl`) so
malformed locale overrides fail the publish loudly instead of being
silently dropped by the daemon parser; README documents the contract.
Generated-By: looper 0.9.11 (runner=fixer, agent=claude-code)
Replace the startsWith("https://") checks in readWhatsNewBlock with a
shared assertHttpsUrl helper that trims, constructs new URL(...), and
requires protocol === "https:", mirroring the daemon readHttpsUrl parser
so values it would silently drop (e.g. "https://") fail the publish
loudly for imageUrl, linkUrl, and locale linkUrl.
Generated-By: looper 0.9.11 (runner=fixer, agent=claude-code)
…first Home load The card's fetch effect armed its once-guard when the fetch STARTED, and the effect teardown re-armed it — so React StrictMode's double-invoke (dev) or a Home toggle mid-fetch cancelled the first fetch, the second effect run saw the guard still set and bailed, and the resolved-but-cancelled fetch re-armed the guard without ever showing the card. The card then never appeared until the Home view toggled again. Arm the guard only once a decision is actually REACHED (fetch resolved with a payload); keep the `cancelled` flag purely as a state-update guard that no longer re-arms the fetch. A superseded fetch drops and the surviving one records the decision, so the card shows on first Home activation and a genuine mid-fetch Home exit still retries on the next activation. Red spec: apps/web/tests/components/WhatsNewPopup.test.tsx renders under StrictMode and asserts the card appears — red on the previous effect, green now.
# Conflicts: # packages/contracts/src/analytics/events.ts
…hangelog-cicd # Conflicts: # apps/web/src/components/EntryShell.tsx # apps/web/src/i18n/locales/zh-CN.ts # apps/web/src/i18n/locales/zh-TW.ts # tools/pack/AGENTS.md # tools/release/src/storage/publish-metadata.ts
Contributor
Author
Contributor
🚀 Landing page previewThis PR is deployed to a Cloudflare Pages preview — not staging or production:
|
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.
Related to #5071 and #5343.
Why
The updater and release work exposed a missing delivery contract: release notes had no channel-consistent source convention, immutable publication path, public metadata projection, or strict stable gate. That left release operators and future consumers to couple source discovery, storage layout, metadata assembly, and rendering decisions together.
This PR establishes that mechanism in
tools-releaseafter the product team split the lightweight What's New experience into an independent path. The goal is to make localized changelog delivery deterministic now while keeping source format, storage, metadata, and eventual rendering independently replaceable.What users will see
There is no new updater UI in this PR. Release operators can author localized Markdown at
docs/CHANGELOG/v<full-release-version>/<locale>.md; successful releases publish immutable locale assets and expose their URL, media type, SHA-256, and byte size throughmetadata.json.releaseNote.content.Stable releases now fail before platform builds when required
enandzh-CNnotes are missing or malformed. Beta, prerelease, and preview use the same pipeline but may explicitly publish without notes when no matching version directory exists.Surface area
apps/weborapps/desktop(including Electron menu bar)odsubcommand or flag, newtools-dev/tools-pack/tools-prflag, or newOD_*env var/api/*endpoint, new SSE event, or changed shape inpackages/contractsskills/,design-systems/,design-templates/, orcraft/, or change to the skills protocolTRANSLATIONS.mdfor the locale workflow)package.json(dependenciesordevDependencies); workspace-packagepackage.jsonfiles are out of scope. Include a paragraph on what we get vs. what bytes we ship (seeCONTRIBUTING.md→ Code style)Screenshots
Not applicable. This PR has no UI diff against
main; release-note rendering remains a separate consumer concern.Bug fix verification
Not applicable; this is a release delivery feature rather than a bug fix.
Validation
pnpm installpnpm guard(78 checks passed after the latestmainmerge)pnpm typecheckpnpm --filter @open-design/tools-release test(16 tests)pnpm --filter @open-design/tools-serve exec vitest run tests/release-metadata-publish.test.ts(2 tests)pnpm --filter @open-design/tools-pack exec vitest run tests/release-workflows.test.tspnpm --filter @open-design/e2e exec vitest run -c vitest.config.ts tests/packaged-smoke-workflow.test.ts(46 tests)actionlintfor all five changed release workflows0.14.1dry-run: planned bilingual immutable uploads, candidate metadata assembly, and verification passed without storage accesstools-serve+tools-packpackaged loops:0.14.1-beta.1000 -> .1001 -> .1002and.2000 -> .2001 -> .2002, including build, install, update, restart, stop, uninstall, and cleanuprelease-betapublishes:0.14.1-beta.3and0.14.1-beta.4, including macOS arm64 and Windows x64 builds/smokes, bilingual immutable note publication, metadata verification, and latest promotionNix is not installed locally, so
nix flake checkwas not rerun. The lockfile hash path remains covered by the repository's PR validation/autofix gate.