166 lines
6.1 KiB
Markdown
166 lines
6.1 KiB
Markdown
# Reproducible Build Notes — ai.opencode.mobile
|
|
|
|
F-Droid's modern `AllowedAPKSigningKeys` path requires that F-Droid's build
|
|
server can compile the same APK and arrive at a binary that matches the
|
|
pre-signed APK we supply via GitHub releases. Any non-determinism in the build
|
|
will break this verification.
|
|
|
|
---
|
|
|
|
## Issues found (2026-05-24)
|
|
|
|
### 1. Kotlin error log files tracked in git — MEDIUM
|
|
|
|
**Files committed:**
|
|
```
|
|
android/.kotlin/errors/errors-1779181311003.log
|
|
android/.kotlin/errors/errors-1779181311094.log
|
|
```
|
|
|
|
**Problem:** These log files contain absolute host paths:
|
|
```
|
|
While analysing /home/azureuser/workspace/opencode-mobile/node_modules/...
|
|
```
|
|
|
|
When F-Droid builds from source on their server, these log files will not
|
|
exist (or will contain different paths). Since they are tracked in git and
|
|
checked out during the build, they could cause differing build outputs
|
|
if the Kotlin compiler reads or embeds them. More practically, they make
|
|
the source tree non-portable — a smell that will draw reviewer attention.
|
|
|
|
**Recommended fix:** Add `.kotlin/` to `android/.gitignore`:
|
|
```
|
|
# android/.gitignore (add this line)
|
|
.kotlin/
|
|
```
|
|
|
|
Then remove the tracked files:
|
|
```bash
|
|
git rm -r --cached android/.kotlin/
|
|
git commit -m "chore: untrack kotlin error log files from android/.kotlin/"
|
|
```
|
|
|
|
This is a trivial fix. Do it before filing the F-Droid MR.
|
|
|
|
---
|
|
|
|
### 2. android/ directory tracked in git — LOW (expected but notable)
|
|
|
|
`expo prebuild` regenerates `android/` from `app.json` and `package.json`.
|
|
F-Droid's build metadata uses `npx expo prebuild` as a `prebuild:` step,
|
|
which means F-Droid rebuilds `android/` from scratch on their server.
|
|
|
|
The tracked `android/app/build.gradle` and other generated files must match
|
|
what `expo prebuild` produces. If the Expo SDK version drifts between what is
|
|
committed and what npm installs, the build will fail.
|
|
|
|
**Mitigation already in place:** `package-lock.json` is committed, which pins
|
|
all npm dependency versions. The F-Droid metadata `Builds:` step uses
|
|
`npm install --legacy-peer-deps` which respects `package-lock.json`.
|
|
|
|
**Residual risk:** If `expo prebuild` is non-deterministic (e.g., writes the
|
|
current date/time into generated files), subsequent runs will produce different
|
|
outputs. This is unlikely but should be verified by running prebuild twice and
|
|
comparing outputs:
|
|
```bash
|
|
npx expo prebuild --platform android --non-interactive --clean
|
|
git diff android/
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Hermes bytecode embedding — LOW
|
|
|
|
The React Native Hermes engine compiles the JavaScript bundle to Hermes bytecode
|
|
at build time. The bytecode format is versioned but should be deterministic for
|
|
the same JS source + Hermes version. The Hermes version is pinned via
|
|
`react-native` in `package-lock.json`, so this is low risk.
|
|
|
|
---
|
|
|
|
### 4. PNG crunching — LOW
|
|
|
|
`build.gradle` has `crunchPngs true` for release builds. PNG crunching via aapt2
|
|
is generally deterministic but can vary across aapt2 versions. F-Droid's build
|
|
environment may use a different Android build tools version.
|
|
|
|
**Mitigation:** Pin `buildToolsVersion` in `android/build.gradle` explicitly
|
|
rather than relying on the Expo-supplied default. Check via:
|
|
```bash
|
|
grep buildToolsVersion android/build.gradle android/app/build.gradle
|
|
```
|
|
|
|
---
|
|
|
|
### 5. No hardcoded timestamps found — PASS
|
|
|
|
Grepped `android/` for `System.currentTimeMillis`, `new Date()`, `buildTime`,
|
|
`BUILD_DATE`, `UUID.randomUUID()` — no results. This is the most common
|
|
reproducibility killer and is clean here.
|
|
|
|
---
|
|
|
|
### 6. No absolute host paths in build files — PASS
|
|
|
|
Grepped `android/` `*.gradle` and `*.properties` for `/home/`, `/Users/`,
|
|
`C:\` — no results in build config files.
|
|
|
|
---
|
|
|
|
## Priority action items before F-Droid MR
|
|
|
|
| Priority | Item | Effort |
|
|
|----------|------|--------|
|
|
| HIGH | Add `.kotlin/` to `android/.gitignore` and untrack log files | 5 min |
|
|
| MEDIUM | Run `expo prebuild` twice, compare output with `git diff` | 15 min |
|
|
| MEDIUM | Pin `buildToolsVersion` explicitly in `android/build.gradle` | 5 min |
|
|
| LOW | Verify Hermes bytecode is deterministic (compare two builds) | 30 min |
|
|
| LOW | Test full reproducible build using F-Droid's Docker build env | Hours |
|
|
|
|
---
|
|
|
|
## How to test reproducible builds
|
|
|
|
F-Droid provides a reproducible build test tool:
|
|
|
|
```bash
|
|
# Install fdroidserver
|
|
pip install fdroidserver
|
|
|
|
# Test reproducibility against a released APK
|
|
fdroid signatures path/to/app-release.apk
|
|
|
|
# Full build test
|
|
fdroid build ai.opencode.mobile:<versionCode> --verbose
|
|
```
|
|
|
|
See https://f-droid.org/en/docs/Reproducible_Builds/ for the full guide.
|
|
|
|
---
|
|
|
|
## Update (2026-07-17) — separate `-fdroid` release asset (issue #95)
|
|
|
|
Root cause of the original mismatch: `Binaries:` pointed at `app-release.apk`,
|
|
which is built WITHOUT the `Builds:.prebuild` patches (it still has Firebase,
|
|
Play Install Referrer, and Sentry). F-Droid's from-source rebuild applies
|
|
those patches, so the two binaries diverge structurally and `fdroid build`'s
|
|
reproducibility check can never match — stripping the patches from the main
|
|
APK instead would have removed push notifications and crash reporting for
|
|
every Play/GitHub/IzzyOnDroid user, which is not acceptable.
|
|
|
|
Fix shipped in `.github/workflows/build.yml` (`build-fdroid` job, tag-release
|
|
only): build a second, additional artifact — `app-release-fdroid.apk` —
|
|
applying the exact same patch commands as `Builds:.prebuild` below, signed
|
|
with the same production keystore (v1+v2 only, matching `publish-fdroid.yml`'s
|
|
existing androguard workaround), and attach it to the GitHub Release next to
|
|
the untouched `app-release.apk`. `Binaries:` now points at the `-fdroid` asset.
|
|
The main `build` job is unmodified — no feature loss for non-F-Droid users.
|
|
|
|
**Remaining human step:** this environment cannot run `fdroid build` (needs
|
|
`fdroidserver` + a maintainer's Docker/Linux environment — see "How to test
|
|
reproducible builds" above). Before relying on this for a real fdroiddata
|
|
submission or an `AllowedAPKSigningKeys` update, a maintainer must run
|
|
`fdroid build ai.opencode.mobile:<versionCode> --verbose` (or the fdroiddata
|
|
CI equivalent) against a tagged release that has an `app-release-fdroid.apk`
|
|
asset, and confirm the from-source rebuild matches it byte-for-byte.
|