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. Ports the setup Calendula already runs on, adapted where Agendula genuinely differs. **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 · 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. CI also gains three fixes: an explicit `SKIP_RE` skip-list that names the build-relevant files in the log, base-ref normalisation, and a fully-qualified `android-actions/setup-android` — Codeberg resolves bare `uses:` refs against `data.forgejo.org`, which does not carry that action. ### 2 · Three release-pipeline safety changes - `detect` and the Renovate job get 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 these 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. - **The Codeberg publish step pushes the tag itself** instead of waiting for it to arrive by mirror. That wait was correct while Gitea mirrored *to* Codeberg; under Codeberg-canonical the mirror runs the other way and it would never resolve. Attaching the release to an already-pushed ref (no `target_commitish`) is what avoids the empty-bodied 500s, and the create call retries with backoff because Codeberg 500s on a tag it has only just received. The step stays **fail-loud**, not `continue-on-error` — it reported green through 0.2.1–0.3.2 while never once publishing, and that must not be possible again. ### 3 · Renovate `renovate.json5` plus a Gitea-side job targeting Codeberg's API. `managerFilePatterns` covers **both** workflow directories, so the pinned Renovate image tag and the action versions in either file keep getting bumped. Needs two new Gitea secrets: `RENOVATE_TOKEN` (Codeberg bot, repo read/write + PR scope) and `GITHUB_COM_TOKEN` (read-only github.com PAT, for changelog lookups). ### 4 · Weblate A parity check (`scripts/check_translations.py`) runs on every PR without a path filter, so the required `Translations` status is always reported. Partial translations are expected, so `MissingTranslation` and `MissingQuantity` become informational — `ExtraTranslation` stays fatal. Agendula had no `lint` block at all, so the first locale to land would otherwise have failed the build. **Settings → App language** now opens a picker carrying a "Help translate" header. That is why it drops floret-kit's `LanguagePickerRow` for a local row: the shared recipe has no `header` slot, and the framing is app-specific rather than a family primitive. ### 5 · Links repointed In-app Source / License / report-issue URLs, F-Droid metadata, README (now with a Codeberg CI badge), and the docs. `floret-kit` follows suit — `.gitmodules` points at `codeberg.org/jlmakiola/floret-kit`, so a clone no longer needs to reach the personal Gitea instance to resolve it. The Gitea copy is **kept**: every existing tag records the old submodule URL, so rebuilds of past releases still resolve. ### 6 · Housekeeping Drops `release-notes.md` — a release-pipeline scratch file that got committed — and gitignores the five others the release job writes into the workspace. ### Not in this PR The Codeberg → Gitea push mirror, the Weblate component, and the Codeberg bot account (all browser-side). Until the mirror is flipped, merging this does **not** reach the Gitea runner. Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/2
123 lines
5.4 KiB
Plaintext
123 lines
5.4 KiB
Plaintext
{
|
|
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
|
|
|
extends: [
|
|
"config:recommended",
|
|
// chore(deps): … — match the repo's conventional-commit style.
|
|
":semanticCommits",
|
|
],
|
|
|
|
// `config:recommended` brings in mergeConfidence:age-confidence-badges, whose
|
|
// Age column is a Mend badge. Mend's Merge Confidence index only covers Maven
|
|
// Central: org.jetbrains.kotlin, junit, truth, turbine et al resolve, but
|
|
// every androidx/compose artifact lives on Google's Maven repo and comes back
|
|
// as a grey UNKNOWN — i.e. most of this project. Renovate already knows the
|
|
// real answer, since it derives release timestamps itself for the
|
|
// minimumReleaseAge rules below (Google Maven serves `last-modified` on its
|
|
// POMs), so take the age from there and leave Mend to the Confidence column,
|
|
// which still carries signal for the Maven Central half.
|
|
prBodyDefinitions: {
|
|
Age: "{{#if releaseTimestamp}}{{{newVersionAgeInDays}}} d{{else}}unknown{{/if}}",
|
|
},
|
|
// Default heading links to the Merge Confidence docs; this column is ours now.
|
|
prBodyHeadingDefinitions: {
|
|
Age: "Age",
|
|
},
|
|
|
|
// No automerge: a dependency bump goes through the same review (and, for
|
|
// anything touching the build, the same on-device check) as a feature
|
|
// before it can ride a release — see docs/RELEASING.md and the mandatory
|
|
// `scripts/verify-release.sh` gate.
|
|
automerge: false,
|
|
|
|
// One reviewable surface; the dashboard issue lists everything pending.
|
|
dependencyDashboard: true,
|
|
|
|
// The cooling-off periods below are advisory, not a gate. "flexible" still
|
|
// prefers a version that has cleared its window, but when every candidate is
|
|
// too young it opens the PR at the newest one anyway, so merging early stays
|
|
// a judgement call. (The default, "strict", would suppress the PR entirely
|
|
// until a release aged in.) A still-young branch carries a yellow
|
|
// `renovate/stability-days` check so it's visible which side of the line
|
|
// it's on; with automerge off, nothing acts on that check by itself.
|
|
//
|
|
// NOT "none": that short-circuits the candidate loop in filter-checks.ts, and
|
|
// that loop is what calls postprocessRelease — the only thing that fetches a
|
|
// Maven artifact's Last-Modified header. Skipping it leaves releaseTimestamp
|
|
// unset, which empties the Age column and quietly makes minimumReleaseAge and
|
|
// the stability check no-ops, since both need that timestamp to compare.
|
|
internalChecksFilter: "flexible",
|
|
|
|
labels: ["dependencies"],
|
|
prConcurrentLimit: 5,
|
|
prHourlyLimit: 0,
|
|
|
|
// Cadence is owned by the Gitea Actions cron (.gitea/workflows/renovate.yml,
|
|
// Mondays) — no internal `schedule` here, so the two don't double-gate and
|
|
// silently skip a run.
|
|
|
|
// Workflows are split by forge and neither directory is .github: CI lives in
|
|
// .forgejo/workflows (Codeberg) and the release/renovate jobs in
|
|
// .gitea/workflows (Gitea). Extend the github-actions manager (same syntax)
|
|
// to watch both — otherwise the pinned Renovate image tag and the action
|
|
// versions in either file would never get bumped. See docs/RELEASING.md.
|
|
// `fileMatch` is deprecated; the replacement takes the regex delimited, and
|
|
// Renovate's config migration was already rewriting this on every run.
|
|
"github-actions": {
|
|
managerFilePatterns: ["/^\\.(gitea|forgejo)/workflows/[^/]+\\.ya?ml$/"],
|
|
},
|
|
|
|
packageRules: [
|
|
// Cooling-off period, scaled by blast radius: how long a release should
|
|
// have been out (and un-yanked, un-hotfixed) before it's considered
|
|
// settled. Advisory only — see `internalChecksFilter` above.
|
|
{
|
|
matchUpdateTypes: ["major"],
|
|
minimumReleaseAge: "30 days",
|
|
},
|
|
{
|
|
matchUpdateTypes: ["minor"],
|
|
minimumReleaseAge: "20 days",
|
|
},
|
|
{
|
|
matchUpdateTypes: ["patch", "digest", "pin", "rollback"],
|
|
minimumReleaseAge: "10 days",
|
|
},
|
|
// material3 is deliberately pinned to the 1.5 *alpha* line for the
|
|
// Expressive APIs (see gradle/libs.versions.toml). Follow the alpha train
|
|
// but keep it in its own PR, reviewed in isolation; revisit the pin when
|
|
// 1.5.0 stable lands.
|
|
{
|
|
matchPackageNames: ["androidx.compose.material3:material3"],
|
|
ignoreUnstable: false,
|
|
groupName: "material3 (alpha)",
|
|
},
|
|
// Test-only deps: group into one low-noise PR.
|
|
{
|
|
matchPackageNames: [
|
|
"org.junit.jupiter:**",
|
|
"org.junit.platform:**",
|
|
"com.google.truth:**",
|
|
"app.cash.turbine:**",
|
|
"androidx.test:**",
|
|
"androidx.test.espresso:**",
|
|
"androidx.test.ext:**",
|
|
],
|
|
groupName: "test dependencies",
|
|
},
|
|
// Last word on the PR table. The merge-confidence preset sets prBodyColumns
|
|
// from inside a packageRule of its own, and only for the datasources Mend
|
|
// supports — so a plain top-level prBodyColumns would lose to it for maven
|
|
// deps, and the Gradle wrapper / Actions / container bumps would keep the
|
|
// default columns and show no age at all. A rule declared after it wins,
|
|
// and gives every PR the same table.
|
|
// "Pending" earns its place under a flexible filter: when the bump lands on
|
|
// a version that has cleared its window but a newer one hasn't, that newer
|
|
// version is named here rather than silently withheld.
|
|
{
|
|
matchPackageNames: ["*"],
|
|
prBodyColumns: ["Package", "Type", "Change", "Age", "Pending", "Confidence"],
|
|
},
|
|
],
|
|
}
|