mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 03:56:15 -08:00
62359c80fc40f615c26348db237a35c5637221dd
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
62359c80fc |
fix(release): act on the fourth Codex pass
- wrapText left a word longer than the line budget intact, so a valid 60-character unbroken highlight rendered past the edge of the 1200px card and was cropped. Overlong words are now broken at the budget, and a test asserts no returned line can exceed it. The previous test asserting the old behavior encoded the bug. - The announcement commands are documented as `pnpm --silent run`: reproduced that plain `pnpm run release:notes:telegram > post` writes pnpm's lifecycle banner into the file ahead of the post, so the advertised paste-ready output was not paste-ready. 186 tests passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8c3aebecac |
fix(release): act on the third Codex pass
Four findings, each with a regression test: - An empty `.changes/` directory was reported as an internal-only release. The realistic cause is this step running after `--consume` deleted the notes, which is the one ordering mistake the pipeline exists to prevent — calling it "internal-only" hid it and shipped no cards at all. It is now a hard failure naming that cause. - A release that turned internal-only after cards were generated for the same version returned before the cleanup pass, leaving obsolete images in the output directory. Cleanup is now a named function both paths use, and `--dry-run` still deletes nothing. - Nothing enforces the lowercase-slug filename convention on notes, so `Player_New-UI.md` produced `card-player_new-ui.png`, which `isOwnedCardFile` cannot match and no later run could ever remove. Card slugs are normalized to the character set the predicate accepts, with collisions from that normalization broken by a numeric suffix. - The pipeline doc's surface table still named the old minor-scoped card directory while the prose below it named the new per-version one. 185 tests passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1417632f73 |
fix(release): act on the second Codex pass
Four P2 findings, each with a regression test: - The authored-body check could never fail. The tag workflow appends GitHub's generated notes to the authored text (FULL_BODY in build-and-make.yaml), so `release.body` is never empty and testing it for emptiness proved nothing. It now compares the body against the local CHANGELOG.md public section — the actual authored source — and reports an internal-only release as such instead of warning about it. - A release with no highlights failed the documented card step, which necessarily included every internal-only release since validation forbids highlights there. Now the hero card is still rendered when public notes exist, an internal-only release writes nothing, and both exit 0. - Card output is keyed by the exact version rather than the minor slug (0.24.0 and 0.24.1 share a blog post but not a card set), and a run first removes the cards a previous run wrote there, so a renamed or dropped highlight cannot leave a stale image waiting to be published. Only files matching what this tool writes are removed. - Telegram could still truncate breaking changes into the counter when a release had no highlights, since the fitting loop trims the tail. Dropping a breaking entry now fails with an actionable error. Docs: docs/architecture/release-pipeline.md, .changes/README.md, CLAUDE.md and AGENTS.md updated for the new output path and the no-highlight and internal-only behavior. 180 tests passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9bd1fa54b0 |
fix(release): correct highlight parsing, card layout and gate reliability
Seven defects found by an adversarial pass over the new tooling, each with a regression test: - `highlight:` was silently truncated at the first ` #`. The comment strip was written when every field was a slug, enum or number; it now applies only to those closed-vocabulary keys. `highlight: Sources #N chip` kept parsing as "Sources" and shipped that to all four surfaces. - Quote stripping removed a leading or trailing quote independently, so `highlight: "Up Next" rail` lost its opening quote. Both ends must now carry the same quote character. - A whitespace-only quoted value passed validation and then crashed the card generator mid-run, after PNGs were already written. Values are trimmed after unquoting, so it fails validation instead; the hero card also no longer indexes wrapText's empty result blindly. - Feature cards painted the opaque screenshot frame over the last body line whenever the headline wrapped to two lines. The body's line budget is now derived from the space actually left above the frame. - Un-highlighted breaking changes were folded into "…plus N more fixes and improvements" on Telegram and never shown. They are always spelled out. - Both CLI guards compared a non-realpath'd argv[1] against a realpath'd import.meta.url, so reaching either script through a symlinked path (on macOS, anything under /tmp or /var) made it a silent exit-0 no-op — which for the publication gate reads exactly like a pass. - spawnSync reports a missing gh binary and a signalled child as `status: null`, both of which were blamed on the build. The highlight cap drops from 80 to 60 characters: 60 is the hero card's single-line budget, so a valid highlight now always renders in full rather than being silently ellipsized. Docs: new docs/architecture/release-pipeline.md carries the full contract — surfaces, the ordering constraint around --consume, highlight semantics, card layout rules, and the 27-asset draft verification table. release-cut now references it instead of restating the asset list (474/500 words), and CLAUDE.md, AGENTS.md and .changes/README.md point at it; the README's release sequence no longer omits the announcement and card steps. 172 tests passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4b30e0613a |
fix(release): address review findings on the release tooling
CodeQL: the pacman rule built a regex from the interpolated version and escaped only dots. It now compares plain strings, so the exported requiredAssetRules() carries no escaping trap; only the literal compression suffix stays a regex. verify-draft-release: - Never exits 0 once the release is published. The asset report still prints (auditing after the fact is useful), but a pre-publication gate must not report success for a boundary already crossed. - Polls for the tag run before concluding it does not exist: `gh run list` reports what is indexed right now, and a run pushed seconds ago routinely is not. Adds injectable progress/sleep io, so runVerification is async. - The live sleep timer is deliberately not unref'd — a pending promise does not hold the event loop open, and an empty loop exits 0, which would turn a mid-poll exit into a silent false success. - `--repo` is shape-checked so a typo fails with the usage line. Announcements: an internal-only release now returns null instead of throwing a misleading "even a single entry exceeds the limit" error. The CLI explains it on stderr, leaves stdout empty and exits 0 — the shape extract-changelog-section.mjs --public already uses for the same case. Highlight cards: card filenames derive from the note filename, never the screenshot slug. Two highlights may legitimately share one manifest shot, which previously made the second card overwrite the first. Tests: the CLI harness moved to spawnSync — execFileSync discards stderr on success, hiding exactly the messages these paths emit. Adds regressions for each finding above (165 passing). Docs: CLAUDE.md and AGENTS.md release sections (mirrored, verified identical) now document the highlight field, the announcement/card commands, and the draft verifier; .changes/README.md records the internal-only announcement behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dcd28c1334 |
feat(release): announcement formats, highlight cards, and draft verification
Extends the release tooling around the .changes notes pipeline: - New optional `highlight:` frontmatter field marks a release's headline changes (max 80 chars, rejected on `type: internal`). - `--format telegram` renders a paste-ready plain-text post guaranteed to fit the 4096-character limit (highlights lead, the rest collapses into a counter); `--format reddit` renders markdown with a suggested title line, a Highlights section, and a collapsed change list. Both are stdout dry runs and must run before `--consume`. - The blog scaffold uses the highlight as a ready section heading instead of a TODO, and highlight notes without screenshots get their own section. - `release:verify:draft` waits for the v* tag build via `gh run watch`, then checks draft status, the authored body, and the complete 27-asset set (verified against a real full matrix build). Read-only; publishing stays manual. - `release:cards:generate` renders branded 1200x630 highlight cards (with a framed mock-server screenshot strip when the note names one, purely typographic otherwise) plus a release hero card into dist/, matching the website palette. Copying into the website tree stays a manual act. Docs: .changes/README.md, release-notes and release-cut skills (both copies byte-identical). No release note: release tooling only, no runtime behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2ac0de752f |
fix(skills): align repository guidance with implementation (#1315)
* docs(skills): design implementation synchronization * docs(skills): plan implementation synchronization * fix(release): filter internal notes from public body * docs(release): synchronize release workflow guidance * fix(stalker): normalize catalog series flags * fix(stalker): preserve progress with scoped episode IDs * fix(playback): expose strict position persistence * docs(stalker): record series position compatibility * test(skills): validate repository skill contracts * fix(database): keep SQL trace values private * docs(skills): refresh Nx and SQLite ownership * docs(skills): align provider and UI guidance * docs(skills): tighten validated guidance * docs(release): require exact release pushes * style(electron): remove trailing blank line * fix(ci): classify repository skills coverage |
||
|
|
e55d55b47f |
feat(mock-data): add shared screenshot-safe poster catalog (#1271)
Moves the fictional movie catalog into `libs/shared/marketing-fixtures` so the
Xtream and Stalker mocks describe the same titles, and adds 20 rendered posters
plus the shared fixture types behind them.
Supporting changes made while getting it green:
- `shared-marketing-fixtures` is classified Tier B in the coverage policy. Not
Tier A: it is fictional fixture data, so a statement percentage over it means
nothing, and a Tier A entry would pull it into the merged coverage map and the
ratchet. Tier B still runs its spec in CI. `stalker-mock-server` needs no entry
of its own — it is already Tier C and the Tier B/C runner falls back to
`pnpm nx test <project>`, so its new `marketing-poster-url.spec.ts` runs.
- Two release-capture defects the catalog reorder introduced, both fixed in
`tools/release/capture-app-driver.ts`:
- VOD stream ids are `MARKETING_VOD_STREAM_ID_BASE + index` and the generator
now lists the showcase movies first, so 62000-62002 became Black Harbor, The
Paper Astronaut and Summer Static while the dashboard seeding still mapped
those ids to the previous titles' backdrops.
- the raw `tsx` spawn of the Xtream mock lacked `--tsconfig
tsconfig.base.json`, so the mock could not resolve
`@iptvnator/shared/marketing-fixtures` and the capture never started. Both
mock projects' own serve targets already passed the flag.
|
||
|
|
02b966895d |
docs(release): backfill curated 0.23.0 release notes, fix the notes CLI -- trap (#1263)
Backfills the 0.23.0 release notes the .changes/ pipeline missed: it landed after most of the release was already merged, leaving 4 notes for 79 commits. Adds 22 curated notes (26 total: 14 features, 11 fixes, 1 perf). Curated rather than exhaustive — the GitHub release body renders these above GitHub's own list of every merged PR, so related PRs are folded into one note per user-facing story: shared player controls (8 PRs), embedded MPV frame-copy (4), manual EPG mapping (3), plus five more pairs. Tooling-only scopes get no note. No screenshot slugs: none of the five manifest shots depicts a 0.23 headline feature, and the capture run asserts TMDB enrichment stays disabled. Two tooling fixes found while writing them: - parseArgs now ignores a bare `--`. npm needs it to forward arguments past the script name; pnpm hands it to the script verbatim, so `pnpm run release:notes:github -- --version 0.24.0` died on the very separator typed to make forwarding work. Unknown flags and missing values still fail as before. Covered by new subprocess CLI tests wired into the release-tools target. - .changes/README.md claimed every non-consume mode was a safe dry run; --format changelog and --format blog write their target file. No app or lib code, no version bump, no --consume, no CHANGELOG.md or website changes — those stay owned by release-cut. |
||
|
|
6c946978b4 |
chore(release): drop the superseded v0.20 screenshot script (#1262)
#1261 replaced this one-off capture with a manifest-driven script, so the v0.20 version is dead weight: hard-coded slugs, paths and output directory, none of the fail-closed guards, and two `RegExp`-from-string constructions of the kind CodeQL flags (one of which it flagged on the replacement before that was rewritten to use predicates). Removing it also drops its `tools/eslint/max-lines-baseline.mjs` entry, so the baseline no longer carries a file that does not exist. Not a pure dead-code deletion, and worth stating: two capabilities go with it, neither reachable from the new pipeline — `createDesignedCopy` (title/kicker overlays on captured frames) and `createHeroImage` (a 1600x900 canvas collage built from three screenshots). The v0.20 assets they produced are already committed under apps/website/public/blog/v0-20/, so nothing published breaks; a future release wanting the same collage needs it ported deliberately rather than resurrected here. Docs: docs/architecture/xtream-mock-server.md now points at capture-release-screenshots.ts and notes its mock-identity check. Verified: no references to the removed file remain anywhere in the repo, and `pnpm run lint` passes for all 42 projects with the shortened baseline. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b4ec68c1fa |
feat(release): manifest-driven screenshot capture with fail-closed mock-data guards (#1261)
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> |
||
|
|
4e5132cbb5 |
ci(release): gate PRs on an authored release note (#1257)
* ci(release): gate PRs on an authored release note Second slice of the release-notes pipeline (#1256 landed the format and generator): make the .changes/ habit survive contact with reality. - "Release note gate" job in ci.yml, PR-only: validates every .changes/*.md, then requires an added note (or the no-release-note label) when the PR touches runtime code under apps/ or libs/. Tests, e2e projects, the website, mock servers, shared testing helpers, snapshots and docs are auto-exempt. - Policy lives in tools/release/check-release-note-gate.mjs as a pure function fed PR files+labels as JSON — unit-tested (10 cases) instead of encoded in workflow bash. The failure message lists the triggering files and names the exact fix. - Labels are fetched live rather than from the stale event payload, so applying the label and re-running the check works without a new push. - The job is dependency-free Node: no pnpm install, runs in seconds. - release-notes and release-cut skills added under .claude/skills/ and mirrored to .codex/skills/; CLAUDE.md/AGENTS.md sections updated to point at the gate and the skills. The no-release-note label itself was created in the repository. Tests: 47 passing in release-tools (10 new gate cases); gate-step shell verified with shellcheck at the CI severity; ci.yml YAML-parse checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(agents): make the release skills discoverable by Claude Code too `.codex/skills/**` was un-ignored so Codex picks up repository skills in any clone, but `.claude` was ignored wholesale — and Claude Code only discovers skills under `.claude/skills/`. The release-notes and release-cut skills therefore existed only on whichever machine authored them. Mirror both skills into `.claude/skills/` and opt them in by name rather than un-ignoring the directory: contributors keep personal skills there (i18n-fill, website, …) which must stay local and out of `git status`. CLAUDE.md/AGENTS.md updated so the "skills live under .codex/skills/" claim does not go stale, including the requirement to keep mirrored copies in sync. The CI gate and the CLAUDE.md/AGENTS.md section remain the load-bearing enforcement; skills only carry the detail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(ci): only a note this PR authored satisfies the release-note gate Review follow-ups on #1257 (Codex P2 ×2, Greptile P1). - Drop `renamed` from the accepted statuses. The PR files API compares base…head, so a note created and then renamed inside the same PR still reports as `added`; a `renamed` entry means the file already existed on the base branch. Accepting it let a runtime-code PR pass by moving another PR's unconsumed note, which documents nothing and gives the generator no adding commit to resolve a PR link from. - Require a direct child of `.changes/`. `loadNotes()` reads only the immediate directory, so `.changes/sub/note.md` satisfied the old prefix check while never being validated or rendered into any release surface. Tests: renamed and nested notes now assert a failing gate (12 gate cases). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
270350c2e1 |
chore(release): author release notes in .changes instead of reconstructing them (#1256)
* chore(release): author release notes in .changes instead of reconstructing them CHANGELOG.md has been frozen at 0.12.0 since 2023 while the app shipped 0.23.0, semantic-release sat in devDependencies with no config, and the real user-facing notes were a 280-line MDX post written from memory at release time. The gap was never version math — it was authored notes captured while the context is still fresh. Add a `.changes/*.md` note format (type, area, issues, screenshot; no version field, since the release version is chosen deliberately) plus a generator that composes the GitHub release body, the CHANGELOG.md section and a blog-post scaffold from the accumulated notes. Changesets was considered and rejected: it versions multiple published packages, and this repo has exactly one private package. Its `version` step would also rewrite CHANGELOG.md into a flatter format than the blog post and fight the deliberate, updater-constrained version choice. - hand-rolled frontmatter parser over a YAML engine: the schema is closed, so it can reject unknown keys, which is what catches typos - PR numbers are resolved from the commit that added the note, never written by the author - MDX-significant characters in note bodies are escaped so a stray `<` cannot break the website build - blog scaffold ships `draft: true` with explicit TODO headings; the prose is editorial work, only the inventory is mechanical - revive CHANGELOG.md with an honest pointer for 0.13.0-0.23.0 rather than fabricating the missing history - drop the five unused semantic-release/conventional-changelog packages Docs: `.changes/README.md`, plus a "Release Notes For User-Visible Changes" section mirrored in CLAUDE.md and AGENTS.md, and a PR template checkbox for contributors who never read either. Tests: 26 unit tests in tools/release/release-notes.test.mjs covering parsing, validation, grouping and all three renderers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(release): default the notes version to package.json and harden alt escaping Review follow-ups on the release-notes generator. - `--version` now defaults to the root package.json version, so the `release🎶*` package scripts run bare instead of failing on a missing argument. Bumping package.json is the deliberate act that starts a release, which makes it the right single source of truth; `--version` remains as an override for dry runs before the bump. The notice goes to stderr so `--format github` keeps a pipeable stdout. - Escape backslashes before apostrophes when building the MDX `alt` string literal. A note body ending in a backslash previously produced an unterminated string and would have broken the website build. - Document that release posts are one per minor version, in the slug helper, the overwrite error, and `.changes/README.md` — a patch release edits the existing post rather than creating a second one. Tests: +1 regression test for the alt escaping, verified to fail without the fix (27 total, all passing). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(ci): put authored notes into the tag release body, fail-closed Wires the .changes pipeline into the release workflow (Codex review P1 on #1256). Calling the generator from the tag build cannot work — --consume deletes .changes/ before the tag exists — so the tag build reads what the generator already wrote: release-meta now fills BODY from the CHANGELOG.md section matching the tag's version via tools/release/extract-changelog-section.mjs. generate_release_notes stays on, so GitHub's commit list renders below the authored notes; the existing draft-metadata repair step already concatenates RELEASE_BODY with the generated notes, so the rare duplicate-draft path keeps the same layering unchanged. The extractor exits non-zero when the section is missing or empty, failing the release instead of silently shipping PR-title-only notes. A hotfix tag cut without running release:notes:changelog therefore fails at create-release by design; the error message names the exact commands to run. Tests: 5 new extractor tests (32 total in release-tools, all passing); packaging suite (247) re-run green since build-and-make.yaml is one of its inputs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(release): escape all regex metacharacters in the changelog extractor CodeQL flagged the version-to-RegExp interpolation in extract-changelog-section.mjs (regex injection + incomplete escaping): only dots were escaped, and while the CLI validates its argument as bare semver before calling, the exported extractSection() carries no such guarantee on its own. Escape the full metacharacter set so no caller can inject pattern syntax, with tests covering wildcard dots, alternation, `.*` and backslashes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(release): make changelog generation idempotent per version Codex review P2 on #1256: rerunning `release:notes:changelog` for the same version — the normal move after correcting a note before --consume — prepended a second section instead of replacing the first, leaving duplicate release entries. Extract the marker insertion into upsertChangelogSection(): it removes any existing section for the version, then rebuilds around the marker rather than string-replacing into it, so the blank-line count on both sides stays exact on both the fresh-insert and replace paths. The CLI reports when a section was replaced. Tests: 4 new cases (insert, replace-not-duplicate, neighbours untouched, missing marker); 37 total passing. End-to-end rerun verified: one heading, latest date wins, extractor output unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1ec7176983 |
chore(xtream-mock-server): add artwork mock data and script to generate artwork for IPTVnator releases
- Implemented a TypeScript script that generates original fictional demo artwork for IPTVnator release screenshots. - The script supports multiple artwork kinds (poster and backdrop) and qualities (low, medium, high, auto). - It includes functionality for dry runs, generating artwork, manifest creation, and validation of generated assets. - Integrated with OpenAI's image generation API to create artwork based on provided prompts and fixture data. - Added a manifest file to track generated assets and their metadata. - Ensured output directories are created as needed and included error handling for API requests. Entire-Checkpoint: c6e522b4276c |