diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index ea102e8..e52d86c 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -18,10 +18,15 @@ # SUPPLY_JSON_KEY_DATA Google Play service-account JSON # # NOTE: `runs-on` must match a label your Forgejo runner registered with. -# fdroid_reference runs as a 3-way ABI matrix (not one job building all 3 -# sequentially): each ABI needs its own NDK/tesseract-native/flutter compile -# from a cold cache, which triggered the runner's max-job-runtime kill when -# done back-to-back in a single job. One ABI per job fits comfortably. +# fdroid_reference is 3 separate jobs chained with `needs:` (armeabi-v7a -> +# arm64-v8a -> x86_64), NOT a matrix: this runner's act_runner does not honor +# `strategy.max-parallel`, so a matrix always ran all 3 concurrently — 2-3 +# simultaneous cold NDK/CMake/Gradle builds contend for RAM and get OOM-killed +# (silent "Gradle build daemon disappeared", no error message). `needs:` is +# core Actions syntax with no such gap, so it actually serializes them. Each +# needs its own full cold NDK+tesseract-native+flutter compile (~30-35min +# alone), which is why they're chained rather than run in one job (that alone +# was too slow even for a 90m timeout). name: release @@ -98,36 +103,20 @@ jobs: working-directory: apps/app_seeds run: rm -f android/key.properties "$GITHUB_WORKSPACE/tane-upload.jks" - fdroid_reference: - strategy: - # Sequential, not parallel: 2-3 concurrent NDK/CMake/Gradle builds on the - # same host contend for RAM and get OOM-killed (silent truncated log, no - # "max run time" message) — confirmed 2026-07-21 when only the ABI that - # ended up running alone (after the other two died) completed. One at a - # time is slower (~30-35min x3) but fits comfortably in the 90m timeout. - max-parallel: 1 - matrix: - abi: [armeabi-v7a, arm64-v8a, x86_64] - include: - - abi: armeabi-v7a - offset: 1 - - abi: arm64-v8a - offset: 2 - - abi: x86_64 - offset: 3 + fdroid_reference_armeabi_v7a: runs-on: docker container: image: registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie steps: - - name: Build the ${{ matrix.abi }} reference APK with fdroid, sign, and attach it + - name: Build the armeabi-v7a reference APK with fdroid, sign, and attach it env: TOKEN: ${{ github.token }} TANE_KEYSTORE_BASE64: ${{ secrets.TANE_KEYSTORE_BASE64 }} TANE_KEYSTORE_PASSWORD: ${{ secrets.TANE_KEYSTORE_PASSWORD }} TANE_KEY_ALIAS: ${{ secrets.TANE_KEY_ALIAS }} TANE_KEY_PASSWORD: ${{ secrets.TANE_KEY_PASSWORD }} - ABI: ${{ matrix.abi }} - OFFSET: ${{ matrix.offset }} + ABI: armeabi-v7a + OFFSET: 1 run: | set -eu @@ -228,3 +217,167 @@ jobs: else echo "built+signed app-${ABI}-release.apk (dispatch: upload skipped)" fi + + fdroid_reference_arm64_v8a: + needs: fdroid_reference_armeabi_v7a + runs-on: docker + container: + image: registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie + steps: + - name: Build the arm64-v8a reference APK with fdroid, sign, and attach it + env: + TOKEN: ${{ github.token }} + TANE_KEYSTORE_BASE64: ${{ secrets.TANE_KEYSTORE_BASE64 }} + TANE_KEYSTORE_PASSWORD: ${{ secrets.TANE_KEYSTORE_PASSWORD }} + TANE_KEY_ALIAS: ${{ secrets.TANE_KEY_ALIAS }} + TANE_KEY_PASSWORD: ${{ secrets.TANE_KEY_PASSWORD }} + ABI: arm64-v8a + OFFSET: 2 + run: | + set -eu + sh /opt/buildserver/setup-env-vars /opt/android-sdk + . /etc/profile.d/bsenv.sh + fds=/opt/fdroidserver-src + mkdir -p "$fds" + curl -sL https://gitlab.com/fdroid/fdroidserver/-/archive/master/fdroidserver-master.tar.gz \ + | tar -xz -C "$fds" --strip-components=1 + export PATH="$fds:$PATH" + export PYTHONPATH="$fds:$fds/examples" + git config --global --add safe.directory '*' + git config --global url."http://x-access-token:${TOKEN}@forgejo:3000/".insteadOf "https://git.comunes.org/" + work=/tmp/tane-src + mkdir -p "$work" && cd "$work" + git init -q . + git remote add origin "http://x-access-token:${TOKEN}@forgejo:3000/${GITHUB_REPOSITORY}.git" + git fetch -q --depth 1 origin "${GITHUB_SHA}" + git checkout -q FETCH_HEAD + base=$(sed -n -E 's/^version:.*\+([0-9]+)$/\1/p' apps/app_seeds/pubspec.yaml) + [ -n "$base" ] + vc=$((base * 10 + OFFSET)) + fdd=/tmp/fdroiddata + mkdir -p "$fdd/metadata" "$fdd/srclibs" + cp "$work/fdroid-ci/config.yml" "$fdd/config.yml" + cp "$work"/fdroid-ci/srclibs/*.yml "$fdd/srclibs/" + cp "$work/docs/fdroid/org.comunes.tane.yml" "$fdd/metadata/org.comunes.tane.yml" + cd "$fdd" + sed -i "s#^\( *commit: \).*#\1${GITHUB_SHA}#" metadata/org.comunes.tane.yml + sed -i '/^ *binary: /d' metadata/org.comunes.tane.yml + mkdir -p build + git clone -q https://git.comunes.org/comunes/tane.git build/org.comunes.tane + git -C build/org.comunes.tane checkout -q "${GITHUB_SHA}" + fdroid build --verbose --no-tarball "org.comunes.tane:${vc}" + f="unsigned/org.comunes.tane_${vc}.apk" + if [ ! -f "$f" ]; then + echo "expected $f, not found" >&2 + tail -n 80 logs/*.log 2>/dev/null || true + exit 1 + fi + apksigner=$(find "$ANDROID_HOME" -name apksigner -type f 2>/dev/null | sort -V | tail -1) + if [ -z "$apksigner" ]; then + yes | sdkmanager "build-tools;34.0.0" >/dev/null 2>&1 || true + apksigner=$(find "$ANDROID_HOME" -name apksigner -type f 2>/dev/null | sort -V | tail -1) + fi + echo "$TANE_KEYSTORE_BASE64" | base64 -d > /tmp/ks.jks + out="/tmp/app-${ABI}-release.apk" + "$apksigner" sign --ks /tmp/ks.jks --ks-key-alias "$TANE_KEY_ALIAS" \ + --ks-pass "pass:$TANE_KEYSTORE_PASSWORD" --key-pass "pass:$TANE_KEY_PASSWORD" \ + --out "$out" "$f" + "$apksigner" verify --print-certs "$out" | grep -i 'SHA-256' || true + rm -f /tmp/ks.jks + api="http://forgejo:3000/api/v1/repos/${GITHUB_REPOSITORY}" + tag="${GITHUB_REF_NAME}" + if [ "${GITHUB_REF_TYPE:-}" = tag ]; then + curl -sf -X POST "$api/releases" \ + -H "Authorization: token ${TOKEN}" -H 'Content-Type: application/json' \ + -d "{\"tag_name\":\"${tag}\",\"name\":\"${tag}\"}" >/dev/null || true + rid=$(curl -sf -H "Authorization: token ${TOKEN}" "$api/releases/tags/${tag}" \ + | python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])') + curl -sf -X POST "$api/releases/${rid}/assets?name=app-${ABI}-release.apk" \ + -H "Authorization: token ${TOKEN}" \ + -F "attachment=@${out};type=application/vnd.android.package-archive" + echo "uploaded app-${ABI}-release.apk (from ${f##*/})" + else + echo "built+signed app-${ABI}-release.apk (dispatch: upload skipped)" + fi + + fdroid_reference_x86_64: + needs: fdroid_reference_arm64_v8a + runs-on: docker + container: + image: registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie + steps: + - name: Build the x86_64 reference APK with fdroid, sign, and attach it + env: + TOKEN: ${{ github.token }} + TANE_KEYSTORE_BASE64: ${{ secrets.TANE_KEYSTORE_BASE64 }} + TANE_KEYSTORE_PASSWORD: ${{ secrets.TANE_KEYSTORE_PASSWORD }} + TANE_KEY_ALIAS: ${{ secrets.TANE_KEY_ALIAS }} + TANE_KEY_PASSWORD: ${{ secrets.TANE_KEY_PASSWORD }} + ABI: x86_64 + OFFSET: 3 + run: | + set -eu + sh /opt/buildserver/setup-env-vars /opt/android-sdk + . /etc/profile.d/bsenv.sh + fds=/opt/fdroidserver-src + mkdir -p "$fds" + curl -sL https://gitlab.com/fdroid/fdroidserver/-/archive/master/fdroidserver-master.tar.gz \ + | tar -xz -C "$fds" --strip-components=1 + export PATH="$fds:$PATH" + export PYTHONPATH="$fds:$fds/examples" + git config --global --add safe.directory '*' + git config --global url."http://x-access-token:${TOKEN}@forgejo:3000/".insteadOf "https://git.comunes.org/" + work=/tmp/tane-src + mkdir -p "$work" && cd "$work" + git init -q . + git remote add origin "http://x-access-token:${TOKEN}@forgejo:3000/${GITHUB_REPOSITORY}.git" + git fetch -q --depth 1 origin "${GITHUB_SHA}" + git checkout -q FETCH_HEAD + base=$(sed -n -E 's/^version:.*\+([0-9]+)$/\1/p' apps/app_seeds/pubspec.yaml) + [ -n "$base" ] + vc=$((base * 10 + OFFSET)) + fdd=/tmp/fdroiddata + mkdir -p "$fdd/metadata" "$fdd/srclibs" + cp "$work/fdroid-ci/config.yml" "$fdd/config.yml" + cp "$work"/fdroid-ci/srclibs/*.yml "$fdd/srclibs/" + cp "$work/docs/fdroid/org.comunes.tane.yml" "$fdd/metadata/org.comunes.tane.yml" + cd "$fdd" + sed -i "s#^\( *commit: \).*#\1${GITHUB_SHA}#" metadata/org.comunes.tane.yml + sed -i '/^ *binary: /d' metadata/org.comunes.tane.yml + mkdir -p build + git clone -q https://git.comunes.org/comunes/tane.git build/org.comunes.tane + git -C build/org.comunes.tane checkout -q "${GITHUB_SHA}" + fdroid build --verbose --no-tarball "org.comunes.tane:${vc}" + f="unsigned/org.comunes.tane_${vc}.apk" + if [ ! -f "$f" ]; then + echo "expected $f, not found" >&2 + tail -n 80 logs/*.log 2>/dev/null || true + exit 1 + fi + apksigner=$(find "$ANDROID_HOME" -name apksigner -type f 2>/dev/null | sort -V | tail -1) + if [ -z "$apksigner" ]; then + yes | sdkmanager "build-tools;34.0.0" >/dev/null 2>&1 || true + apksigner=$(find "$ANDROID_HOME" -name apksigner -type f 2>/dev/null | sort -V | tail -1) + fi + echo "$TANE_KEYSTORE_BASE64" | base64 -d > /tmp/ks.jks + out="/tmp/app-${ABI}-release.apk" + "$apksigner" sign --ks /tmp/ks.jks --ks-key-alias "$TANE_KEY_ALIAS" \ + --ks-pass "pass:$TANE_KEYSTORE_PASSWORD" --key-pass "pass:$TANE_KEY_PASSWORD" \ + --out "$out" "$f" + "$apksigner" verify --print-certs "$out" | grep -i 'SHA-256' || true + rm -f /tmp/ks.jks + api="http://forgejo:3000/api/v1/repos/${GITHUB_REPOSITORY}" + tag="${GITHUB_REF_NAME}" + if [ "${GITHUB_REF_TYPE:-}" = tag ]; then + curl -sf -X POST "$api/releases" \ + -H "Authorization: token ${TOKEN}" -H 'Content-Type: application/json' \ + -d "{\"tag_name\":\"${tag}\",\"name\":\"${tag}\"}" >/dev/null || true + rid=$(curl -sf -H "Authorization: token ${TOKEN}" "$api/releases/tags/${tag}" \ + | python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])') + curl -sf -X POST "$api/releases/${rid}/assets?name=app-${ABI}-release.apk" \ + -H "Authorization: token ${TOKEN}" \ + -F "attachment=@${out};type=application/vnd.android.package-archive" + echo "uploaded app-${ABI}-release.apk (from ${f##*/})" + else + echo "built+signed app-${ABI}-release.apk (dispatch: upload skipped)" + fi