Skip to content

Merge pull request #621 from brazilian-utils/hyan/eloquent-hamilton-1… #32

Merge pull request #621 from brazilian-utils/hyan/eloquent-hamilton-1…

Merge pull request #621 from brazilian-utils/hyan/eloquent-hamilton-1… #32

Workflow file for this run

# Fully CI-driven release flow, no local commands. Two human confirmations, both without
# running anything on a maintainer's machine:
#
# 1. release-please watches pushes to `main` and keeps a "release PR" open, whose title/body
# and CHANGELOG.md entry are generated from Conventional Commit messages since the last
# release (feat -> minor, fix -> patch, `!`/`BREAKING CHANGE:` -> major). Nothing is
# published yet. A maintainer reviews and merges that PR: that merge is confirmation #1.
# 2. Merging the release PR makes release-please tag the release commit and create a GitHub
# Release, which triggers this same workflow again. This time `release_created` is `true`,
# so the `publish-npm` job builds, validates and STAGES the package on npm
# (`npm stage publish --provenance`). A staged version is not installable until a
# maintainer approves it with 2FA, on npmjs.com (package -> Staged versions) or with
# `npm stage approve <stage-id>`. That approval is confirmation #2 (npm's
# "proof-of-presence"), and the trusted publisher is configured for staged publishing only,
# so no workflow can ever publish directly.
#
# Dependabot and dataset-update PRs are included automatically: any conventional commit merged
# to `main` (via any PR) feeds the next release PR, no extra step needed.
#
# One-time npm Trusted Publishing setup (done by a package maintainer, not by CI):
# 1. Sign in at npmjs.com and open this package.
# 2. Settings -> Trusted Publishing -> Add a trusted publisher -> GitHub Actions.
# 3. Organization/user: brazilian-utils | Repository: javascript
# Workflow filename: release.yml | Environment: npm
# Allowed actions: leave "publish directly" UNCHECKED (staged publishing only).
# 4. Save. No NPM_TOKEN secret is needed: npm exchanges the workflow's OIDC token for a
# short-lived token automatically.
#
# One-time JSR setup (done by a package maintainer, not by CI):
# 1. Sign in at jsr.io with GitHub and create the scope `brazilian-utils` and the package
# `brazilian-utils` in it.
# 2. Package Settings -> GitHub Actions -> link `brazilian-utils/javascript`. JSR then trusts
# this repository's OIDC token; no JSR token is stored anywhere.
#
# One-time GitHub Environment setup (done by a package maintainer, not by CI):
# Repository Settings -> Environments -> New environment -> name it `npm`. No protection rules
# are needed: the environment only exists so the npm trusted publisher can be bound to it.
name: Release
on:
push:
branches: [main]
workflow_dispatch:
concurrency:
group: release
permissions:
contents: read
jobs:
release-please:
name: Release Please
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
pull-requests: write
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
version: ${{ steps.release.outputs.version }}
steps:
- name: Run release-please
id: release
uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
publish-npm:
name: Publish to npm
needs: release-please
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 15
environment: npm
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
persist-credentials: false
- name: Setup
uses: ./.github/actions/setup
with:
node-version: 24
- name: Install dependencies
run: npm ci
- name: Run build
run: npm run build
- name: Compare tree-shaking against the published version
continue-on-error: true
run: |
mkdir -p /tmp/prev
npm pack @brazilian-utils/brazilian-utils@latest --pack-destination /tmp/prev
tar -xzf /tmp/prev/*.tgz -C /tmp/prev
node scripts/tree-shaking.ts --dist /tmp/prev/package --json /tmp/prev/published.json
node scripts/tree-shaking.ts --compare /tmp/prev/published.json --markdown tree-shaking.md
cat tree-shaking.md >> "$GITHUB_STEP_SUMMARY"
- name: Ensure npm supports staged publishing and OIDC (npm >= 11.15)
# Node.js 24.18 and later bundle npm 11.16+, so the npm that actions/setup-node ships is
# enough; upgrading it with `npm install -g` is an unpinned install for OpenSSF Scorecard
# (Pinned-Dependencies), so the step only asserts the version and never installs anything.
run: |
npm_version="$(npm --version)"
echo "npm ${npm_version}"
echo "${npm_version}" | awk -F. '{ exit !($1 > 11 || ($1 == 11 && $2 >= 15)) }'
- name: Generate the CycloneDX SBOM shipped inside the package
# `files` in package.json lists brazilian-utils.cdx.json, so the tarball carries it;
# --package-lock-only reads the lockfile instead of installing anything.
run: npm sbom --sbom-format cyclonedx --omit dev --package-lock-only > brazilian-utils.cdx.json
- name: Stage on npm
run: npm stage publish --provenance --access public
publish-jsr:
name: Publish to JSR
needs: release-please
# JSR publishes through OIDC as well: no token is stored. It needs the one-time JSR setup
# described at the top of this file; until that is done this job fails, and the npm job, which
# does not depend on it, still publishes.
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
steps:
- name: Checkout the release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
persist-credentials: false
- name: Setup
uses: ./.github/actions/setup
- name: Setup Deno
uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version: v2.x
- name: Publish to JSR
run: deno publish
sbom:
name: Record the SBOM of the release
needs: release-please
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- name: Checkout the release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
persist-credentials: false
- name: Setup
uses: ./.github/actions/setup
- name: Generate the CycloneDX SBOM of the published package
# The package has no runtime dependencies, so the SBOM describes the package itself;
# --package-lock-only reads the lockfile instead of installing anything.
run: npm sbom --sbom-format cyclonedx --omit dev --package-lock-only > brazilian-utils.cdx.json
# Releases are immutable in this repository, so an asset cannot be added after release-please
# creates one (HTTP 422 on 2.4.0); the SBOM is kept as an artifact of the release run instead,
# next to the provenance statement npm records for the package.
- name: Keep the SBOM as an artifact of the release run
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: sbom-${{ needs.release-please.outputs.tag_name }}
path: brazilian-utils.cdx.json
if-no-files-found: error