mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
docs(release): synchronize release workflow guidance
This commit is contained in:
1 parent
6a65ee4efb
commit
ec2a8137f9
10 files changed
+192
-243
No files matched your search
+9
-5
@@ -49,14 +49,18 @@ The body is capped at 400 characters — depth belongs in the blog post.
|
||||
- ❌ "Fix off-by-one in `resolveEnrichmentSeasonNumber`"
|
||||
- ✅ "Series whose title carries a season marker no longer show the wrong season"
|
||||
|
||||
`type: internal` is for changes with no user-visible effect that are still worth
|
||||
recording (dependency bumps with behaviour risk, packaging moves). They stay out
|
||||
of the release body and blog post, and land collapsed in `CHANGELOG.md`.
|
||||
`type: internal` records invisible maintenance. Internal notes stay collapsed in
|
||||
`CHANGELOG.md`, are omitted from the blog scaffold, and are removed from the
|
||||
authored public GitHub body by
|
||||
`extract-changelog-section.mjs --public`. GitHub's generated commit list remains
|
||||
separate. An internal-only release can therefore have an empty authored body.
|
||||
|
||||
## When a note is not needed
|
||||
|
||||
Skip the note — and apply the `no-release-note` label — for test-only changes,
|
||||
docs, CI/workflow plumbing, and pure refactors with no behaviour change.
|
||||
The gate auto-exempts website, E2E and mock-server apps, `*.spec.{js,ts}`,
|
||||
`*.e2e.{js,ts}`, snapshots, any `/testing/` path, and Markdown. For other
|
||||
test-only, documentation, CI/workflow, or pure-refactor changes under
|
||||
`apps/`/`libs/`, apply `no-release-note` when no user-visible note is warranted.
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
@@ -1,88 +1,72 @@
|
||||
---
|
||||
name: release-cut
|
||||
description: 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.
|
||||
description: Use when preparing, cutting, tagging, publishing, or verifying an IPTVnator release or its release assets.
|
||||
---
|
||||
|
||||
# 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.
|
||||
The tag build takes authored public text from the new CHANGELOG section. Keep
|
||||
the full changelog committed before tagging.
|
||||
|
||||
## Sequence
|
||||
## Preflight
|
||||
|
||||
1. **Pick the version** — deliberate choice, edit `version` in the root
|
||||
`package.json`. Bare semver only: any suffix flips electron-updater into
|
||||
prerelease mode and leaks into installer version fields.
|
||||
Work from clean, current `master` with the intended remote named explicitly.
|
||||
Confirm `package.json` contains bare semver, the exact `v<version>` tag does not
|
||||
exist locally or remotely, CI is green, and all notes validate.
|
||||
|
||||
2. **Review the notes** — read every file in `.changes/`. Fix wording (user
|
||||
language, not reviewer language), then:
|
||||
```bash
|
||||
pnpm run release:notes:validate
|
||||
pnpm run i18n:check
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:validate
|
||||
```
|
||||
## Generate
|
||||
|
||||
3. **Generate the changelog section** (idempotent per version — rerunning
|
||||
replaces the section, so regenerate freely until it reads well):
|
||||
1. Set `package.json.version`.
|
||||
2. Run `pnpm run release:notes:changelog`.
|
||||
3. Minor release: run `pnpm run release:notes:blog` and finish every editorial
|
||||
field. Patch release: edit the existing `vX-Y` post; do not scaffold or
|
||||
force-overwrite it.
|
||||
4. Capture required manifest screenshots only against mock servers:
|
||||
`pnpm nx run electron-backend:build-e2e`, then
|
||||
`pnpm run release:screenshots`.
|
||||
5. Consume notes only after reviewing all generated output:
|
||||
`node tools/release/build-release-notes.mjs --consume`.
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:changelog
|
||||
```
|
||||
The consume command is the destructive boundary: it deletes the direct note
|
||||
files. Stage only release-owned files, including exact website post/assets and
|
||||
`git add -A -- .changes`, then commit and create the exact tag.
|
||||
|
||||
4. **Scaffold the website post**:
|
||||
```bash
|
||||
git commit -m "chore(release): v0.24.0"
|
||||
git tag v0.24.0
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:blog
|
||||
```
|
||||
## Push and External Effects
|
||||
|
||||
Output is `apps/website/src/content/blog/v0-XX-release-notes.mdx` with
|
||||
`draft: true`. The narrative intro, headlines, and `description` are
|
||||
editorial — fill every `TODO` by hand. One post per **minor** version:
|
||||
for a patch release, edit the existing post (the scaffold refuses to
|
||||
overwrite without `--force`).
|
||||
Push only the intended branch and tag; never use broad `git push --tags`.
|
||||
|
||||
5. **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.
|
||||
```bash
|
||||
git push --atomic origin \
|
||||
HEAD:refs/heads/master \
|
||||
refs/tags/v0.24.0
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm nx run electron-backend:build-e2e # once
|
||||
pnpm run release:screenshots # all manifest shots, dark+light
|
||||
```
|
||||
Master and `v*` pushes can publish Docker images. The tag build creates a draft
|
||||
GitHub release. Verify authored text plus generated commits and all required
|
||||
macOS, Windows, DEB, RPM, Pacman (`.pacman`/`.pkg.tar.*`), AppImage, Snap,
|
||||
Flatpak, updater metadata, blockmaps, and
|
||||
`linux-frame-copy-runtime-sources.tar.xz`.
|
||||
|
||||
Output goes to `apps/website/public/blog/v0-XX/screenshots/`. New feature
|
||||
to showcase = new entry in `tools/release/screenshots.manifest.json`
|
||||
(slug must match the note's `screenshot:` field). The run aborts and
|
||||
deletes its frames on any guard violation (real-DB touch, external
|
||||
request, credential-shaped text in frame, TMDB active).
|
||||
After verification, manually publish the GitHub release. That publication
|
||||
automatically verifies its Snap assets and uploads them to `edge`.
|
||||
Installed-Snap smoke and candidate/stable promotion remain manual. Keep the
|
||||
blog draft during artifact verification; publish it in a follow-up commit and
|
||||
verify the website deployment.
|
||||
|
||||
6. **Consume the notes** (the only destructive step):
|
||||
## Failure Safety
|
||||
|
||||
```bash
|
||||
node tools/release/build-release-notes.mjs --consume
|
||||
```
|
||||
Missing CHANGELOG section: regenerate, commit, delete the bad tag locally and
|
||||
remotely only after resolving its exact target, then retag. Never publish a
|
||||
draft until the source archive and Snap contract pass.
|
||||
|
||||
7. **Commit, tag, push**:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
8. **Verify the draft release** once `build-and-make.yaml` finishes: 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 to `draft: 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 website `v0-XX` blog assets, announce in Telegram.
|
||||
The `.codex` and `.claude` copies of this skill must remain byte-identical.
|
||||
@@ -1,70 +1,49 @@
|
||||
---
|
||||
name: release-notes
|
||||
description: Write the .changes/ release note that every PR with a user-visible change must include. Use when creating or finishing a PR that changes behavior in apps/ or libs/, when the "Release note gate" CI check fails, or when deciding whether the no-release-note label applies.
|
||||
description: "Use when a change may need a .changes note, the Release note gate fails, or deciding whether type: internal or no-release-note applies."
|
||||
---
|
||||
|
||||
# Release Notes (`.changes/`)
|
||||
# Release Notes
|
||||
|
||||
Every PR with a user-visible change adds **one** note file under `.changes/`.
|
||||
At release time the notes become the GitHub release body, the `CHANGELOG.md`
|
||||
section, and the website blog scaffold. CI enforces this: the **Release note
|
||||
gate** check fails any PR that touches runtime code under `apps/` or `libs/`
|
||||
without an added `.changes/*.md` file or the `no-release-note` label.
|
||||
Every user-visible change gets one direct `.changes/<area>-<slug>.md` file. The
|
||||
area matches the conventional-commit scope; the body is present tense, user
|
||||
language, one to three sentences, and at most 400 characters.
|
||||
|
||||
## File format
|
||||
|
||||
Name: `.changes/<area>-<short-slug>.md` — `area` matches the
|
||||
conventional-commit scope of the PR.
|
||||
## Format
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: feature
|
||||
area: playback
|
||||
issues: [1187]
|
||||
screenshot: up-next-rail
|
||||
type: fix
|
||||
area: stalker
|
||||
issues: [1234]
|
||||
screenshot: optional-manifest-slug
|
||||
---
|
||||
|
||||
Series now show an "Up Next" rail beside the player on wide windows: the rest
|
||||
of the current season, watch progress, and click-to-play inline.
|
||||
Stalker series now resume the correct episode.
|
||||
```
|
||||
|
||||
| Field | Required | Value |
|
||||
| ------------ | -------- | ---------------------------------------------------- |
|
||||
| `type` | yes | `breaking` / `feature` / `fix` / `perf` / `internal` |
|
||||
| `area` | yes | lowercase slug = conventional-commit scope |
|
||||
| `issues` | no | `[1187]` or bare `1187` — issues this PR closes |
|
||||
| `screenshot` | no | slug from the release screenshot manifest |
|
||||
`type` is `breaking`, `feature`, `fix`, `perf`, or `internal`. Omit optional
|
||||
fields instead of inventing values. Never add a version or PR number.
|
||||
|
||||
- **No version field.** The release version is chosen at release time.
|
||||
- **Never write a PR number.** The generator resolves it from git.
|
||||
- Unknown keys fail validation — this is what catches typos like `scopr:`.
|
||||
`internal` records invisible maintenance. It stays collapsed in `CHANGELOG.md`
|
||||
but is omitted from the blog and the authored public GitHub body. GitHub's
|
||||
generated commit list may still mention the underlying commits.
|
||||
|
||||
## Writing the body
|
||||
## Skip or Label
|
||||
|
||||
One to three sentences, present tense, max 400 characters, **written for a
|
||||
user, not a reviewer**:
|
||||
The gate auto-exempts website, E2E and mock-server apps, `*.spec.{js,ts}`,
|
||||
`*.e2e.{js,ts}`, snapshots, any `/testing/` path, and Markdown. Other test-only,
|
||||
docs, CI, workflow, or pure-refactor PRs use `no-release-note` when the gate
|
||||
would otherwise require a note. At least one newly added direct
|
||||
`.changes/*.md` file satisfies the gate.
|
||||
|
||||
- ❌ "Refactor `WebVideoControlsAdapter` to hoist volume state"
|
||||
- ✅ "The player now remembers volume between episodes"
|
||||
- ❌ "Fix off-by-one in `resolveEnrichmentSeasonNumber`"
|
||||
- ✅ "Series with a season marker in the title no longer show the wrong season"
|
||||
|
||||
`type: internal` is for changes worth recording but invisible to users
|
||||
(dependency bumps with behavior risk, packaging moves). They are excluded
|
||||
from the release body and blog, and collapsed in `CHANGELOG.md`.
|
||||
|
||||
## When to skip (`no-release-note` label)
|
||||
|
||||
Test-only changes, docs, CI/workflow plumbing, pure refactors with no
|
||||
behavior change. The gate auto-exempts `*.spec.ts`, `*.e2e.ts`,
|
||||
`__snapshots__/`, `apps/website/`, `apps/*-e2e/`, `apps/*-mock-server/`,
|
||||
`libs/shared/testing/` and `*.md` — if only those changed, no label needed.
|
||||
|
||||
## Verify before finishing
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:validate
|
||||
```
|
||||
|
||||
Full format reference: `.changes/README.md`. Gate policy:
|
||||
Full format: `.changes/README.md`. Gate policy:
|
||||
`tools/release/check-release-note-gate.mjs`.
|
||||
|
||||
The `.codex` and `.claude` copies of this skill must remain byte-identical.
|
||||
@@ -1,88 +1,72 @@
|
||||
---
|
||||
name: release-cut
|
||||
description: 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.
|
||||
description: Use when preparing, cutting, tagging, publishing, or verifying an IPTVnator release or its release assets.
|
||||
---
|
||||
|
||||
# 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.
|
||||
The tag build takes authored public text from the new CHANGELOG section. Keep
|
||||
the full changelog committed before tagging.
|
||||
|
||||
## Sequence
|
||||
## Preflight
|
||||
|
||||
1. **Pick the version** — deliberate choice, edit `version` in the root
|
||||
`package.json`. Bare semver only: any suffix flips electron-updater into
|
||||
prerelease mode and leaks into installer version fields.
|
||||
Work from clean, current `master` with the intended remote named explicitly.
|
||||
Confirm `package.json` contains bare semver, the exact `v<version>` tag does not
|
||||
exist locally or remotely, CI is green, and all notes validate.
|
||||
|
||||
2. **Review the notes** — read every file in `.changes/`. Fix wording (user
|
||||
language, not reviewer language), then:
|
||||
```bash
|
||||
pnpm run release:notes:validate
|
||||
pnpm run i18n:check
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:validate
|
||||
```
|
||||
## Generate
|
||||
|
||||
3. **Generate the changelog section** (idempotent per version — rerunning
|
||||
replaces the section, so regenerate freely until it reads well):
|
||||
1. Set `package.json.version`.
|
||||
2. Run `pnpm run release:notes:changelog`.
|
||||
3. Minor release: run `pnpm run release:notes:blog` and finish every editorial
|
||||
field. Patch release: edit the existing `vX-Y` post; do not scaffold or
|
||||
force-overwrite it.
|
||||
4. Capture required manifest screenshots only against mock servers:
|
||||
`pnpm nx run electron-backend:build-e2e`, then
|
||||
`pnpm run release:screenshots`.
|
||||
5. Consume notes only after reviewing all generated output:
|
||||
`node tools/release/build-release-notes.mjs --consume`.
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:changelog
|
||||
```
|
||||
The consume command is the destructive boundary: it deletes the direct note
|
||||
files. Stage only release-owned files, including exact website post/assets and
|
||||
`git add -A -- .changes`, then commit and create the exact tag.
|
||||
|
||||
4. **Scaffold the website post**:
|
||||
```bash
|
||||
git commit -m "chore(release): v0.24.0"
|
||||
git tag v0.24.0
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:blog
|
||||
```
|
||||
## Push and External Effects
|
||||
|
||||
Output is `apps/website/src/content/blog/v0-XX-release-notes.mdx` with
|
||||
`draft: true`. The narrative intro, headlines, and `description` are
|
||||
editorial — fill every `TODO` by hand. One post per **minor** version:
|
||||
for a patch release, edit the existing post (the scaffold refuses to
|
||||
overwrite without `--force`).
|
||||
Push only the intended branch and tag; never use broad `git push --tags`.
|
||||
|
||||
5. **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.
|
||||
```bash
|
||||
git push --atomic origin \
|
||||
HEAD:refs/heads/master \
|
||||
refs/tags/v0.24.0
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm nx run electron-backend:build-e2e # once
|
||||
pnpm run release:screenshots # all manifest shots, dark+light
|
||||
```
|
||||
Master and `v*` pushes can publish Docker images. The tag build creates a draft
|
||||
GitHub release. Verify authored text plus generated commits and all required
|
||||
macOS, Windows, DEB, RPM, Pacman (`.pacman`/`.pkg.tar.*`), AppImage, Snap,
|
||||
Flatpak, updater metadata, blockmaps, and
|
||||
`linux-frame-copy-runtime-sources.tar.xz`.
|
||||
|
||||
Output goes to `apps/website/public/blog/v0-XX/screenshots/`. New feature
|
||||
to showcase = new entry in `tools/release/screenshots.manifest.json`
|
||||
(slug must match the note's `screenshot:` field). The run aborts and
|
||||
deletes its frames on any guard violation (real-DB touch, external
|
||||
request, credential-shaped text in frame, TMDB active).
|
||||
After verification, manually publish the GitHub release. That publication
|
||||
automatically verifies its Snap assets and uploads them to `edge`.
|
||||
Installed-Snap smoke and candidate/stable promotion remain manual. Keep the
|
||||
blog draft during artifact verification; publish it in a follow-up commit and
|
||||
verify the website deployment.
|
||||
|
||||
6. **Consume the notes** (the only destructive step):
|
||||
## Failure Safety
|
||||
|
||||
```bash
|
||||
node tools/release/build-release-notes.mjs --consume
|
||||
```
|
||||
Missing CHANGELOG section: regenerate, commit, delete the bad tag locally and
|
||||
remotely only after resolving its exact target, then retag. Never publish a
|
||||
draft until the source archive and Snap contract pass.
|
||||
|
||||
7. **Commit, tag, push**:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
8. **Verify the draft release** once `build-and-make.yaml` finishes: 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 to `draft: 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 website `v0-XX` blog assets, announce in Telegram.
|
||||
The `.codex` and `.claude` copies of this skill must remain byte-identical.
|
||||
@@ -1,70 +1,49 @@
|
||||
---
|
||||
name: release-notes
|
||||
description: Write the .changes/ release note that every PR with a user-visible change must include. Use when creating or finishing a PR that changes behavior in apps/ or libs/, when the "Release note gate" CI check fails, or when deciding whether the no-release-note label applies.
|
||||
description: "Use when a change may need a .changes note, the Release note gate fails, or deciding whether type: internal or no-release-note applies."
|
||||
---
|
||||
|
||||
# Release Notes (`.changes/`)
|
||||
# Release Notes
|
||||
|
||||
Every PR with a user-visible change adds **one** note file under `.changes/`.
|
||||
At release time the notes become the GitHub release body, the `CHANGELOG.md`
|
||||
section, and the website blog scaffold. CI enforces this: the **Release note
|
||||
gate** check fails any PR that touches runtime code under `apps/` or `libs/`
|
||||
without an added `.changes/*.md` file or the `no-release-note` label.
|
||||
Every user-visible change gets one direct `.changes/<area>-<slug>.md` file. The
|
||||
area matches the conventional-commit scope; the body is present tense, user
|
||||
language, one to three sentences, and at most 400 characters.
|
||||
|
||||
## File format
|
||||
|
||||
Name: `.changes/<area>-<short-slug>.md` — `area` matches the
|
||||
conventional-commit scope of the PR.
|
||||
## Format
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: feature
|
||||
area: playback
|
||||
issues: [1187]
|
||||
screenshot: up-next-rail
|
||||
type: fix
|
||||
area: stalker
|
||||
issues: [1234]
|
||||
screenshot: optional-manifest-slug
|
||||
---
|
||||
|
||||
Series now show an "Up Next" rail beside the player on wide windows: the rest
|
||||
of the current season, watch progress, and click-to-play inline.
|
||||
Stalker series now resume the correct episode.
|
||||
```
|
||||
|
||||
| Field | Required | Value |
|
||||
| ------------ | -------- | ---------------------------------------------------- |
|
||||
| `type` | yes | `breaking` / `feature` / `fix` / `perf` / `internal` |
|
||||
| `area` | yes | lowercase slug = conventional-commit scope |
|
||||
| `issues` | no | `[1187]` or bare `1187` — issues this PR closes |
|
||||
| `screenshot` | no | slug from the release screenshot manifest |
|
||||
`type` is `breaking`, `feature`, `fix`, `perf`, or `internal`. Omit optional
|
||||
fields instead of inventing values. Never add a version or PR number.
|
||||
|
||||
- **No version field.** The release version is chosen at release time.
|
||||
- **Never write a PR number.** The generator resolves it from git.
|
||||
- Unknown keys fail validation — this is what catches typos like `scopr:`.
|
||||
`internal` records invisible maintenance. It stays collapsed in `CHANGELOG.md`
|
||||
but is omitted from the blog and the authored public GitHub body. GitHub's
|
||||
generated commit list may still mention the underlying commits.
|
||||
|
||||
## Writing the body
|
||||
## Skip or Label
|
||||
|
||||
One to three sentences, present tense, max 400 characters, **written for a
|
||||
user, not a reviewer**:
|
||||
The gate auto-exempts website, E2E and mock-server apps, `*.spec.{js,ts}`,
|
||||
`*.e2e.{js,ts}`, snapshots, any `/testing/` path, and Markdown. Other test-only,
|
||||
docs, CI, workflow, or pure-refactor PRs use `no-release-note` when the gate
|
||||
would otherwise require a note. At least one newly added direct
|
||||
`.changes/*.md` file satisfies the gate.
|
||||
|
||||
- ❌ "Refactor `WebVideoControlsAdapter` to hoist volume state"
|
||||
- ✅ "The player now remembers volume between episodes"
|
||||
- ❌ "Fix off-by-one in `resolveEnrichmentSeasonNumber`"
|
||||
- ✅ "Series with a season marker in the title no longer show the wrong season"
|
||||
|
||||
`type: internal` is for changes worth recording but invisible to users
|
||||
(dependency bumps with behavior risk, packaging moves). They are excluded
|
||||
from the release body and blog, and collapsed in `CHANGELOG.md`.
|
||||
|
||||
## When to skip (`no-release-note` label)
|
||||
|
||||
Test-only changes, docs, CI/workflow plumbing, pure refactors with no
|
||||
behavior change. The gate auto-exempts `*.spec.ts`, `*.e2e.ts`,
|
||||
`__snapshots__/`, `apps/website/`, `apps/*-e2e/`, `apps/*-mock-server/`,
|
||||
`libs/shared/testing/` and `*.md` — if only those changed, no label needed.
|
||||
|
||||
## Verify before finishing
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
pnpm run release:notes:validate
|
||||
```
|
||||
|
||||
Full format reference: `.changes/README.md`. Gate policy:
|
||||
Full format: `.changes/README.md`. Gate policy:
|
||||
`tools/release/check-release-note-gate.mjs`.
|
||||
|
||||
The `.codex` and `.claude` copies of this skill must remain byte-identical.
|
||||
@@ -1450,15 +1450,12 @@ jobs:
|
||||
if [ "${IS_TAG_BUILD}" = "true" ]; then
|
||||
NAME="Release v${VERSION}"
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
# Authored release notes: the release flow writes this
|
||||
# CHANGELOG section from .changes/*.md before tagging
|
||||
# (see .changes/README.md), so at tag time the changelog
|
||||
# is the authored source of truth. The extractor exits
|
||||
# non-zero when the section is missing, failing the
|
||||
# release rather than silently shipping PR-title-only
|
||||
# notes. generate_release_notes stays on below, so the
|
||||
# GitHub commit list still renders under this body.
|
||||
BODY="$(node tools/release/extract-changelog-section.mjs "${VERSION}")"
|
||||
# The full committed CHANGELOG retains internal notes,
|
||||
# while the tag release uses authored public extraction.
|
||||
# An internal-only release intentionally has no authored
|
||||
# public text. GitHub's generated commit list remains
|
||||
# separate below.
|
||||
BODY="$(node tools/release/extract-changelog-section.mjs --public "${VERSION}")"
|
||||
elif [ "${EVENT_NAME}" = "pull_request" ]; then
|
||||
NAME="v${VERSION} — PR #${PR_NUMBER} @ ${SHORT_SHA} [test]"
|
||||
TAG="test-pr-${PR_NUMBER}"
|
||||
|
||||
@@ -40,10 +40,13 @@ This file provides guidance to coding agents working in this repository.
|
||||
- Any change a user could notice — new behavior, changed behavior, bug fix, performance win, breaking change — must add one note file under `.changes/` in the same PR. Format, field table, and writing rules: `.changes/README.md`.
|
||||
- Name it `<area>-<short-slug>.md`; `area` matches the conventional-commit scope. There is no version field — the release version is chosen at release time.
|
||||
- Write the body for a user, not a reviewer: "the player now remembers volume between episodes", not "hoist volume state into the session". Max 400 characters; depth belongs in the release blog post.
|
||||
- `type: internal` records invisible maintenance. Internal notes stay collapsed in `CHANGELOG.md`, are omitted from the blog scaffold, and are removed from the authored public GitHub body by `extract-changelog-section.mjs --public`; GitHub's generated commit list remains separate, so an internal-only release can have an empty authored body.
|
||||
- Skip the note for test-only changes, docs, CI/workflow plumbing, and pure refactors with no behavior change. When skipping on a PR that touches `apps/**` or `libs/**`, apply the `no-release-note` label.
|
||||
- CI enforces this: the "Release note gate" job in `.github/workflows/ci.yml` fails PRs that change runtime code without an added `.changes/*.md` or the label (policy in `tools/release/check-release-note-gate.mjs`; tests/e2e/website/mock-server/docs paths are auto-exempt).
|
||||
- The `release-notes` skill covers writing notes; the `release-cut` skill covers the full release sequence.
|
||||
- Validate before finishing: `pnpm run release:notes:validate`.
|
||||
- Pushes to `master` and `v*` can publish Docker images. A `v*` tag build creates a draft GitHub release.
|
||||
- Publishing the GitHub release verifies its Snap assets and automatically uploads them to `edge`; installed-Snap smoke and candidate/stable promotion remain manual.
|
||||
- Release-post screenshots come only from the release capture script running against the mock servers. Never add a screenshot taken from a real playlist or account to `apps/website/public/blog/**` — real streams, logos, and metadata are copyrighted, and credentials must never reach a published image.
|
||||
- Final task summaries should state whether a release note was added or why it was skipped.
|
||||
|
||||
|
||||
@@ -31,10 +31,13 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
- Any change a user could notice — new behavior, changed behavior, bug fix, performance win, breaking change — must add one note file under `.changes/` in the same PR. Format, field table, and writing rules: `.changes/README.md`.
|
||||
- Name it `<area>-<short-slug>.md`; `area` matches the conventional-commit scope. There is no version field — the release version is chosen at release time.
|
||||
- Write the body for a user, not a reviewer: "the player now remembers volume between episodes", not "hoist volume state into the session". Max 400 characters; depth belongs in the release blog post.
|
||||
- `type: internal` records invisible maintenance. Internal notes stay collapsed in `CHANGELOG.md`, are omitted from the blog scaffold, and are removed from the authored public GitHub body by `extract-changelog-section.mjs --public`; GitHub's generated commit list remains separate, so an internal-only release can have an empty authored body.
|
||||
- Skip the note for test-only changes, docs, CI/workflow plumbing, and pure refactors with no behavior change. When skipping on a PR that touches `apps/**` or `libs/**`, apply the `no-release-note` label.
|
||||
- CI enforces this: the "Release note gate" job in `.github/workflows/ci.yml` fails PRs that change runtime code without an added `.changes/*.md` or the label (policy in `tools/release/check-release-note-gate.mjs`; tests/e2e/website/mock-server/docs paths are auto-exempt).
|
||||
- The `release-notes` skill covers writing notes; the `release-cut` skill covers the full release sequence.
|
||||
- Validate before finishing: `pnpm run release:notes:validate`.
|
||||
- Pushes to `master` and `v*` can publish Docker images. A `v*` tag build creates a draft GitHub release.
|
||||
- Publishing the GitHub release verifies its Snap assets and automatically uploads them to `edge`; installed-Snap smoke and candidate/stable promotion remain manual.
|
||||
- Release-post screenshots come only from the release capture script running against the mock servers. Never add a screenshot taken from a real playlist or account to `apps/website/public/blog/**` — real streams, logos, and metadata are copyrighted, and credentials must never reach a published image.
|
||||
- Final task summaries should state whether a release note was added or why it was skipped.
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"{workspaceRoot}/tools/release/build-release-notes.mjs",
|
||||
"{workspaceRoot}/tools/release/screenshot-guards.mjs",
|
||||
"{workspaceRoot}/tools/release/screenshots.manifest.json",
|
||||
"{workspaceRoot}/.github/workflows/build-and-make.yaml",
|
||||
"{workspaceRoot}/tools/release/release-notes.test.mjs",
|
||||
"{workspaceRoot}/tools/release/release-note-gate.test.mjs",
|
||||
"{workspaceRoot}/tools/release/build-release-notes.test.mjs",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { after, describe, it } from 'node:test';
|
||||
@@ -610,6 +610,21 @@ describe('extractPublicSection', () => {
|
||||
});
|
||||
|
||||
describe('extract-changelog-section CLI contracts', () => {
|
||||
it('uses public extraction for authored tag-release text', () => {
|
||||
const workflow = readFileSync(
|
||||
new URL(
|
||||
'../../.github/workflows/build-and-make.yaml',
|
||||
import.meta.url
|
||||
),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/extract-changelog-section\.mjs --public "\$\{VERSION\}"/
|
||||
);
|
||||
});
|
||||
|
||||
it('parses the public flag', () => {
|
||||
assert.deepEqual(parseExtractArguments(['--public', '0.24.0']), {
|
||||
version: '0.24.0',
|
||||
|
||||
Reference in new issue
Block a user