-
Notifications
You must be signed in to change notification settings - Fork 4
chore(release): add CI release pipeline with npm provenance #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| name: Release | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| increment: | ||
| description: 'Version increment (auto = derive from conventional commits)' | ||
| type: choice | ||
| options: | ||
| - auto | ||
| - patch | ||
| - minor | ||
| - major | ||
| default: auto | ||
| dry_run: | ||
| description: 'Dry run — resolve the version and print the plan without tagging or publishing' | ||
| type: boolean | ||
| default: true | ||
|
|
||
| concurrency: | ||
| group: release | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| release: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| # Push the version commit + tag and create the GitHub Release. | ||
| contents: write | ||
| # Mint the OIDC token npm exchanges for a short-lived publish token, | ||
| # and sign the provenance attestation. | ||
| id-token: write | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | ||
| with: | ||
| # Full history so conventional-changelog can derive the bump from all | ||
| # commits since the previous tag. | ||
| fetch-depth: 0 | ||
| # release-it pushes the version commit and tag, so the checkout | ||
| # credentials must stay available. | ||
| persist-credentials: true | ||
|
|
||
| - name: Setup Bun | ||
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | ||
| with: | ||
| bun-version: latest | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | ||
| with: | ||
| # Node 24 ships npm >= 11.5.1, which is required for npm trusted | ||
| # publishing (OIDC). Node 22 ships npm 10 and cannot authenticate. | ||
| node-version: 24 | ||
| registry-url: 'https://registry.npmjs.org' | ||
|
|
||
| - name: Verify npm supports trusted publishing | ||
| run: | | ||
| npm_version="$(npm --version)" | ||
| required=11.5.1 | ||
| echo "npm $npm_version (need >= $required)" | ||
| if [ "$(printf '%s\n%s\n' "$required" "$npm_version" | sort -V | head -n1)" != "$required" ]; then | ||
| echo "::error::npm $npm_version is too old for trusted publishing (need >= $required)." | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Install dependencies | ||
| run: bun install --frozen-lockfile | ||
|
|
||
| - name: Codegen | ||
| run: bun run nitrogen | ||
|
|
||
| - name: Lint | ||
| run: bun run lint | ||
|
|
||
| - name: Typecheck | ||
| run: bun run typecheck | ||
|
|
||
| - name: Build | ||
| run: bun run build | ||
|
|
||
| - name: Test | ||
| run: bun run --filter react-native-better-maps test | ||
|
|
||
| - name: Resolve target version | ||
| id: version | ||
| working-directory: package | ||
| env: | ||
| INCREMENT: ${{ inputs.increment }} | ||
| run: | | ||
| # `--hooks.before:init=` keeps the test gate from running a second time | ||
| # here; the Test step above and the release run below both cover it. | ||
| args=(--release-version --hooks.before:init=) | ||
| [ "$INCREMENT" != "auto" ] && args+=(--increment "$INCREMENT") | ||
| version="$(bunx release-it "${args[@]}")" | ||
| echo "Resolved version: $version" | ||
| echo "version=$version" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Verify CHANGELOG entry exists | ||
| env: | ||
| VERSION: ${{ steps.version.outputs.version }} | ||
| run: | | ||
| if ! grep -qE "^## +${VERSION//./\\.}( |$)" CHANGELOG.md; then | ||
| echo "::error::CHANGELOG.md has no '## $VERSION' section. Add the release notes before publishing." | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Configure git identity | ||
| run: | | ||
| git config user.name 'github-actions[bot]' | ||
| git config user.email '41898282+github-actions[bot]@users.noreply.github.com' | ||
|
|
||
| - name: Release | ||
| working-directory: package | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| INCREMENT: ${{ inputs.increment }} | ||
| DRY_RUN: ${{ inputs.dry_run }} | ||
| run: | | ||
| args=(--ci) | ||
| [ "$INCREMENT" != "auto" ] && args+=(--increment "$INCREMENT") | ||
| [ "$DRY_RUN" = "true" ] && args+=(--dry-run) | ||
| echo "release-it ${args[*]}" | ||
| bunx release-it "${args[@]}" | ||
|
Comment on lines
+117
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- release-it npm publish configuration ---'
sed -n '1,240p' package/.release-it.json
printf '%s\n' '--- configured npm tag arguments ---'
rg -n -C 3 --glob 'package.json' --glob '.release-it.json' \
'publishArgs|npm[.]tag|"tag"|--tag|prerelease' packageRepository: gmi-software/react-native-better-maps Length of output: 1017 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- workflow context ---'
sed -n '1,145p' .github/workflows/release.yml
printf '%s\n' '--- package metadata ---'
sed -n '1,180p' package/package.json
printf '%s\n' '--- deterministic prerelease/tag check ---'
python3 - <<'PY'
import json
from pathlib import Path
release = json.loads(Path("package/.release-it.json").read_text())
package = json.loads(Path("package/package.json").read_text())
publish_args = release.get("npm", {}).get("publishArgs", [])
version = package.get("version", "")
print("version:", version)
print("is_prerelease:", "-" in version.split("+", 1)[0])
print("publishArgs:", publish_args)
print("has_tag_option:", any(a == "--tag" or a.startswith("--tag=") for a in publish_args))
print("has_non_latest_tag:", any(
(a.startswith("--tag=") and a.split("=", 1)[1] != "latest")
for a in publish_args
))
PYRepository: gmi-software/react-native-better-maps Length of output: 8556 🌐 Web query:
💡 Result: By default, the npm publish command automatically assigns the latest dist-tag to the version being published unless you explicitly specify a different tag using the --tag option [1][2][3]. This behavior applies even to pre-release versions (e.g., 1.0.0-beta.1), which can cause them to become the default version installed by users if they run npm install [4][5]. To avoid this, you should use the --tag flag to specify a non-latest tag (such as beta or next) when publishing pre-release versions [1][3]. Regarding release-it, if you are using it to automate your publishing process, you can configure the distribution tag and pass extra arguments to the underlying npm publish command via the configuration file (e.g.,.release-it.json or in package.json) [6][7][8]. To control the tag, use the npm.tag option [6][9]: { "npm": { "tag": "beta" } } To pass extra arguments to the publish command, use the npm.publishArgs option [6][9]: { "npm": { "publishArgs": ["--tag", "beta"] } } Note that npm.tag is the dedicated setting for managing the dist-tag, while npm.publishArgs provides a way to pass any additional arguments directly to the npm publish command [6][7]. Citations:
Set a non- The workflow accepts prerelease tags, but 🤖 Prompt for AI Agents |
||
|
|
||
| - name: Summary | ||
| if: always() | ||
| env: | ||
| VERSION: ${{ steps.version.outputs.version }} | ||
| DRY_RUN: ${{ inputs.dry_run }} | ||
| run: | | ||
| { | ||
| echo "### Release ${VERSION:-unknown}" | ||
| echo | ||
| if [ "$DRY_RUN" = "true" ]; then | ||
| echo "Dry run — nothing was tagged or published." | ||
| else | ||
| echo "- npm: https://www.npmjs.com/package/react-native-better-maps/v/$VERSION" | ||
| echo "- GitHub Release: ${{ github.server_url }}/${{ github.repository }}/releases/tag/v$VERSION" | ||
| fi | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to this project are documented in this file. | ||
|
|
||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), | ||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
|
||
| ## 1.1.0 | ||
|
|
||
| ### Behavior changes | ||
|
|
||
| Two changes alter runtime behavior without changing any type signatures, so your | ||
| code keeps compiling but may behave differently after upgrading. | ||
|
|
||
| **`onRegionChange` and `onRegionChangeComplete` now fire once per gesture** | ||
|
|
||
| In 1.0.0 these fired repeatedly while the map was moving, and also fired for | ||
| programmatic camera updates. Now: | ||
|
|
||
| - `onRegionChange` fires **once** when a user-initiated region change **begins** | ||
| - `onRegionChangeComplete` fires **once** when the user gesture **ends** | ||
| - Programmatic updates (`setCamera`, `animateCamera`, `fitToCoordinates`) no | ||
| longer emit either callback | ||
|
|
||
| If you relied on a continuous stream of region updates — a live coordinate | ||
| readout, or a "search this area" button that re-renders while panning — move that | ||
| work to `onRegionChangeComplete`, which now marks the end of the gesture: | ||
|
|
||
| ```tsx | ||
| // Before: fired continuously during the gesture | ||
| <MapView onRegionChange={(region) => setSearchArea(region)} /> | ||
|
|
||
| // After: fires once when the user stops moving the map | ||
| <MapView onRegionChangeComplete={(region) => setSearchArea(region)} /> | ||
| ``` | ||
|
|
||
| **`MapViewRef` camera methods now return `Promise<void>`** | ||
|
|
||
| `setCamera`, `animateCamera`, and `fitToCoordinates` previously returned `void`. | ||
| Existing call sites still compile, but linters configured with | ||
| `@typescript-eslint/no-floating-promises` will now flag them, and any custom | ||
| implementation or test mock of `MapViewRef` must be updated to match. | ||
|
|
||
| ```tsx | ||
| // Await the call, or explicitly ignore the promise | ||
| await mapRef.current?.animateCamera(camera, 300); | ||
| ``` | ||
|
|
||
| ### Features | ||
|
|
||
| - Add native POI press events with provider-specific payloads | ||
| (`onPoiPress`, `PoiPressEvent`, `ApplePoiPressEvent`, `GooglePoiPressEvent`) | ||
| ([#36](https://github.com/gmi-software/react-native-better-maps/pull/36)) | ||
| - Add Expo SDK 57 support | ||
| ([#49](https://github.com/gmi-software/react-native-better-maps/pull/49)) | ||
| - Rework map region change handling and camera update logic; programmatic | ||
| updates now skip no-op native calls | ||
| ([#48](https://github.com/gmi-software/react-native-better-maps/pull/48)) | ||
|
|
||
| ### Bug Fixes | ||
|
|
||
| - **ios:** Remove `main.sync` from `HybridMapView` and make camera APIs async, | ||
| fixing main-thread deadlocks | ||
| ([#45](https://github.com/gmi-software/react-native-better-maps/pull/45)) | ||
| - **ios:** Fix threading issues in map view ownership | ||
| ([#43](https://github.com/gmi-software/react-native-better-maps/pull/43)) | ||
| - **android:** Align SDK versions with the nitro-modules prefab | ||
| ([#41](https://github.com/gmi-software/react-native-better-maps/pull/41)) | ||
| - Fix failure on first-time build | ||
| ([#39](https://github.com/gmi-software/react-native-better-maps/pull/39)) | ||
|
|
||
| ## 1.0.0 | ||
|
|
||
| Initial public release: high-performance maps for React Native built on Nitro | ||
| Modules and the New Architecture, with Apple Maps and Google Maps providers on | ||
| iOS and Android. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # Releasing | ||
|
|
||
| Releases are cut by the [Release workflow](.github/workflows/release.yml) and | ||
| published to npm from CI with [provenance](https://docs.npmjs.com/generating-provenance-statements). | ||
| Nothing is published from a developer machine, and no long-lived npm token | ||
| exists anywhere. | ||
|
|
||
| ## Versioning | ||
|
|
||
| The version bump is derived from [Conventional Commits](https://www.conventionalcommits.org/) | ||
| since the previous tag: `fix:` gives a patch, `feat:` a minor, and a | ||
| `BREAKING CHANGE:` footer a major. You can override this with the workflow's | ||
| `increment` input. | ||
|
|
||
| Two things are worth knowing because tooling cannot infer them: | ||
|
|
||
| - A commit that is not conventional (for example `Fix threading issues on ios`) | ||
| is invisible to the bump calculation and to the generated release notes. Add | ||
| it to the changelog by hand. | ||
| - A change in runtime behavior that keeps the same types — such as a callback | ||
| that starts firing once per gesture instead of continuously — is a breaking | ||
| change for consumers even though their code still compiles. Either take the | ||
| major, or ship it as a minor with a prominent **Behavior changes** section, as | ||
| 1.1.0 did. | ||
|
|
||
| ## Changelog | ||
|
|
||
| `CHANGELOG.md` is written by hand, not generated. This is deliberate: the parts | ||
| of a release that matter most to users — behavior changes, migration snippets, | ||
| the reason a fix exists — cannot be derived from commit subjects. | ||
|
|
||
| The workflow **fails** if `CHANGELOG.md` has no `## <version>` section for the | ||
| version being released, so the notes cannot be forgotten. | ||
|
|
||
| Auto-generated notes from conventional commits still go into the GitHub Release | ||
| body, so the commit-level detail is not lost. | ||
|
|
||
| ## Cutting a release | ||
|
|
||
| 1. Make sure `main` is green and contains everything you want to ship. | ||
| 2. Add a `## <version>` section to `CHANGELOG.md` and merge it to `main`. | ||
| 3. Run the **Release** workflow from the Actions tab with `dry_run: true`. It | ||
| resolves the version, runs the full gate, and prints the plan without tagging | ||
| or publishing. | ||
| 4. Re-run with `dry_run: false`. | ||
|
|
||
| The workflow then bumps `package/package.json`, commits `chore: release vX.Y.Z`, | ||
| tags `vX.Y.Z`, pushes both, publishes to npm with provenance, and creates the | ||
| GitHub Release. | ||
|
|
||
| The iOS podspec reads its version from `package.json`, so there is no second | ||
| version to keep in sync. | ||
|
|
||
| ## One-time setup | ||
|
|
||
| ### npm trusted publishing | ||
|
|
||
| Publishing uses OIDC, so it only works once npm knows which workflow is allowed | ||
| to publish this package. On npmjs.com, open the `react-native-better-maps` | ||
| package settings and add a **GitHub Actions** trusted publisher pointing at: | ||
|
|
||
| - repository: `gmi-software/react-native-better-maps` | ||
| - workflow: `release.yml` | ||
|
|
||
| Until this is configured the publish step will fail with an authentication | ||
| error. If an `NPM_TOKEN` secret still exists in the repository, delete it once | ||
| trusted publishing works — it is no longer used. | ||
|
|
||
| ### Branch protection | ||
|
|
||
| The workflow pushes the release commit and tag directly to `main`. If `main` | ||
| requires pull requests or status checks, allow `github-actions[bot]` to bypass | ||
| those rules, or the push will be rejected after the package has already been | ||
| published. | ||
|
|
||
| ## Notes on the configuration | ||
|
|
||
| `package/.release-it.json` sets `npm.skipChecks: true`. release-it otherwise | ||
| runs `npm whoami` during startup, which fails under trusted publishing because | ||
| the short-lived token is only minted at publish time. | ||
|
|
||
| ## Releasing locally | ||
|
|
||
| Not supported for real releases — a local publish would produce a package | ||
| without provenance. To inspect what a release would do: | ||
|
|
||
| ```bash | ||
| cd package | ||
| bunx release-it --dry-run | ||
| ``` |
Uh oh!
There was an error while loading. Please reload this page.