Releases 0.4.3-0.4.7 uploaded zero source-map files to Sentry, leaving every JS frame unsymbolicated (app:///index.android.bundle:1). Root-caused two independent bugs: 1. No metro.config.js existed, so Metro never ran Sentry's debug-ID injection. Without an embedded debug ID, sentry.gradle's upload task falls back to matching source maps to events by release/dist string alone (see has-sourcemap-debugid.js check in sentry.gradle) - and that fallback was broken (see #2). Added metro.config.js wrapping Expo's default config with getSentryExpoConfig from @sentry/react-native/metro, the officially documented path for Expo + debug-ID symbolication. The installed @sentry/react-native@6.14.0 could not actually bundle with this enabled: its metro integration does a hard `require("metro/src/lib/ countLines")`, a deep path metro 0.83.x (bundled by Expo SDK 54) no longer exposes via its package.json `exports` map, crashing every build. Bumped to ~6.22.0 (package.json:18), which vendors countLines and adds metro/private/* fallbacks for other deep metro imports. Verified via a real `npx expo export:embed` run: bundle and source map now share a matching `debugId`. 2. sentry.gradle's default release/dist for the upload is `${applicationId}@${versionName}+${versionCode}` (computed from android/app/build.gradle), which never matched what Sentry.init() reports at runtime (`opencode-mobile@${app.json version}`, src/lib/sentry.ts:33-34). Every source map was therefore filed under a release Sentry never queries. Added a "Set Sentry release identifiers" step to build.yml, publish-play-store.yml, and publish-fdroid.yml that exports SENTRY_RELEASE/SENTRY_DIST from app.json's version before the Gradle build step, forcing an exact match. Also filled in organization/project on the `@sentry/react-native/expo` plugin in app.json (previously a bare string, which only warned "Missing config for organization, project" and relied on env-var fallback) so android/sentry.properties is generated deterministically instead of by accident/history. Verified locally (no push - GitHub is down, consolidating to local main): - npx expo export:embed (real Metro bundle) succeeds and embeds a matching debugId in both index.android.bundle and its .map - npm run typecheck: clean - npm test: 81/81 passing - Full ./gradlew Android build not verified: this machine has no ANDROID_HOME/SDK and a JDK/Gradle-wrapper version mismatch unrelated to this change; CI's Java 17 + Android SDK toolchain is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJKAQ6HAikWGQK7PGZ5Y4E
162 lines
6.6 KiB
YAML
162 lines
6.6 KiB
YAML
name: Publish F-Droid Repo
|
||
|
||
on:
|
||
push:
|
||
tags: ["v*"]
|
||
workflow_dispatch:
|
||
|
||
jobs:
|
||
publish-fdroid:
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
env:
|
||
EXPO_PUBLIC_SENTRY_DSN: ${{ secrets.EXPO_PUBLIC_SENTRY_DSN }}
|
||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
|
||
- uses: actions/setup-node@v6
|
||
with:
|
||
node-version: 20
|
||
cache: npm
|
||
|
||
- uses: actions/setup-java@v5
|
||
with:
|
||
distribution: temurin
|
||
java-version: 17
|
||
|
||
- name: Setup Android SDK
|
||
uses: android-actions/setup-android@v4
|
||
|
||
- name: Cache Gradle
|
||
uses: actions/cache@v5
|
||
with:
|
||
path: |
|
||
~/.gradle/caches
|
||
~/.gradle/wrapper
|
||
android/.gradle
|
||
key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/*.gradle*', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
||
restore-keys: |
|
||
${{ runner.os }}-gradle-
|
||
|
||
- name: Install dependencies
|
||
run: npm install --legacy-peer-deps
|
||
|
||
- name: Set Sentry release identifiers
|
||
# sentry.gradle (applied from android/app/build.gradle) defaults the
|
||
# upload's --release/--dist to `${applicationId}@${versionName}+${versionCode}`,
|
||
# which does NOT match the release/dist Sentry.init() reports at runtime
|
||
# (`opencode-mobile@${app.json version}`, see src/lib/sentry.ts). That
|
||
# mismatch made every uploaded source map land under a release Sentry
|
||
# never looks up, so symbolication silently failed. Pin the Gradle-side
|
||
# values to exactly what the app reports.
|
||
run: |
|
||
VERSION=$(node -p "require('./app.json').expo.version")
|
||
echo "SENTRY_RELEASE=opencode-mobile@${VERSION}" >> "$GITHUB_ENV"
|
||
echo "SENTRY_DIST=${VERSION}" >> "$GITHUB_ENV"
|
||
echo "Sentry release=opencode-mobile@${VERSION} dist=${VERSION}"
|
||
|
||
- name: Expo prebuild
|
||
run: npx expo prebuild --platform android --no-install
|
||
|
||
- name: Setup signing
|
||
run: |
|
||
if [[ -n "${{ secrets.KEYSTORE_BASE64 }}" ]]; then
|
||
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/release.keystore
|
||
echo "RELEASE_STORE_FILE=release.keystore" >> "$GITHUB_ENV"
|
||
echo "RELEASE_STORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> "$GITHUB_ENV"
|
||
echo "RELEASE_KEY_ALIAS=${{ secrets.KEY_ALIAS }}" >> "$GITHUB_ENV"
|
||
echo "RELEASE_KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> "$GITHUB_ENV"
|
||
echo "Signing: production keystore"
|
||
else
|
||
keytool -genkey -v -keystore android/app/debug.keystore -storepass android \
|
||
-alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 \
|
||
-dname "CN=Android Debug,O=Android,C=US"
|
||
echo "Signing: debug keystore"
|
||
fi
|
||
|
||
- name: Build APK
|
||
working-directory: android
|
||
run: ./gradlew assembleRelease
|
||
|
||
- name: Re-sign APK v1+v2 only (drop v3/v4 for fdroidserver compatibility)
|
||
if: ${{ env.RELEASE_STORE_FILE != '' }}
|
||
run: |
|
||
# androguard (used by fdroidserver) crashes parsing a v2+v3 signature
|
||
# block pair: "'NoOverwriteDict' object has no attribute 'append'".
|
||
# expo prebuild regenerates build.gradle, so we can't rely on the
|
||
# gradle signing flags surviving — force v1+v2-only here deterministically.
|
||
APK=android/app/build/outputs/apk/release/app-release.apk
|
||
APKSIGNER=$(ls "$ANDROID_HOME"/build-tools/*/apksigner | sort -V | tail -1)
|
||
echo "Using $APKSIGNER"
|
||
"$APKSIGNER" sign \
|
||
--ks android/app/release.keystore \
|
||
--ks-pass "pass:${RELEASE_STORE_PASSWORD}" \
|
||
--ks-key-alias "${RELEASE_KEY_ALIAS}" \
|
||
--key-pass "pass:${RELEASE_KEY_PASSWORD}" \
|
||
--v1-signing-enabled true \
|
||
--v2-signing-enabled true \
|
||
--v3-signing-enabled false \
|
||
--v4-signing-enabled false \
|
||
"$APK"
|
||
echo "=== signature schemes after re-sign ==="
|
||
"$APKSIGNER" verify -v "$APK" | grep -i "Verified using" || true
|
||
|
||
- name: Install fdroidserver
|
||
# androguard version matters. 4.1.4 crashes extracting the signer cert
|
||
# ("'NoOverwriteDict' object has no attribute 'append'" in
|
||
# parse_v2_v3_signature) — this is what broke the publish from v0.4.2 on.
|
||
# 4.1.3 is the version that successfully published v0.3.2–v0.4.1 and parses
|
||
# our (v1+v2-only, re-signed above) APK cleanly — verified locally against
|
||
# the release APK via fdroidserver.common.get_first_signer_certificate.
|
||
run: pip install "fdroidserver==2.4.4" "androguard==4.1.3"
|
||
|
||
- name: Setup F-Droid repo
|
||
id: fdroid-setup
|
||
run: |
|
||
FDROID_DIR="$HOME/fdroid-repo"
|
||
mkdir -p "$FDROID_DIR/repo"
|
||
mkdir -p "$FDROID_DIR/metadata"
|
||
|
||
echo "${{ secrets.FDROID_REPO_KEYSTORE_B64 }}" | base64 -d > "$FDROID_DIR/repo-keystore.jks"
|
||
|
||
cat > "$FDROID_DIR/config.yml" << CONFIGEOF
|
||
repo_url: https://dzianisv.github.io/opencode-mobile/fdroid/repo
|
||
repo_name: OpenCode Mobile
|
||
repo_description: OpenCode Mobile - AI coding assistant companion app
|
||
keystore: $FDROID_DIR/repo-keystore.jks
|
||
repo_keyalias: ${{ secrets.FDROID_REPO_KEY_ALIAS }}
|
||
keystorepass: ${{ secrets.FDROID_REPO_KEYSTORE_PASS }}
|
||
keypass: ${{ secrets.FDROID_REPO_KEY_PASS }}
|
||
CONFIGEOF
|
||
|
||
cp android/app/build/outputs/apk/release/app-release.apk "$FDROID_DIR/repo/"
|
||
|
||
echo "fdroid-dir=$FDROID_DIR" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Generate F-Droid repo index
|
||
run: |
|
||
cd "${{ steps.fdroid-setup.outputs.fdroid-dir }}"
|
||
fdroid update --create-metadata
|
||
|
||
- name: Verify F-Droid repo index was generated
|
||
run: |
|
||
IDX="${{ steps.fdroid-setup.outputs.fdroid-dir }}/repo/index.xml"
|
||
if [[ ! -f "$IDX" ]]; then
|
||
echo "ERROR: F-Droid repo index not generated at $IDX"
|
||
ls -la "${{ steps.fdroid-setup.outputs.fdroid-dir }}/repo/" || true
|
||
exit 1
|
||
fi
|
||
echo "F-Droid repo index verified: $(wc -c < "$IDX") bytes"
|
||
|
||
- name: Deploy to GitHub Pages
|
||
uses: peaceiris/actions-gh-pages@v4
|
||
with:
|
||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||
publish_dir: ${{ steps.fdroid-setup.outputs.fdroid-dir }}/repo
|
||
destination_dir: fdroid/repo
|
||
keep_files: true
|