|
| 1 | +# F-Droid submission |
| 2 | + |
| 3 | +Reference notes for publishing SleepTimer (`dev.xitee.sleeptimer`) on F-Droid. |
| 4 | +Verified against live F-Droid docs and precedent recipes on 2026-07-06. |
| 5 | + |
| 6 | +## What lives where |
| 7 | + |
| 8 | +- **This repo** carries everything F-Droid reads from source: `LICENSE` (GPL-3.0), |
| 9 | + `fastlane/metadata/android/{en-US,de-DE}/` (title, descriptions, icon, screenshots, |
| 10 | + per-`versionCode` changelogs), `app/version.properties` (version literals for the |
| 11 | + update checker), and the `-PdisableSigning` build path in `app/build.gradle.kts`. |
| 12 | +- **The fdroiddata fork** carries the build recipe below. It is **not** committed to |
| 13 | + this repo — F-Droid lint rejects a recipe that duplicates the fastlane |
| 14 | + `Summary`/`Description`, and the recipe references paths inside this repo by URL. |
| 15 | + |
| 16 | +## The recipe — `metadata/dev.xitee.sleeptimer.yml` |
| 17 | + |
| 18 | +Add this file in a fork of https://gitlab.com/fdroid/fdroiddata (not here): |
| 19 | + |
| 20 | +```yaml |
| 21 | +Categories: |
| 22 | + - Timer |
| 23 | + - Multimedia |
| 24 | +License: GPL-3.0-or-later |
| 25 | +AuthorName: Xitee |
| 26 | +SourceCode: https://github.com/Xitee1/sleep-timer |
| 27 | +IssueTracker: https://github.com/Xitee1/sleep-timer/issues |
| 28 | +Changelog: https://github.com/Xitee1/sleep-timer/releases |
| 29 | +Donate: https://ko-fi.com/xitee165479 |
| 30 | + |
| 31 | +AutoName: SleepTimer |
| 32 | + |
| 33 | +RepoType: git |
| 34 | +Repo: https://github.com/Xitee1/sleep-timer.git |
| 35 | + |
| 36 | +Builds: |
| 37 | + - versionName: 1.1.1 |
| 38 | + versionCode: 101010 |
| 39 | + commit: v1.1.1 |
| 40 | + subdir: app |
| 41 | + gradle: |
| 42 | + - yes |
| 43 | + gradleprops: |
| 44 | + - disableSigning=true |
| 45 | + binary: https://github.com/Xitee1/sleep-timer/releases/download/v%v/SleepTimer-v%v.apk |
| 46 | + |
| 47 | +AllowedAPKSigningKeys: 87e5fe65c58d5b8406d239d68e7a9d6d7f40245ee6c3a1f458e67094b0a67fb0 |
| 48 | + |
| 49 | +AutoUpdateMode: Version |
| 50 | +UpdateCheckMode: Tags ^v\d+\.\d+\.\d+$ |
| 51 | +UpdateCheckData: app/version.properties|versionCode=(\d+)|.|versionName=(.+) |
| 52 | +CurrentVersion: 1.1.1 |
| 53 | +CurrentVersionCode: 101010 |
| 54 | +``` |
| 55 | +
|
| 56 | +This is the **reproducible-builds (your-key)** form: `binary:` points F-Droid at your |
| 57 | +signed GitHub release APK as the reference, and `AllowedAPKSigningKeys` is the SHA-256 of |
| 58 | +your release signing certificate (`CN=Xitee`, extracted from the v1.1.0 release APK — valid |
| 59 | +as long as v1.1.1 is signed with the same keystore). F-Droid rebuilds from source, confirms |
| 60 | +its build is byte-identical to your APK, and ships your APK's signature. **Only submit this |
| 61 | +form if the probe below passes for v1.1.1.** If it does not, drop `binary:` and |
| 62 | +`AllowedAPKSigningKeys:` and F-Droid signs with its own key (a one-way door — you cannot |
| 63 | +switch to your signature later). |
| 64 | + |
| 65 | +Notes: |
| 66 | +- No `Summary:`/`Description:` — the fastlane metadata in this repo supplies them. |
| 67 | +- No `AntiFeatures:` — Shizuku is an *optional* Apache-2.0 dependency from Maven Central |
| 68 | + (no `NonFreeDep`; precedent: the Hail recipe, which also has optional Shizuku), and the |
| 69 | + app has no `INTERNET` permission and no trackers. |
| 70 | +- `UpdateCheckData` reads `app/version.properties` because the Gradle build derives |
| 71 | + `versionCode`/`versionName` dynamically from the git tag (axion-release), which the |
| 72 | + static update checker cannot parse. The `.` reuses the same file for the versionName |
| 73 | + regex. The `Tags` regex excludes the old `v0.0.1-beta` tag. |
| 74 | +- After the first release, the checkupdates bot appends new build entries automatically |
| 75 | + (copying `gradleprops`), so future releases need no recipe edits. |
| 76 | + |
| 77 | +## Reproducible-builds probe — preliminary result (already run against v1.1.0) |
| 78 | + |
| 79 | +Reproducible builds are a one-way door: if the F-Droid build is bit-identical to the |
| 80 | +signed GitHub release APK, F-Droid ships *your* signature (users cross-update between |
| 81 | +GitHub and F-Droid); if not enabled at inclusion time, F-Droid signs with its own key and |
| 82 | +you cannot switch later. |
| 83 | + |
| 84 | +**A preliminary probe was run on 2026-07-06 against the existing v1.1.0 release** (build the |
| 85 | +`v1.1.0` tag from a clean clone, unsigned, and compare its zip entries by CRC against |
| 86 | +`SleepTimer-v1.1.0.apk`). Result: **121 of 122 entries were already byte-identical** — |
| 87 | +including all DEX, resources, and `resources.arsc`. The only mismatch was |
| 88 | +`libdatastore_shared_counter.so` (the AndroidX DataStore native lib, 4 ABIs): GitHub Actions |
| 89 | +**strips** it (host-dependent, via the runner's NDK), while a plain build leaves it as the |
| 90 | +pristine bytes shipped in the `datastore-core` AAR. The unstripped bytes are fixed by the |
| 91 | +dependency version, so they are identical on every machine. |
| 92 | + |
| 93 | +**Fix applied in this repo** (`app/build.gradle.kts`): a `packaging { jniLibs { |
| 94 | +keepDebugSymbols += "**/*.so" } }` block, which tells AGP not to strip native libs, so every |
| 95 | +environment (GitHub Actions, F-Droid, local) packages the pristine AAR bytes. Verified: with |
| 96 | +the block, the four `.so` CRCs equal the pristine AAR CRCs. This **must ship in the v1.1.1 |
| 97 | +tag** so the reference GitHub APK is also unstripped — with it in place, all 122 entries |
| 98 | +match and the build is reproducible. |
| 99 | + |
| 100 | +**Confirm on v1.1.1 before submitting** (definitive check, once v1.1.1 is tagged & released): |
| 101 | + |
| 102 | +```sh |
| 103 | +# at the v1.1.1 tag, JDK 17, ANDROID_HOME set: |
| 104 | +./gradlew :app:assembleRelease -PdisableSigning |
| 105 | +export PATH="$ANDROID_HOME/build-tools/<ver>:$PATH" # for apksigner |
| 106 | +pip install apksigcopier |
| 107 | +apksigcopier compare SleepTimer-v1.1.1.apk \ |
| 108 | + --unsigned app/build/outputs/apk/release/app-release-unsigned.apk |
| 109 | +# apksigcopier 1.1.1 may error parsing the signing block; the robust fallback is a |
| 110 | +# per-entry CRC diff of `unzip -v` on both APKs (ignoring META-INF signature files). |
| 111 | +``` |
| 112 | + |
| 113 | +The definitive check is F-Droid's own CI on the submission MR (or a local |
| 114 | +`fdroid build` in a fdroiddata checkout), which builds in F-Droid's controlled environment. |
| 115 | +Given the preliminary result (only the now-fixed `.so` differed), it is very likely to pass. |
| 116 | +If it unexpectedly fails, drop `binary:` + `AllowedAPKSigningKeys:` from the recipe and write |
| 117 | +"No, I don't want this." in the MR reproducibility question. |
| 118 | + |
| 119 | +## Local validation |
| 120 | + |
| 121 | +```sh |
| 122 | +python3 -m venv ~/venvs/fdroid && ~/venvs/fdroid/bin/pip install fdroidserver |
| 123 | +git clone https://gitlab.com/<your-gitlab-user>/fdroiddata.git && cd fdroiddata |
| 124 | +git switch -c dev.xitee.sleeptimer # branch name = applicationId (convention) |
| 125 | +# add metadata/dev.xitee.sleeptimer.yml, then: |
| 126 | +fdroid readmeta && fdroid rewritemeta dev.xitee.sleeptimer |
| 127 | +fdroid lint dev.xitee.sleeptimer # must be clean |
| 128 | +fdroid checkupdates dev.xitee.sleeptimer # only after v1.1.1 is tagged |
| 129 | +fdroid build -v -l dev.xitee.sleeptimer # optional locally (needs ANDROID_HOME); fork CI runs it too |
| 130 | +``` |
| 131 | + |
| 132 | +## Submission MR |
| 133 | + |
| 134 | +1. Fork fdroiddata (public fork, unprotected branch `dev.xitee.sleeptimer`), push. |
| 135 | +2. Wait for the fork's GitLab pipelines to go green (they run lint + a full `fdroid build`). |
| 136 | +3. Open an MR against fdroiddata `master` with the **App inclusion** template, title |
| 137 | + `New app: SleepTimer`, squash enabled. |
| 138 | +4. In the description: state you are the upstream author (RFP issue not needed), that there |
| 139 | + are no anti-features (optional FLOSS Shizuku, no `INTERNET` permission, no trackers), and |
| 140 | + your reproducible-builds decision. |
| 141 | + |
| 142 | +Review typically takes days to a few weeks; after merge the app appears on f-droid.org |
| 143 | +within about a week. |
| 144 | + |
| 145 | +## After publication |
| 146 | + |
| 147 | +- Replace the `_Submission pending._` line in `README.md` with the F-Droid badge linking |
| 148 | + `https://f-droid.org/packages/dev.xitee.sleeptimer`. |
| 149 | +- Optional: add `fastlane/metadata/android/en-US/images/featureGraphic.png` (1024×500) for |
| 150 | + a nicer "Latest" listing card. |
0 commit comments