publish.sh's overwrite guard (fdroid-repo-ci#6) is correct -- a published versionCode's artifact must stay immutable -- but nothing tells a consumer that this implies the filename has to carry the versionCode. A build that always emits the same filename (Flutter's app-<flavor>-release.apk, independent of --build-number) gets exactly one successful publish into a prefix, ever, and every publish after that fails with a message that reads like a bucket-state problem rather than a naming contract. weblib-viewer hit this for real (weblib-viewer#29) and it cost a full CI cycle to diagnose, because the failure only shows up on the second publish.
bit replied "Both" on the issue, so this implements both options it proposed:
README: a new subsection states the naming contract -- <applicationId>_<versionCode>.apk, F-Droid's own convention -- with the Flutter/weblib-viewer#29 case spelled out concretely.
Better refusal message: when the colliding object's basename contains no digits at all, publish.sh appends a hint pointing at the naming convention. This is a pure text addition -- the guard's control flow is untouched. A filename with digits that still collides keeps refusing with exactly today's message; a filename with no digits still gets refused, just with an extra line explaining why that is very likely to keep happening.
Not touched
No override, bypass or weakening of the overwrite guard -- it behaved correctly and caught a real overwrite. This is discoverability only.
Testing
Baseline was 104 passed / 4 skipped / 0 failed. Added assertions for both branches of the new heuristic (colliding filename with digits -> no hint; without digits -> hint present, still exits non-zero) plus the existing collision/negative-control/--index-only cases untouched. Full suite (run_tests.sh) now reports 111 passed / 4 skipped / 0 failed -- the 4 skips are the opt-in live-bucket tests, same as baseline.
## What
`publish.sh`'s overwrite guard (fdroid-repo-ci#6) is correct -- a published versionCode's artifact must stay immutable -- but nothing tells a consumer that this implies **the filename has to carry the versionCode**. A build that always emits the same filename (Flutter's `app-<flavor>-release.apk`, independent of `--build-number`) gets exactly one successful publish into a prefix, ever, and every publish after that fails with a message that reads like a bucket-state problem rather than a naming contract. weblib-viewer hit this for real (weblib-viewer#29) and it cost a full CI cycle to diagnose, because the failure only shows up on the *second* publish.
bit replied "Both" on the issue, so this implements both options it proposed:
1. **README**: a new subsection states the naming contract -- `<applicationId>_<versionCode>.apk`, F-Droid's own convention -- with the Flutter/weblib-viewer#29 case spelled out concretely.
2. **Better refusal message**: when the colliding object's basename contains no digits at all, `publish.sh` appends a hint pointing at the naming convention. This is a pure text addition -- the guard's control flow is untouched. A filename with digits that still collides keeps refusing with exactly today's message; a filename with no digits still gets refused, just with an extra line explaining why that is very likely to keep happening.
## Not touched
No override, bypass or weakening of the overwrite guard -- it behaved correctly and caught a real overwrite. This is discoverability only.
## Testing
Baseline was 104 passed / 4 skipped / 0 failed. Added assertions for both branches of the new heuristic (colliding filename with digits -> no hint; without digits -> hint present, still exits non-zero) plus the existing collision/negative-control/`--index-only` cases untouched. Full suite (`run_tests.sh`) now reports **111 passed / 4 skipped / 0 failed** -- the 4 skips are the opt-in live-bucket tests, same as baseline.
Closes #13.
publish.sh's overwrite guard (fdroid-repo-ci#6) is correct but implies a
contract nothing states: a colliding filename with no versionCode in it
means only the first publish into a prefix can ever succeed. Flutter's
constant app-<flavor>-release.apk hit this in weblib-viewer (#29), and the
failure only surfaces on the second publish with an error that reads like a
bucket-state problem.
Add the naming contract to the README, and append a hint to the refusal
message when the colliding object's basename has no digits at all -- a
pure text addition, never changing which publishes are refused.
Closes fdroid-repo-ci#13.
Co-authored-by: bit <bit@das-labor.org>
bit
merged commit c529ca9564 into main2026-09-13 08:01:21 +00:00
bit
deleted branch apk-versioncode-naming-contract2026-09-13 08:01:22 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
publish.sh's overwrite guard (fdroid-repo-ci#6) is correct -- a published versionCode's artifact must stay immutable -- but nothing tells a consumer that this implies the filename has to carry the versionCode. A build that always emits the same filename (Flutter'sapp-<flavor>-release.apk, independent of--build-number) gets exactly one successful publish into a prefix, ever, and every publish after that fails with a message that reads like a bucket-state problem rather than a naming contract. weblib-viewer hit this for real (weblib-viewer#29) and it cost a full CI cycle to diagnose, because the failure only shows up on the second publish.bit replied "Both" on the issue, so this implements both options it proposed:
<applicationId>_<versionCode>.apk, F-Droid's own convention -- with the Flutter/weblib-viewer#29 case spelled out concretely.publish.shappends a hint pointing at the naming convention. This is a pure text addition -- the guard's control flow is untouched. A filename with digits that still collides keeps refusing with exactly today's message; a filename with no digits still gets refused, just with an extra line explaining why that is very likely to keep happening.Not touched
No override, bypass or weakening of the overwrite guard -- it behaved correctly and caught a real overwrite. This is discoverability only.
Testing
Baseline was 104 passed / 4 skipped / 0 failed. Added assertions for both branches of the new heuristic (colliding filename with digits -> no hint; without digits -> hint present, still exits non-zero) plus the existing collision/negative-control/
--index-onlycases untouched. Full suite (run_tests.sh) now reports 111 passed / 4 skipped / 0 failed -- the 4 skips are the opt-in live-bucket tests, same as baseline.Closes #13.