Skip to content

chore: automate releases via workflow_dispatch#1246

Merged
dunglas merged 7 commits into
mainfrom
feat/release-workflow
May 18, 2026
Merged

chore: automate releases via workflow_dispatch#1246
dunglas merged 7 commits into
mainfrom
feat/release-workflow

Conversation

@dunglas

@dunglas dunglas commented May 16, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add .github/workflows/release.yml, modeled after FrankenPHP's new release workflow: bumps caddy/go.mod, bumps charts/mercure/Chart.yaml, regenerates the chart README via helm-docs, commits + tags v<version> and caddy/v<version> through the GitHub API (server-signed under github-actions[bot]), then dispatches cd.yml against the new tag so GoReleaser drafts the release.
  • Shrink release.sh to a thin dispatcher that keeps the existing operator-side guards (on-main, clean tree, in-sync with origin/main) and calls gh workflow run release.yml. The semver check now lives in the workflow itself so UI-triggered dispatches are also validated.

The workflow is idempotent: re-dispatching after a partial failure detects which steps already completed (tag existence is the source of truth) and skips them. As a fallback, a release-shaped main HEAD without tags is also detected as resumable.

Notes

  • GITHUB_TOKEN-driven API tag creation does not trigger push events, so cd.yml is dispatched explicitly against the tag ref; cd-chart.yml continues to fire automatically on release: published.
  • No PGO step (Mercure has none) and no Homebrew bump (Mercure isn't published there), unlike the FrankenPHP source workflow.

Copilot AI review requested due to automatic review settings May 16, 2026 10:15

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

Automates the Mercure release process: a thin release.sh validates preconditions and dispatches a new release.yml workflow that bumps the Caddy module + Helm chart, regenerates docs, commits/tags via the GitHub API (server-signed under github-actions[bot]), and triggers the downstream cd.yml build. The workflow is designed to be idempotent so partial failures can be resumed.

Changes:

  • Add .github/workflows/release.yml performing version bump, helm-docs regeneration, API-based commit and tagging, and downstream cd.yml dispatch.
  • Replace release.sh with a small dispatcher that retains operator-side semver/branch/tree/in-sync guards and calls gh workflow run.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
.github/workflows/release.yml New end-to-end release workflow with resume detection, API-based tree/commit/tag creation, and downstream build dispatch.
release.sh Shrunk to a pre-flight checker and gh workflow run dispatcher; drops the previous local bump/tag/push pipeline.

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

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread release.sh Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread release.sh Outdated
Comment thread .github/workflows/release.yml Outdated
dunglas added a commit to php/frankenphp that referenced this pull request May 16, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Use matching-refs in create_tag so transient API failures aren't
  read as "tag absent".
- Build the release tree from `git status` partitions so additions,
  deletions, and modifications round-trip correctly.
- Clarify the "no file changes" error with operator guidance.
- Check that git is installed in release.sh.
@dunglas dunglas requested a review from Copilot May 16, 2026 15:13

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml
dunglas added a commit to php/frankenphp that referenced this pull request May 16, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Verify the resumed commit actually contains the expected
  caddy/go.mod frankenphp pin and a non-trivial PGO profile before
  re-tagging or dispatching downstream builds.
- Use --no-renames so renames decompose into add+delete and both
  halves land in the API tree mutation.
- Preserve each file's existing mode (executable bit) when building
  tree entries instead of hardcoding 100644.
- Scope the concurrency group per version so a pending environment
  approval doesn't block dispatches for a different version.
@dunglas dunglas requested a review from Copilot May 16, 2026 15:57

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml
Comment thread release.sh Outdated
Comment thread release.sh
Comment thread .github/workflows/release.yml
dunglas added a commit to php/frankenphp that referenced this pull request May 16, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Match the frankenphp require entry in caddy/go.mod under both block
  and single-line forms so a future `go mod tidy` reformat doesn't fail
  the resume verification.
- Detect a release-shaped main HEAD without tags as resumable, covering
  the case where a previous run pushed the commit but failed before
  create_tag.
- Guard against main advancing between checkout and the commit step by
  asserting parent_sha equals the original checkout SHA before building
  the API tree.
- release.sh: restore set -o errtrace so the ERR trap fires inside any
  future helpers, and fetch tags so the operator sees existing release
  tags before dispatching.
@dunglas dunglas requested a review from Copilot May 18, 2026 13:04

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

Comment thread release.sh
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml Outdated
dunglas added 7 commits May 18, 2026 15:08
Replace the local release.sh logic with a Release workflow that bumps
the Caddy module and Helm chart, regenerates chart docs, commits and
tags via the GitHub API (server-signed under github-actions[bot]), and
dispatches cd.yml against the new tag. release.sh becomes a thin
dispatcher that keeps the pre-flight guards (semver, on-main, clean
tree, in-sync with origin/main).
- Drop the unused GOFLAGS env from the release job (this workflow never
  compiles Mercure code; build tags belong to ci.yml/cd.yml).
- Validate the version input inside the workflow as well, so a
  UI-triggered dispatch can't slip a non-semver string through.
- Pin helm-docs to v1.14.2 instead of @latest for reproducibility.
- Replace fragile "(HTTP 404)" stderr grepping with a matching-refs
  lookup that returns an empty array for absent tags, and extend the
  split-state guard so a mismatched caddy/v<version> on the resume
  path is caught up-front instead of inside create_tag.
- Build the release tree from `git diff --name-only HEAD` so any file
  the bump or helm-docs touches (e.g. an unexpected top-level go.sum
  delta) is captured, matching the previous `git commit -a` behavior.
- On resume, skip the cd.yml dispatch when a prior run is already in
  flight or has succeeded; only failed runs are retried.
- Restore the three-way behind/ahead/diverged diagnostic in release.sh
  that the rewrite collapsed into a single equality check.
Trim explanatory comments to one or two lines each and drop the
operator-side semver regex from release.sh — release.yml validates it.
- Hard-fail the cd.yml dispatch step on error (was emitting a warning
  while reporting success).
- Use matching-refs in create_tag so transient API failures aren't
  read as "tag absent".
- Build the release tree from `git status` partitions so additions,
  deletions, and modifications round-trip correctly.
- Detect cd.yml in-flight via the runs API instead of `gh run list
  --branch`, and skip dispatch only if a GitHub Release already exists
  or a run is still active — cancelled/failed runs are now retried.
- Clarify the "no file changes" error with operator guidance.
- Check that git is installed in release.sh.
- Verify the resumed commit actually contains the expected Chart.yaml
  version and caddy/go.mod mercure pin before re-tagging or dispatching.
- Use --no-renames so renames decompose into add+delete and both halves
  land in the API tree mutation.
- Preserve each file's existing mode (executable bit) when building tree
  entries instead of hardcoding 100644.
- Scope the concurrency group per version so a pending environment
  approval doesn't block dispatches for a different version; document
  why same-version dispatches still serialize.
- Clarify that the release-existence guard works because GoReleaser is
  configured with `release.draft: true`.
- Match the mercure require entry in caddy/go.mod under both block and
  single-line forms so a future `go mod tidy` reformat doesn't fail the
  resume verification.
- Detect a release-shaped main HEAD without tags as resumable, covering
  the case where a previous run pushed the commit but failed before
  create_tag.
- Guard against main advancing between checkout and the commit step by
  asserting parent_sha equals the original checkout SHA before building
  the API tree.
- release.sh: restore set -o errtrace so the ERR trap fires inside any
  future helpers, and fetch tags so the operator sees existing release
  tags before dispatching.
- Add `set -euo pipefail` to the Validate inputs step for consistency
  with the other run blocks.
- Hardcode mode 100644 for deletion entries since the mode is purely
  formal when sha is null.
- Reword the verify_release_content comment to spell out that omitting
  the `require` keyword from the grep is what makes it match both block
  and single-line layouts.
@dunglas dunglas force-pushed the feat/release-workflow branch from 0c014d1 to 273c1d5 Compare May 18, 2026 13:09
dunglas added a commit to php/frankenphp that referenced this pull request May 18, 2026
Cross-port of dunglas/mercure#1246 review feedback:

- Validate the version input inside release.yaml as well, so a
  UI-triggered dispatch can't slip a non-semver string through.
- Replace the fragile "(HTTP 404)" stderr grep with a matching-refs
  lookup that returns an empty array for absent tags, and extend the
  split-state guard so a mismatched caddy/v<version> on the resume
  path is caught up-front instead of inside create_tag.
- Build the release tree from `git diff --name-only HEAD` so anything
  the PGO refresh or `go mod tidy` step touches (beyond the previously
  hardcoded three paths) is captured.
- Restore the three-way behind/ahead/diverged diagnostic in release.sh
  that the rewrite collapsed into a single equality check.
dunglas added a commit to php/frankenphp that referenced this pull request May 18, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Use matching-refs in create_tag so transient API failures aren't
  read as "tag absent".
- Build the release tree from `git status` partitions so additions,
  deletions, and modifications round-trip correctly.
- Clarify the "no file changes" error with operator guidance.
- Check that git is installed in release.sh.
dunglas added a commit to php/frankenphp that referenced this pull request May 18, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Verify the resumed commit actually contains the expected
  caddy/go.mod frankenphp pin and a non-trivial PGO profile before
  re-tagging or dispatching downstream builds.
- Use --no-renames so renames decompose into add+delete and both
  halves land in the API tree mutation.
- Preserve each file's existing mode (executable bit) when building
  tree entries instead of hardcoding 100644.
- Scope the concurrency group per version so a pending environment
  approval doesn't block dispatches for a different version.
dunglas added a commit to php/frankenphp that referenced this pull request May 18, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Match the frankenphp require entry in caddy/go.mod under both block
  and single-line forms so a future `go mod tidy` reformat doesn't fail
  the resume verification.
- Detect a release-shaped main HEAD without tags as resumable, covering
  the case where a previous run pushed the commit but failed before
  create_tag.
- Guard against main advancing between checkout and the commit step by
  asserting parent_sha equals the original checkout SHA before building
  the API tree.
- release.sh: restore set -o errtrace so the ERR trap fires inside any
  future helpers, and fetch tags so the operator sees existing release
  tags before dispatching.
dunglas added a commit to php/frankenphp that referenced this pull request May 18, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Add `set -euo pipefail` to the Validate inputs step.
- Hardcode mode 100644 for deletion entries since the mode is purely
  formal when sha is null.
- Reword the verify_release_content comment to spell out that omitting
  the `require` keyword from the grep is what makes it match both
  block and single-line layouts.
@dunglas dunglas merged commit 3420db1 into main May 18, 2026
43 checks passed
@dunglas dunglas deleted the feat/release-workflow branch May 18, 2026 15:09
dunglas added a commit to php/frankenphp that referenced this pull request May 26, 2026
Cross-port of dunglas/mercure#1246 review feedback:

- Validate the version input inside release.yaml as well, so a
  UI-triggered dispatch can't slip a non-semver string through.
- Replace the fragile "(HTTP 404)" stderr grep with a matching-refs
  lookup that returns an empty array for absent tags, and extend the
  split-state guard so a mismatched caddy/v<version> on the resume
  path is caught up-front instead of inside create_tag.
- Build the release tree from `git diff --name-only HEAD` so anything
  the PGO refresh or `go mod tidy` step touches (beyond the previously
  hardcoded three paths) is captured.
- Restore the three-way behind/ahead/diverged diagnostic in release.sh
  that the rewrite collapsed into a single equality check.
dunglas added a commit to php/frankenphp that referenced this pull request May 26, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Use matching-refs in create_tag so transient API failures aren't
  read as "tag absent".
- Build the release tree from `git status` partitions so additions,
  deletions, and modifications round-trip correctly.
- Clarify the "no file changes" error with operator guidance.
- Check that git is installed in release.sh.
dunglas added a commit to php/frankenphp that referenced this pull request May 26, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Verify the resumed commit actually contains the expected
  caddy/go.mod frankenphp pin and a non-trivial PGO profile before
  re-tagging or dispatching downstream builds.
- Use --no-renames so renames decompose into add+delete and both
  halves land in the API tree mutation.
- Preserve each file's existing mode (executable bit) when building
  tree entries instead of hardcoding 100644.
- Scope the concurrency group per version so a pending environment
  approval doesn't block dispatches for a different version.
dunglas added a commit to php/frankenphp that referenced this pull request May 26, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Match the frankenphp require entry in caddy/go.mod under both block
  and single-line forms so a future `go mod tidy` reformat doesn't fail
  the resume verification.
- Detect a release-shaped main HEAD without tags as resumable, covering
  the case where a previous run pushed the commit but failed before
  create_tag.
- Guard against main advancing between checkout and the commit step by
  asserting parent_sha equals the original checkout SHA before building
  the API tree.
- release.sh: restore set -o errtrace so the ERR trap fires inside any
  future helpers, and fetch tags so the operator sees existing release
  tags before dispatching.
dunglas added a commit to php/frankenphp that referenced this pull request May 26, 2026
Cross-port of dunglas/mercure#1246 follow-up:

- Add `set -euo pipefail` to the Validate inputs step.
- Hardcode mode 100644 for deletion entries since the mode is purely
  formal when sha is null.
- Reword the verify_release_content comment to spell out that omitting
  the `require` keyword from the grep is what makes it match both
  block and single-line layouts.
dunglas added a commit to php/frankenphp that referenced this pull request Jun 3, 2026
Cross-port of review feedback from
dunglas/mercure#1246, applied to the changes
that this workflow shares structurally with Mercure's.

## Summary

- **Validate the `version` input inside `release.yaml`.** The semver
regex in `release.sh` only protects operators using the script; a
UI-triggered dispatch could otherwise propagate `latest`, `1.2`, or any
string containing `/` straight into `go get @v...`, `sed`, and tag refs.
- **Replace the `(HTTP 404)` stderr grep** with a
`git/matching-refs/tags/...` lookup that returns an empty array for
absent tags. Distinguishing "tag missing" from rate-limit / 5xx / auth
failures no longer depends on the exact wording of `gh`'s error
messages. The split-state guard now also fires symmetrically: a
mismatched `caddy/v<version>` on the resume path is caught before any
writes.
- **Build the release tree from `git diff --name-only HEAD`** so the
commit captures every file the PGO refresh or `go mod tidy` step
touches, rather than the previously hardcoded three paths.
- **Restore the three-way behind/ahead/diverged diagnostic in
`release.sh`** so the operator knows whether to `git pull`, `git push`,
or reconcile a divergence.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants