`sync_changelog_to_fastlane.sh` has been printing "note: >500 chars — F-Droid
may truncate this changelog in-client" for three releases, and every release
since 0.2.0 has sailed past it: 1824, 696, 916 characters. A note nobody acts
on is not a check.
F-Droid truncates the in-client "What's New" box, so everything past the limit
is written for nobody — the reader gets a sentence cut mid-word and no way to
expand it. The limit is now a hard failure: the script exits non-zero, with a
message naming the section to shorten, and `MAX_CHARS` is a variable so the
bound lives in one place rather than being copied into the workflows.
Enforced in two places, for two different failures:
- `.forgejo/workflows/ci.yaml` runs it as an always-on guard beside the
reproducible-release invariant, then checks `git status --porcelain` over the
changelogs directory. That second half catches a CHANGELOG.md edit whose
generated fastlane file was never committed — which until now degraded
silently into the official F-Droid listing showing the *previous* version's
notes, exactly as RELEASING.md step 3 warns. Porcelain rather than
`git diff --exit-code`, so a brand-new file for a bumped versionCode counts
as dirty instead of being missed as untracked.
- `.gitea/workflows/release.yaml` gets the same call in the cheap `detect`
gate. The release job already regenerated the file, but only at step 15 of
24 — after the build, the signing and the keystore setup. Failing in `detect`
costs one bash invocation and publishes nothing.
Port Calendula's current CI/release pipeline:
- ci.yaml: pull_request-triggered, change-scope classification
(docs/metadata-only PRs skip the Android build but still report a
green CI), and a reproducible-release invariant guard.
- release.yaml: the committed versionName is the source of truth — a
bump reaching main triggers the release, which builds, signs,
publishes to the F-Droid repo, then mints the vX.Y.Z tag + Gitea
release and mirrors it to Codeberg with the signed APK + SHA-256
checksum. workflow_dispatch runs the re-sign-only recovery path.
- Gitea releases are flagged as pre-releases while MAJOR is 0.
- build.gradle.kts: reproducible-release invariants (vcsInfo,
dependenciesInfo) + a releaseTest variant for the on-device gate.
- fastlane/ becomes the single source of truth for store metadata;
the localized F-Droid layout is generated from it at release time.
- Port scripts/, .gitea/ISSUE_TEMPLATE/, and rewrite docs/RELEASING.md
for the versionName-in-main model; fix stale references elsewhere.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>