Files
orca/.github/workflows/mobile-android-release.yml
T
Brennan BensonandMerge Sim 3da1c5b2b1 fix(ci): stop Android release notes exceeding the GitHub body limit (#19114)
* fix(ci): stop Android release notes exceeding the GitHub body limit

gh release create --generate-notes let GitHub pick the previous tag. Release
tags live on side branches, so 0.0.46 and 0.0.47 are not ancestors of main and
detection reached back to 0.0.44, generating four releases' worth of notes:
130413 characters against a 125000 limit, which 422'd the publish after a full
Gradle build. The span grows every release.

Pin the comparison to the previous mobile-android release (0.0.47 -> 81862
characters) and cap the body so an unexpected span can never fail the publish.

* fix(ci): fall back when release-notes generation returns an HTTP error

gh writes the JSON error body to stdout on a failed request, so the redirect
left it in the notes file. The non-empty check then treated that blob as valid
notes and skipped the fallback, publishing {"message":...} as the release body.
Gate on exit status instead. Also match the current tag literally when picking
the previous release, so the dots are not regex wildcards.

* fix(ci): reuse the shared character-safe release-body truncation

The byte-based cap could split a multi-byte character at the boundary.
config/scripts/create-draft-release.mjs already exports truncateReleaseBody
with the same 120000 cap and a truncation notice, and the desktop release path
uses it. Import is side-effect free; its main() is guarded.

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-06 13:28:33 -07:00

151 lines
5.5 KiB
YAML

name: Mobile Android Release
# Why a separate workflow from iOS: iOS releases go through App Store review,
# which can take days. Decoupling the triggers lets an Android release ship
# immediately without waiting on iOS, and vice versa.
on:
push:
tags:
- 'mobile-android-v*'
workflow_dispatch:
inputs:
release_version:
description: 'Optional exact mobile marketing version assertion, e.g. 0.0.22'
required: false
type: string
publish_github_release:
description: 'Create or update the mobile-android-v<version> GitHub Release'
required: false
default: true
type: boolean
jobs:
android-build:
runs-on: ubuntu-latest
# Why: the "Create GitHub Release" step below uses the default GITHUB_TOKEN
# to call `gh release create`, which requires contents:write. Without this,
# the job builds the APK fine but fails at release time with
# "HTTP 403: Resource not accessible by integration".
permissions:
contents: write
defaults:
run:
working-directory: mobile
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Setup pnpm
uses: pnpm/setup@v2
with:
install: false
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Resolve release version and version code
id: release
env:
MOBILE_ANDROID_RELEASE_VERSION: ${{ github.event.inputs.release_version }}
MOBILE_ANDROID_PUBLISH_RELEASE: ${{ github.event.inputs.publish_github_release }}
run: node scripts/prepare-android-release.mjs
- name: Setup JDK 17
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
- name: Expo prebuild
run: npx expo prebuild --platform android --no-install
- name: Build Android release APK
run: cd android && ./gradlew assembleRelease
- name: Upload APK artifact
uses: actions/upload-artifact@v7
with:
name: orca-mobile-apk
path: mobile/android/app/build/outputs/apk/release/*.apk
- name: Ensure GitHub release tag
if: steps.release.outputs.publish_release == 'true' && !startsWith(github.ref, 'refs/tags/mobile-android-v')
run: |
set -euo pipefail
tag="${{ steps.release.outputs.tag }}"
if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null 2>&1; then
echo "Tag $tag already exists"
exit 0
fi
git tag "$tag" "$GITHUB_SHA"
git push origin "refs/tags/$tag"
- name: Create GitHub Release
if: steps.release.outputs.publish_release == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="${{ steps.release.outputs.tag }}"
if gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release upload "$tag" \
--repo "$GITHUB_REPOSITORY" \
--clobber \
android/app/build/outputs/apk/release/*.apk
else
# Why: release tags live on side branches, so GitHub's automatic
# previous-tag detection reaches back several releases; that body
# already exceeds the 125000-character API limit and grows each
# release. Pin the comparison base and cap the size.
notes_file="$RUNNER_TEMP/android-release-notes.md"
previous_tag="$(
gh release list --repo "$GITHUB_REPOSITORY" --limit 200 --json tagName --jq '.[].tagName' \
| grep '^mobile-android-v' | grep -Fxv "$tag" | sort -V | tail -1 || true
)"
if [ -n "$previous_tag" ]; then
# Why: gh writes the JSON error body to stdout on an HTTP error, so a
# non-empty file is not proof of success — gate on exit status.
if ! gh api "repos/$GITHUB_REPOSITORY/releases/generate-notes" -X POST \
-f tag_name="$tag" \
-f target_commitish="$GITHUB_SHA" \
-f previous_tag_name="$previous_tag" \
--jq .body > "$notes_file"; then
: > "$notes_file"
fi
fi
if [ ! -s "$notes_file" ]; then
printf 'Orca Mobile Android %s\n' "$tag" > "$notes_file"
fi
# Why: reuse the desktop release path's character-safe truncation so a
# multi-byte character cannot be split at the cap.
NOTES_FILE="$notes_file" \
NOTES_MODULE="$GITHUB_WORKSPACE/config/scripts/create-draft-release.mjs" \
node --input-type=module -e '
const { readFileSync, writeFileSync } = await import("node:fs")
const { pathToFileURL } = await import("node:url")
const { truncateReleaseBody } = await import(pathToFileURL(process.env.NOTES_MODULE).href)
const file = process.env.NOTES_FILE
writeFileSync(file, truncateReleaseBody(readFileSync(file, "utf8")))
'
gh release create "$tag" \
--repo "$GITHUB_REPOSITORY" \
--title "Orca Mobile Android $tag" \
--prerelease \
--latest=false \
--notes-file "$notes_file" \
android/app/build/outputs/apk/release/*.apk
fi