From 4af6562976d1c78c057c8ff7b515b422ff61a9dd Mon Sep 17 00:00:00 2001 From: Jay Cary Date: Fri, 16 Oct 2026 14:52:49 -0400 Subject: [PATCH 1/3] Update CI workflow --- .github/PULL_REQUEST_TEMPLATE.md | 7 +++ .github/workflows/npm-release.yml | 98 ++++++++++++++++++++----------- CHANGELOG.md | 2 + README.md | 19 ++++-- package-lock.json | 4 +- package.json | 2 +- 6 files changed, 89 insertions(+), 43 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..d8adbe75 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +## Summary + +## Test plan + +## Todo + +- [ ] PR title has a `fix:`/`feat:`/`feat!:` prefix (or intentionally none, for no release) \ No newline at end of file diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index 5e9ddc03..b5501975 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -2,33 +2,28 @@ name: NPM release on: push: - tags: - - '*' + branches: ["main"] + workflow_dispatch: {} env: CI: true +permissions: + id-token: write + contents: write + pull-requests: write + jobs: publish: name: "Publish ${{ github.ref_name }}" runs-on: ubuntu-latest + # npm's Trusted Publisher config for @mapbox/mr-ui is bound to this workflow file and environment. environment: npm-release - permissions: - id-token: write - contents: write steps: - - uses: actions/checkout@v4 - - - name: Extract package version from package.json - run: | - packageVersion=$(jq -r .version package.json) - echo "versionTag=v$packageVersion" >> "$GITHUB_ENV" - - - name: Verify tag matches package.json version - if: ${{ github.ref_name != env.versionTag }} - run: | - echo "Tag ${{ github.ref_name }} does not match package.json version (expected ${{ env.versionTag }})." - exit 1 + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + persist-credentials: false - uses: actions/setup-node@v4 with: @@ -41,35 +36,70 @@ jobs: - name: Install npm dependencies run: npm ci --no-fund --no-audit --prefer-offline - - name: Build publishable package - # Publishes from pkg/, not the repo root - see scripts/build-package.js. - # This compiles src/components into pkg/, with a package.json that has - # `private`/`scripts`/`devDependencies` stripped out. - run: npm run build + # pretest runs lint and tsc + - name: Lint, typecheck, and test + run: npm test - - name: Release production - if: ${{ !contains(github.ref_name, '-') }} - working-directory: pkg - run: npm publish + # Reads the current version from public npmjs and git history + - id: version + uses: mapbox/gha@action-conventional-commit-version-v0 + with: + before-sha: ${{ github.event.before }} + after-sha: ${{ github.event.after }} + + # Versioned before building because scripts/build-package.js copies package.json into pkg/ + - name: Build publishable package + if: steps.version.outputs.skip != 'true' + run: | + npm version ${{ steps.version.outputs.version }} --no-git-tag-version + npm run build - - name: Release development - if: ${{ contains(github.ref_name, '-') }} + - name: Publish + if: steps.version.outputs.skip != 'true' working-directory: pkg - run: npm publish --tag dev + run: npm publish --tag ${{ steps.version.outputs.npm_publish_tag }} + + - id: release + name: Create GitHub Release + continue-on-error: true + if: steps.version.outputs.skip != 'true' && steps.version.outputs.npm_publish_tag == 'latest' + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.version.outputs.version }} + run: gh release create "v$VERSION" --target "$GITHUB_SHA" --title "v$VERSION" --generate-notes + + - name: Comment released version on PRs + continue-on-error: true + if: steps.release.outcome == 'success' + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.version.outputs.version }} + BEFORE_SHA: ${{ github.event.before }} + run: | + set -o pipefail + package_name="$(node -p 'require("./package.json").name')" + for sha in $(git rev-list "$BEFORE_SHA..$GITHUB_SHA"); do + gh api "repos/$GITHUB_REPOSITORY/commits/$sha/pulls" --jq '.[].number' + done | sort -u | while read -r pr; do + gh pr comment "$pr" --body "Released in \`$package_name@$VERSION\` (release [\`v$VERSION\`](https://github.com/$GITHUB_REPOSITORY/releases/tag/v$VERSION))." + done - name: Build docs - if: ${{ !contains(github.ref_name, '-') }} + if: steps.version.outputs.skip != 'true' && steps.version.outputs.npm_publish_tag == 'latest' run: npm run build-docs - name: Deploy docs - if: ${{ !contains(github.ref_name, '-') }} + if: steps.version.outputs.skip != 'true' && steps.version.outputs.npm_publish_tag == 'latest' + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.version.outputs.version }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" worktree=$(mktemp -d) git fetch origin gh-pages --depth=1 - git worktree add "$worktree" gh-pages + git worktree add "$worktree" FETCH_HEAD rsync -a --delete --exclude=.git build/ "$worktree/" @@ -78,6 +108,6 @@ jobs: if git diff --cached --quiet; then echo "No changes to deploy." else - git commit -m "Deploy docs for ${{ github.ref_name }}" - git push origin gh-pages + git commit -m "Deploy docs for v$VERSION" + git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:gh-pages fi diff --git a/CHANGELOG.md b/CHANGELOG.md index a4283a0b..419ed7a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Changelog +Starting after 2.25.0, release notes are published as [GitHub Releases](https://github.com/mapbox/mr-ui/releases). + ## HEAD ## 2.25.0 diff --git a/README.md b/README.md index 1da7146c..80973771 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,17 @@ npm test The `build` command creates a `pkg/` directory that contains the code we want to publish, organized the way we want it. So `pkg/` is the directory that we publish. `pkg/package.json` is a clone of `package.json` but with `private`/`scripts`/`devDependencies` removed. -Publishing is automated via the `NPM release` GitHub Actions workflow, triggered by pushing a version tag: +Pull Requests merged to `main` with conventional commit prefixes are automatically published to npm by the `NPM release` GitHub Actions workflow, as new major/minor/patch versions (`feat!:`/`feat:`/`fix:` respectively). When using "Squash and merge", ensure your PR title includes the conventional commit prefix; a `BREAKING CHANGE:` footer only counts if it ends up in the squash commit's body. Other prefixes (`chore:`, `docs:`, etc.) or no prefix don't trigger a release. -- Increment version numbers in `package.json` and `package-lock.json`, and ensure the changelog has an entry for the latest version. Commit this. -- Create a Git tag matching the version, prefixed with `v`: `git tag v2.25.0`. -- Push the tag: `git push origin v2.25.0`. -- This triggers the workflow, which builds `pkg/` and publishes it via [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) (OIDC) — no npm token needed. It also builds and deploys the docs to https://mapbox.github.io/mr-ui/. -- Any prerelease tag (a version containing `-`, e.g. `v2.25.0-dev.0`) publishes under the npm `dev` dist-tag instead of `latest`, for testing a release without affecting `latest` consumers - dev releases don't touch the live docs site. +Examples: +- `fix: resolved issue with dropdowns` -> patch +- `feat: added dropdowns` -> minor +- `feat!: Overhauled dropdowns` (requires manual migration work) -> major + +The workflow builds `pkg/` and publishes it via [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) (OIDC) — no npm token needed. It then creates a GitHub Release with generated notes, comments the released version on the merged PRs, and builds and deploys the docs to https://mapbox.github.io/mr-ui/. + +`package.json`'s `version` may be incremented manually if needed (for example, to skip a minor version), but you shouldn't normally need to touch it; the published npm version, not `package.json`, is the source of truth for what gets released next. + +#### Pre-release versions on other branches + +For testing in downstream projects, you can manually trigger the "NPM release" workflow on a branch to publish a pre-release tagged with the short commit sha (e.g. `2.24.3-aa79c6a`) under the npm `prerelease` dist-tag, without affecting `latest` or the live docs site. Check the workflow results for the published version. diff --git a/package-lock.json b/package-lock.json index 5ca9b798..5994b859 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mapbox/mr-ui", - "version": "2.25.0", + "version": "2.24.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mapbox/mr-ui", - "version": "2.25.0", + "version": "2.24.3", "license": "BSD-2-Clause", "dependencies": { "@mapbox/mbx-assembly": "^1.7.1", diff --git a/package.json b/package.json index b93a70b5..e9cef1fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/mr-ui", - "version": "2.25.0", + "version": "2.24.3", "description": "UI components for Mapbox projects", "publishConfig": { "access": "public" From 39c155e5a77d8140f141a539d94daa0d68f1adf1 Mon Sep 17 00:00:00 2001 From: Jay Cary Date: Tue, 20 Oct 2026 14:40:23 -0400 Subject: [PATCH 2/3] feat: release from CI with auto-incrementing versions --- .github/workflows/npm-release.yml | 2 +- .github/workflows/test.yml | 4 ++-- README.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index b5501975..02c56807 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version-file: .nvmrc cache: npm diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2bfc5421..e2818be8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,9 +9,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version-file: .nvmrc cache: npm diff --git a/README.md b/README.md index 80973771..ef82dee8 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,4 @@ The workflow builds `pkg/` and publishes it via [npm Trusted Publishing](https:/ #### Pre-release versions on other branches -For testing in downstream projects, you can manually trigger the "NPM release" workflow on a branch to publish a pre-release tagged with the short commit sha (e.g. `2.24.3-aa79c6a`) under the npm `prerelease` dist-tag, without affecting `latest` or the live docs site. Check the workflow results for the published version. +For testing in downstream projects, you can manually trigger the "NPM release" workflow on a branch to publish a pre-release tagged with the short commit sha (e.g. `2.25.0-3ce8575`) under the npm `prerelease` dist-tag, without affecting `latest` or the live docs site. Check the workflow results for the published version. diff --git a/package-lock.json b/package-lock.json index 5994b859..5ca9b798 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mapbox/mr-ui", - "version": "2.24.3", + "version": "2.25.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mapbox/mr-ui", - "version": "2.24.3", + "version": "2.25.0", "license": "BSD-2-Clause", "dependencies": { "@mapbox/mbx-assembly": "^1.7.1", diff --git a/package.json b/package.json index e9cef1fa..b93a70b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/mr-ui", - "version": "2.24.3", + "version": "2.25.0", "description": "UI components for Mapbox projects", "publishConfig": { "access": "public" From fa6a33f35de35785fc31b5bdd0c6073876cb254b Mon Sep 17 00:00:00 2001 From: Jay Cary Date: Wed, 21 Oct 2026 11:47:02 -0400 Subject: [PATCH 3/3] concurrency group --- .github/workflows/npm-release.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index 02c56807..f369d85f 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -5,6 +5,12 @@ on: branches: ["main"] workflow_dispatch: {} +# Run releases one at a time, so two runs can't publish the same version or move `latest` +# backwards. A queued run that gets superseded is dropped; its commits roll into the next release. +concurrency: + group: npm-release-${{ github.ref_name }} + cancel-in-progress: false + env: CI: true