Compare commits

...

5 Commits

Author SHA1 Message Date
eb51175ceb Merge pull request 'fix(widget): widgets stuck on loading spinner in release build (#18)' (#22) from fix/widget-release into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 4s
Release — F-Droid repo + Gitea release / release (push) Successful in 7m8s
Reviewed-on: #22
2026-06-21 12:15:44 +00:00
bc59200f77 fix(widget): keep WorkManager InputMerger so widgets render under R8 (#18)
All checks were successful
CI / ci (pull_request) Successful in 8m45s
Home-screen widgets were stuck on the Glance loading layout (a blank spinner)
in the minified release build — but worked in debug. Root cause: Glance renders
every widget through a WorkManager worker (androidx.glance.session.SessionWorker),
and WorkManager instantiates the InputMerger reflectively from the fully-qualified
class name persisted in the WorkSpec (Class.newInstance, no-arg ctor). Under R8
full mode (AGP 9 default) the unused no-arg constructor of
androidx.work.OverwritingInputMerger was stripped, so WorkManager threw
"OverwritingInputMerger has no zero argument constructor", the SessionWorker
never ran, and provideContent never executed — leaving the widget on its
initial loading layout forever.

Same R8-reflection family as the v2.7.0 Room keep-rule fix. Keep the name +
constructor of every androidx.work.InputMerger.

Verified on-device with the releaseTest build (R8-minified): the agenda widget,
which showed only a spinner before, now renders its content; the WM-InputMerger
InstantiationException is gone. Closes #18. Cuts v2.7.3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 14:06:34 +02:00
ed6b0fa89f Merge pull request 'chore(fdroid): single-source fastlane metadata + reproducible-build prep' (#21) from chore/release-verification-process into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 5s
Release — F-Droid repo + Gitea release / release (push) Has been skipped
Reviewed-on: #21
2026-06-21 11:38:20 +00:00
c503199e06 ci: make change-scope classification robust to divergent PR branches
All checks were successful
CI / ci (pull_request) Successful in 4m24s
The scope step shallow-fetched the base (`--depth=1`) and ran
`git diff origin/<base>...HEAD`. The three-dot diff needs the merge-base,
which a depth-1 tip doesn't contain once a branch has forked a few commits
back — git aborts with "no merge base" (exit 128) and the whole CI job
fails before lint/test/build ever run.

Fetch the base fully, resolve the merge-base explicitly, and diff from it.
If no common ancestor is found, default to code=true so the build still
runs rather than being wrongly skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:32:30 +02:00
f3f155b5ff chore(fdroid): single-source fastlane metadata + reproducible-build prep
Some checks failed
CI / ci (pull_request) Failing after 8s
Make fastlane/metadata/android/ the single source of truth for store
listing metadata, consumed directly by the official F-Droid repo and
transformed into the self-hosted repo's localized layout at release time.

- Move fdroid-metadata/<appid>/<locale>/ -> fastlane/metadata/android/<locale>/
  (git-tracked renames: summary->short_description, description->full_description,
  + title.txt, images/icon.png, images/phoneScreenshots/); keep the app-level
  .yml control file for the self-hosted `fdroid update`.
- Add scripts/fastlane_to_fdroid_localized.sh (fastlane -> F-Droid localized,
  incl. changelogs) and scripts/sync_changelog_to_fastlane.sh (CHANGELOG.md ->
  fastlane changelog); verified byte-identical to the previous metadata.
- release.yaml: build self-hosted metadata from the fastlane tree and sync the
  per-version changelog before the transform (one changelog source for both
  channels).
- Disable AGP VCS-info embedding on release builds (vcsInfo { include = false })
  so builds reproduce byte-for-byte vs the distributed APK — the only file that
  otherwise differed (META-INF/version-control-info.textproto). Effective from
  the next release.
- Add docs/fdroid-official/ (draft fdroiddata recipe: reproducible build +
  AllowedAPKSigningKeys + Binaries + notes).
- Repoint README screenshots/icon, update docs/README + RELEASING, and skip the
  Android build on fastlane-only changes (ci.yaml).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:17:21 +02:00
34 changed files with 333 additions and 36 deletions

View File

@@ -38,10 +38,20 @@ jobs:
run: |
set -e
BASE="${{ github.base_ref }}"
git fetch --no-tags --depth=1 origin "$BASE"
CHANGED=$(git diff --name-only "origin/$BASE...HEAD")
# Full (not --depth=1) base fetch so the merge-base is present even when
# the PR branch forked several commits back; a shallow tip has no merge
# base with a divergent branch and `git diff base...HEAD` aborts.
git fetch --no-tags origin "$BASE"
MB=$(git merge-base "origin/$BASE" HEAD 2>/dev/null || true)
if [ -z "$MB" ]; then
# No common ancestor available — don't risk skipping the build.
echo "No merge base with origin/$BASE — running the full build to be safe."
echo "code=true" >> "$GITHUB_OUTPUT"
exit 0
fi
CHANGED=$(git diff --name-only "$MB" HEAD)
echo "Changed files:"; echo "$CHANGED"
if echo "$CHANGED" | grep -vE '(\.md$|^docs/|^fdroid-metadata/|^LICENSE$)' | grep -q .; then
if echo "$CHANGED" | grep -vE '(\.md$|^docs/|^fdroid-metadata/|^fastlane/|^LICENSE$)' | grep -q .; then
echo "code=true" >> "$GITHUB_OUTPUT"
else
echo "code=false" >> "$GITHUB_OUTPUT"

View File

@@ -222,31 +222,27 @@ jobs:
mkdir -p fdroid/repo
cp app/build/outputs/apk/release/app-release.apk "fdroid/repo/calendula_v${VERSION}.apk"
- name: Copy metadata to F-Droid repo
# Per-version "What's New": ensure this version's changelog exists in the
# fastlane tree (committed at release-cut time for the official repo; this
# regenerates it from CHANGELOG.md so the self-hosted repo never depends on
# the commit having happened). The transform below then carries it across.
- name: Ensure this version's changelog is in the fastlane tree
if: env.IS_RELEASE == 'true'
run: bash scripts/sync_changelog_to_fastlane.sh
- name: Build F-Droid metadata from fastlane (single source of truth)
run: |
mkdir -p fdroid/metadata
cp -r fdroid-metadata/* fdroid/metadata/
# Per-version "What's New" for F-Droid clients: this version's CHANGELOG
# section written to changelogs/<versionCode>.txt (same extraction as the
# Gitea release notes). en-US only — F-Droid falls back to it for locales
# without their own changelog. fdroid update bakes this into the index.
- name: Generate F-Droid changelog for this version
if: env.IS_RELEASE == 'true'
run: |
set -e
awk -v ver="$VERSION" '
$0 ~ "^## \\[" ver "\\]" { flag = 1; next }
/^## \[/ { flag = 0 }
flag' CHANGELOG.md > /tmp/changelog.txt
sed -i -e '/./,$!d' /tmp/changelog.txt
if [ ! -s /tmp/changelog.txt ]; then
echo "See CHANGELOG.md for $VERSION." > /tmp/changelog.txt
fi
CL_DIR="fdroid/metadata/de.jeanlucmakiola.calendula/en-US/changelogs"
mkdir -p "$CL_DIR"
cp /tmp/changelog.txt "$CL_DIR/${VERSION_CODE}.txt"
echo "Wrote $CL_DIR/${VERSION_CODE}.txt"
# App-level control file (Categories/License/links) for the self-hosted
# repo's `fdroid update`.
cp fdroid-metadata/de.jeanlucmakiola.calendula.yml fdroid/metadata/
# Localized text + graphics + per-version changelogs come from the SAME
# fastlane tree the official F-Droid repo harvests from source,
# transformed into the F-Droid repo "localized" layout. One source of
# truth, both channels.
bash scripts/fastlane_to_fdroid_localized.sh \
fastlane/metadata/android \
fdroid/metadata/de.jeanlucmakiola.calendula
- name: Generate F-Droid Index
run: |

View File

@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.7.3] — 2026-06-21
### Fixed
- Home-screen widgets no longer get stuck on a loading spinner in the published
(F-Droid) release build. They render via Android's background-work system, and
release optimisation (R8) was stripping a helper class it loads by name, so the
render job never ran. Added the missing keep rule — widgets now load normally.
## [2.7.2] — 2026-06-21
### Added

View File

@@ -1,6 +1,6 @@
<div align="center">
<img src="fdroid-metadata/de.jeanlucmakiola.calendula/en-US/icon.png" width="112" alt="Calendula icon">
<img src="fastlane/metadata/android/en-US/images/icon.png" width="112" alt="Calendula icon">
<h1>Calendula</h1>
@@ -16,11 +16,11 @@ Reads, writes, and reminds — on top of the system calendar, with zero network
</p>
<p>
<img src="fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/01-week.png" width="19%" alt="Week view">&nbsp;
<img src="fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/02-month.png" width="19%" alt="Month view">&nbsp;
<img src="fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/04-detail.png" width="19%" alt="Event detail">&nbsp;
<img src="fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/05-edit.png" width="19%" alt="Event form">&nbsp;
<img src="fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/06-onboarding.png" width="19%" alt="Reminder onboarding">
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/01-week.png" width="19%" alt="Week view">&nbsp;
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/02-month.png" width="19%" alt="Month view">&nbsp;
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/04-detail.png" width="19%" alt="Event detail">&nbsp;
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/05-edit.png" width="19%" alt="Event form">&nbsp;
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/06-onboarding.png" width="19%" alt="Reminder onboarding">
</p>
</div>

View File

@@ -28,8 +28,8 @@ android {
// which builds this version and then creates the matching vX.Y.Z tag +
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
// PATCH from versionName, e.g. 2.7.2 -> 20702). See docs/RELEASING.md.
versionCode = 20702
versionName = "2.7.2"
versionCode = 20703
versionName = "2.7.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -47,6 +47,11 @@ android {
buildTypes {
release {
// Keep release builds reproducible for F-Droid: don't let AGP embed
// build-environment git metadata (META-INF/version-control-info.textproto),
// whose `revision`/path content varies by build machine and is the only
// thing that otherwise differs from a clean from-source rebuild.
vcsInfo { include = false }
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(

View File

@@ -15,3 +15,14 @@
# Keep the generated Room database implementations fully intact.
-keep class * extends androidx.room.RoomDatabase { *; }
-dontwarn androidx.room.paging.**
# WorkManager instantiates an InputMerger reflectively (Class.newInstance) from
# the fully-qualified class name persisted in the WorkSpec, so the class must
# keep both its name and a no-arg constructor. Glance renders every widget
# through a WorkManager worker (androidx.glance.session.SessionWorker) whose
# default merger is androidx.work.OverwritingInputMerger. Under R8 full mode
# (AGP 9 default) that unused no-arg constructor was stripped, so WorkManager
# threw "OverwritingInputMerger has no zero argument constructor", the
# SessionWorker never ran, and widgets were stuck on their loading layout
# (a blank spinner) in release builds. Keep every InputMerger's name + ctor.
-keep class * extends androidx.work.InputMerger { <init>(...); }

View File

@@ -12,7 +12,9 @@ Where to look for what:
| [`../.planning/STATE.md`](../.planning/STATE.md) | Snapshot of where development currently stands |
| [`superpowers/specs/`](superpowers/specs/) | The original design spec (2026-06-08) — historical record, not updated |
| [`superpowers/plans/`](superpowers/plans/) | Per-milestone implementation plans with task checklists — historical record of how each slice was built, including provider lessons learned |
| [`../fdroid-metadata/`](../fdroid-metadata/) | F-Droid/fastlane store metadata: descriptions, icon, screenshots (DE + EN) |
| [`../fastlane/metadata/android/`](../fastlane/metadata/android/) | Store metadata (single source of truth): descriptions, title, icon, screenshots (DE + EN). Harvested directly by the official F-Droid repo; transformed into the self-hosted repo layout at release time by [`../scripts/fastlane_to_fdroid_localized.sh`](../scripts/fastlane_to_fdroid_localized.sh) |
| [`../fdroid-metadata/`](../fdroid-metadata/) | App-level F-Droid control file (`*.yml`: Categories, License, links) for the self-hosted repo's `fdroid update` |
| [`fdroid-official/`](fdroid-official/) | Draft recipe + notes for publishing to the **official** F-Droid repo (reproducible build + developer-signed binary) |
Conventions: plans and specs under `superpowers/` are point-in-time
artifacts of the agentic workflow that built each milestone — they get

View File

@@ -33,7 +33,15 @@ Published version codes so far: `v0.1.0`→100 … `v1.0.0`→10000 … `v2.0.0`
the F-Droid per-version changelog.
3. Bump the committed `versionName` (and `versionCode`) in
`app/build.gradle.kts` to the new version. **This bump is what triggers the
release** when the branch merges to `main`.
release** when the branch merges to `main`. Then run
```bash
scripts/sync_changelog_to_fastlane.sh
```
and commit the generated
`fastlane/metadata/android/en-US/changelogs/<versionCode>.txt`. This is what
makes the **official** F-Droid repo show this version's changelog (it reads
the changelog from the tagged source tree). The self-hosted pipeline
regenerates it regardless, so forgetting only affects the official listing.
4. **Verify the release build on a real device** — the mandatory gate. The
shipped APK is R8-shrunk/obfuscated, and bugs that only appear there, or
only on first run, never show up in the debug build or on a device that

View File

@@ -0,0 +1,94 @@
# Official F-Droid submission (draft)
Goal: publish Calendula to the **official F-Droid repo** *alongside* the
self-hosted repo, as **one app** — same signed binary in both channels, so
existing self-hosted users migrate with no reinstall and no data loss.
This folder holds the **draft** fdroiddata recipe. Nothing here is submitted
yet. The self-hosted pipeline (`../../fdroid-metadata/`, `.gitea/workflows/release.yaml`)
is unaffected by these files.
## Publishing model: reproducible build + developer-signed binary
F-Droid builds Calendula from source on its buildserver, then verifies the
output is byte-for-byte identical to **our** signed APK (fetched via the
`Binaries` URL). On a match it publishes **our** binary, signed with **our**
key — identified by `AllowedAPKSigningKeys`. Result: official and self-hosted
both carry the same signature. If a build ever fails to match, F-Droid simply
skips that version (fails safe — no bad publish).
## Verification status (2026-06-21)
-**Run-to-run reproducible**`v2.7.0` built twice from a clean worktree
(R8 minify + resource-shrink, build cache off) → byte-for-byte identical
(`sha256 568c944a…`).
-**Cross-JDK reproducible** — rebuilt with JDK 17 and JDK 21 → identical
APK. JDK version is not a sensitivity, so it need not be pinned.
-**Tag self-consistent** — committed `versionCode`/`versionName` already
equal the tag-derived values, so F-Droid building the tag as-is matches the
CI-published binary (CI's `sed` substitution is a no-op).
-**App signing cert SHA-256 extracted** from published APKs (identical
across v1.0.0 / v2.0.0 / v2.4.0): `5cdaee8e…`. No keystore needed.
-**Eligibility** — MIT, 100% FOSS deps (no Play Services / Firebase /
analytics / billing), no `INTERNET` permission, no native code.
-**End-to-end reproducible vs the DISTRIBUTED binary** — built tag `v2.7.1`
from source and compared against the published `calendula_v2.7.1.apk`: every
app entry (dex / resources / assets / manifest) byte-identical. The only
difference was `META-INF/version-control-info.textproto` (AGP's git metadata,
env-dependent). Fixed with `vcsInfo { include = false }` on the release build;
re-validated → 0 non-signature differences. **Takes effect from the first
release built after that change — submit the recipe starting at that version**
(v2.7.1 and earlier still embed the textproto and won't verify).
- ⚠️ **Not yet proven**: cross-host / fixed-build-path reproducibility on
F-Droid's buildserver (low risk for a no-NDK pure-JVM app; with vcsInfo
disabled, the env-dependent field is gone; F-Droid confirms at review).
-**Buildserver toolchain supported** (checked 2026-06-21):
- **Gradle 9.5.1** is in F-Droid's gradle-transparency-log (`checksums.json`)
with a verified sha256, so `gradlew.py` will download + verify + run it.
- **AGP 9.2** is in `gradlew.py`'s `MIN_GRADLE_VERSION` map (`9.2 -> 9.4.1`).
- **JDK 17** is standard on the buildserver (AGP 9.2 requires exactly 17).
- **build-tools 36.0.0 / android-37**: not statically preinstalled (baseline
stops at 33), but `provision-android-sdk` makes `$ANDROID_HOME/build-tools`
and `/platforms` group-writable so AGP/Gradle install newer ones on demand.
## Before submitting — checklist
1. Confirm the `Binaries` URL is publicly reachable and stable long-term, e.g.
`https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo/calendula_v2.7.0.apk`
resolves to the dev-signed APK. F-Droid re-fetches it on every build.
2. Confirm F-Droid's buildserver supports the toolchain (see timing risk).
3. (Recommended) Reproduce one published release end-to-end: build the tag from
source, strip signatures, and diff against the downloaded `calendula_v<ver>.apk`
to confirm the from-source build matches the *distributed* binary — not just
another local build.
## Submission steps (fdroiddata, GitLab)
1. Fork `https://gitlab.com/fdroid/fdroiddata`.
2. Copy `de.jeanlucmakiola.calendula.yml` to `metadata/` in the fork.
3. Test locally with `fdroid build -v de.jeanlucmakiola.calendula` and
`fdroid lint de.jeanlucmakiola.calendula` (and `fdroid readmeta`).
4. Open a Merge Request. Initial review can take weeks to months; the
self-hosted repo keeps serving in the meantime, so there's no rush.
## Listing metadata (descriptions, screenshots, icon)
These are **not** in the recipe `.yml`. F-Droid's `fdroid update` harvests them
automatically from the fastlane tree in the app's source repo:
fastlane/metadata/android/<locale>/
short_description.txt full_description.txt title.txt
images/icon.png images/phoneScreenshots/*.png
changelogs/<versionCode>.txt (optional, per release)
This is the single source of truth: en-US + de-DE already exist there, and the
self-hosted repo is generated from the same tree at release time via
`scripts/fastlane_to_fdroid_localized.sh`. Nothing extra to add to fdroiddata —
F-Droid picks the listing up from source. (Screenshots may not be committed to
the fdroiddata repo anyway, so the source-tree fastlane layout is required.)
Per-version changelogs: the self-hosted release workflow generates
`changelogs/<versionCode>.txt` at release time. For the official repo to show a
changelog, that file must be committed into `fastlane/metadata/android/<locale>/changelogs/`
at the tagged commit — wire this into the release/version-bump step, or accept
no in-client changelog for the official listing initially.

View File

@@ -0,0 +1,58 @@
# ---------------------------------------------------------------------------
# DRAFT fdroiddata metadata for the OFFICIAL F-Droid repository.
#
# This is NOT the self-hosted metadata (that lives in ../../fdroid-metadata/).
# When submitting, this file's contents go to fdroiddata on GitLab as
# metadata/de.jeanlucmakiola.calendula.yml
# See README.md in this folder for the verification status and submission steps.
#
# Publishing model: REPRODUCIBLE BUILD + developer-signed binary.
# F-Droid builds from source on its buildserver, then verifies the result is
# identical to our own signed APK (fetched via `Binaries`). If it matches,
# F-Droid publishes OUR binary signed with OUR key (`AllowedAPKSigningKeys`),
# so the official and self-hosted channels carry the SAME signature and users
# migrate between them with no reinstall / no data loss.
# ---------------------------------------------------------------------------
Categories:
- Time
License: MIT
AuthorName: Jean-Luc Makiola
SourceCode: https://gitea.jeanlucmakiola.de/makiolaj/calendula
IssueTracker: https://gitea.jeanlucmakiola.de/makiolaj/calendula/issues
Changelog: https://gitea.jeanlucmakiola.de/makiolaj/calendula/src/branch/main/CHANGELOG.md
AutoName: Calendula
RepoType: git
Repo: https://gitea.jeanlucmakiola.de/makiolaj/calendula.git
# IMPORTANT: the first build entry MUST target the first release that contains
# `vcsInfo { include = false }` (added on branch chore/release-verification-process).
# v2.7.1 and earlier embed META-INF/version-control-info.textproto, whose
# env-dependent git metadata breaks reproducibility — they will NOT verify.
# Update the versionName/versionCode/commit/Binaries below once that release is
# tagged (e.g. v2.7.2 -> versionCode 20702). Values below are placeholders.
Builds:
- versionName: 2.7.2
versionCode: 20702
commit: v2.7.2
subdir: app
gradle:
- yes
# No NDK / no flavors. The release buildType applies a signingConfig only
# when key.properties exists; on the buildserver it does not, so this
# produces the unsigned APK F-Droid compares against our binary.
# SHA-256 of our app signing certificate (public; embedded in every published
# APK). Locks F-Droid to publish only binaries signed with our key.
AllowedAPKSigningKeys: 5cdaee8eb31cb0df9157c646ae3ec8b3dc43b5bb72624e088c9ddea0c4eb5ec1
# Our own developer-signed APK for reproducible-build verification. %v -> the
# versionName, so v2.7.2 resolves to calendula_v2.7.2.apk on the self-hosted repo.
Binaries: https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo/calendula_v%v.apk
AutoUpdateMode: Version v%v
UpdateCheckMode: Tags
CurrentVersion: 2.7.2
CurrentVersionCode: 20702

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1 @@
Calendula

View File

@@ -0,0 +1,8 @@
### Fixed
- Fixed the app crashing immediately on launch whenever calendar access hadn't
been granted yet (a fresh install, or after revoking the permission). The app
set up its live calendar-change listener before the permission screen could
appear, which newer Android versions reject outright — so the app died before
you could grant access. The listener now waits for the permission and attaches
itself the moment it's granted.

View File

@@ -0,0 +1,6 @@
### Fixed
- Home-screen widgets no longer get stuck on a loading spinner in the published
(F-Droid) release build. They render via Android's background-work system, and
release optimisation (R8) was stripping a helper class it loads by name, so the
render job never ran. Added the missing keep rule — widgets now load normally.

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1 @@
Calendula

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Single source of truth: fastlane/metadata/android/<locale>/ feeds BOTH the
# official F-Droid repo (harvested from source automatically) and the
# self-hosted repo. This script transforms the fastlane layout into the F-Droid
# "localized" layout that the self-hosted `fdroid update` consumes, so we don't
# maintain two copies.
#
# usage: fastlane_to_fdroid_localized.sh <fastlane_android_dir> <out_localized_dir>
# e.g. scripts/fastlane_to_fdroid_localized.sh \
# fastlane/metadata/android \
# fdroid/metadata/de.jeanlucmakiola.calendula
#
# Mapping (fastlane -> F-Droid repo localized):
# short_description.txt -> summary.txt
# full_description.txt -> description.txt
# title.txt -> name.txt
# images/icon.png -> icon.png
# images/phoneScreenshots/* -> phoneScreenshots/*
# changelogs/<versionCode>.txt -> changelogs/<versionCode>.txt
# (changelogs are seeded into the fastlane tree by
# scripts/sync_changelog_to_fastlane.sh.)
set -euo pipefail
SRC="${1:?need fastlane android dir, e.g. fastlane/metadata/android}"
OUT="${2:?need output localized dir, e.g. fdroid/metadata/<appid>}"
shopt -s nullglob
for locdir in "$SRC"/*/; do
loc="$(basename "$locdir")"
dst="$OUT/$loc"
mkdir -p "$dst"
[ -f "$locdir/short_description.txt" ] && cp "$locdir/short_description.txt" "$dst/summary.txt"
[ -f "$locdir/full_description.txt" ] && cp "$locdir/full_description.txt" "$dst/description.txt"
[ -f "$locdir/title.txt" ] && cp "$locdir/title.txt" "$dst/name.txt"
[ -f "$locdir/images/icon.png" ] && cp "$locdir/images/icon.png" "$dst/icon.png"
if [ -d "$locdir/images/phoneScreenshots" ]; then
mkdir -p "$dst/phoneScreenshots"
cp "$locdir"images/phoneScreenshots/* "$dst/phoneScreenshots/"
fi
# Per-version changelogs live in the same fastlane tree (see
# scripts/sync_changelog_to_fastlane.sh) and map straight across.
if [ -d "$locdir/changelogs" ]; then
mkdir -p "$dst/changelogs"
cp "$locdir"changelogs/* "$dst/changelogs/"
fi
done
echo "Built F-Droid localized metadata in '$OUT' from '$SRC'"

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Write the current version's CHANGELOG.md section into the fastlane changelog
# file that F-Droid harvests: fastlane/metadata/android/en-US/changelogs/<code>.txt
# (en-US is F-Droid's fallback locale, so it covers every language).
#
# Run this when cutting a release (after editing CHANGELOG.md and bumping
# versionName in app/build.gradle.kts) and COMMIT the result, so the OFFICIAL
# F-Droid repo — which reads the changelog from the tagged source tree — shows
# this version's "What's New". The self-hosted release pipeline also runs it so
# its changelog never depends on the file having been committed. Idempotent.
#
# Extraction matches the awk used for the Gitea release notes so all three
# (release notes, self-hosted changelog, official changelog) stay in sync.
set -euo pipefail
cd "$(dirname "$0")/.." # repo root
VERSION=$(grep -oP 'versionName\s*=\s*"\K[^"]+' app/build.gradle.kts)
[ -n "$VERSION" ] || { echo "No versionName in app/build.gradle.kts" >&2; exit 1; }
MAJOR=${VERSION%%.*}; rest=${VERSION#*.}; MINOR=${rest%%.*}; PATCH=${rest##*.}
MAJOR=${MAJOR:-0}; MINOR=${MINOR:-0}; PATCH=${PATCH:-0}
VERSION_CODE=$(( MAJOR * 10000 + MINOR * 100 + PATCH ))
CL_DIR="fastlane/metadata/android/en-US/changelogs"
mkdir -p "$CL_DIR"
OUT="$CL_DIR/${VERSION_CODE}.txt"
awk -v ver="$VERSION" '
$0 ~ "^## \\[" ver "\\]" { flag = 1; next }
/^## \[/ { flag = 0 }
flag' CHANGELOG.md > "$OUT"
# Trim leading blank lines (same as the pipeline did).
sed -i -e '/./,$!d' "$OUT"
if [ ! -s "$OUT" ]; then
echo "See CHANGELOG.md for $VERSION." > "$OUT"
fi
CHARS=$(wc -m < "$OUT" | tr -d ' ')
echo "Wrote $OUT (version $VERSION, code $VERSION_CODE, ${CHARS} chars)"
if [ "$CHARS" -gt 500 ]; then
echo " note: >500 chars — F-Droid may truncate this changelog in-client." >&2
fi