Skip to content

Release automation - #6352

Open
swissspidy wants to merge 18 commits into
mainfrom
try/release-workflow
Open

swissspidy wants to merge 18 commits into
mainfrom
try/release-workflow

Conversation

@swissspidy

@swissspidy swissspidy commented Jul 21, 2026

Copy link
Copy Markdown
Member

Needs rigorous testing. Requires some new secrets too.

How it fits together

  1. Prepare Release (manual dispatch, X.Y.0 only) validates and tags. validate holds no secrets and is where the release Phar and Composer run; tag holds ACTIONS_BOT, tags exactly the commits validate chose, and dispatches Release from main.
    • wp-cli/wp-cli is tagged at the commit the bundle's composer.lock locks (must be on main, must carry the version).
    • wp-cli/wp-cli-bundle is tagged at main, which must have the same tree as the commit wp-cli/builds records the release Phar was built from.
  2. Release (dispatch only, no tag trigger) creates the draft releases with Phar, manifest and checksums. It never touches wp-cli/builds, never turns a published release back into a draft, and fails if a signed draft's bytes would change.
  3. Signing stays manual. The maintainer downloads the Phar from the draft, signs it, and attaches .asc/.gpg to both drafts.
  4. Post-Release Automation (on released) verifies checksums and both signatures against the committed public key (.github/release-signing-key.asc, fingerprint pinned in the workflow) before executing the Phar, then promotes Phar, manifest, checksums and signatures to stable in wp-cli/builds, opens the version bump / constraint reset PRs, and closes milestones. The branch alias is left to the org-wide Check Branch Alias workflow (X.x-dev).

Required secrets, environment & permissions

These have to be in place before this can run against a real release.

Secrets

Secret Used by Notes
ACTIONS_BOT all three workflows Existing token, but it needs broader scope than before — see below.

No GPG secrets are needed. The releases@wp-cli.org key pair is deliberately not uploaded to GitHub. Only its public key is committed, and post-release refuses anything not signed with it.

release environment

Every job that holds ACTIONS_BOT runs in an environment called release. It has to be created in the repository settings with:

  • Required reviewers: the maintainers who may approve a release.
  • Deployment branches and tags: main plus v*.*.0. Post-Release Automation runs on the release event, where GITHUB_REF is the tag, so main alone would reject it.

Jobs that execute the Phar or Composer run outside the environment and hold no secrets.

Tag protection

Nothing runs from a tag push anymore, so a modified workflow on a tagged commit cannot reach ACTIONS_BOT. A ruleset protecting v* tags (creation restricted to the bot and maintainers) is still worth adding so tags cannot be created or moved by hand.

ACTIONS_BOT scope

Previously this token was only used against other repositories (wp-cli/builds, wp-cli/wp-cli-bundle). It is now also used against wp-cli/wp-cli itself, and needs to be able to:

  • push tags to wp-cli/wp-cli and wp-cli/wp-cli-bundle (prepare-release.yml).
  • create and edit draft releases on both repositories (release.yml).
  • push to wp-cli/builds gh-pages (post-release.yml).
  • push branches and open pull requests on wp-cli/wp-cli and wp-cli/wp-cli-bundle (post-release.yml). No bypass of main branch protection is needed: the version bump and the constraint reset are pull requests that a maintainer approves. Auto-merge is enabled on them best-effort.
  • close milestones across the org (post-release.yml).

Worth considering as a follow-up: replacing the long-lived ACTIONS_BOT PAT with actions/create-github-app-token, so each run gets a short-lived, per-repository token instead of one credential with write access across several repositories.

Dependencies

Still outstanding

  • The recovery paths (partial tag push, lookup error, published-release rerun, changed bytes after signing, post-bump rerun, lock freshness, missing deb) are handled in the workflows but have only been exercised by reading and linting; a dry run against a real release candidate is still needed.

Summary by CodeRabbit

  • New Features

    • Added automated release workflows triggered by version tags or manual runs.
    • Releases now automatically build, checksum, and attach artifacts, with generated release notes and contributor information.
    • Post-release automation updates versions, closes completed milestones, and triggers website updates.
  • Documentation

    • Simplified release checklist instructions to focus on bundle pull requests and tag creation.
    • Updated post-release tasks for Homebrew, blog publication, and release announcements.

@swissspidy swissspidy added the scope:distribution Related to distribution label Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The release process now validates versions, creates release tags, promotes and signs Phar artifacts, creates draft releases, verifies published assets, updates development repositories, closes milestones, and dispatches website notifications. The checklist documents the automated flow and remaining manual tasks.

Changes

Release automation

Layer / File(s) Summary
Release validation and tag preparation
.github/workflows/prepare-release.yml, .github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md
The Prepare Release workflow validates stable versions and release artifacts, creates bundle-first annotated tags, and supports dry runs.
Tagged release build and draft publishing
.github/workflows/release.yml, .github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md
The release workflow validates tags, generates release notes, promotes and signs artifacts, updates wp-cli/builds, and creates draft releases for both repositories.
Published-release maintenance and dispatch
.github/workflows/post-release.yml
The post-release workflow verifies published assets and build parity, updates development versions and dependencies, closes milestones, and conditionally dispatches a website notification.
Release checklist alignment
.github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md
The checklist replaces manual workflow steps with automation instructions and retains Homebrew, blog, and Twitter/Slack tasks as manual follow-up.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Maintainer
  participant PrepareRelease
  participant Bundle as wp-cli/wp-cli-bundle
  participant WPCLI as wp-cli/wp-cli
  participant ReleaseWorkflow
  participant Builds as wp-cli/builds
  participant Releases as GitHub Releases
  Maintainer->>PrepareRelease: Start stable release
  PrepareRelease->>Bundle: Create bundle tag
  PrepareRelease->>WPCLI: Create main repository tag
  WPCLI->>ReleaseWorkflow: Trigger tagged release
  ReleaseWorkflow->>Builds: Promote, checksum, and sign artifacts
  ReleaseWorkflow->>Releases: Create draft releases with assets
Loading
sequenceDiagram
  participant Release as Published release
  participant PostReleaseWorkflow
  participant WPCLI as wp-cli/wp-cli
  participant Bundle as wp-cli/wp-cli-bundle
  participant WPDev as wp-cli/wp-cli-dev
  participant Website as wp-cli.github.com
  Release->>PostReleaseWorkflow: Trigger stable release workflow
  PostReleaseWorkflow->>WPCLI: Verify assets and update development version
  PostReleaseWorkflow->>Bundle: Reset dependency and alias
  PostReleaseWorkflow->>WPDev: Close released milestones
  PostReleaseWorkflow->>Website: Dispatch release notification
Loading

Suggested reviewers: schlessera

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: automating release tagging, artifact verification, and post-release processing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch try/release-workflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md:
- Around line 94-102: Update
.github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md lines 94-102 to instruct
pushing the matching v2.x.0 tag to wp-cli/wp-cli-bundle before creating its
draft release. Update both gh release create commands in
.github/workflows/release.yml lines 125-130 and 139-144 to include --verify-tag,
ensuring releases fail instead of synthesizing or accepting an incorrect tag.

In @.github/workflows/post-release.yml:
- Around line 75-85: Update the “Reset framework constraint and branch-alias”
workflow step so Composer refreshes composer.lock after changing composer.json,
then stage and commit both files before pushing. Preserve the existing dev-main
requirement and branch-alias update while ensuring the lockfile matches them.
- Around line 25-37: Add a validation step in the workflow before any changes
are pushed to main that reads wp-cli/VERSION and compares it with VERSION
derived from TAG; if they differ, fail the job and skip subsequent milestone or
branch updates. Keep the existing next-version calculations and outputs
unchanged for matching versions.

In @.github/workflows/release.yml:
- Around line 107-116: Update the stable-build commit/push step to detect
whether staged changes exist and skip commit and push when there is no diff. In
the draft release steps, replace create-only commands with `gh release edit` for
existing drafts and `gh release upload --clobber` for assets, preserving
rerun-safe behavior when the tag already exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4d091471-292f-4a64-a612-e44c9482c214

📥 Commits

Reviewing files that changed from the base of the PR and between a39887a and 2923241.

📒 Files selected for processing (3)
  • .github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md
  • .github/workflows/post-release.yml
  • .github/workflows/release.yml

Comment thread .github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md Outdated
Comment thread .github/workflows/post-release.yml Outdated
Comment thread .github/workflows/post-release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/post-release.yml:
- Around line 48-58: Update the version validation step around
CURRENT_BASE_VERSION to also accept the calculated NEXT_ALPHA value as a valid
already-bumped state, allowing retries after the later push while continuing to
reject versions unrelated to RELEASED_VERSION. Preserve the existing mismatch
error and failure behavior for all other values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ca03b773-50a0-4233-9372-7a8f0e3b375f

📥 Commits

Reviewing files that changed from the base of the PR and between 2923241 and 9bacb6d.

📒 Files selected for processing (3)
  • .github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md
  • .github/workflows/post-release.yml
  • .github/workflows/release.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md
  • .github/workflows/release.yml

Comment thread .github/workflows/post-release.yml Outdated
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@swissspidy
swissspidy requested a review from schlessera July 22, 2026 11:57
@swissspidy
swissspidy marked this pull request as ready for review August 4, 2026 21:47
@swissspidy
swissspidy requested a review from a team as a code owner August 4, 2026 21:47
Copilot AI lite review requested due to automatic review settings August 4, 2026 21:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces GitHub Actions-based automation for WP-CLI’s release and post-release processes, and updates the regular release checklist to align with the new workflow-driven steps.

Changes:

  • Added a Release workflow to generate release notes, promote/sign stable PHAR artifacts, and create/update draft releases for wp-cli/wp-cli and wp-cli/wp-cli-bundle.
  • Added a Post-Release Automation workflow to bump versions/branch-aliases, reset bundle dependencies, close milestones, and dispatch a website notification after a release is published.
  • Simplified the regular release checklist to reflect the new automated steps and remaining manual tasks.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
.github/workflows/release.yml New workflow to promote/sign artifacts and create/update draft GitHub releases.
.github/workflows/post-release.yml New workflow to automate post-release version bumps, dependency reset, milestone closure, and website dispatch.
.github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md Updated checklist to match the new automated release flow and remaining manual steps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/release.yml Outdated
swissspidy and others added 4 commits August 4, 2026 23:57
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Tagging by hand meant pushing two tags in the right order: the wp-cli tag
triggers the release workflow, which needs the wp-cli-bundle tag to already
exist before it can attach a draft release to it. Nothing enforced that.

This workflow tags the bundle first and wp-cli second, and gates both on the
checks that were previously spread across the checklist:

- the VERSION file on main matches the version being released
- wp-cli-bundle has the framework constraint locked to that version
- wp-cli-release.phar in the builds repo actually reports that version
- neither tag exists yet

Tags are pushed with ACTIONS_BOT rather than GITHUB_TOKEN, because pushes made
with GITHUB_TOKEN do not trigger further workflow runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xw2XwqE3C4cGGZ6EbS8p3V
Correctness fixes:

- post-release ran `wp maintenance close-released`, which is not a registered
  command. wp-cli-dev's bootstrap registers Release_Command as `maintenance
  release`, with `close-released` as a subcommand of that, so the milestones
  were never going to close.

- post-release triggered on `release: [published]`, which also fires for
  pre-releases. Publishing something like v3.0.0-beta.1 would have bumped
  versions, reset the bundle constraint and closed milestones. It now uses
  `released`, matching trigger-handbook-regeneration.yml and
  check-branch-alias.yml, and rejects non-stable versions outright.

- The version bump assumed every release is a minor bump and failed the whole
  run when it was not. A patch release cut from a maintenance branch, or a
  re-run after the bump already happened, now skips the bump with a notice and
  still closes milestones and dispatches to the website.

- post-release pushed to wp-cli/wp-cli main with GITHUB_TOKEN. main is
  branch-protected, and pushes made with GITHUB_TOKEN do not trigger downstream
  workflows such as check-branch-alias. Both repos now use ACTIONS_BOT.

Safety:

- The stable Phar was promoted, signed and published without ever checking what
  was in it. wp-cli-release.phar is produced by another workflow in another
  repository, so nothing tied it to the tag being released; a stale or failed
  bundle build would have been signed and shipped as stable. The workflow now
  refuses to continue unless the Phar reports the version being released, and
  runs the `--info` sanity check the checklist used to ask for by hand.

- Signatures and checksums are verified after they are generated, so a
  mis-imported key or an empty hash file fails the run instead of being
  published.

- Both workflows now name bash explicitly, which enables `-o pipefail`. Without
  it a failing `sha512sum` in `sha512sum ... | cut ...` produced an empty
  checksum file that was then signed and published.

- Release notes are written to separate files and asserted non-empty before
  use. `contrib-list` and `release-notes` read from the currently open
  milestone, so re-running after post-release closes them would otherwise
  overwrite good release notes with nothing. They are also uploaded as an
  artifact so they survive a later failure.

- Both workflows have a concurrency group, and the builds push retries on top
  of concurrent nightly pushes rather than failing.

- Restores the `deb/php-wpcli_latest_all.deb` symlink update, which the
  checklist dropped without automating.

Adds a verify-release job that post-release depends on. It downloads the
published assets and checks the checksums, the GPG signatures, that the Phar
runs and reports the right version, that the manifest is valid JSON, and that
the release asset is byte-identical to the stable build in wp-cli/builds. None
of the version bumps or milestone closures run unless that passes.

Both workflows also take a dry_run input that runs everything except the
pushes, releases and dispatch, printing what would have changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xw2XwqE3C4cGGZ6EbS8p3V
Tagging is now done by the Prepare Release workflow rather than by hand, so the
manual tag steps are replaced by running that workflow (with a dry run first).
Artifact verification is now automated in post-release, so the manual
verification step is narrowed to the upgrade path that automation cannot reach.

Also corrects the claim that post-release triggers handbook regeneration --
that runs from trigger-handbook-regeneration.yml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xw2XwqE3C4cGGZ6EbS8p3V

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md (1)

139-146: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Removing the Debian verification step relies on a workflow step that can silently skip.

The automated Debian symlink update in .github/workflows/release.yml Lines 227-240 emits a ::warning:: and exits 0 when php-wpcli_${VERSION}_all.deb is absent. No manual step now catches that case, so php-wpcli_latest_all.deb can stay pointed at the previous release without anyone noticing.

Either restore a short manual verification item here, or make the release workflow fail on a missing .deb. See the related comment on .github/workflows/release.yml Lines 227-240.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md around lines 139 -
146, Restore a concise manual checklist item under “Post-Release Manual Tasks”
to verify that the Debian symlink points to the newly released
php-wpcli_${VERSION}_all.deb, covering the case where the automated release
workflow skips the update because the package is missing.
.github/workflows/post-release.yml (1)

268-289: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use the bundle’s own state to gate the bundle reset.

After the first run pushes the wp-cli/wp-cli version bump, reruns compute should_bump=false and skip the wp-cli/wp-cli-bundle reset. If the first run exited before that step, the bundle remains pinned to the released framework on later reruns. The staged git diff --quiet check already makes this step safe when no bundle changes are needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/post-release.yml around lines 268 - 289, Update the
condition on the “Reset framework constraint, branch-alias and lockfile in
wp-cli/wp-cli-bundle” step to use the bundle repository’s own pending-change
state rather than steps.vars.outputs.should_bump. Preserve the existing
staged-diff safety check and commit/push behavior so reruns can reset an
unfinished bundle without creating unnecessary commits.
🧹 Nitpick comments (5)
.github/workflows/post-release.yml (1)

26-27: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Reduce the workflow token to contents: read.

Every write in this workflow authenticates with secrets.ACTIONS_BOT: the checkouts at Lines 187-195, 260-266 and 291-295, the milestone step at Line 299, and the dispatch at Line 320. The only GITHUB_TOKEN use is the fallback for gh release download at Line 80, which needs read access only.

contents: write on the default token therefore grants more than the workflow uses.

🛡️ Proposed change
 permissions:
-  contents: write
+  contents: read
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/post-release.yml around lines 26 - 27, Change the
workflow-level permissions block in post-release.yml from contents: write to
contents: read. Keep the existing ACTIONS_BOT-authenticated write operations and
the GITHUB_TOKEN fallback for gh release download unchanged.

Source: Linters/SAST tools

.github/workflows/release.yml (2)

50-65: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Non-stable tag pushes will produce a failed workflow run.

This workflow triggers on tag pushes. If someone pushes a pre-release tag such as v3.0.0-beta.1, the regex rejects it and the job exits 1. That marks a red failure on a legitimate action rather than a skip.

Consider filtering the trigger with a tag pattern, or exiting 0 with a ::notice:: for non-stable tags and gating later steps on an output. Keep exit 1 for the workflow_dispatch path, where an invalid input is an operator error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 50 - 65, Update the Determine
version step to distinguish tag-push events from workflow_dispatch using the
existing REF_NAME or event context: for non-stable pushed tags, emit a notice
and exit successfully while setting an output that prevents later release steps
from running; retain exit 1 for invalid workflow_dispatch inputs. Ensure stable
tags continue through the existing tag and version outputs.

120-128: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

The org-wide composer install is unbounded in both workflows. The wp-cli-dev pre-install-cmd hook clones every repository in the WP-CLI organization. Neither step sets timeout-minutes, so a throttled or stalled clone runs to the default six-hour job limit while holding a cancel-in-progress: false concurrency group. That blocks all later release runs.

  • .github/workflows/release.yml#L120-L128: add timeout-minutes to the "Install dependencies for maintenance tools" step.
  • .github/workflows/post-release.yml#L297-L315: add timeout-minutes to the "Close released milestones across bundle repos" step, and move the DRY_RUN check above the composer install so a dry run does not pay the clone cost.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 120 - 128, Bound the composer
install steps to prevent stalled organization-wide clones from occupying the
release concurrency group: in .github/workflows/release.yml lines 120-128, add a
suitable timeout-minutes value to the “Install dependencies for maintenance
tools” step; in .github/workflows/post-release.yml lines 297-315, add
timeout-minutes to the “Close released milestones across bundle repos” step and
move its DRY_RUN check before composer install so dry runs skip dependency
installation.
.github/workflows/prepare-release.yml (2)

79-84: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Read-only checkouts persist the default token. Both workflows check out wp-cli/builds for inspection only, and neither sets persist-credentials: false. actions/checkout therefore writes the GITHUB_TOKEN into the checked-out .git/config, where any later step or uploaded artifact can read it. zizmor reports this as artipacked at both sites.

  • .github/workflows/prepare-release.yml#L79-L84: add persist-credentials: false to the wp-cli/builds checkout. Leave the wp-cli and wp-cli-bundle checkouts at Lines 64-77 unchanged, because their persisted credentials serve the later git push steps.
  • .github/workflows/post-release.yml#L150-L159: add persist-credentials: false to the wp-cli/builds checkout, and to the wp-cli-dev checkout at Lines 291-295. Leave the wp-cli and wp-cli-bundle checkouts unchanged, because they push with secrets.ACTIONS_BOT.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prepare-release.yml around lines 79 - 84, Disable
credential persistence on the read-only wp-cli/builds checkout in
.github/workflows/prepare-release.yml lines 79-84. Also disable it on the
wp-cli/builds checkout in .github/workflows/post-release.yml lines 150-159 and
the wp-cli-dev checkout in .github/workflows/post-release.yml lines 291-295;
leave the wp-cli and wp-cli-bundle checkouts unchanged because their credentials
are required for later pushes.

Source: Linters/SAST tools


156-186: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

A partial tag push leaves the release in a state that blocks reruns.

The bundle tag is pushed first. If the second push to wp-cli/wp-cli fails, the bundle tag stays in place. A rerun then fails at the "Verify the tags do not already exist" step, because that step treats any existing tag as fatal. Recovery requires manual tag deletion in wp-cli/wp-cli-bundle.

Two options:

  • Make the existence check tolerant: allow an existing tag when it already points at the expected commit, and fail only on a mismatch.
  • Document the manual recovery step in the release checklist so an operator knows to delete the orphaned bundle tag.

Do you want me to draft the tolerant-check version?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/prepare-release.yml around lines 156 - 186, Update the
release tag verification step to allow an existing tag when it already resolves
to the expected commit, while failing on missing or mismatched tags. Apply this
consistently to both wp-cli-bundle and wp-cli so reruns after a partial push can
proceed without manual tag deletion; keep the existing fatal behavior for
conflicting tags.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/post-release.yml:
- Around line 116-148: Reorder the workflow so the “Import GPG key” and “Verify
the published GPG signatures” steps run before “Verify the published Phar runs
and reports the right version,” ensuring no Phar command executes before
signature verification. Update the import step to use a GPG_PUBLIC_KEY secret or
another public-key source, remove the private-key and passphrase inputs, and
retain the existing gpg verification commands.

In @.github/workflows/prepare-release.yml:
- Around line 141-154: Update the “Verify the tags do not already exist” step to
capture the exit status of git ls-remote separately from the grep emptiness
check for each repository. Treat any nonzero git ls-remote status as a
validation failure and exit nonzero before release tag creation, while
preserving the existing FOUND=1 behavior when an existing tag is detected.

In @.github/workflows/release.yml:
- Around line 225-240: Update the “Point the latest Debian package” step to fail
when the expected DEB_FILE is missing instead of emitting a warning and exiting
successfully. If package creation is asynchronous, add polling consistent with
the existing bundle-tag polling flow, then terminate with a failure after the
timeout; only create the php-wpcli_latest_all.deb symlink once the package
exists.

---

Outside diff comments:
In @.github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md:
- Around line 139-146: Restore a concise manual checklist item under
“Post-Release Manual Tasks” to verify that the Debian symlink points to the
newly released php-wpcli_${VERSION}_all.deb, covering the case where the
automated release workflow skips the update because the package is missing.

In @.github/workflows/post-release.yml:
- Around line 268-289: Update the condition on the “Reset framework constraint,
branch-alias and lockfile in wp-cli/wp-cli-bundle” step to use the bundle
repository’s own pending-change state rather than
steps.vars.outputs.should_bump. Preserve the existing staged-diff safety check
and commit/push behavior so reruns can reset an unfinished bundle without
creating unnecessary commits.

---

Nitpick comments:
In @.github/workflows/post-release.yml:
- Around line 26-27: Change the workflow-level permissions block in
post-release.yml from contents: write to contents: read. Keep the existing
ACTIONS_BOT-authenticated write operations and the GITHUB_TOKEN fallback for gh
release download unchanged.

In @.github/workflows/prepare-release.yml:
- Around line 79-84: Disable credential persistence on the read-only
wp-cli/builds checkout in .github/workflows/prepare-release.yml lines 79-84.
Also disable it on the wp-cli/builds checkout in
.github/workflows/post-release.yml lines 150-159 and the wp-cli-dev checkout in
.github/workflows/post-release.yml lines 291-295; leave the wp-cli and
wp-cli-bundle checkouts unchanged because their credentials are required for
later pushes.
- Around line 156-186: Update the release tag verification step to allow an
existing tag when it already resolves to the expected commit, while failing on
missing or mismatched tags. Apply this consistently to both wp-cli-bundle and
wp-cli so reruns after a partial push can proceed without manual tag deletion;
keep the existing fatal behavior for conflicting tags.

In @.github/workflows/release.yml:
- Around line 50-65: Update the Determine version step to distinguish tag-push
events from workflow_dispatch using the existing REF_NAME or event context: for
non-stable pushed tags, emit a notice and exit successfully while setting an
output that prevents later release steps from running; retain exit 1 for invalid
workflow_dispatch inputs. Ensure stable tags continue through the existing tag
and version outputs.
- Around line 120-128: Bound the composer install steps to prevent stalled
organization-wide clones from occupying the release concurrency group: in
.github/workflows/release.yml lines 120-128, add a suitable timeout-minutes
value to the “Install dependencies for maintenance tools” step; in
.github/workflows/post-release.yml lines 297-315, add timeout-minutes to the
“Close released milestones across bundle repos” step and move its DRY_RUN check
before composer install so dry runs skip dependency installation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 672436bb-7f10-4538-a664-4e01d6ebb0d2

📥 Commits

Reviewing files that changed from the base of the PR and between c9a463f and e6fd286.

📒 Files selected for processing (4)
  • .github/ISSUE_TEMPLATE/4-REGULAR_RELEASE_CHECKLIST.md
  • .github/workflows/post-release.yml
  • .github/workflows/prepare-release.yml
  • .github/workflows/release.yml

Comment thread .github/workflows/post-release.yml Outdated
Comment thread .github/workflows/prepare-release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
actions-user and others added 2 commits August 7, 2026 18:14
Nothing consumes it. wp-cli/wp-cli.github.com has no repository_dispatch
workflow and no reference to the release-published event type, and
createDispatchEvent returns 204 regardless of whether anything is listening, so
the step went green either way.

The site it targets is no longer maintained, so there is nothing to trigger.
Removed the step, the now-unused TAG job variable, and the corresponding line
in the release checklist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xw2XwqE3C4cGGZ6EbS8p3V
The site these steps target is no longer maintained, so verifying its readme,
updating homepage version references and tagging a website release are all
dead work. The regular release checklist lost the same section earlier in this
branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xw2XwqE3C4cGGZ6EbS8p3V
actions-user and others added 4 commits August 9, 2026 10:59
Homebrew switched autobump from an opt-in list to opt-out: every homebrew-core
formula is now bumped by BrewTestBot unless it declares no_autobump! or a
livecheck skip. Neither wp-cli nor wp-cli-completion opts out, so both are
bumped without anyone doing anything, typically a few hours after the release
is published since BrewTestBot polls every three hours.

Both checklists now say to confirm the bump landed rather than to perform it,
and keep the manual command only as a fallback. Also corrects the formula path,
which moved to the sharded Formula/w/ layout, and drops a stray argument that
made the bump command pass "-" to wget as a second URL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xw2XwqE3C4cGGZ6EbS8p3V
The releases@wp-cli.org key pair is not going into GitHub secrets for now,
so the workflows no longer import it:

- `release.yml` promotes the Phar and generates the checksums, but does not
  sign, and attaches 5 assets to the drafts instead of 7.
- `post-release.yml` verifies the published checksums, the Phar itself and
  that it matches the stable build, but no longer verifies signatures.

Neither workflow needs GPG_PRIVATE_KEY or GPG_PASSPHRASE anymore.

Signing goes back to being a manual step, so the release checklist gets it
back: sign the stable Phar in wp-cli/builds by hand and attach the .gpg and
.asc files to both drafts before publishing. The commit step in release.yml
now lists the checksum files explicitly rather than globbing
phar/wp-cli.phar.*, so that the signatures are only ever committed by
whoever produced them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXzEZqS2c8xEKQkfkTycyw

@schlessera schlessera left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work on this, @swissspidy.

⚠️ Before the details: I am still blocking any move of the signing process into GitHub Actions. Keeping the signing keys off GitHub has been one of the fundamental security boundaries of this project. The incentive for a supply-chain attack on the Phar is real, and I do not think an uploaded GPG key can be secured on Actions. Signing stays a manual step that makes it impossible to trigger a malicious release and have it shipped to all WP infrastructure. Let's see how far the automation can go with that constraint, and discuss the wider security implications off record. ⚠️

Request changes. The PR respects the manual signing, but it publishes the stable Phar before the signature, so the signing step no longer gates anything. That, the tag trigger and the rerun paths are the blocking items; the specifics are in the inline comments.

Concurrency controls are already in place; keep them. Assign this PR to the 3.0.0 milestone.

Next step: add coverage for the failure and recovery paths named inline (partial tag push, lookup error, published-release rerun, changed bytes after signing, post-bump rerun, lock freshness, missing deb), then a dry run showing a failed run leaves stable untouched and a rerun finishes without moving tags.

Comment thread .github/workflows/release.yml Outdated
on:
push:
tags:
- 'v*.*.*'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A tag push runs this workflow from the tagged commit even when that commit never reached main, and there is no protected-tag ruleset or release environment on this repository. Anyone with write access can tag a commit that carries a modified workflow and use ACTIONS_BOT to push to wp-cli/builds and create the releases. The X.Y.Z check at :64 only filters suffixes, it does not close this.

Either dispatch from reviewed main only, or protect v* tags and put this job in a release environment with required approval.

Comment thread .github/workflows/release.yml Outdated
name: release-notes-${{ steps.vars.outputs.version }}
path: release-assets/*.md

- name: Promote release Phar to stable & generate checksums

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stable Phar, manifest, hashes and the deb symlink are promoted here and pushed at :232 before the checklist's signing step (4-REGULAR_RELEASE_CHECKLIST.md:110). Fresh installs and wp cli update --stable get unsigned bytes for as long as signing takes, and post-release never checks that a signature exists, so the manual signing no longer gates anything.

Prepare the draft assets first, sign locally, verify against the trusted public key, then publish Phar, manifest, hashes and signatures together. The parity check in post-release.yml:146 compares the release asset with stable, so it moves after that promotion. The Phar verification at :99-123 stays where it is.

Comment thread .github/workflows/release.yml Outdated
cd builds/deb
DEB_FILE="php-wpcli_${VERSION}_all.deb"

if [ ! -f "$DEB_FILE" ]; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A missing deb only warns, and the bundle deployment builds the deb last (wp-cli-bundle deployment.yml:263,352), so a release cut right after the Phar appears leaves php-wpcli_latest_all.deb on the previous version. Wait for the full deployment and fail when the package is missing.

Comment thread .github/workflows/release.yml Outdated
mkdir -p release-assets
cd wp-cli-dev

vendor/bin/wp maintenance contrib-list --format=markdown > ../release-assets/contributors.md

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither generator is tied to the release being cut. contrib-list drops the first open milestone (.maintenance/src/Contrib_List_Command.php:100,106) and then iterates what remains, and there is exactly one open 3.0.0 milestone in framework, bundle and handbook right now, so contributors from all three are omitted. Both commands read the lock of the last closed bundle milestone (Contrib_List_Command.php:123-135, Release_Notes_Command.php:110-129), so packages added since (ability, ai, block, site-health) are missing. WP_CLI::log writes to stdout, so the empty-file guard at :150 never fires.

Pass the release milestone and the bundle lock at the new tag explicitly, and land the contrib-list fix in wp-cli-dev before relying on this output.

Comment thread .github/workflows/release.yml Outdated

if gh release view "$TAG" --repo wp-cli/wp-cli >/dev/null 2>&1; then
echo "Release $TAG already exists on wp-cli/wp-cli. Updating..."
gh release edit "$TAG" \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gh release view only checks existence. gh release edit --draft turns a published release back into a draft, and upload --clobber then replaces assets users already consumed; a rerun or a manual dispatch for an older tag rewrites a published release. Same at :334 for the bundle.

Query isDraft, update only an existing draft, keep its notes, and abort on a published release. On rerun compare the Phar bytes with what was already promoted: unchanged bytes keep their signatures, changed bytes for a version that was already signed fail.

Comment thread .github/workflows/post-release.yml Outdated
# milestone closing and the website dispatch below still need to run.
if [ "$CURRENT_BASE" = "$VERSION" ]; then
SHOULD_BUMP=true
elif [ "$CURRENT_VERSION" = "$NEXT_ALPHA" ]; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the framework bump has landed, a rerun takes this branch, should_bump=false skips the bundle reset at :254, and a run that failed between the two pushes cannot finish. Decide the bundle step from the bundle's own state.

Comment thread .github/workflows/post-release.yml Outdated
git diff --staged
else
git commit -m "Bump version to $NEXT_ALPHA"
git push origin main

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push is rejected by the current main branch protection: one approved PR is required and the default Actions token has no bypass. Decide explicitly whether this opens a PR or uses a dedicated bot identity with a documented, scoped bypass.

Comment thread .github/workflows/post-release.yml Outdated
NEXT_ALIAS: ${{ steps.vars.outputs.next_alias }}
run: |
cd wp-cli-bundle
composer require "wp-cli/wp-cli:dev-main" --no-interaction --no-install

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

composer require writes the lockfile first, then composer config at :260 changes extra.branch-alias, which is part of Composer's content hash, so the committed lock is already stale (composer validate fails on current bundle main with these exact commands). Set the alias first, or run composer update --lock --no-install after both, and finish with composer validate.

Comment thread .github/workflows/post-release.yml Outdated

# Registered as `maintenance release` in wp-cli-dev's bootstrap.php,
# with `close-released` as a subcommand of that.
vendor/bin/wp maintenance release close-released --bundle

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

close-released --bundle builds its list from the bundle lock's packages (.maintenance/src/Release_Command.php:308), so wp-cli/wp-cli, wp-cli/wp-cli-bundle and package-command (require-dev) are never closed. Pass them explicitly.

### Updating the Phar build
### Updating the Phar build & Publishing GitHub Releases

- [ ] Create a PR from the `release-x-x-x` branch in `wp-cli/wp-cli-bundle` and merge it. This will trigger the `wp-cli-release.*` builds.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging the bundle PR does not trigger the release build: the push to release-** does, merging to main builds nightly (wp-cli-bundle deployment.yml:5-8,221).

…ssing packages

Addresses the CodeRabbit review on #6352.

prepare-release.yml:
- Fail closed when `git ls-remote` errors instead of treating a failed
  lookup as "tag does not exist".
- Accept an existing tag only when it already points at the commit being
  released, and push only the missing tag, so a rerun after a partial
  push can finish. A tag pointing elsewhere is fatal; tags are never moved.
- Stop persisting credentials on the read-only wp-cli/builds checkout.

release.yml:
- Wait for the bundle deployment to finish (the Debian package is built
  last) before promoting anything, and fail when the package is missing
  instead of leaving php-wpcli_latest_all.deb on the previous release.
- Skip, rather than fail, on a pushed non-stable tag; a non-stable tag on
  workflow_dispatch still fails.
- Reduce the default token to contents: read (all writes use ACTIONS_BOT),
  stop persisting credentials on the wp-cli-dev checkout, and bound the
  org-wide composer install with timeout-minutes.

post-release.yml:
- Decide the bundle reset from the bundle's own composer.json rather than
  should_bump, so a rerun after the wp-cli bump can still finish it.
- Set the branch alias before `composer require` writes the lockfile,
  otherwise the committed lock is stale; verify with `composer validate`.
- Reduce the default token to contents: read, stop persisting credentials
  on the read-only checkouts, bound the milestone step and skip its
  composer install on dry runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmVGVSRR8Spk5ABae46VoC

@schlessera schlessera left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes. bece8bf fixes four of the seventeen points and part of a fifth: the tag lookup fails closed and a rerun after a partial push finishes without moving tags (prepare-release.yml:151-192), the bundle reset no longer depends on should_bump (post-release.yml:260), the branch alias is set before the lockfile is written and validated (post-release.yml:280-282), and a missing Debian package now fails the run (release.yml:134-161, :291). Read-only checkouts no longer persist credentials and the default token is down to contents: read.

Still open, the three blocking ones first:

  • release.yml:253-322: the stable Phar, manifest and hashes are still pushed before the manual signing step (4-REGULAR_RELEASE_CHECKLIST.md:111). Nothing verifies a signature before promotion.
  • release.yml:17,93: a tag push still runs the privileged job from the tagged commit, with no protected-tag rule and no release environment.
  • release.yml:364-381,393-410: a rerun still turns a published release back into a draft, replaces its notes and clobbers its assets. No isDraft check, no comparison against the bytes already promoted.
  • prepare-release.yml:111,159: still reads composer.json rather than the lock, and tags whatever main is at run time.
  • prepare-release.yml:56,67,75: non-zero patch versions still pass while everything is cut from main; the patch checklist is unchanged.
  • prepare-release.yml:68,76 and release.yml:109,176: the unverified Phar and composer still run in the job that holds ACTIONS_BOT; post-release.yml:311 still hands the bot token to composer install.
  • release.yml:216-217: contrib-list and release-notes still run without the release milestone and the bundle lock at the tag.
  • post-release.yml:82-90: manual dispatch still downloads without checking that the release is published and not a pre-release.
  • post-release.yml:191: the alias still becomes 3.1.x-dev while the org checker expects 3.x-dev (reusable-check-branch-alias.yml:85).
  • post-release.yml:199: the website and roadmap steps are gone with nothing replacing them.
  • post-release.yml:246,295: direct pushes to protected main; no decision yet between a PR and a documented bot bypass.
  • post-release.yml:326: close-released --bundle still misses wp-cli, wp-cli-bundle and package-command.
  • 4-REGULAR_RELEASE_CHECKLIST.md:94: merging the bundle PR does not trigger the release build; the push to release-** does.

The coverage for the failure and recovery paths and the dry run showing a failed run leaves stable untouched are still outstanding. Milestone is still unset; this is 3.0.0.

I'll add commits to help fix the remaining issues, let's see how far we can take this.

Addresses the second review round on #6352.

Signing now gates everything. The Release workflow only creates draft
releases with the Phar, manifest and checksums; nothing touches
wp-cli/builds. Post-Release Automation verifies the published assets
against the committed release signing key (.github/release-signing-key.asc,
fingerprint pinned in the workflow) before it runs the Phar, and only then
promotes Phar, manifest, checksums and signatures to stable and repoints
the Debian symlink.

Every job that executes the Phar or Composer holds no secrets; the jobs
holding ACTIONS_BOT only push tags, upload assets, push to wp-cli/builds
or open pull requests, and run in a `release` environment.

prepare-release.yml:
- Reads the framework commit from the bundle's composer.lock and tags
  wp-cli/wp-cli at that commit, which has to be on main and carry the
  version. Requires the bundle's main to have the same tree as the commit
  wp-cli/builds records the release Phar was built from.
- Checks lock freshness with `composer validate --strict`.
- Rejects non-zero patch versions; patch releases stay manual.
- Dispatches the Release workflow from main instead of relying on a tag
  push, so the reviewed workflow file is what runs.

release.yml:
- workflow_dispatch only; no tag trigger.
- Verifies the tag is on main and the Phar was built from the tagged
  bundle commit.
- Never turns a published release back into a draft: an existing draft
  with the same Phar is left alone, a signed draft with different bytes
  fails, a published release is never modified. Existing notes are kept.

post-release.yml:
- Checks the release is published and not a pre-release before
  downloading, with the default token only.
- Verifies both signatures were made with the release key and that the
  .gpg file wraps the same Phar, before executing it.
- Opens pull requests for the version bump and the bundle constraint
  reset instead of pushing to protected main.
- Passes wp-cli/wp-cli, wp-cli/wp-cli-bundle and wp-cli/package-command
  to `close-released` explicitly.
- Leaves the branch alias to the org-wide Check Branch Alias workflow,
  which owns the `X.x-dev` convention.
- Composer never runs next to ACTIONS_BOT.

Checklists: the bundle release build is triggered by the push to the
release-x-x-x branch, signing happens from the draft release, and the
patch checklist notes that the automation does not cover it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VDHkafhoHtgAPuYkvgvqh
@swissspidy swissspidy added this to the 3.0.0 milestone Sep 14, 2026 — with Claude

Copy link
Copy Markdown
Member Author

466f050 addresses the second round. Going down the list:

Blocking

  • Stable pushed before signingrelease.yml no longer touches wp-cli/builds at all; it only creates draft releases with Phar, manifest and checksums. Stable is promoted by post-release.yml, after both signatures verify against the committed public key (.github/release-signing-key.asc, fingerprint 63AF7AA15067C05616FDDD88A3A2E8F226F0BC06 pinned in the workflow) and before the Phar is executed. The parity check moved after that promotion and reads back from the remote.
  • Tag push runs the privileged job from the tagged commit → the push: tags trigger is gone. prepare-release.yml dispatches release.yml from main with GITHUB_TOKEN (workflow_dispatch is one of the two events it may trigger). Every job holding ACTIONS_BOT runs in a release environment; the required setup is in the PR description.
  • Rerun turns a published release back into a draftgh release view --json isDraft,assets first. Published: fail. Draft with the same Phar bytes: untouched (signatures and edited notes survive). Draft with different bytes and a signature attached: fail. Different bytes, unsigned: assets replaced, notes kept.

Rest

  • composer.json instead of the lock / tags whatever main is → the framework commit is read from composer.lock (source.reference), must be on main and carry the version, and is what gets tagged. The bundle's main must have the same tree as the commit wp-cli/builds records in phar build: wp-cli/wp-cli-bundle@<sha>. composer validate --strict checks lock freshness. release.yml repeats the Phar-to-tagged-commit check.
  • Patch versions → all three workflows accept X.Y.0 only; post-release.yml skips a published vX.Y.Z with a notice. The patch checklist says so and keeps the manual process (with .sha256 added).
  • Phar and Composer next to ACTIONS_BOT → split into separate jobs. validate, inspect, notes, verify-release and prepare-bumps hold no secrets; tag, publish, promote-stable, open-pull-requests only push what the others produced (with a sha512 check across the artifact hand-off). close-milestones installs wp-cli-dev with the default token and hands the bot token to the one command that needs it.
  • contrib-list / release-notes without milestone and lock → the commands only take a single repo plus milestones, and no lock at all, so passing them explicitly loses the bundle packages. Left as-is with a comment; the output is attached to the draft for the maintainer to edit and never written back. Needs the wp-cli-dev change first.
  • Manual dispatch downloads without checking stateverify-release checks isDraft and isPrerelease with the default token only, which cannot see drafts anyway.
  • 3.1.x-dev vs 3.x-dev → post-release no longer touches the alias. Check Branch Alias runs on the same event in both repos and owns X.x-dev; minor releases keep it, which is also what lets the bundle lock ^3.1 before the tag exists.
  • Website and roadmap steps → dropped on purpose.
  • Direct pushes to protected main → both bumps are now pull requests on a post-release-vX.Y.0 branch (created with the bot token so CI runs, updated rather than duplicated on rerun, auto-merge enabled best-effort). No bypass actor needed.
  • close-released --bundlewp-cli/wp-cli wp-cli/wp-cli-bundle wp-cli/package-command passed explicitly.
  • Merging the bundle PR triggers the build → checklist now says the push to release-x-x-x builds the release Phar and merging builds the nightly, and that a stale build has to be re-pushed.

Milestone set to 3.0.0. actionlint (with shellcheck) and zizmor are clean apart from the intentional credential persistence on the checkouts that push.

Still open: the wp-cli-dev change for the release notes, and a dry run against a real candidate. The recovery paths are handled in the workflows, but I could only exercise them by reading and linting from here.


Generated by Claude Code

Passes --release to `wp maintenance contrib-list` and `release-notes`, so
they use the milestone titled after the release and the bundle's lock at
the release tag instead of the lowest open milestone and the previous
release's lock (wp-cli/wp-cli-dev#76). The output no longer depends on
whether Post-Release Automation has closed the milestones yet.

The empty-output guards now look for actual markdown content, since the
commands' informational lines no longer land in the files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VDHkafhoHtgAPuYkvgvqh

Copy link
Copy Markdown
Member Author

Follow-up on the contrib-list / release-notes item: wp-cli/wp-cli-dev#76 teaches both commands to take the release explicitly (--release=<version>, optional --bundle-ref=<ref>), fixes the dropped-first-milestone bug, and reads the bundled packages from the lock at the release tag so newly bundled packages are included. The notes job here now passes --release="$VERSION" and checks the output for actual markdown instead of non-emptiness. That PR needs to land before this workflow is used for real.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants