From a144403ac32bd6d3dd4cf79165302e590af9f77a Mon Sep 17 00:00:00 2001 From: LB Date: Sat, 29 Aug 2026 17:33:14 -0400 Subject: [PATCH 1/2] fdroid: fix the buildserver recipe and add a remote build check Running the recipe in F-Droid's own buildserver image turned up two things that would have failed fdroiddata's CI after the merge request was open. The sudo block fetched node as a .tar.xz, but the buildserver has no xz binary -- only gzip -- so `tar xf` died before anything was built. Switch to the gzip tarball nodejs.org publishes alongside it. The recipe was also not in fdroid's canonical format. fdroiddata CI runs `fdroid rewritemeta` on every changed file and fails if the file changes, and that rewrite strips YAML comments, so the rationale the comments carried moves to MaintainerNotes where a reviewer will actually see it. Add Game Helper alongside Sports & Health: upstream describes it as "game helper, assistant, companion, score board, timer", which is what this is. FDROID.md documents the one blocker left. React Native forces a Java 17 toolchain on every module and the buildserver ships only JDK 21, with no openjdk-17 available on trixie at all. Three ways out are written up; the choice belongs in the merge request, not here. .github/workflows/fdroid-build.yml runs `fdroid build` in the buildserver image on a runner, so the recipe can be checked remotely. Its provision_jdk17 input installs the same Temurin 17 the release build already uses -- on, it verifies the rest of the recipe end to end; off, it reproduces the blocker exactly. Co-Authored-By: Claude Opus 5 --- .github/workflows/fdroid-build.yml | 125 +++++++++++++++++++++++++++++ FDROID.md | 97 ++++++++++++++++++++-- fdroid/com.bracketup.app.yml | 83 ++++++++----------- 3 files changed, 251 insertions(+), 54 deletions(-) create mode 100644 .github/workflows/fdroid-build.yml diff --git a/.github/workflows/fdroid-build.yml b/.github/workflows/fdroid-build.yml new file mode 100644 index 0000000..aa22fcf --- /dev/null +++ b/.github/workflows/fdroid-build.yml @@ -0,0 +1,125 @@ +# Verify fdroid/com.bracketup.app.yml the way fdroiddata's CI does: run +# `fdroid build` inside F-Droid's own buildserver image. Manual only -- this is +# a slow, large job and nothing about a normal push needs it. +# +# See FDROID.md ("Known blocker: the Java 17 toolchain") for why provision_jdk17 +# exists. Leave it on to verify the rest of the recipe end to end; turn it off +# to reproduce exactly what F-Droid's buildserver does today. +name: F-Droid recipe build + +on: + workflow_dispatch: + inputs: + versioncode: + description: Which build entry to run (21 armeabi-v7a, 22 arm64-v8a, 23 x86_64) + type: choice + options: ['22', '21', '23'] + default: '22' + provision_jdk17: + description: Install Temurin 17 in the buildserver (upstream CI builds with it) + type: boolean + default: true + +jobs: + fdroid-build: + runs-on: ubuntu-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + # The buildserver image plus the SDK, NDK and a Gradle build do not fit + # alongside the runner's preinstalled toolchains. + - name: Free disk space + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ + /usr/local/share/boost "$AGENT_TOOLSDIRECTORY" + df -h / + + - name: Run fdroid build in the buildserver image + env: + VERSIONCODE: ${{ inputs.versioncode }} + PROVISION_JDK17: ${{ inputs.provision_jdk17 }} + run: | + mkdir -p work && cp fdroid/com.bracketup.app.yml work/ + cat > work/build.sh <<'SCRIPT' + set -e + source /etc/profile.d/bsenv.sh + export PYTHONUNBUFFERED=true + + # fdroidserver from git master needs a git checkout to report a + # version, so use the distro package. + apt-get update -qq + apt-get install -qy --no-install-recommends fdroidserver + fdroid --version + + # fdroiddata CI refreshes gradlew-fdroid before every build. The copy + # bundled in the .deb lags the gradle-transparency-log, so point + # fdroid at the image's checkout instead. + git config --global --add safe.directory /home/vagrant/gradlew-fdroid + git -C /home/vagrant/gradlew-fdroid pull --quiet || true + + yes | sdkmanager "platform-tools" "build-tools;36.0.0" \ + "platforms;android-36" "ndk;27.1.12297006" "cmake;3.22.1" > /tmp/sdk.log 2>&1 \ + || { tail -20 /tmp/sdk.log; exit 1; } + + if [ "$PROVISION_JDK17" = "true" ]; then + # What actions/setup-java does for the release build, done by hand: + # RN forces a Java 17 toolchain on every module and Debian trixie + # has no openjdk-17 package. + curl -Lo jdk17.tar.gz https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.20.1%2B1/OpenJDK17U-jdk_x64_linux_hotspot_17.0.20.1_1.tar.gz + echo "3808d1d15e3ec6bd5b84057fb5d84c33d8a1536a258146bcea2e603fc726e08e jdk17.tar.gz" | sha256sum -c - + mkdir -p /usr/lib/jvm && tar xzf jdk17.tar.gz --directory=/usr/lib/jvm + rm jdk17.tar.gz + fi + + mkdir -p "$home_vagrant"/{metadata,build,tmp,logs,unsigned,srclibs,.android,.gradle} + printf 'gradle: /usr/local/bin/gradle\n' > "$home_vagrant/config.yml" + chmod 0600 "$home_vagrant/config.yml" + cp /work/com.bracketup.app.yml "$home_vagrant/metadata/" + chown -R vagrant "$home_vagrant" "$ANDROID_HOME" + cd "$home_vagrant" + + asvagrant() { + sudo --preserve-env --user vagrant \ + env PATH="$PATH" PYTHONUNBUFFERED=true HOME="$home_vagrant" "$@" + } + + # `fdroid build` expects build/ to exist. fdroiddata CI creates + # it with `fdroid fetchsrclibs`, which only exists in git master. + asvagrant git clone https://github.com/lbellows/bracket-up.git \ + "$home_vagrant/build/com.bracketup.app" + + set +e + asvagrant fdroid build --verbose --test --refresh-scanner --on-server \ + --no-tarball "com.bracketup.app:$VERSIONCODE" + rc=$? + set -e + + cp -v "$home_vagrant"/tmp/*.apk /work/ 2>/dev/null || true + mkdir -p /work/logs && cp -v "$home_vagrant"/logs/* /work/logs/ 2>/dev/null || true + chmod -R a+rw /work + exit $rc + SCRIPT + docker run --rm \ + -e VERSIONCODE -e PROVISION_JDK17 \ + -v "$PWD/work:/work" \ + registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie \ + bash /work/build.sh + + - name: Inspect the built APK + if: always() + run: | + APK=$(ls work/*.apk 2>/dev/null | head -1) || exit 0 + [ -n "$APK" ] || { echo "no APK produced"; exit 0; } + BT=$(ls -d "$ANDROID_HOME"/build-tools/* | tail -1) + "$BT/aapt2" dump badging "$APK" | grep -E '^package:|^native-code:|^uses-permission:' + ls -l "$APK" + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: fdroid-build-${{ inputs.versioncode }} + path: | + work/*.apk + work/logs/ + if-no-files-found: warn diff --git a/FDROID.md b/FDROID.md index e139b3c..def2509 100644 --- a/FDROID.md +++ b/FDROID.md @@ -54,7 +54,12 @@ in this repo, so the description, screenshots and changelogs are already in plac everything except publish — that is the way to check a build before tagging. - `fdroid/com.bracketup.app.yml` is the recipe to submit to fdroiddata. It has one build entry per architecture, each pinned to a single ABI with - `-PreactNativeArchitectures=`. + `gradleprops: reactNativeArchitectures=`. It is kept in fdroid's canonical + format — the exact output of `fdroid rewritemeta` — so it can be copied into a + fdroiddata fork verbatim. fdroiddata's CI runs `fdroid rewritemeta` on every + changed file and fails the merge request if the file changes, and that rewrite + strips YAML comments, which is why the recipe carries no comments and the + rationale a reviewer needs lives in its `MaintainerNotes` instead. - Version numbers live in `app.json` (`expo.version` and `expo.android.versionCode`). EAS was removed entirely (`eas.json` and the `extra.eas` project binding), so the repo is the single source of truth — F-Droid reads the version from source and @@ -262,15 +267,36 @@ within a day — and you do not file anything again. Users install by adding `VercodeOperation` (`10 * %c + 1/2/3`) tells the auto-updater to copy all three entries on a new tag and assign each the code that ABI's APK declares. The entries must stay in ascending-offset order for that mapping to hold. -3. Test the recipe if you can — it needs Docker and a lot of disk: - - ```bash - fdroid build -v -l com.bracketup.app - ``` +3. Let fdroiddata's CI build it. The merge request runs `fdroid build` on + F-Droid's own buildserver image, which is the verification that counts — + building locally means reproducing that environment by hand, and a local pass + would not prove anything the CI run does not. 4. Open a merge request. Review is slow and reviewers do ask questions; the common ones for this app are answered in the recipe's `MaintainerNotes`. +Before opening the merge request, run the two checks fdroiddata's CI runs — both +work on an ordinary machine, no Docker or Android SDK needed for the first: + +```bash +# in a fdroiddata checkout, with the recipe copied to metadata/ +fdroid lint -f com.bracketup.app # metadata + canonical formatting +fdroid rewritemeta com.bracketup.app # must leave the file unchanged + +# the scan Izzy and fdroiddata both run over the built APKs +fdroid scanner -r -e BracketUp--arm64-v8a.apk + +# the source-tree scan; clones the tag and runs `npm ci`, needs no Android SDK +fdroid scanner -e com.bracketup.app: +``` + +`fdroid scanner` needs `dexdump` from the Android SDK build-tools to scan an APK; +unpacking `build-tools_r36.1_linux.zip` from + into `$ANDROID_HOME/build-tools/36.1.0` +is enough, and does not require a JDK. Note that `fdroid lint` run outside a +fdroiddata checkout wrongly reports the category as invalid — it reads the valid +list from that repo's `config/categories.yml` and finds nothing without it. + Things a reviewer may raise, and where they stand here: - **Prebuilt binaries in `node_modules`.** Covered by `scanignore`. React Native's @@ -287,6 +313,65 @@ Things a reviewer may raise, and where they stand here: `expo prebuild` (which fetches templates at build time). CI proves the committed project matches `app.json`. +### Known blocker: the Java 17 toolchain + +The recipe has not yet built on F-Droid's buildserver, and the reason is not +something the recipe can currently express. + +React Native's Gradle plugin (`JdkConfiguratorUtils`) applies +`kotlin { jvmToolchain(17) }` and `sourceCompatibility/targetCompatibility = +VERSION_17` to *every* module in the build, and `expo-modules-core` sets +`kotlin.jvmToolchain(17)` for KSP as well. Gradle matches a toolchain version +exactly. The buildserver installs `default-jdk-headless` and nothing else, which +on Debian trixie is JDK 21 — and trixie has no `openjdk-17` package at all, only +21 and 25. So the build fails at `:app:compileReleaseJavaWithJavac` with: + +``` +Cannot find a Java installation on your machine matching: +{languageVersion=17, ...}. Toolchain auto-provisioning is not enabled. +``` + +Three ways out, none yet chosen: + +- **Fetch a pinned JDK 17 in the `sudo:` block**, the way the recipe already + fetches Node. Verified to work — it produces the APK. The cost is a ~193 MB + binary download that a reviewer has to accept. Worth saying in the merge + request: this is not a workaround invented for F-Droid. The `build` and + `smoke-test` jobs in `.github/workflows/release.yml` both run + `actions/setup-java` with `distribution: temurin, java-version: 17`, so every + APK on the GitHub Releases page — including the ones IzzyOnDroid serves — is + already built with Temurin 17. Pinning it in the recipe only makes the + buildserver match how the app is built everywhere else. +- **Set `react.internal.disableJavaVersionAlignment`.** React Native checks this + property and skips all of the above. It is a documented escape hatch, but it + sets no replacement target, so `android/app/build.gradle` would need explicit + `compileOptions` and a Kotlin `jvmTarget` to avoid an inconsistent-JVM-target + failure, and it does not cover `expo-modules-core`'s KSP toolchain. +- **Wait for the toolchain requirement to move.** It is upstream React Native's + choice, not this app's; when RN aligns on 21 the problem disappears. + +Worth raising in the merge request rather than guessing — F-Droid reviewers deal +with this class of problem across every React Native app in the repo and will +have a preference. + +### Testing the recipe remotely + +`.github/workflows/fdroid-build.yml` runs `fdroid build` inside F-Droid's own +buildserver image on a GitHub runner — the same thing fdroiddata's CI does, so +the recipe can be checked without reproducing that environment locally. It is +`workflow_dispatch` only: + +```bash +gh workflow run "F-Droid recipe build" # arm64-v8a, JDK 17 provisioned +gh workflow run "F-Droid recipe build" -f versioncode=21 # a different ABI +gh workflow run "F-Droid recipe build" -f provision_jdk17=false # reproduce the blocker +``` + +It builds from the tag named in the recipe's `commit:`, not from the branch you +dispatch it on, and uploads the APK and fdroid's build logs as artifacts. +Leaving `provision_jdk17` on verifies everything else in the recipe end to end; +turning it off reproduces exactly what F-Droid's buildserver does today. + --- ## Development caveat diff --git a/fdroid/com.bracketup.app.yml b/fdroid/com.bracketup.app.yml index 2329f6c..3f7dec4 100644 --- a/fdroid/com.bracketup.app.yml +++ b/fdroid/com.bracketup.app.yml @@ -1,11 +1,5 @@ -# F-Droid build recipe for BracketUp. -# -# This file is not used by this repository — it is the metadata to submit to -# https://gitlab.com/fdroid/fdroiddata as metadata/com.bracketup.app.yml. -# It is kept here so the recipe is versioned alongside the code it builds. -# See FDROID.md for the submission procedure. - Categories: + - Game Helper - Sports & Health License: MIT AuthorName: LCB @@ -19,88 +13,71 @@ RepoType: git Repo: https://github.com/lbellows/bracket-up.git Builds: - # One build per architecture. The Gradle build splits the APK by ABI, and - # `reactNativeArchitectures` narrows each run to a single one so every build - # here produces exactly one APK. Each APK's versionCode is - # `expo.android.versionCode * 10 + ` (armeabi-v7a 1, arm64-v8a 2, - # x86_64 3), assigned in plugins/withAbiSplits.js. - versionName: 1.0.1 versionCode: 21 commit: v1.0.1 subdir: android/app - gradleprops: - - reactNativeArchitectures=armeabi-v7a sudo: - - curl -Lo node.tar.xz https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.xz - - echo "d60acfe00a2932254bb0ad20e01b0d74397a0875595de719654b214f4b03f307 node.tar.xz" | sha256sum -c - - - tar xf node.tar.xz --directory=/opt + - curl -Lo node.tar.gz https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.gz + - echo "b294a556e639d64338823920e5866c21c02741742d2e1529ee1a225c1ec9252a node.tar.gz" + | sha256sum -c - + - tar xzf node.tar.gz --directory=/opt - mv /opt/node-v22.23.2-linux-x64 /opt/node - ln -s /opt/node/bin/node /opt/node/bin/npm /opt/node/bin/npx /usr/local/bin/ - init: - - npm ci + init: npm ci gradle: - yes rm: - .github - store-assets scanignore: - # React Native ships prebuilt tooling (hermesc, the Gradle plugin's jars) - # inside node_modules. These are build-time only and are not packaged into - # the APK; the shipped native libraries are compiled from the Maven-hosted - # React Native and Hermes sources during the Gradle build. - node_modules + gradleprops: + - reactNativeArchitectures=armeabi-v7a - versionName: 1.0.1 versionCode: 22 commit: v1.0.1 subdir: android/app - gradleprops: - - reactNativeArchitectures=arm64-v8a sudo: - - curl -Lo node.tar.xz https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.xz - - echo "d60acfe00a2932254bb0ad20e01b0d74397a0875595de719654b214f4b03f307 node.tar.xz" | sha256sum -c - - - tar xf node.tar.xz --directory=/opt + - curl -Lo node.tar.gz https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.gz + - echo "b294a556e639d64338823920e5866c21c02741742d2e1529ee1a225c1ec9252a node.tar.gz" + | sha256sum -c - + - tar xzf node.tar.gz --directory=/opt - mv /opt/node-v22.23.2-linux-x64 /opt/node - ln -s /opt/node/bin/node /opt/node/bin/npm /opt/node/bin/npx /usr/local/bin/ - init: - - npm ci + init: npm ci gradle: - yes rm: - .github - store-assets scanignore: - # React Native ships prebuilt tooling (hermesc, the Gradle plugin's jars) - # inside node_modules. These are build-time only and are not packaged into - # the APK; the shipped native libraries are compiled from the Maven-hosted - # React Native and Hermes sources during the Gradle build. - node_modules + gradleprops: + - reactNativeArchitectures=arm64-v8a - versionName: 1.0.1 versionCode: 23 commit: v1.0.1 subdir: android/app - gradleprops: - - reactNativeArchitectures=x86_64 sudo: - - curl -Lo node.tar.xz https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.xz - - echo "d60acfe00a2932254bb0ad20e01b0d74397a0875595de719654b214f4b03f307 node.tar.xz" | sha256sum -c - - - tar xf node.tar.xz --directory=/opt + - curl -Lo node.tar.gz https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.gz + - echo "b294a556e639d64338823920e5866c21c02741742d2e1529ee1a225c1ec9252a node.tar.gz" + | sha256sum -c - + - tar xzf node.tar.gz --directory=/opt - mv /opt/node-v22.23.2-linux-x64 /opt/node - ln -s /opt/node/bin/node /opt/node/bin/npm /opt/node/bin/npx /usr/local/bin/ - init: - - npm ci + init: npm ci gradle: - yes rm: - .github - store-assets scanignore: - # React Native ships prebuilt tooling (hermesc, the Gradle plugin's jars) - # inside node_modules. These are build-time only and are not packaged into - # the APK; the shipped native libraries are compiled from the Maven-hosted - # React Native and Hermes sources during the Gradle build. - node_modules + gradleprops: + - reactNativeArchitectures=x86_64 MaintainerNotes: |- Expo (React Native) app. The native android/ project is committed to the @@ -108,9 +85,19 @@ MaintainerNotes: |- it drifts from app.json, so the checked-in project always matches the config. Every release has three builds, one per ABI, because a universal APK is ~97 MB. - VercodeOperation reproduces the versionCode scheme, so the auto-updater copies - all three entries on a new tag and gives each the right code — the build blocks - are ordered to match the operations, ascending. + The Gradle build splits the APK by ABI and `reactNativeArchitectures` narrows + each build entry to a single one, so every entry here produces exactly one APK, + whose versionCode is `expo.android.versionCode * 10 + ` + (armeabi-v7a 1, arm64-v8a 2, x86_64 3), assigned in plugins/withAbiSplits.js. + VercodeOperation reproduces that scheme, so the auto-updater copies all three + entries on a new tag and gives each the right code — the build blocks are + ordered to match the operations, ascending. + + `scanignore: node_modules` covers the prebuilt tooling React Native ships + inside node_modules (hermesc, the Gradle plugin's jars) and the local Maven + repos a few Expo modules carry. All of it is build-time only and none of it is + packaged into the APK; the native libraries that are shipped are compiled + during the Gradle build from the Maven-hosted React Native and Hermes sources. `gradle assembleRelease` produces an unsigned APK here: the release signing config activates only when a keystore is supplied via environment variables, From 06c2fd8042beb030b089a899edb28c4be8837aeb Mon Sep 17 00:00:00 2001 From: LB Date: Sat, 29 Aug 2026 18:00:32 -0400 Subject: [PATCH 2/2] fdroid: pin Temurin 17 in the recipe; bump the GitHub actions React Native forces a Java 17 toolchain on every module and the buildserver ships only JDK 21, with no openjdk-17 available on trixie at all, so the recipe fetches a pinned Temurin 17 the same way it fetches node and names the path in gradleprops. This is the JDK the app is already built with everywhere else: release.yml's build and smoke-test jobs both run setup-java with temurin 17, so the buildserver now matches rather than diverges. That settles the question FDROID.md was holding open, so its section is rewritten around the decision, and the fdroid-build workflow loses the provision_jdk17 toggle that existed to demonstrate the failure. Also bump every action to its current major. GitHub is deprecating Node 20 and was already forcing checkout and setup-node onto Node 24. Note that the publish job only runs on a tag, so action-gh-release is the one bump a workflow_dispatch dry run cannot exercise. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 4 +- .github/workflows/fdroid-build.yml | 27 +++---------- .github/workflows/release.yml | 20 +++++----- FDROID.md | 64 +++++++++++++++--------------- fdroid/com.bracketup.app.yml | 18 +++++++++ 5 files changed, 67 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 985eee5..ea28935 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,8 +9,8 @@ jobs: checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: node-version: 22 cache: npm diff --git a/.github/workflows/fdroid-build.yml b/.github/workflows/fdroid-build.yml index aa22fcf..2a430fe 100644 --- a/.github/workflows/fdroid-build.yml +++ b/.github/workflows/fdroid-build.yml @@ -2,9 +2,9 @@ # `fdroid build` inside F-Droid's own buildserver image. Manual only -- this is # a slow, large job and nothing about a normal push needs it. # -# See FDROID.md ("Known blocker: the Java 17 toolchain") for why provision_jdk17 -# exists. Leave it on to verify the rest of the recipe end to end; turn it off -# to reproduce exactly what F-Droid's buildserver does today. +# The recipe provisions its own Temurin 17 (see FDROID.md, "The Java 17 +# toolchain"), so this job only has to supply the SDK and NDK that fdroiddata's +# CI installs on top of the image. name: F-Droid recipe build on: @@ -15,17 +15,13 @@ on: type: choice options: ['22', '21', '23'] default: '22' - provision_jdk17: - description: Install Temurin 17 in the buildserver (upstream CI builds with it) - type: boolean - default: true jobs: fdroid-build: runs-on: ubuntu-latest timeout-minutes: 90 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 # The buildserver image plus the SDK, NDK and a Gradle build do not fit # alongside the runner's preinstalled toolchains. @@ -38,7 +34,6 @@ jobs: - name: Run fdroid build in the buildserver image env: VERSIONCODE: ${{ inputs.versioncode }} - PROVISION_JDK17: ${{ inputs.provision_jdk17 }} run: | mkdir -p work && cp fdroid/com.bracketup.app.yml work/ cat > work/build.sh <<'SCRIPT' @@ -62,16 +57,6 @@ jobs: "platforms;android-36" "ndk;27.1.12297006" "cmake;3.22.1" > /tmp/sdk.log 2>&1 \ || { tail -20 /tmp/sdk.log; exit 1; } - if [ "$PROVISION_JDK17" = "true" ]; then - # What actions/setup-java does for the release build, done by hand: - # RN forces a Java 17 toolchain on every module and Debian trixie - # has no openjdk-17 package. - curl -Lo jdk17.tar.gz https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.20.1%2B1/OpenJDK17U-jdk_x64_linux_hotspot_17.0.20.1_1.tar.gz - echo "3808d1d15e3ec6bd5b84057fb5d84c33d8a1536a258146bcea2e603fc726e08e jdk17.tar.gz" | sha256sum -c - - mkdir -p /usr/lib/jvm && tar xzf jdk17.tar.gz --directory=/usr/lib/jvm - rm jdk17.tar.gz - fi - mkdir -p "$home_vagrant"/{metadata,build,tmp,logs,unsigned,srclibs,.android,.gradle} printf 'gradle: /usr/local/bin/gradle\n' > "$home_vagrant/config.yml" chmod 0600 "$home_vagrant/config.yml" @@ -101,7 +86,7 @@ jobs: exit $rc SCRIPT docker run --rm \ - -e VERSIONCODE -e PROVISION_JDK17 \ + -e VERSIONCODE \ -v "$PWD/work:/work" \ registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie \ bash /work/build.sh @@ -115,7 +100,7 @@ jobs: "$BT/aapt2" dump badging "$APK" | grep -E '^package:|^native-code:|^uses-permission:' ls -l "$APK" - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 if: always() with: name: fdroid-build-${{ inputs.versioncode }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 208ad0d..07d8c67 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,19 +23,19 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: 22 cache: npm - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v6 with: distribution: temurin java-version: 17 - - uses: android-actions/setup-android@v3 + - uses: android-actions/setup-android@v4 - name: Install dependencies run: npm ci @@ -118,7 +118,7 @@ jobs: done ls -l dist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: apks path: dist/*.apk @@ -131,14 +131,14 @@ jobs: needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v6 with: distribution: temurin java-version: 17 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: apks path: dist @@ -164,13 +164,13 @@ jobs: if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: apks path: dist - name: Publish GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 with: files: dist/*.apk generate_release_notes: true diff --git a/FDROID.md b/FDROID.md index def2509..fb4de2f 100644 --- a/FDROID.md +++ b/FDROID.md @@ -313,10 +313,7 @@ Things a reviewer may raise, and where they stand here: `expo prebuild` (which fetches templates at build time). CI proves the committed project matches `app.json`. -### Known blocker: the Java 17 toolchain - -The recipe has not yet built on F-Droid's buildserver, and the reason is not -something the recipe can currently express. +### The Java 17 toolchain React Native's Gradle plugin (`JdkConfiguratorUtils`) applies `kotlin { jvmToolchain(17) }` and `sourceCompatibility/targetCompatibility = @@ -324,35 +321,36 @@ VERSION_17` to *every* module in the build, and `expo-modules-core` sets `kotlin.jvmToolchain(17)` for KSP as well. Gradle matches a toolchain version exactly. The buildserver installs `default-jdk-headless` and nothing else, which on Debian trixie is JDK 21 — and trixie has no `openjdk-17` package at all, only -21 and 25. So the build fails at `:app:compileReleaseJavaWithJavac` with: +21 and 25. Without a JDK 17 the build fails at +`:app:compileReleaseJavaWithJavac` with: ``` Cannot find a Java installation on your machine matching: {languageVersion=17, ...}. Toolchain auto-provisioning is not enabled. ``` -Three ways out, none yet chosen: - -- **Fetch a pinned JDK 17 in the `sudo:` block**, the way the recipe already - fetches Node. Verified to work — it produces the APK. The cost is a ~193 MB - binary download that a reviewer has to accept. Worth saying in the merge - request: this is not a workaround invented for F-Droid. The `build` and - `smoke-test` jobs in `.github/workflows/release.yml` both run - `actions/setup-java` with `distribution: temurin, java-version: 17`, so every - APK on the GitHub Releases page — including the ones IzzyOnDroid serves — is - already built with Temurin 17. Pinning it in the recipe only makes the - buildserver match how the app is built everywhere else. -- **Set `react.internal.disableJavaVersionAlignment`.** React Native checks this - property and skips all of the above. It is a documented escape hatch, but it - sets no replacement target, so `android/app/build.gradle` would need explicit - `compileOptions` and a Kotlin `jvmTarget` to avoid an inconsistent-JVM-target - failure, and it does not cover `expo-modules-core`'s KSP toolchain. -- **Wait for the toolchain requirement to move.** It is upstream React Native's - choice, not this app's; when RN aligns on 21 the problem disappears. - -Worth raising in the merge request rather than guessing — F-Droid reviewers deal -with this class of problem across every React Native app in the repo and will -have a preference. +So the recipe fetches a pinned Temurin 17 in its `sudo:` block, the same way it +fetches Node, and names the path in `gradleprops` so Gradle finds it whatever +else it auto-detects. This is verified — it produces the APK. + +If a reviewer questions the download, the answer is that it is not a workaround +invented for F-Droid. The `build` and `smoke-test` jobs in +`.github/workflows/release.yml` both run `actions/setup-java` with +`distribution: temurin, java-version: 17`, so every APK on the GitHub Releases +page — including the one IzzyOnDroid serves — is already built with Temurin 17. +Pinning it here only makes the buildserver match how the app is built +everywhere else. + +Two alternatives, if it comes to that. React Native checks +`react.internal.disableJavaVersionAlignment` and skips all of the above, but it +sets no replacement target, so `android/app/build.gradle` would need explicit +`compileOptions` and a Kotlin `jvmTarget` to avoid an inconsistent-JVM-target +failure, and it does not cover `expo-modules-core`'s KSP toolchain. Or wait: the +requirement is upstream React Native's, not this app's, and disappears when RN +aligns on 21. + +Bump the pinned JDK the same way you would bump Node — new URL, new checksum +from the same release, and the new directory name in the `gradleprops` path. ### Testing the recipe remotely @@ -362,15 +360,15 @@ the recipe can be checked without reproducing that environment locally. It is `workflow_dispatch` only: ```bash -gh workflow run "F-Droid recipe build" # arm64-v8a, JDK 17 provisioned -gh workflow run "F-Droid recipe build" -f versioncode=21 # a different ABI -gh workflow run "F-Droid recipe build" -f provision_jdk17=false # reproduce the blocker +gh workflow run "F-Droid recipe build" # arm64-v8a +gh workflow run "F-Droid recipe build" -f versioncode=21 # a different ABI ``` It builds from the tag named in the recipe's `commit:`, not from the branch you -dispatch it on, and uploads the APK and fdroid's build logs as artifacts. -Leaving `provision_jdk17` on verifies everything else in the recipe end to end; -turning it off reproduces exactly what F-Droid's buildserver does today. +dispatch it on, and uploads the APK and fdroid's build logs as artifacts. The +job installs only what fdroiddata's CI installs on top of the buildserver image +— platform-tools, build-tools, a platform, the NDK and CMake — so anything else +the build needs has to come from the recipe, which is the point. --- diff --git a/fdroid/com.bracketup.app.yml b/fdroid/com.bracketup.app.yml index 3f7dec4..c8b8b35 100644 --- a/fdroid/com.bracketup.app.yml +++ b/fdroid/com.bracketup.app.yml @@ -24,6 +24,11 @@ Builds: - tar xzf node.tar.gz --directory=/opt - mv /opt/node-v22.23.2-linux-x64 /opt/node - ln -s /opt/node/bin/node /opt/node/bin/npm /opt/node/bin/npx /usr/local/bin/ + - curl -Lo jdk17.tar.gz https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.20.1%2B1/OpenJDK17U-jdk_x64_linux_hotspot_17.0.20.1_1.tar.gz + - echo "3808d1d15e3ec6bd5b84057fb5d84c33d8a1536a258146bcea2e603fc726e08e jdk17.tar.gz" + | sha256sum -c - + - mkdir -p /usr/lib/jvm + - tar xzf jdk17.tar.gz --directory=/usr/lib/jvm init: npm ci gradle: - yes @@ -34,6 +39,7 @@ Builds: - node_modules gradleprops: - reactNativeArchitectures=armeabi-v7a + - org.gradle.java.installations.paths=/usr/lib/jvm/jdk-17.0.20.1+1 - versionName: 1.0.1 versionCode: 22 @@ -46,6 +52,11 @@ Builds: - tar xzf node.tar.gz --directory=/opt - mv /opt/node-v22.23.2-linux-x64 /opt/node - ln -s /opt/node/bin/node /opt/node/bin/npm /opt/node/bin/npx /usr/local/bin/ + - curl -Lo jdk17.tar.gz https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.20.1%2B1/OpenJDK17U-jdk_x64_linux_hotspot_17.0.20.1_1.tar.gz + - echo "3808d1d15e3ec6bd5b84057fb5d84c33d8a1536a258146bcea2e603fc726e08e jdk17.tar.gz" + | sha256sum -c - + - mkdir -p /usr/lib/jvm + - tar xzf jdk17.tar.gz --directory=/usr/lib/jvm init: npm ci gradle: - yes @@ -56,6 +67,7 @@ Builds: - node_modules gradleprops: - reactNativeArchitectures=arm64-v8a + - org.gradle.java.installations.paths=/usr/lib/jvm/jdk-17.0.20.1+1 - versionName: 1.0.1 versionCode: 23 @@ -68,6 +80,11 @@ Builds: - tar xzf node.tar.gz --directory=/opt - mv /opt/node-v22.23.2-linux-x64 /opt/node - ln -s /opt/node/bin/node /opt/node/bin/npm /opt/node/bin/npx /usr/local/bin/ + - curl -Lo jdk17.tar.gz https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.20.1%2B1/OpenJDK17U-jdk_x64_linux_hotspot_17.0.20.1_1.tar.gz + - echo "3808d1d15e3ec6bd5b84057fb5d84c33d8a1536a258146bcea2e603fc726e08e jdk17.tar.gz" + | sha256sum -c - + - mkdir -p /usr/lib/jvm + - tar xzf jdk17.tar.gz --directory=/usr/lib/jvm init: npm ci gradle: - yes @@ -78,6 +95,7 @@ Builds: - node_modules gradleprops: - reactNativeArchitectures=x86_64 + - org.gradle.java.installations.paths=/usr/lib/jvm/jdk-17.0.20.1+1 MaintainerNotes: |- Expo (React Native) app. The native android/ project is committed to the