Skip to content

CI(release): setup npm OIDC Trusted Publisher for monorepo #1

CI(release): setup npm OIDC Trusted Publisher for monorepo

CI(release): setup npm OIDC Trusted Publisher for monorepo #1

Workflow file for this run

name: Release
on:
pull_request:
branches:
- master
types: [closed]
jobs:
# Job 1: Check if release is needed (read-only permissions)
check:
permissions:
contents: read
runs-on: ubuntu-latest
outputs:
EXISTS_TAG: ${{ steps.tag_check.outputs.EXISTS_TAG }}
if: github.event.pull_request.merged == true
steps:
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- name: Set PACKAGE_VERSION
run: echo "PACKAGE_VERSION=$(cat lerna.json | jq -r .version)" >> $GITHUB_ENV
- name: Tag Check
id: tag_check
run: |
if git ls-remote --tags origin | grep -q "refs/tags/v${PACKAGE_VERSION}$"; then
echo "EXISTS_TAG=true" >> $GITHUB_OUTPUT
else
echo "EXISTS_TAG=false" >> $GITHUB_OUTPUT
fi
env:
PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}
# Job 2: Actual release process (minimal permissions)
release:
runs-on: ubuntu-latest
needs: check
if: always() && (needs.check.outputs.EXISTS_TAG == 'false')
# Environment configuration - Required for OIDC authentication
environment:
name: npm
outputs:
version: ${{ steps.set_version.outputs.version }}
release-url: ${{ steps.create_release.outputs.release-url }}
released: ${{ steps.create_release.outputs.released }}
permissions:
contents: write
# Required for OIDC authentication
id-token: write
steps:
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
# Release workflow should not use cache to prevent cache poisoning
# See: https://docs.zizmor.sh/audits/#cache-poisoning
# cache: 'pnpm'
node-version: 22
registry-url: https://registry.npmjs.org
- name: Git Identity
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/$GITHUB_REPOSITORY
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set PACKAGE_VERSION
id: set_version
run: |
VERSION=$(cat lerna.json | jq -r .version)
echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Install
run: pnpm install
- name: Build
run: pnpm run build
- name: Publish
run: pnpm run ci:release
env:
# Publish to npm with OIDC authentication
# See: https://docs.npmjs.com/generating-provenance-statements
NPM_CONFIG_PROVENANCE: true
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Git Tag
run: |
git tag "v${PACKAGE_VERSION}"
git push origin "v${PACKAGE_VERSION}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}
- name: Create Release
id: create_release
run: |
RELEASE_URL=$(gh release create "v${PACKAGE_VERSION}" \
--title "${GITHUB_EVENT_PULL_REQUEST_TITLE}" \
--notes "${GITHUB_EVENT_PULL_REQUEST_BODY}" \
--json url -q .url)
echo "release-url=$RELEASE_URL" >> $GITHUB_OUTPUT
echo "released=true" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}
GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }}
GITHUB_EVENT_PULL_REQUEST_BODY: ${{ github.event.pull_request.body }}
# Job 3: Comment on PR (write-only permissions)
comment:
needs: release
if: |
always() &&
github.event_name == 'pull_request' &&
needs.release.outputs.released == 'true'
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Comment on PR - Success
if: needs.release.result == 'success'
run: |
gh pr comment "$PR_NUMBER" \
--repo "$REPOSITORY" \
--body "✅ **Release v$VERSION completed successfully!**
- 🏷️ GitHub Release: $RELEASE_URL
- 🔗 Workflow run: $SERVER_URL/$REPOSITORY/actions/runs/$RUN_ID"
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
VERSION: ${{ needs.release.outputs.version }}
RELEASE_URL: ${{ needs.release.outputs.release-url }}
SERVER_URL: ${{ github.server_url }}
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
- name: Comment on PR - Failure
if: needs.release.result == 'failure'
run: |
gh pr comment "$PR_NUMBER" \
--repo "$REPOSITORY" \
--body "❌ **Release v$VERSION failed**
Please check the workflow logs for details.
🔗 Workflow run: $SERVER_URL/$REPOSITORY/actions/runs/$RUN_ID"
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
VERSION: ${{ needs.release.outputs.version }}
SERVER_URL: ${{ github.server_url }}
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}