diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 19f8df4..b3cae4b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,9 +2,6 @@ name: Publish to NPM on: workflow_dispatch: - push: - tags: - - 'v*' permissions: contents: read @@ -17,6 +14,14 @@ jobs: - name: Checkout uses: actions/checkout@v5 + - name: Require a release tag matching package.json + run: | + VERSION="v$(node -p "require('./package.json').version")" + if [ "${{ github.ref_type }}" != "tag" ] || [ "${{ github.ref_name }}" != "$VERSION" ]; then + echo "::error::Run this workflow from the release tag (expected $VERSION, got ${{ github.ref_type }} ${{ github.ref_name }})." + exit 1 + fi + - name: Setup Node.js uses: actions/setup-node@v5 with: diff --git a/.github/workflows/tag_and_release.yml b/.github/workflows/tag_and_release.yml index fb4a636..19bd989 100644 --- a/.github/workflows/tag_and_release.yml +++ b/.github/workflows/tag_and_release.yml @@ -1,4 +1,4 @@ -name: Manual Tag & Release +name: Tag & Release (Manual Trigger) on: workflow_dispatch: @@ -8,6 +8,21 @@ on: required: true type: boolean default: false + release_type: + description: 'Version bump: auto (infer from commits) or force patch/minor/major' + required: true + type: choice + default: 'auto' + options: + - auto + - patch + - minor + - major + generate_changelog: + description: 'Generate/update CHANGELOG.md for this release?' + required: true + type: boolean + default: false jobs: release: @@ -25,6 +40,24 @@ jobs: with: node-version-file: '.nvmrc' + # The conventionalcommits preset bumps breaking changes to major even on + # 0.x, so an inferred bump could ship 1.0.0 by accident. + - name: Require an explicit bump during initial development + if: ${{ inputs.release_type == 'auto' }} + run: | + if node -p "require('./package.json').version" | grep -q '^0\.'; then + echo "::error::release_type must be patch, minor or major while the version is 0.x." + exit 1 + fi + + - name: Ensure CHANGELOG.md exists + if: ${{ inputs.generate_changelog }} + run: touch CHANGELOG.md + + - name: Force version bump + if: ${{ inputs.release_type != 'auto' }} + run: npm version ${{ inputs.release_type }} --no-git-tag-version + - name: Generate Changelog and Tag id: changelog uses: TriPSs/conventional-changelog-action@v5 @@ -34,15 +67,18 @@ jobs: git-user-email: 'github-actions[bot]@users.noreply.github.com' git-message: 'chore(release): {version}' skip-on-empty: 'false' + skip-bump: ${{ inputs.release_type != 'auto' }} preset: 'conventionalcommits' skip-ci: 'false' tag-prefix: 'v' + input-file: ${{ inputs.generate_changelog && './CHANGELOG.md' || '' }} + output-file: ${{ inputs.generate_changelog && './CHANGELOG.md' || 'false' }} version-file: './package.json, ./package-lock.json' pre-commit: './.github/scripts/pre-commit.js' - name: Create GitHub Release - if: ${{ github.event.inputs.create_github_release == 'true' && steps.changelog.outputs.skipped == 'false' }} - uses: softprops/action-gh-release@v1 + if: ${{ inputs.create_github_release && steps.changelog.outputs.skipped == 'false' }} + uses: softprops/action-gh-release@v3 with: tag_name: ${{ steps.changelog.outputs.tag }} body: ${{ steps.changelog.outputs.clean_changelog }}