diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 83b7ee5..1132547 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -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" diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index 13f8d7f..635c6a8 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -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/.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: | diff --git a/README.md b/README.md index bab998a..6753ecc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-Calendula icon +Calendula icon

Calendula

@@ -16,11 +16,11 @@ Reads, writes, and reminds — on top of the system calendar, with zero network

-Week view  -Month view  -Event detail  -Event form  -Reminder onboarding +Week view  +Month view  +Event detail  +Event form  +Reminder onboarding

diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fb505cd..86b2587 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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( diff --git a/docs/README.md b/docs/README.md index 3eef8c7..dddbcf7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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 diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 4346228..5d0102d 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -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/.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 diff --git a/docs/fdroid-official/README.md b/docs/fdroid-official/README.md new file mode 100644 index 0000000..1a238f1 --- /dev/null +++ b/docs/fdroid-official/README.md @@ -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.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// + short_description.txt full_description.txt title.txt + images/icon.png images/phoneScreenshots/*.png + changelogs/.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/.txt` at release time. For the official repo to show a +changelog, that file must be committed into `fastlane/metadata/android//changelogs/` +at the tagged commit — wire this into the release/version-bump step, or accept +no in-client changelog for the official listing initially. diff --git a/docs/fdroid-official/de.jeanlucmakiola.calendula.yml b/docs/fdroid-official/de.jeanlucmakiola.calendula.yml new file mode 100644 index 0000000..42be209 --- /dev/null +++ b/docs/fdroid-official/de.jeanlucmakiola.calendula.yml @@ -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 diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/description.txt b/fastlane/metadata/android/de-DE/full_description.txt similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/description.txt rename to fastlane/metadata/android/de-DE/full_description.txt diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/icon.png b/fastlane/metadata/android/de-DE/images/icon.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/icon.png rename to fastlane/metadata/android/de-DE/images/icon.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/01-week.png b/fastlane/metadata/android/de-DE/images/phoneScreenshots/01-week.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/01-week.png rename to fastlane/metadata/android/de-DE/images/phoneScreenshots/01-week.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/02-month.png b/fastlane/metadata/android/de-DE/images/phoneScreenshots/02-month.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/02-month.png rename to fastlane/metadata/android/de-DE/images/phoneScreenshots/02-month.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/03-day.png b/fastlane/metadata/android/de-DE/images/phoneScreenshots/03-day.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/03-day.png rename to fastlane/metadata/android/de-DE/images/phoneScreenshots/03-day.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/04-detail.png b/fastlane/metadata/android/de-DE/images/phoneScreenshots/04-detail.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/04-detail.png rename to fastlane/metadata/android/de-DE/images/phoneScreenshots/04-detail.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/05-edit.png b/fastlane/metadata/android/de-DE/images/phoneScreenshots/05-edit.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/05-edit.png rename to fastlane/metadata/android/de-DE/images/phoneScreenshots/05-edit.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/06-onboarding.png b/fastlane/metadata/android/de-DE/images/phoneScreenshots/06-onboarding.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/phoneScreenshots/06-onboarding.png rename to fastlane/metadata/android/de-DE/images/phoneScreenshots/06-onboarding.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/summary.txt b/fastlane/metadata/android/de-DE/short_description.txt similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/de-DE/summary.txt rename to fastlane/metadata/android/de-DE/short_description.txt diff --git a/fastlane/metadata/android/de-DE/title.txt b/fastlane/metadata/android/de-DE/title.txt new file mode 100644 index 0000000..4f9e6a5 --- /dev/null +++ b/fastlane/metadata/android/de-DE/title.txt @@ -0,0 +1 @@ +Calendula diff --git a/fastlane/metadata/android/en-US/changelogs/20701.txt b/fastlane/metadata/android/en-US/changelogs/20701.txt new file mode 100644 index 0000000..59e3142 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/20701.txt @@ -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. + diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/description.txt b/fastlane/metadata/android/en-US/full_description.txt similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/description.txt rename to fastlane/metadata/android/en-US/full_description.txt diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/icon.png b/fastlane/metadata/android/en-US/images/icon.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/icon.png rename to fastlane/metadata/android/en-US/images/icon.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/01-week.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/01-week.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/01-week.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/01-week.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/02-month.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/02-month.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/02-month.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/02-month.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/03-day.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/03-day.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/03-day.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/03-day.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/04-detail.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/04-detail.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/04-detail.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/04-detail.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/05-edit.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/05-edit.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/05-edit.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/05-edit.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/06-onboarding.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/06-onboarding.png similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/phoneScreenshots/06-onboarding.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/06-onboarding.png diff --git a/fdroid-metadata/de.jeanlucmakiola.calendula/en-US/summary.txt b/fastlane/metadata/android/en-US/short_description.txt similarity index 100% rename from fdroid-metadata/de.jeanlucmakiola.calendula/en-US/summary.txt rename to fastlane/metadata/android/en-US/short_description.txt diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt new file mode 100644 index 0000000..4f9e6a5 --- /dev/null +++ b/fastlane/metadata/android/en-US/title.txt @@ -0,0 +1 @@ +Calendula diff --git a/scripts/fastlane_to_fdroid_localized.sh b/scripts/fastlane_to_fdroid_localized.sh new file mode 100755 index 0000000..99a54e8 --- /dev/null +++ b/scripts/fastlane_to_fdroid_localized.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Single source of truth: fastlane/metadata/android// 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 +# 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/.txt -> changelogs/.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/}" + +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'" diff --git a/scripts/sync_changelog_to_fastlane.sh b/scripts/sync_changelog_to_fastlane.sh new file mode 100755 index 0000000..f6d0e93 --- /dev/null +++ b/scripts/sync_changelog_to_fastlane.sh @@ -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/.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