The v prefix was never the constraint. checkupdates resolves whatever ref it finds to a commit hash before writing the build entry, and Tags mode with no pattern matches every tag regardless of name. The rule that does bite: a hand-written build entry must pin a full commit hash. The 1.0 submission used the tag and a reviewer sent it back. Worth writing down because fdroid lint only checks this under RepoManifest, so the pipeline passes with a tag sitting in commit:. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.2 KiB
Releasing a new version
The clock lives in two places: the web app in public_html/, which you host
yourself, and the Android app on F-Droid, which is built from a git tag.
Everything F-Droid needs comes from a tag in this repository. After the first
release was accepted there is nothing to do in F-Droid's own repo — their bot
watches the tags here and writes the build entry itself. Don't open a merge
request against fdroiddata for a new version.
Worked example below bumps 1.0 to 1.1. Substitute your own numbers.
1. Make the change
Both versions share one copy of the app. Edit public_html/index.html and the
Android build picks it up automatically — the Gradle build reads public_html/
directly rather than keeping a second copy, so the two can't drift.
If you changed anything visible, bump the service worker cache or installed web users will keep the old page:
# public_html/sw.js
const CACHE = "bgclock-v18"; # any new value; it only has to differ
2. Bump the version
In android/app/build.gradle.kts:
versionCode = 2 // an integer, +1 every release, never reused
versionName = "1.1" // what people see
versionCode is what Android uses to decide one version is newer than another.
It must go up every single release, even for a one-character fix.
3. Write the changelog
A new file named after the versionCode, not the version name:
fastlane/metadata/android/en-US/changelogs/2.txt
Maximum 500 characters. This is what shows up in F-Droid's "What's New".
4. Build it and put it on your phone
export ANDROID_HOME="$HOME/Android/Sdk" # already in ~/.bashrc
cd android
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
./gradlew --stop # the daemon holds ~500 MB otherwise
Play a real game before tagging. Worth checking specifically: start a match, make some moves, force-quit from the task switcher and reopen — everything should come back, paused. That is the check that proves the app's storage is working, and it fails silently rather than loudly.
5. Commit, tag, push
git add -A
git commit -m "…"
git tag -a v1.1 -m "Backgammon Clock 1.1"
git push
git push origin v1.1
Keep tags in the v1.1 shape for consistency, but nothing depends on it:
UpdateCheckMode: Tags with no pattern matches every tag whatever it's called,
and F-Droid's bot resolves whichever tag it finds to a commit hash before it
writes the build entry.
Never move or delete a tag that has been pushed — F-Droid may already have built it. If a release is wrong, bump the version and release again. (The recipe pins a hash, so a moved tag can't retroactively change a version they already built; it would just leave your repo disagreeing with what's on people's phones.)
If you ever write a build entry by hand, its commit: must be a full 40-char
commit hash — never a tag, never a branch. Tags are mutable, so a tag there means
the thing F-Droid builds and signs can change under them, and they reject it on
sight. This came up on the very first submission and is easy to get wrong,
because fdroid lint doesn't check it under UpdateCheckMode: Tags — the
pipeline goes green and a human catches it days later. Get the hash with:
git rev-parse v1.1^{commit} # ^{commit}, or an annotated tag gives you the
# tag object's hash instead of the commit's
This doesn't come up in a normal release: the bot writes the entry, and the bot writes hashes.
6. Wait
Their bot notices the new tag, adds a build entry, and the build server picks it up on its next cycle. Expect a day or two. Nothing to do.
Deploying the web version
Separate from all of the above, and manual: upload the six files in
public_html/ — index.html, manifest.webmanifest, sw.js, icon-192.png,
icon-512.png, icon-maskable-512.png — to the web host. README.md and
RELEASING.md stay out of it; they live in the repo root for that reason.
If the screenshots need redoing
fastlane/metadata/android/en-US/images/phoneScreenshots/ holds five shots at
1170×2532. They are generated from public_html/index.html itself with headless
Chromium, not taken on a phone, so they can be regenerated exactly. Two traps if
you do it by hand:
- The capture lands whenever the virtual-time budget runs out, not when your
script finishes — so freeze the render loop (
requestAnimationFrame = () => 0) once the frame you want is on screen. - CSS transitions don't advance under
--virtual-time-budget, so a panel caught mid-transition photographs in its old colour. Disable transitions in the screenshot build.
Reference
- App ID:
nl.hansdezwart.bgclock— fixed forever; changing it makes a new app. - F-Droid listing text:
fastlane/metadata/android/en-US/(title ≤50 chars, summary ≤80, description ≤4000, changelog ≤500). Only these HTML tags work in the description:b big blockquote br cite em i li ol small strike strong sub sup tt u ul. - The original submission: https://gitlab.com/fdroid/fdroiddata/-/merge_requests/45478
- F-Droid signs the APK with their key, not yours. Reproducible builds were declined at submission and can't be enabled later for this app ID.