From 21f431441e9e88f1bc699ab4ffd6da28408da801 Mon Sep 17 00:00:00 2001 From: Marcus Rehbock Date: Fri, 7 Aug 2026 17:47:07 -0700 Subject: [PATCH] Gitea Actions: auto-build signed arm64 APK release on push to main Mirrors the meditation-timer pipeline; VERSION_CODE/VERSION_NAME are stamped from the run number via app.config.js so Obtainium sees each release as an upgrade. Keystore comes from repo secrets. Co-Authored-By: Claude Fable 5 --- .gitea/workflows/release.yml | 84 ++++++++++++++++++++++++++++++++++++ app.config.js | 10 +++++ 2 files changed, 94 insertions(+) create mode 100644 .gitea/workflows/release.yml create mode 100644 app.config.js diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..46924f6 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,84 @@ +name: Build & Release APK + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + container: + image: eclipse-temurin:21-jdk + steps: + # actions/checkout needs node, which this container lacks — clone directly + - name: Checkout + run: | + apt-get update -qq && apt-get install -y -qq git unzip curl xz-utils >/dev/null + git init -q . + git fetch -q --depth 1 "https://git.rehbock.xyz/${{ github.repository }}.git" "${{ github.sha }}" + git checkout -q FETCH_HEAD + + - name: Install Node 22 + run: | + curl -sLo /tmp/node.tar.xz https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-x64.tar.xz + mkdir -p /opt/node && tar xJf /tmp/node.tar.xz -C /opt/node --strip-components=1 + echo "/opt/node/bin" >> "$GITHUB_PATH" + /opt/node/bin/node --version + + - name: npm ci + run: npm ci --no-audit --no-fund + + - name: Install Android SDK + NDK + run: | + mkdir -p "$HOME/android-sdk/cmdline-tools" + curl -sLo /tmp/ct.zip https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip + unzip -q /tmp/ct.zip -d /tmp + mv /tmp/cmdline-tools "$HOME/android-sdk/cmdline-tools/latest" + SDKMAN="$HOME/android-sdk/cmdline-tools/latest/bin/sdkmanager" + yes | "$SDKMAN" --sdk_root="$HOME/android-sdk" --licenses >/dev/null 2>&1 || true + "$SDKMAN" --sdk_root="$HOME/android-sdk" \ + "platform-tools" "platforms;android-36" "build-tools;36.0.0" \ + "ndk;27.1.12297006" "cmake;3.22.1" >/dev/null + + - name: Decode signing keystore + env: + KEYSTORE_B64: ${{ secrets.KEYSTORE_B64 }} + run: echo "$KEYSTORE_B64" | base64 -d > /tmp/release.jks + + - name: Prebuild android project + env: + VERSION_CODE: ${{ github.run_number }} + VERSION_NAME: 1.0.${{ github.run_number }} + run: npx expo prebuild --platform android --no-install + + - name: Build signed release APK (arm64) + env: + HEALTH_KEYSTORE: /tmp/release.jks + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: health + VERSION_CODE: ${{ github.run_number }} + VERSION_NAME: 1.0.${{ github.run_number }} + run: | + export ANDROID_HOME="$HOME/android-sdk" + echo "sdk.dir=$ANDROID_HOME" > android/local.properties + cd android && ./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a \ + --no-daemon --console=plain + + - name: Publish Gitea release with APK + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + API="https://git.rehbock.xyz/api/v1/repos/${{ github.repository }}" + TAG="v1.0.${{ github.run_number }}" + APK=android/app/build/outputs/apk/release/app-release.apk + test -f "$APK" + curl -sf -X POST "$API/releases" \ + -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \ + -d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":\"Automated build of commit ${{ github.sha }}\",\"target_commitish\":\"${{ github.sha }}\"}" \ + > /tmp/release.json + RID=$(grep -o '"id":[0-9]*' /tmp/release.json | head -1 | cut -d: -f2) + echo "release id: $RID" + curl -sf -X POST "$API/releases/$RID/assets?name=health-app-$TAG-arm64.apk" \ + -H "Authorization: token $TOKEN" \ + -F "attachment=@$APK;type=application/vnd.android.package-archive" >/dev/null + echo "published $TAG" diff --git a/app.config.js b/app.config.js new file mode 100644 index 0000000..d34ff9d --- /dev/null +++ b/app.config.js @@ -0,0 +1,10 @@ +/* Wraps app.json so CI can stamp versions: VERSION_NAME → expo.version, + VERSION_CODE → android.versionCode (Obtainium needs it to increase). */ +module.exports = ({ config }) => { + const versionCode = parseInt(process.env.VERSION_CODE || '', 10); + if (process.env.VERSION_NAME) config.version = process.env.VERSION_NAME; + if (Number.isFinite(versionCode)) { + config.android = { ...config.android, versionCode }; + } + return config; +};