From 2c643450fdc76de2d4ba18220413fe1b21f5df1e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 01:30:37 +0000 Subject: [PATCH 1/2] ci: raise Gradle heap to 4g to stop Jetifier OOM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The android job fails intermittently — roughly half of recent runs — while Jetifier rewrites react-android-0.81.1-debug.aar: Failed to transform react-android-0.81.1-debug.aar > Execution failed for JetifyTransform: .../react-android-0.81.1-debug.aar > Java heap space The failure happens during dependency resolution, before any project code is compiled, and is unrelated to whatever change is under test. 2g is no longer enough for the transform; ubuntu runners have ample memory. This matters beyond the flake itself: publishing is gated on a green Build, so an intermittently red android job intermittently blocks releases. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GWYKHSVSNoLM8Hquum8CuD --- example/android/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/android/gradle.properties b/example/android/gradle.properties index b4687e9b..11f7ca80 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -11,7 +11,7 @@ # particularly useful for configuring JVM memory settings for build performance. # This does not affect the JVM settings for the Gradle client VM. # The default is `-Xmx512m -XX:MaxMetaspaceSize=256m`. -org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will fork up to org.gradle.workers.max JVMs to execute # projects in parallel. To learn more about parallel task execution, see the From 9115752ecaaca4819bb400333d833ac98ed5340d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 01:30:37 +0000 Subject: [PATCH 2/2] ci: publish to npm automatically from CI Releases were a manual, local operation: a maintainer ran `yarn release` with npm credentials on hand. Every published release is authored by one person, and master has carried four releasable commits since v2.0.0 without them reaching npm. Add a Release workflow that runs the existing release-it configuration in CI. It triggers on the Build workflow completing on master and releases only if Build passed, so publishing is gated on lint, tsc, and the Android/iOS builds. Version and changelog still come from @release-it/conventional-changelog, so output matches the current manual release. Two guards keep it honest: - If master has moved past the commit Build validated, the run skips rather than publishing code CI never checked. The next green Build picks it up. - The `chore: release` commit release-it pushes is ignored, so a release cannot trigger another release. A run whose commits are all chore/docs/test/refactor stops before touching npm, since the angular preset would produce no version bump. Authentication uses an NPM_TOKEN repository secret, read via the .npmrc that actions/setup-node writes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GWYKHSVSNoLM8Hquum8CuD --- .github/workflows/release.yml | 106 ++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 21 +++++++ 2 files changed, 127 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..bdc19045 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,106 @@ +name: Release + +# Publishes to npm automatically once `Build` has passed on master. +# +# The version and changelog come from release-it + @release-it/conventional-changelog +# (see the "release-it" block in package.json), so they are derived from the +# conventional commit messages since the previous tag. +# +# Requires an `NPM_TOKEN` repository secret holding an npm access token with +# publish rights on @react-native-menu/menu. +on: + workflow_run: + workflows: ["Build"] + types: + - completed + branches: + - master + # Manual escape hatch, e.g. to retry a release that failed partway through. + # Note this path does not check Build, so only run it on a commit you know is green. + workflow_dispatch: + +concurrency: + group: release + cancel-in-progress: false + +jobs: + release: + name: Publish to npm + runs-on: ubuntu-latest + # Never react to the `chore: release` commit release-it pushes itself, + # and never publish off a red Build. + if: >- + github.event_name == 'workflow_dispatch' || + (github.event.workflow_run.conclusion == 'success' && + !startsWith(github.event.workflow_run.head_commit.message, 'chore: release')) + permissions: + # release-it pushes the release commit and tag, and creates the GitHub release. + contents: write + steps: + - uses: actions/checkout@v4 + with: + ref: master + # Full history and tags, so conventional-changelog can see the commits + # since the previous release. + fetch-depth: 0 + + - name: Decide whether to release + id: guard + run: | + set -euo pipefail + + # `Build` validated a specific commit. If master has moved on since then, + # publishing now would ship code CI never checked. + if [ "${{ github.event_name }}" = "workflow_run" ]; then + expected='${{ github.event.workflow_run.head_sha }}' + actual="$(git rev-parse HEAD)" + if [ "$expected" != "$actual" ]; then + echo "master moved past the commit Build validated ($expected -> $actual)." + echo "Skipping; the next green Build will pick this up." + echo "release=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + fi + + last_tag="$(git describe --tags --abbrev=0 2>/dev/null || true)" + if [ -n "$last_tag" ]; then + range="$last_tag..HEAD" + else + range="HEAD" + fi + + # Only feat/fix/perf and breaking changes produce a version bump under the + # angular preset. Without one, release-it has nothing to do. + releasable="$(git log "$range" --format='%s%n%b' \ + | grep -cE '^(feat|fix|perf)(\([^)]*\))?!?: |^BREAKING[ -]CHANGE:' || true)" + + echo "Releasable commits since ${last_tag:-the start of history}: $releasable" + if [ "$releasable" -eq 0 ]; then + echo "release=false" >> "$GITHUB_OUTPUT" + else + echo "release=true" >> "$GITHUB_OUTPUT" + fi + + - uses: actions/setup-node@v4 + if: steps.guard.outputs.release == 'true' + with: + node-version: 22 + registry-url: https://registry.npmjs.org + + - name: Install dependencies + if: steps.guard.outputs.release == 'true' + run: yarn install --immutable + + - name: Configure git identity + if: steps.guard.outputs.release == 'true' + run: | + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + + - name: Release + if: steps.guard.outputs.release == 'true' + run: yarn release --ci + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # setup-node writes an .npmrc that reads NODE_AUTH_TOKEN. + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7e00ca57..09e10bca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,6 +113,27 @@ When you're sending a pull request: - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. - For version-dependent changes, follow the versioning structure for `MenuViewManager` outlined in the **Versioning in `MenuViewManager`** section. Ensure all version-specific files are included in `reactNativeVersionPatch` and referenced in `build.gradle`. +### Releasing + +Releases are automated. Merging to `master` is all that is needed — no one runs `yarn release` locally. + +`.github/workflows/release.yml` waits for the `Build` workflow to finish on `master` and then, if it passed, runs `release-it` in CI. `@release-it/conventional-changelog` derives the new version and the release notes from the commit messages since the previous tag, so **the version bump is decided entirely by your commit message**: + +- `fix:` or `perf:` → patch +- `feat:` → minor +- `!` after the type, or a `BREAKING CHANGE:` footer → major + +A push containing only `chore:`, `docs:`, `test:`, or `refactor:` commits releases nothing, and the workflow stops before touching npm. Note that nothing currently enforces the commit format, so a `feat` mislabelled as `chore` silently ships nothing — squash-merge titles matter here. + +The workflow publishes to npm, pushes the `chore: release ` commit and the `v` tag, and creates the GitHub release. It authenticates with an `NPM_TOKEN` repository secret; if publishing starts failing with an auth error, that token has most likely expired. + +Two things the workflow deliberately refuses to do: + +- It skips if `master` has moved past the commit `Build` validated, rather than publishing code CI never checked. The next green `Build` picks it up. +- It ignores its own `chore: release` commit, so a release cannot trigger another release. + +`Release` can also be started manually from the Actions tab, which is useful when a release fails partway through. That path skips the `Build` gate, so only use it on a commit you know is green. + ## Code of Conduct ### Our Pledge