Skip to content

ci: migrate releases to semantic-release#59

Open
gnapse wants to merge 1 commit into
nextfrom
ernesto/adopt-semantic-release
Open

ci: migrate releases to semantic-release#59
gnapse wants to merge 1 commit into
nextfrom
ernesto/adopt-semantic-release

Conversation

@gnapse
Copy link
Copy Markdown

@gnapse gnapse commented May 22, 2026

Overview

  • migrate package releases from manual GitHub release creation to semantic-release
  • publish automatically on pushes to main
  • validate pull request titles against Conventional Commits so squash merges produce valid release input
  • keep next as a staging branch only, so this change can merge into next safely before the first automated stable release

After this gets merged

Once this PR has landed in next and we are ready to release everything currently staged there, promote next to main without squash-merging it. That preserves the existing breaking feat! commits so semantic-release computes v3.0.0.

Preferred promotion flow:

git fetch origin
git switch main
git pull --ff-only origin main
git merge --ff-only origin/next
git push origin main

Then:

  1. Wait for .github/workflows/publish.yml on main to finish.
  2. Confirm it created tag v3.0.0, a GitHub release, npm package @doist/react-interpolate@3.0.0, and the release commit on main updating package.json, package-lock.json, and CHANGELOG.md.
  3. Sync next back with main so the semantic-release commit is carried forward:
git fetch origin
git switch next
git pull --ff-only origin next
git merge --ff-only origin/main
git push origin next

If main is protected and direct push is not allowed, open a PR from next to main and merge it with a merge commit or rebase merge, but do not squash it.

@gnapse gnapse self-assigned this May 22, 2026
@gnapse gnapse force-pushed the ernesto/adopt-semantic-release branch from 8136978 to 04954f2 Compare May 22, 2026 15:22
@gnapse gnapse marked this pull request as ready for review May 22, 2026 15:25
@gnapse gnapse requested a review from rfgamaral May 22, 2026 15:25
Copy link
Copy Markdown
Member

@doistbot doistbot left a comment

Choose a reason for hiding this comment

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

This pull request migrates the package release process to semantic-release, automating npm publishing and Conventional Commits validation. These updates will significantly streamline the release workflow and ensure consistent, predictable versioning. There are a few areas to review regarding npm provenance configuration for OIDC publishing, GitHub App permission syntax, a potential security risk with the pull_request_target trigger, and the retry logic for GitHub Packages.

Share FeedbackReview Logs

private-key: ${{ secrets.DOIST_RELEASE_BOT_PRIVATE_KEY }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
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.

[P1] GitHub App permissions use an underscore (pull_requests), not a hyphen. The actions/create-github-app-token action forwards this directly to the GitHub API, which will reject pull-requests with an input validation error. Update this key to permission-pull_requests: write.

run: npx semantic-release
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.release-bot.outputs.token || github.token }}
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.

[P0] @semantic-release/npm strictly enforces the presence of an NPM_TOKEN unless npm provenance is explicitly configured. Because this workflow relies on passwordless Trusted Publishing via OIDC, semantic-release will fail during the verifyConditions step with an ENPMTOKEN error. Add NPM_CONFIG_PROVENANCE: true to this env block (or add "provenance": true to publishConfig in package.json) so it knows it can safely bypass the token check.

- name: Clear npm config between GitHub/npm registries
run: rm -f $NPM_CONFIG_USERCONFIG
- name: Clear npm config between registries
if: ${{ steps.semantic-release.outputs.package-published == 'true' }}
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.

[P1] This gate makes the GitHub Packages publish non-retryable. If semantic-release has already published to npm/tagged the release and the later GitHub Packages npm publish fails, a rerun won't set package-published == 'true' again, so this step is skipped and the two registries stay out of sync. Move the GitHub Packages publish into the semantic-release publish flow, or key this step off the resolved release version instead of a one-run success flag.

if: ${{ steps.semantic-release.outputs.package-published == 'true' }}
uses: actions/setup-node@v4
with:
cache: npm
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.

[P3] Dependencies are already cached and installed by the first setup-node step. Copying cache: npm here unnecessarily re-runs the post-job cache save action. You can remove this line to streamline configuring the second registry.

Comment thread release.config.js
"@semantic-release/exec",
{
verifyConditionsCmd:
'if [ -n "$GITHUB_OUTPUT" ]; then echo "package-published=false" >> "$GITHUB_OUTPUT"; fi',
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.

[P3] This shell script is duplicated for successCmd. To keep it DRY and avoid drift, consider extracting a helper for setting the output:

const setOutput = (value) => `if [ -n "$GITHUB_OUTPUT" ]; then echo "package-published=${value}" >> "$GITHUB_OUTPUT"; fi`;
// ...
verifyConditionsCmd: setOutput('false'),
successCmd: setOutput('true'),

env:
CI: true

- name: Test the codebase
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.

[P2] This extends the same checkout/setup/install/check/test/build sequence that's already defined in .github/workflows/check-pull-request-health.yml. Pull that shared CI path into a reusable workflow or composite action and call it from both places, otherwise changes like this new test step have to be mirrored manually and the two workflows will keep drifting.

release:
types: [created]
workflow_dispatch:
push:
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.

[P2] Switching this workflow to every push on main means we now pay for a full install/check/test/build cycle before semantic-release decides whether there's anything to publish. Commits like docs: or chore: will still run the whole job and then no-op. Add an early release-needed gate and skip the expensive steps when no release is due.

name: Semantic Pull Request

on:
pull_request_target:
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.

[P2] Per the Secrets Management standard, workflows triggered by pull requests from forks should not have access to secrets to prevent potential exfiltration. While this workflow currently doesn't check out fork code, using pull_request_target exposes repository secrets to the run context and creates a security risk if the workflow is later modified. Change this to pull_request, which safely runs with a read-only token and is fully sufficient for validating PR titles.

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