diff --git a/README.md b/README.md index 493088b..73fb3a2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,57 @@ # Offchain Labs GitHub Actions A collection of reusable GitHub actions and workflows. + +## Publish an npm package + +The [`publish-npm`](publish-npm/action.yml) composite action publishes a pnpm package to npm using [trusted publishing](https://docs.npmjs.com/trusted-publishers) and [staged publishing](https://docs.npmjs.com/staged-publishing). + +It accepts stable tags (`v1.2.3`) and `alpha`, `beta`, or `rc` prerelease tags (`v1.2.3-beta.0`), verifies that the tag matches `package.json`, and selects the matching npm dist-tag (`latest`, `alpha`, `beta`, or `rc`). The packed tarball is checked with a dry run before it is staged for publishing. + +Configure the calling repository and `.github/workflows/publish-npm.yml` as a trusted publisher on npm, using the `Publish` GitHub environment. The calling workflow is responsible for checking out and building the package before invoking the action: + +```yml +name: Publish NPM Package + +on: + push: + tags: + - "v*" + +jobs: + publish: + name: Publish to npm + runs-on: ubuntu-latest + environment: Publish + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up pnpm and Node.js + uses: pnpm/setup@v1 + with: + runtime: node@24 + cache: true + + - name: Build + run: pnpm build + + - name: Publish package + uses: OffchainLabs/actions/publish-npm@main + with: + working-directory: src +``` + +### Permissions + +The calling job must grant these permissions because composite actions cannot declare job-level permissions: + +- `contents: read` allows the workflow to check out the repository. +- `id-token: write` allows pnpm to request the GitHub OIDC token required by npm trusted publishing. + +With trusted publishing configured, no npm token secret is required. diff --git a/publish-npm/action.yml b/publish-npm/action.yml new file mode 100644 index 0000000..5840d8b --- /dev/null +++ b/publish-npm/action.yml @@ -0,0 +1,50 @@ +name: Publish npm package +description: Validate, pack, and publish an npm package using pnpm + +inputs: + working-directory: + description: Directory containing the package to publish + required: false + default: . + +runs: + using: composite + steps: + - name: Resolve package version and npm dist-tag + env: + RELEASE_TAG: ${{ github.ref_name }} + shell: bash + run: | + if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-(alpha|beta|rc)\.(0|[1-9][0-9]*))?$ ]]; then + echo "Tag must be vX.Y.Z or vX.Y.Z-{alpha,beta,rc}.N, for example v1.0.2 or v1.0.2-alpha.0." >&2 + exit 1 + fi + + echo "NPM_PACKAGE_VERSION=${RELEASE_TAG#v}" >> "$GITHUB_ENV" + echo "NPM_DIST_TAG=${BASH_REMATCH[5]:-latest}" >> "$GITHUB_ENV" + + - name: Verify package version matches tag + env: + PACKAGE_JSON: ${{ inputs.working-directory }}/package.json + shell: bash + run: | + PACKAGE_VERSION=$(jq -er '.version' "$PACKAGE_JSON") + if [ "$PACKAGE_VERSION" != "$NPM_PACKAGE_VERSION" ]; then + echo "$PACKAGE_JSON version ($PACKAGE_VERSION) must match the tag version ($NPM_PACKAGE_VERSION). Bump and commit $PACKAGE_JSON manually before tagging." >&2 + exit 1 + fi + + - name: Pack package + working-directory: ${{ inputs.working-directory }} + shell: bash + run: pnpm pack + + - name: Verify package + working-directory: ${{ inputs.working-directory }} + shell: bash + run: pnpm stage publish ./*.tgz --dry-run --no-git-checks --tag "$NPM_DIST_TAG" + + - name: Publish package + working-directory: ${{ inputs.working-directory }} + shell: bash + run: pnpm stage publish ./*.tgz --no-git-checks --tag "$NPM_DIST_TAG"