Ctrl K
ComunidadGuías…

RELEASING.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Releasing Lamb

Maintainer checklist for cutting a release. Lamb ships from the `release`
branch (see `BRANCHES`); `main` is the active development branch. Versions are
plain SemVer tags (`0.9.0`); pre-releases use a suffix (`0.9.0-rc1`). There is
no version string in the code — **the Git tag is the source of truth**.

> **Run everything below from Lamb's devbox shell** (`devbox shell` in the repo)
> or via `devbox run -- …`. The pre-push hook runs the test suite, which needs
> PHP — a bare shell (or the *global* devbox) won't have it.

## 1. Pre-flight (on `main`)

- [ ] All intended PRs are merged into `main`; nothing release-worthy is still open.
- [ ] Working tree is clean and `main` is up to date: `git checkout main && git pull`.
- [ ] Tests pass: `vendor/bin/codecept run` (Unit + Acceptance).
      Acceptance needs `.env` (`SITE_URL`, `LAMB_TEST_PASSWORD`); generate it with
      `LAMB_WRITE_TEST_PASSWORD=1 php make-password.php <pw>` so the cleartext
      `LAMB_TEST_PASSWORD` is written (it is omitted by default). Acceptance starts
      its own server, so don't have another server on the test port.
- [ ] Static checks pass: `viv run lint` && `viv run analyse`.
- [ ] Docs are accurate for any user-facing change (`docs/`, `README.md`).
- [ ] `changelog.d/` has a fragment for every user-visible change (see
      `changelog.d/README.md`). Catch anything missed from the milestone's
      closed issues: `scripts/changelog.sh '<milestone>' <version>` prints a
      generated section to compare against; hand-written bullets beat generated
      titles, so add a fragment for what's missing rather than replacing the
      block.
- [ ] Outstanding **major** dependency updates reviewed. Patch/minor bumps
      auto-merge, so majors are the only ones that accumulate — and a stale
      major is how you end up on a branch that no longer gets security fixes.
      List them without needing an install:
      `viv outdated` and `pnpm outdated`.
      Either take the bump now (its own PR, not this release) or consciously
      leave it; check the branch you're on is still supported upstream.

## 2. Choose the version

- [ ] Decide the new version from the change set (SemVer):
      patch = fixes only, minor = new features, major = breaking changes.
- [ ] **Pre-1.0, the minor slot is the major slot.** SemVer says only that
      "anything MAY change at any time" under `0.y.z`, so it gives no boundary
      of its own; the convention every package manager encodes (`^0.14.0`
      resolves `>=0.14.0 <0.15.0`) makes `0.y` the compatibility boundary, and
      that is the one Lamb uses. So a breaking change bumps `0.14.0` to
      `0.15.0`, not to `1.0.0` — read "major" above as `0.y` until 1.0.
- [ ] **Anything deprecated in the last published version comes out in this
      one.** A deprecation buys exactly one published major (pre-1.0: one
      `0.y`) of grace, so the removal is due in the next one — not "a future
      release". Check for anything owed before you settle the version, since
      the removals are breaking and shape it:
      `grep -rn 'deprecat' src bin docs`.
- [ ] Confirm it's unused: `git tag | sort -V | tail`.
- [ ] If cutting a pre-release first, use an `-rcN` suffix and mark it
      pre-release in step 6.

## 3. Generate end-user release notes

Notes are for **people running a Lamb blog**, not contributors. Start from the
fragments in `changelog.d/`, then curate.

- [ ] Promote the changelog: run
      `scripts/changelog-assemble.sh <version> [<date>]` from the repository
      root (add `--dry-run` to preview the section first). It inserts
      `## [<version>] - <date>` below `## [Unreleased]`, updates the link lines
      at the bottom and deletes the fragments it used. A fragment can't hold
      prose, so add any intro sentence a section needs (such as the release
      that removes the deprecated keys) by hand afterwards. This lands in the
      release PR (step 4).
- [ ] Cross-check against the commit list for anything the changelog missed:

```sh
# Everything on main since the last release tag (use the previous final tag):
git log --format='- %s' <last-tag>..main
```

- [ ] **Keep** changes an end user would notice: new/changed features, bug
      fixes affecting the blog or admin, new config keys, install/upgrade
      requirements (e.g. a newly required PHP extension), deployment changes
      (Docker/FrankenPHP/Nginx/DDev/Devbox).
- [ ] **Drop** internal-only changes: dev-environment tooling (e.g. Workshop),
      CI, tests, refactors, code-comments/`CLAUDE.md`/`DECISIONS.md`, and
      dependency bumps with no user-visible effect.
- [ ] Rewrite each kept line in plain language (what changed for the user, not
      the PR title). Group under **Added / Changed / Fixed**.
- [ ] **Breaking changes go first, always.** If the release removes or changes
      anything an existing install depends on — a removed CLI entry point or
      config key, a renamed theme part, a changed default, a dropped platform
      requirement — open the notes with a **⚠️ Breaking changes** section,
      above Added/Changed/Fixed. One line per change, each saying what broke
      and what to use instead. A reader deciding whether to upgrade must see
      this without scrolling; never leave it to be discovered further down.
- [ ] Call out anything else requiring action on upgrade in an **Upgrade
      notes** section after the grouped changes (e.g. "install the
      `pdo_mysql` PHP extension", config changes).
- [ ] Save the notes to a temp file (e.g. `/tmp/notes.md`) for step 5.

## 4. Promote `main` to `release` via PR

Direct pushes to `release` are rejected by the branch ruleset (`GH013` —
changes must go through a pull request), so promotion happens as a PR merged
with a **merge commit** (not squash/rebase, to keep histories connected):

```sh
gh pr create --base release --head main \
  --title "Release <version>" \
  --body "Promote main to release for <version>."
gh pr merge --merge --subject "Release <version>"
```

- [ ] Confirm the **release-verify** check is green on the PR before merging.
      It runs the Acceptance suite against the Docker/FrankenPHP release image
      and an nginx + php-fpm install — the well-travelled production paths —
      in addition to `ci`'s built-in-server run.
- [ ] If the PR reports `BEHIND`, `release` has commits not on `main` (e.g.
      old release merges). Sync first: branch from `main`, `git merge
      origin/release` (a merge commit, no content changes expected), PR that
      into `main`, then re-check the release PR.
- [ ] Resolve any conflicts (uncommon — `main` is the source of truth, though
      `release` may also carry the occasional release-only commit).
- [ ] Re-run `vendor/bin/codecept run` on `release` to confirm green.

## 5. Tag and create the GitHub release

`gh release create` creates the tag itself at `--target release` (the merged
branch tip) and publishes the release in one step — no separate `git tag` needed.

```sh
gh release create <version> \
  --target release \
  --title "<version>" \
  --notes-file /tmp/notes.md
# add --prerelease for an -rcN tag
# add --latest to mark a final release as the latest
```

**No local `gh`?** The `Cut release` workflow (`.github/workflows/cut-release.yml`)
does the same `gh release create --target release` server-side from a
`workflow_dispatch`, so it can be driven without a local checkout or token (e.g.
from an automation session that can trigger workflows but not create releases):

```sh
gh workflow run cut-release.yml \
  -f version=<version> \
  -f prerelease=true \
  -f notes="$(cat /tmp/notes.md)"
# omit prerelease (or set =false) for a final; add -f latest=true to mark it latest
```

It tags the current `release` tip, so promote main → release (step 4) first.

- [ ] Merge the release PR (step 4) first, so `--target release` tags the
      intended commit. If the tag landed on the wrong commit, move it — the
      release object and its notes follow the tag:
      `git tag -f -m "<version>" <version> origin/release && git push -f origin <version>`.
      The `release: published` event will **not** re-fire for a moved tag; use
      the `workflow_dispatch` re-run from step 6 instead.
- [ ] Final releases: pass `--latest`. Pre-releases: pass `--prerelease` and
      do **not** mark latest.
- [ ] Verify: `gh release view <version>`, and `git fetch --tags` to pull the
      tag `gh` created.

## 6. Post-release

- [ ] Publishing the release triggers the `Release artifacts` workflow. Verify it
      attached `lamb-<version>.tar.gz` to the release (`gh release view <version>`).
      Re-run via `gh workflow run release-artifacts.yml -f tag=<version>` if needed.
- [ ] Open an announcement discussion for the release in the **Announcements**
      category, linking the release tag with a short note. Match the existing
      per-release discussions (e.g. `Lamb 0.14.0`):

      ```sh
      REPO_ID=$(gh api graphql -f query='{repository(owner:"svandragt",name:"lamb"){id}}' --jq '.data.repository.id')
      gh api graphql -f query='mutation($repo:ID!,$cat:ID!,$title:String!,$body:String!){createDiscussion(input:{repositoryId:$repo,categoryId:$cat,title:$title,body:$body}){discussion{url}}}' \
        -f repo="$REPO_ID" -f cat="DIC_kwDOJIhg0c4CWhS7" \
        -f title="Lamb <version>" \
        -f body="https://github.com/svandragt/lamb/releases/tag/<version>

      <short note>" --jq '.data.createDiscussion.discussion.url'
      ```
- [ ] Announce / update any demo site if applicable.
- [ ] Note that the Docker/Devbox/DDev users pull from `release`; confirm a
      clean checkout of `release` installs and runs.
- [ ] If a `next` branch exists (work parked for the next version — see
      `BRANCHES`), open a PR to merge it into `main` now that the release is
      out:

      ```sh
      git fetch origin
      git show-ref --verify --quiet refs/remotes/origin/next && \
        gh pr create --base main --head next \
          --title "Merge next into main" \
          --body "Bring parked next-version work into main after the <version> release."
      ```

## Related

- `BRANCHES` — branch roles (`main`, `release`, `next`, pinned).
- `CONTRIBUTING` — contribution workflow.
- `docs/upgrading.md` — what end users run to upgrade.