Skip to content

ci(release): refactor to monorepo senior pattern (single workflow on PR-merge) - #380

Merged
codewizdave merged 1 commit into
mainfrom
refactor/monorepo-release
Aug 3, 2026
Merged

ci(release): refactor to monorepo senior pattern (single workflow on PR-merge)#380
codewizdave merged 1 commit into
mainfrom
refactor/monorepo-release

Conversation

@martyy-code

Copy link
Copy Markdown
Contributor

Summary

Replaces the two-workflow design (changeset-version.yml on staging + publish.yml on main) with a single workflow that runs on PR-merge into main, modeled after the @deessejs/errors release workflow in this same repository.

Why

The current design is broken in two ways:

  1. changeset-version.yml (push to staging) → invalid Version Packages PR. The action tries to open a PR targeting main from a branch based on staging. The diff is enormous and GitHub rejects it: Validation Failed: {"resource":"PullRequest","field":"base","code":"invalid"} (observed on run 30828060355).

  2. publish.yml (push to main) → release job silently skipped. The check-release job counts .changeset/*.md files; at push-main time, by design, the changeset has already been consumed. The release job is gated on has_changeset == 'true', which is always false, so the canonical Version Packages merge never publishes.

The senior pattern

@deessejs/errors in this same repo does it correctly with one workflow:

  • Trigger: pull_request: closed (merged) on main, plus push: tags, plus workflow_dispatch.
  • Detect: did the merge introduce changeset files? (git diff --name-only HEAD~1 HEAD | grep '.changeset/.*\.md$')
  • Bump: pnpm changeset version
  • Push bump back to main as a follow-up commit by the bot.
  • Build, test, smoke.
  • Publish to npm via Trusted Publishing.
  • Tag and GitHub Release.

Changes

  • .github/workflows/publish.yml: rewritten as a single release job covering all three publish paths. Inlined steps, no check-release gating, no reusable workflows.
  • .github/workflows/changeset-version.yml: deleted (no longer needed; the version bump is in-line in publish.yml).

The workflow file content shrinks because the routing logic (3 jobs, one entry per path) is replaced by a single conditional flow (if: on each step).

Trust chain (unchanged)

  • npm Trusted Publisher: workflow filename = publish.yml, environment = release.
  • OIDC token requested at job level (id-token: write).
  • contents: write so the bot can push the version bump commit and the tag.

Concurrency

  • Single concurrency group on github.ref. cancel-in-progress: false so retried publishes finish rather than getting cancelled.

Test plan

  • pnpm turbo type-check passes.
  • pnpm turbo lint passes.
  • Post-merge: a PR with a .changeset/*.md file that is merged into main → release workflow runs → bump committed to main → npm publish → tag + GitHub Release.
  • Post-merge: a PR without a changeset that is merged into main → release workflow runs and exits 0 with "No changesets in the merge — skipping release".

Risk

Medium. The release pipeline is rewritten. Until exercised end-to-end, behavior on the canonical PR-merge path is unverified. The previous design was broken (could not publish via the canonical flow); the new design is closer to the proven @deessejs/errors pattern, but is adapted.

If something fails, rollback is a single revert. The release still works manually via workflow_dispatch (with dry_run: true first to validate).

Rollback

Revert the merge commit. The two-workflow design returns (broken in the same way as before).

🤖 Generated with Claude Code

Replaces the two-workflow design (changeset-version.yml on
staging + publish.yml on main) with a single workflow that runs
on PR-merge into main, modeled after the @deessejs/errors
release workflow in this same repository.

Old design (broken):
- changeset-version.yml ran on push to staging and opened a
  'Version Packages' PR targeting main. The PR's source branch
  was based on staging, so the diff was enormous and GitHub
  rejected the PR with 'Validation Failed: base' (run 30828060355).
- publish.yml ran on push to main and dispatched to three jobs
  gated on a check-release job. The check-release job counted
  .changeset/*.md files, but at push-main time the changeset had
  already been consumed (in theory). Result: the release job
  was skipped on the canonical Version Packages merge.

New design (working):
- publish.yml runs on PR-merged into main, tag push on main,
  or workflow_dispatch. The job detects whether the merge
  commit introduced changeset files. If yes: bump, push back
  to main, build, test, smoke, publish, tag, GitHub Release —
  all in one run. If no: exit 0 with a 'No changesets in the
  merge — skipping release' log.
- No more separate 'Version Packages' PR. The version bump is
  applied as a follow-up commit on main by the bot, after the
  merge that introduced the changeset.
- Tag push and manual workflow_dispatch skip the changeset
  detection and always publish.

Files:
- .github/workflows/publish.yml: rewritten as a single
  release job covering all three publish paths.
- .github/workflows/changeset-version.yml: deleted (no longer
  needed; the version bump is now in-line in publish.yml).

Trust chain:
- npm Trusted Publisher: workflow filename = publish.yml,
  environment = release (unchanged).
- OIDC token requested at job level (id-token: write).
- contents: write at job level so the bot can push the version
  bump commit and the tag.

Concurrency:
- Single concurrency group on github.ref. cancel-in-progress:
  false because we want retried publishes to finish, not get
  cancelled.

All third-party actions remain SHA-pinned.
@codewizdave
codewizdave merged commit 6555572 into main Aug 3, 2026
4 checks passed
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