Skip to content

feat(release): publish on merge of the release pr - #290

Merged
pviti merged 3 commits into
mainfrom
feat/release-on-merge
Oct 15, 2026
Merged

pviti merged 3 commits into
mainfrom
feat/release-on-merge

Conversation

@pviti

@pviti pviti commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Release on merge

The approved release PR becomes the only manual step of a release: merging it publishes everything.

New flow

  1. pnpm release:publish bumps the changed packages and opens the chore(release) PR. The release notes of each package are in its description, so the reviewer approves exactly what will be published.

  2. A reviewer approves the PR, and it is merged.

  3. publish.yml runs on the push to main and calls cl-release ship, which does, in dependency order:

    • tag <dir>-v<version> on the commit that set the version;
    • pnpm publish with OIDC trusted publishing and provenance;
    • publish the GitHub release.

    Then a Slack message for each package published to npm: package, version (no dist-tag) and the link to its release notes.

Changes

  • New ship command. --plan finds the versions not on npm and the commit that set them.
    • The run is resumable: what is already on npm is skipped, and existing tags and releases are reused.
    • A draft left by the previous flow is published, not recreated.
    • Packages are published one after the other in a single job, which removes the E409 race of concurrent runs.
  • publish.yml. It is triggered by push: main and workflow_dispatch (concurrency: publish).
    • Plan job, then a publish job: checkout of the release commit, build, tests, manifest check, then ship.
    • The trusted publisher stays tied to publish.yml.
  • version. The release notes now go into the PR description, cut at 60k characters with a link to the commits.
  • Removed: the drafts command, release.yml, and the release:version and release:tag scripts. tag, resolve and publish remain for manual recovery.
  • Release notes headings. A range ending on the release tag printed the version heading twice (an empty block for the tagged commit, as in the 7.0.0 notes). Now each range gives one block.
  • PR titles. The new pr-title.yml checks the pull request title with commitlint. Merge-style titles fail, because the title becomes the message of the merge commit on main.
  • Docs: README, commitlint and tag comments updated.

⚠️ On merge

Merging this PR triggers publish.yml on main, which publishes the pending majors from #288: cleanups 4.0.0 (its run failed with E409 on cli-ux), metrics, microstore, orders, provisioning, resources, seeder, tags, token, triggers and webhooks.

It reuses the existing tags and drafts. I checked it with cl-release ship --dry-run on the release commit: 11 packages.

Repository setting (needs an admin)

On main the merge commits are titled Merge pull request #N from <branch>, and the PR title is only in their body. To title them with the PR title:

gh api -X PATCH repos/commercelayer/commercelayer-cli -f merge_commit_title=PR_TITLE -f merge_commit_message=PR_BODY -f squash_merge_commit_title=PR_TITLE

Tests

  • Unit tests for planShip (grouping by commit, oldest first, dependency order) and releasePrBody (truncation).
  • pnpm lint and check-packages pass.

🤖 Generated with Claude Code

The approved release PR is the only manual step: publish.yml runs on every
push to main and `cl-release ship` tags, publishes to npm and publishes the
GitHub releases of the versions not on npm yet, then posts one Slack summary.

- `pnpm release:publish` (cl-release version) opens the release PR with the
  release notes of each package in its description
- new `ship` command, dependencies first, resumable; drafts left by the
  previous flow are published instead of recreated
- remove `drafts` and release.yml; tag/publish stay for manual recovery

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
malessani
malessani previously approved these changes Oct 13, 2026
- the notes of a range ending on the release tag printed the version heading
  twice (an empty block for the tagged commit): write one block per range
- check the pull request title with commitlint (pr-title.yml), merge-style
  titles included: it becomes the message of the merge commit on main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The announce job of publish.yml posts a message for each package that the
run published to npm (package, version, link to its release notes), also
when a later package fails; a run that resumes doesn't announce again what
is already on npm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pviti
pviti merged commit 7d73576 into main Oct 15, 2026
11 checks passed
@pviti
pviti deleted the feat/release-on-merge branch October 15, 2026 10:02
pviti added a commit that referenced this pull request Oct 15, 2026
## Release only when every check of the commit passed

> Stacked on #291 (base `fix/publish-release-tooling`). When #291 is
merged and its branch deleted, GitHub moves the base of this PR to
`main`.

On the push to main, `publish.yml` used to start alongside Verify,
Integration and CodeQL, without waiting for them. The merge of #290
shows it: Publish ended at 10:04, Verify at 10:07. A release could
therefore reach npm from a commit whose lint, tests on the minimum Node
version or integration suites were failing.

### Change

A new first job, **Checks of the commit**, waits for every other
workflow run of the commit, Publish excluded.
- It accepts only `success`, `skipped` or `neutral`. Otherwise it stops
the release with an error that names the failed workflows.
- The plan, the publication and the Slack messages run only after it.
- On a manual run (`workflow_dispatch`, to resume a release) it checks
the same commit and ignores earlier failed Publish runs.
- Timeout: 60 minutes.

### Verification

I tested the wait and filter logic on the merge commit of #290
(7d73576):
- Integration, Verify and CodeQL succeeded;
- nothing is pending, nothing failed;
- the failed Publish run is excluded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
pviti added a commit that referenced this pull request Oct 15, 2026
## Fix the publish job after #290

The first run of the new `publish.yml` (merge of #290, [run
38717349030](https://github.com/commercelayer/commercelayer-cli/actions/runs/38717349030))
failed with `Unknown command 'ship'`. It stopped before publishing
anything: no tags, nothing on npm, no releases.

### Cause

The publish job checks out the commit that set the versions (`cf7fd277`,
the release commit of #288) and ran the `cl-release` of that commit,
which predates `ship`.

### Fix

`cl-release` now comes from the workflow's own commit, checked out in
`.release-tooling`, and is used by the manifest check and by `ship`. The
packages are still built, tested and published from the release commit.
The tooling must match the workflow that calls it anyway: in the future
the release commit could also have older or newer tooling.

### Release only when every check passed (#292, merged into this branch)

A first job, **Checks of the commit**, waits for every other workflow
run of the commit (Verify, Integration, CodeQL, …). The release stops
unless all of them succeeded. Before, Publish ran alongside them without
waiting: on the merge of #290 it ended at 10:04, Verify at 10:07.

### Verification

Locally I reproduced the job: checkout of `cf7fd277` with this branch's
tooling nested in `.release-tooling`, then `cl-release manifest` and
`cl-release ship --dry-run`. Result: the 11 pending packages.

### ⚠️ On merge

Merging this PR triggers `publish.yml` on main. Once the checks of the
merge commit are green, it publishes the 11 pending majors: cleanups
4.0.0, metrics, microstore, orders, provisioning, resources, seeder,
tags, token, triggers and webhooks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

4 participants