Third slice of the release-notes pipeline (#1256 format+generator, #1257 CI gate): release screenshots become reproducible and provably mock-only. The v0.20 capture script was single-use (hard-coded slugs, paths, hero) and fail-open: a lost IPTVNATOR_E2E_DATA_DIR silently fell back to the user's real ~/.iptvnator database, `...process.env` leaked ambient TMDB keys and proxies, nothing gated network access, and no frame content was ever validated. Each hole leaks real playlists, credentials, or copyrighted artwork into published screenshots without a single signal. New pipeline: - tools/release/screenshots.manifest.json — declarative shots (slug, title, named setup steps, themes). Adding a feature shot = one manifest entry. - capture-release-screenshots.ts — orchestrator; output goes to apps/website/public/blog/<release>/screenshots/<slug>-<theme>.png, release slug derived from package.json (or --release), --only/--theme filters. - capture-app-driver.ts / capture-navigation.ts — launch, seeding, theme, and the named-action vocabulary; actions are order-independent (every portal action starts from the dashboard). - screenshot-guards.mjs — the fail-closed policy, pure and unit-tested: G1 the real database is snapshotted (sha256+mtime) before launch and must be byte-identical after; the isolated DB must actually exist G2 the app receives an allowlisted environment, never ...process.env G3 deny-by-default network gate; known app-level calls (GitHub update check) are answered by local stubs; any other blocked request fails the run — a silently-blocked TMDB call would leave a frame that looks broken rather than unsafe G4 every frame is scanned before capture: external img/background URLs, credential-shaped text, MAC addresses, non-localhost m3u8 references G5 TMDB enrichment asserted disabled via the renderer's IndexedDB Any violation deletes every frame captured in the run and exits non-zero. The guards paid for themselves on the first live run: G3 caught the mock server redirecting stream endpoints to a public demo HLS (test-streams.mux.dev) — meaning earlier hand-run captures could embed third-party video frames. The M3U shot now deliberately captures the groups layout without starting playback. `.changes` validation now cross-checks `screenshot:` slugs against the manifest, so a note cannot reference an image the capture run never produces. Verified end-to-end: 10/10 shots (5 slugs × dark/light) captured against dist build + xtream-mock-server, frames visually inspected (fictional titles/artwork only), guard-violation paths exercised live. 67 unit tests in release-tools, lint green, script files within the repo size limit. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
3.3 KiB
name, description
| name | description |
|---|---|
| release-cut | Cut an IPTVnator release — bump the version, generate release notes from .changes/, scaffold the website post, tag, and verify the draft. Use when asked to release, cut a version, prepare release notes, or publish a new version. |
Release Cut
The pipeline turns accumulated .changes/*.md notes into all three release
surfaces. Order matters: the tag build extracts the CHANGELOG section into
the GitHub release body and fails if it is missing, so the changelog step
is not optional.
Sequence
-
Pick the version — deliberate choice, edit
versionin the rootpackage.json. Bare semver only: any suffix flips electron-updater into prerelease mode and leaks into installer version fields. -
Review the notes — read every file in
.changes/. Fix wording (user language, not reviewer language), then:pnpm run release:notes:validate -
Generate the changelog section (idempotent per version — rerunning replaces the section, so regenerate freely until it reads well):
pnpm run release:notes:changelog -
Scaffold the website post:
pnpm run release:notes:blogOutput is
apps/website/src/content/blog/v0-XX-release-notes.mdxwithdraft: true. The narrative intro, headlines, anddescriptionare editorial — fill everyTODOby hand. One post per minor version: for a patch release, edit the existing post (the scaffold refuses to overwrite without--force). -
Screenshots — only from the fail-closed capture script against the mock servers, never from a real playlist or account: real streams, logos, and TMDB artwork are copyrighted, and credentials must never reach a published image.
pnpm nx run electron-backend:build-e2e # once pnpm run release:screenshots # all manifest shots, dark+lightOutput goes to
apps/website/public/blog/v0-XX/screenshots/. New feature to showcase = new entry intools/release/screenshots.manifest.json(slug must match the note'sscreenshot:field). The run aborts and deletes its frames on any guard violation (real-DB touch, external request, credential-shaped text in frame, TMDB active). -
Consume the notes (the only destructive step):
node tools/release/build-release-notes.mjs --consume -
Commit, tag, push:
git add CHANGELOG.md .changes apps/website package.json git commit -m "chore(release): v0.XX.0" git tag v0.XX.0 && git push && git push --tags -
Verify the draft release once
build-and-make.yamlfinishes: authored notes on top, GitHub's generated commit list below, all platform assets present (.dmg/.zip+latest-mac.yml,.exe/.msi+latest.yml,.deb/.rpm/.AppImage/.snap/.flatpak+latest-linux*.yml, blockmaps). Publish manually; flip the blog post todraft: false.
Failure modes
- create-release fails with "CHANGELOG.md has no section for X" — step 3 was skipped. Run it, commit, delete and re-push the tag.
- Snap store publication is a separate manual flow after the public
release exists (
publish-snap.yaml). - Post-release checklist candidates: i18n drift (
pnpm run i18n:check), update the websitev0-XXblog assets, announce in Telegram.