Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: Release

# Publishing is driven by a version tag, not by a commit pushed from CI.
# `main` requires pull request reviews with `enforce_admins` enabled, so nothing —
# including github-actions[bot] — can push a release commit to it. The version
# bump therefore lands through a normal reviewed pull request, and pushing the
# matching tag afterwards triggers the publish.
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-*'
# Rehearsal against the current branch. Always a dry run: it validates the
# version, changelog and full gate without tagging or publishing anything.
workflow_dispatch:

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
release:
runs-on: ubuntu-latest
permissions:
# Create the GitHub Release for the pushed tag.
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 build the release notes
# from every commit since the previous tag.
fetch-depth: 0
persist-credentials: false

- 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, required for npm trusted publishing.
# `registry-url` is deliberately omitted: it writes an `_authToken`
# entry into .npmrc, which makes npm assume classic token auth and skip
# the OIDC flow. The registry is already pinned by publishConfig.
node-version: 24

Comment thread
coderabbitai[bot] marked this conversation as resolved.
- 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: Resolve version
id: version
run: |
version="$(node -p "require('./package/package.json').version")"
echo "package.json version: $version"
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Verify tag matches package.json
if: github.event_name == 'push'
env:
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ github.ref_name }}
run: |
if [ "$TAG" != "v$VERSION" ]; then
echo "::error::Tag $TAG does not match package.json version $VERSION. Bump the version on main first."
exit 1
fi

- 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: Verify version is not already published
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
if npm view "react-native-better-maps@$VERSION" version >/dev/null 2>&1; then
echo "::error::react-native-better-maps@$VERSION is already on npm."
exit 1
fi

- 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: Publish
working-directory: package
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# --no-increment: the version already lives in package.json, reviewed
# and merged through a pull request.
# --no-git: the tag is pushed by the maintainer; CI never writes to git.
args=(--ci --no-increment --no-git)
if [ "${{ github.event_name }}" != 'push' ]; then
args+=(--dry-run)
echo 'Rehearsal run — nothing will be tagged or published.'
fi
bunx release-it "${args[@]}"
Comment on lines +117 to +130

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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' package

Repository: 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
))
PY

Repository: gmi-software/react-native-better-maps

Length of output: 8556


🌐 Web query:

npm publish default dist-tag prerelease version --tag latest release-it publishArgs documentation

💡 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-latest dist-tag for prereleases.

The workflow accepts prerelease tags, but package/.release-it.json sets no npm tag. npm will publish a prerelease as latest unless the command supplies another tag. Pass --tag next for prereleases, or reject prerelease tags.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 117 - 130, Update the Publish
step’s release arguments to ensure prerelease versions are not published under
npm’s latest dist-tag. In the release flow around the args array and
package/.release-it.json configuration, supply a non-latest tag such as next
when publishing prereleases, or explicitly reject prerelease tags; preserve the
existing dry-run behavior for non-push events.


- name: Summary
if: always()
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
{
echo "### Release v$VERSION"
echo
if [ "${{ github.event_name }}" != 'push' ]; then
echo "Rehearsal only — nothing was 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"
76 changes: 76 additions & 0 deletions CHANGELOG.md
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.
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ chore: add commitlint configuration
- Place imports at the top of files.
- Use exhaustive switch handling for discriminated unions.

## Releasing

Maintainers cut releases from CI — see [RELEASING.md](RELEASING.md).

## Reporting issues

Please use [GitHub Issues](https://github.com/gmi-software/react-native-better-maps/issues) and include:
Expand Down
104 changes: 104 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Releasing

Releases are published to npm from CI with
[provenance](https://docs.npmjs.com/generating-provenance-statements) by the
[Release workflow](.github/workflows/release.yml). Nothing is published from a
developer machine, and no long-lived npm token exists anywhere.

Publishing is triggered by pushing a version tag. CI never writes to git: `main`
requires pull request reviews with `enforce_admins` enabled, so nothing — not
even `github-actions[bot]` — can push a release commit to it. The version bump
goes through a normal reviewed pull request instead, which has the useful side
effect of putting the changelog in front of a reviewer.

## Cutting a release

**1. Open a release pull request.** Bump the version and write the notes:

```bash
cd package
npm version 1.1.0 --no-git-tag-version
```

Then add a matching `## 1.1.0` section to `CHANGELOG.md`, and open a pull request
with both changes. Review and merge it as usual.

**2. Rehearse (optional).** Run the **Release** workflow manually from the
Actions tab. A manual run is always a dry run: it validates the version, the
changelog and the full gate without publishing.

**3. Push the tag.**

```bash
git checkout main && git pull
git tag -a v1.1.0 -m 'v1.1.0'
git push origin v1.1.0
```

The workflow then verifies the tag matches `package/package.json`, that
`CHANGELOG.md` has a section for it, and that the version is not already on npm;
runs the full gate; publishes to npm with provenance; and creates the GitHub
Release with notes generated from the conventional commits since the last tag.

The iOS podspec reads its version from `package.json`, so there is no second
version to keep in sync.

## Versioning

The bump is a judgement call, made when you open the release pull request. The
usual rules apply — `fix:` is a patch, `feat:` a minor, an incompatible API
change a major — but two cases are easy to get wrong:

- A commit that is not conventional (for example `Fix threading issues on ios`)
is invisible 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 — breaks 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 — behavior changes, migration snippets, the reason
a fix exists — cannot be derived from commit subjects. The workflow **fails** if
there is no `## <version>` section, so the notes cannot be forgotten.

Notes generated from conventional commits still go into the GitHub Release body,
so commit-level detail is not lost.

## One-time setup: npm trusted publishing

Publishing uses OIDC, so it only works once npm knows which workflow may 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 fails 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.

## Notes on the configuration

A few settings exist for non-obvious reasons:

- `npm.skipChecks: true` in `package/.release-it.json` — release-it otherwise
runs `npm whoami` at startup, which fails under trusted publishing because the
token is only minted at publish time.
- The workflow does **not** set `registry-url` on `actions/setup-node` — doing so
writes an `_authToken` entry into `.npmrc`, which makes npm assume classic
token auth and skip the OIDC flow. The registry is pinned by `publishConfig`.
- `release-it` runs with `--no-increment --no-git`: the version is already
committed and the tag already pushed, so CI only publishes and creates the
release.

## Inspecting a release locally

A local publish would produce a package without provenance, so it is not
supported. To see what a release would do:

```bash
cd package
bunx release-it --no-increment --no-git --dry-run
```
Loading
Loading