Makes Codeberg canonical for git, issues, PRs, tags and releases. The self-hosted Gitea instance stays build infrastructure: signing key, F-Droid publishing, release pipeline. **This PR is its own test.** It is the first PR opened on Codeberg, so a green `CI` check proves the new runner works *and* that the submodule resolves from its new home. ### 1 · floret-kit moved Mirrored to `jlmakiola/floret-kit` (6 branches, 3 tags, every SHA verified identical) and `.gitmodules` repointed. This is what actually unblocks contributors — a clone previously could not resolve its submodule without reaching the personal Gitea instance. The Gitea copy is **kept**: every existing tag records the old submodule URL, so rebuilds of past releases (including F-Droid reproducible rebuilds) still resolve. ### 2 · Workflows split by directory Forgejo's lookup is first-match-wins across `.forgejo/` → `.gitea/` → `.github/`, and Gitea cannot see `.forgejo/` at all. So each forge sees exactly one set, with no duplicated files and no expression to keep in sync: | Directory | Runs on | Contains | Secrets | | --- | --- | --- | --- | | `.forgejo/workflows/` | Codeberg | `ci.yaml`, `translations.yaml` | **none** | | `.gitea/workflows/` | Gitea | `release.yaml`, `renovate.yml` | all of them | The line is drawn at **secrets, not CI-vs-release** — that is what makes fork PRs safe. Renovate deliberately does *not* move despite opening PRs here; it keeps running where its token already lives and merely talks to Codeberg's API. ### 3 · Two release-pipeline safety changes - `release.yaml`'s `detect` gets an explicit `repository_owner` guard. The directory split only holds while `.forgejo/` is non-empty; empty it and Codeberg would fall back to `.gitea/` and start running the release pipeline on the contributor-facing runner, without secrets. - `detect` now reads tags from **Codeberg**, not from the Gitea instance it runs on. Push mirroring is `git push --mirror`, so a tag minted on Gitea is deleted by the next sync until the Codeberg tag push propagates back — asking Gitea inside that window reports "no tag" for an already-shipped release and would cut it twice. It also now fails on any status other than 200/404 rather than reading a transient error as "no tag": a failed job is recoverable, a duplicate release is not. ### 4 · Links repointed In-app Source/License links, README badge, both F-Droid metadata files. **`Repo:` in `docs/fdroid-official/` deliberately stays on Gitea** — it keeps receiving `main` and every tag, so it remains a complete build source, and leaving it alone means no fdroiddata MR and no reproducible-build risk. ### Not in this PR Renovate + Weblate repointing, and the Codeberg → Gitea push mirror (browser-side). Supersedes Gitea PR #104. Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/86
Calendula
A modern Material 3 Expressive calendar for Android.
Reads, writes, and reminds — on top of the system calendar, with zero network access.
Calendula is named after the flower whose name — like the word calendar —
comes from the Latin kalendae, the first day of the month. It lives
entirely on top of Android's CalendarContract: any calendar synced to your
device (CalDAV via DAVx5, Google, local, WebCal subscriptions, …) simply
appears, and everything you create or edit syncs back the same way. No own
database, no sync stack reinvented.
✨ Features
Calendar
- Month, week, and day views with a one-tap view switcher
- Full event details — attendees and their responses, reminders, recurrence (humanized), availability, visibility, foreign time zones
- Per-calendar visibility toggle, grouped by account
Editing
- Create, edit, and delete events — including recurring events with scoped writes: only this event, this and all following, or the whole series
- Recurrence picker with one-tap presets and custom rules (interval, weekday toggles, end conditions); rules it can't express are preserved verbatim
- Conflict-safe saves: if an event changed elsewhere while you were editing, Calendula asks instead of silently overwriting
- Read-only calendars (WebCal, birthdays) are detected and respected
Reminders
- Event reminders delivered by Calendula itself as notifications — essential when it's your only calendar app, since Android delegates reminder delivery to calendar apps
- Tap a reminder to land on the event
Design & privacy
- Real Material 3 Expressive throughout — dynamic color (Android 12+), expressive motion and shapes, light/dark theme
- German and English UI, per-app language setting — and open to community translations
- Zero telemetry, zero analytics, no internet permission — your data never leaves the device
📦 Install
F-Droid (recommended)
Calendula is on the official F-Droid repository — just search for Calendula in any F-Droid client, or install it from f-droid.org.
Self-hosted repo (latest builds)
New versions are built, signed, and published to a self-hosted repository the moment each tag lands — usually a few days ahead of the official repo, which rebuilds on F-Droid's own schedule. Add it for the freshest builds:
-
In your F-Droid client, open Settings → Repositories → Add (or open the link below on your phone):
https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo?fingerprint=C2C0640402BF458FC0ED957AF0B37AA4C14022E72F89CE90B5965B458CF73425Repo:
https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo· fingerprint (SHA-256):C2C0 6404 02BF 458F C0ED 957A F0B3 7AA4 C140 22E7 2F89 CE90 B596 5B45 8CF7 3425 -
Refresh, search for Calendula, install.
Both channels share the same signing key, so you can switch between them without reinstalling. Or build from source — see below.
📚 Documentation
- Building from source — requirements and Gradle tasks
- Architecture — the layered design and key pipelines
- Roadmap — what's shipped and what's next
🌍 Translations
Calendula ships in German and English, and you're warmly invited to add your language. Translations are managed on a self-hosted Weblate:
No coding needed — register on the Weblate server, pick (or request) a language, and translate the strings in your browser. You can also reach this link in the app from the top of Settings → App language.
📜 License
MIT — Jean-Luc Makiola, 2026






