Compare commits
6 Commits
main
...
a98b5a4e39
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a98b5a4e39 | ||
|
|
98ca454361 | ||
|
|
3bbc5afe4b | ||
|
|
f99f6d3e1a | ||
|
|
b959de2a14 | ||
|
|
f8ea8d8508 |
@@ -1,13 +1,11 @@
|
|||||||
name: Release — F-Droid repo + Gitea/Codeberg release
|
name: Release — F-Droid repo + Gitea release
|
||||||
|
|
||||||
# A release is cut by merging a release branch into main with a bumped
|
# A release is cut by merging a release branch into main with a bumped
|
||||||
# versionName (see docs/RELEASING.md). This workflow reads that versionName and,
|
# versionName (see docs/RELEASING.md). This workflow reads that versionName and,
|
||||||
# if no matching tag exists yet, runs tests, builds + signs the APK, publishes
|
# if no matching tag exists yet, runs tests, builds + signs the APK, publishes
|
||||||
# it to the F-Droid repo, creates the vX.Y.Z tag + Gitea release, and mirrors
|
# it to the F-Droid repo, and only then creates the vX.Y.Z tag + Gitea release
|
||||||
# that release to Codeberg with the signed APK + a SHA-256 checksum as a
|
# itself — the tag is an output of the pipeline, not its trigger. Ordinary
|
||||||
# direct-download channel — the tag is an output of the pipeline, not its
|
# merges (no version bump) fall through `detect` and do nothing.
|
||||||
# trigger. Ordinary merges (no version bump) fall through `detect` and do
|
|
||||||
# nothing.
|
|
||||||
#
|
#
|
||||||
# A manual workflow_dispatch (from a branch) runs the re-sign-only recovery
|
# A manual workflow_dispatch (from a branch) runs the re-sign-only recovery
|
||||||
# path: it re-signs the existing F-Droid index with the repo key and re-uploads,
|
# path: it re-signs the existing F-Droid index with the repo key and re-uploads,
|
||||||
@@ -352,88 +350,3 @@ jobs:
|
|||||||
curl -s -X POST -H "Authorization: token $TOKEN" \
|
curl -s -X POST -H "Authorization: token $TOKEN" \
|
||||||
-F "attachment=@/tmp/$ASSET" \
|
-F "attachment=@/tmp/$ASSET" \
|
||||||
"$API/releases/$ID/assets?name=$ASSET" -o /dev/null -w "asset upload HTTP %{http_code}\n"
|
"$API/releases/$ID/assets?name=$ASSET" -o /dev/null -w "asset upload HTTP %{http_code}\n"
|
||||||
|
|
||||||
# Mirror the release to the Codeberg mirror as a direct-download channel
|
|
||||||
# for users who don't want F-Droid. Gitea already push-mirrors branches +
|
|
||||||
# tags to Codeberg, but releases aren't git objects so they don't sync —
|
|
||||||
# we create the release there over the API and attach the signed APK plus
|
|
||||||
# a SHA-256 checksum. The APK is identical to the F-Droid one (same app
|
|
||||||
# key), so this adds no trust surface. Best-effort: a Codeberg outage
|
|
||||||
# (it 504s under load) must never fail an already-published F-Droid
|
|
||||||
# release. Needs the CODEBERG_RELEASE_TOKEN secret; skips cleanly if unset.
|
|
||||||
- name: Publish release to Codeberg
|
|
||||||
if: env.IS_RELEASE == 'true'
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
|
||||||
TOKEN: ${{ secrets.CODEBERG_RELEASE_TOKEN }}
|
|
||||||
API: https://codeberg.org/api/v1/repos/jlmakiola/calendula
|
|
||||||
SHA: ${{ github.sha }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
if [ -z "${TOKEN:-}" ]; then
|
|
||||||
echo "CODEBERG_RELEASE_TOKEN not set — skipping Codeberg publish."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
TAG="v$VERSION"
|
|
||||||
APK="app/build/outputs/apk/release/app-release.apk"
|
|
||||||
if [ ! -f "$APK" ]; then echo "No release APK found — skipping." >&2; exit 1; fi
|
|
||||||
ASSET_APK="calendula_v${VERSION}.apk"
|
|
||||||
ASSET_SUM="${ASSET_APK}.sha256"
|
|
||||||
cp "$APK" "/tmp/$ASSET_APK"
|
|
||||||
( cd /tmp && sha256sum "$ASSET_APK" > "$ASSET_SUM" )
|
|
||||||
|
|
||||||
# Release notes: reuse the section extracted for the Gitea release,
|
|
||||||
# fall back to the CHANGELOG entry if that step's file is gone.
|
|
||||||
if [ ! -s release-notes.md ]; then
|
|
||||||
awk -v ver="$VERSION" '
|
|
||||||
$0 ~ "^## \\[" ver "\\]" { flag = 1; next }
|
|
||||||
/^## \[/ { flag = 0 }
|
|
||||||
flag' CHANGELOG.md > release-notes.md
|
|
||||||
sed -i -e '/./,$!d' release-notes.md
|
|
||||||
fi
|
|
||||||
[ -s release-notes.md ] || echo "_See CHANGELOG.md for ${VERSION}._" > release-notes.md
|
|
||||||
# The pipeline creates the tag via the Gitea API, which the push mirror
|
|
||||||
# (sync_on_commit only fires on real git pushes) doesn't propagate
|
|
||||||
# promptly — so a release POST that carries a target_commitish can
|
|
||||||
# outrun the mirror and 500 on a commit/tag Codeberg hasn't received.
|
|
||||||
# Push the tag straight to Codeberg so it's guaranteed present, then
|
|
||||||
# attach the release to that existing tag with NO target_commitish
|
|
||||||
# (which is what triggered the 500).
|
|
||||||
git tag -f "$TAG" "$SHA"
|
|
||||||
git push -f "https://jlmakiola:${TOKEN}@codeberg.org/jlmakiola/calendula.git" \
|
|
||||||
"refs/tags/$TAG"
|
|
||||||
python3 - "$TAG" <<'PY' > cb-payload.json
|
|
||||||
import json, sys
|
|
||||||
print(json.dumps({
|
|
||||||
"tag_name": sys.argv[1],
|
|
||||||
"name": sys.argv[1],
|
|
||||||
"body": open("release-notes.md").read(),
|
|
||||||
"draft": False,
|
|
||||||
"prerelease": False,
|
|
||||||
}))
|
|
||||||
PY
|
|
||||||
# Upsert (re-run safe). POST also creates the tag at target_commitish
|
|
||||||
# if the push mirror hasn't synced it yet.
|
|
||||||
ID=$(curl -s -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG" | jq -r '.id // empty')
|
|
||||||
if [ -n "$ID" ]; then
|
|
||||||
curl -s -o /dev/null -w "release PATCH HTTP %{http_code}\n" -X PATCH \
|
|
||||||
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
||||||
-d @cb-payload.json "$API/releases/$ID"
|
|
||||||
else
|
|
||||||
curl -s -o cb-response.json -w "release POST HTTP %{http_code}\n" -X POST \
|
|
||||||
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
|
||||||
-d @cb-payload.json "$API/releases"
|
|
||||||
ID=$(jq -r '.id // empty' cb-response.json 2>/dev/null || true)
|
|
||||||
fi
|
|
||||||
if [ -z "$ID" ]; then echo "Could not resolve Codeberg release id." >&2; exit 1; fi
|
|
||||||
|
|
||||||
# Attach APK + checksum, replacing any prior asset of the same name.
|
|
||||||
for A in "$ASSET_APK" "$ASSET_SUM"; do
|
|
||||||
OLD=$(curl -s -H "Authorization: token $TOKEN" "$API/releases/$ID/assets" \
|
|
||||||
| jq -r --arg n "$A" '.[] | select(.name==$n) | .id')
|
|
||||||
[ -n "$OLD" ] && curl -s -X DELETE -H "Authorization: token $TOKEN" "$API/releases/$ID/assets/$OLD" >/dev/null || true
|
|
||||||
curl -s -X POST -H "Authorization: token $TOKEN" \
|
|
||||||
-F "attachment=@/tmp/$A" \
|
|
||||||
"$API/releases/$ID/assets?name=$A" -o /dev/null -w "asset $A HTTP %{http_code}\n"
|
|
||||||
done
|
|
||||||
echo "Published $TAG to Codeberg."
|
|
||||||
|
|||||||
@@ -475,21 +475,19 @@
|
|||||||
<string name="special_dates_default_title_anniversary">Jahrestag von {name} ({year})</string>
|
<string name="special_dates_default_title_anniversary">Jahrestag von {name} ({year})</string>
|
||||||
<string name="special_dates_default_title_custom">{name}</string>
|
<string name="special_dates_default_title_custom">{name}</string>
|
||||||
<string name="settings_week_numbers">Kalenderwochen</string>
|
<string name="settings_week_numbers">Kalenderwochen</string>
|
||||||
<string name="settings_week_numbers_summary">Kalenderwochen in Monatsansicht zeigen</string>
|
<string name="settings_week_numbers_summary">Kalenderwochen der Monatsansicht anzeigen</string>
|
||||||
<string name="calendars_export_title">Kalender exportieren</string>
|
<string name="calendars_export_title">Kalender exportieren</string>
|
||||||
<string name="calendars_export_hint">Wähle aus, welche Kalender in die .ics-Datei aufgenommen werden sollen.</string>
|
<string name="calendars_export_hint">Wähle aus, welche Kalender in die .ics-Datei aufgenommen werden sollen.</string>
|
||||||
<string name="calendars_export_action">Exportieren</string>
|
<string name="calendars_export_action">Exportieren</string>
|
||||||
<string name="calendars_restore_header">Wiederherstellen</string>
|
<string name="calendars_restore_header">Wiederherstellen</string>
|
||||||
<string name="calendars_restore_action">Aus .ics-Datei wiederherstellen</string>
|
<string name="calendars_restore_action">Aus .ics-Datei wiederherstellen</string>
|
||||||
<string name="calendars_restore_hint">Einträge von einem Backup oder einer anderen Kalender App importieren.</string>
|
<string name="calendars_restore_hint">Importiere Ereignisse aus einem Backup oder einer anderen Kalender-App.</string>
|
||||||
<string name="import_done_dedup_note">Bereits im Kalender vorhandene Ereignisse wurden übersprungen.</string>
|
<string name="import_done_dedup_note">Bereits im Kalender vorhandene Ereignisse wurden übersprungen.</string>
|
||||||
<string name="import_done_added_label">Hinzugefügt</string>
|
<string name="import_done_added_label">Hinzugefügt</string>
|
||||||
<string name="import_done_skipped_label">Dopplungen</string>
|
<string name="import_done_skipped_label">Duplikate</string>
|
||||||
<string name="import_button">Importieren</string>
|
<string name="import_button">Importieren</string>
|
||||||
<plurals name="import_title_count">
|
<plurals name="import_title_count">
|
||||||
<item quantity="one">%d Ereignis wird importiert</item>
|
<item quantity="one">%d Ereignis wird importiert</item>
|
||||||
<item quantity="other">%d Ereignisse werden importiert</item>
|
<item quantity="other">%d Ereignisse werden importiert</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="event_detail_duplicate">Duplikate</string>
|
|
||||||
<string name="reminder_day_tomorrow">Morgen</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -82,24 +82,9 @@ release work when a merge actually cuts a release:
|
|||||||
merged commit, build & sign the release APK with the **app key**, copy it into
|
merged commit, build & sign the release APK with the **app key**, copy it into
|
||||||
the F-Droid repo, generate the per-version changelog, re-sign the index with
|
the F-Droid repo, generate the per-version changelog, re-sign the index with
|
||||||
the **repo key**, upload `repo/` + `metadata/`, then create the `vX.Y.Z` tag +
|
the **repo key**, upload `repo/` + `metadata/`, then create the `vX.Y.Z` tag +
|
||||||
Gitea release (CHANGELOG section as notes), attach the R8 `mapping.txt`, and
|
Gitea release (CHANGELOG section as notes) and attach the R8 `mapping.txt`
|
||||||
mirror the release to **Codeberg** with the signed APK + a SHA-256 checksum
|
(best-effort). Ordinary merges with no version bump fall through `detect` and
|
||||||
(both best-effort). Ordinary merges with no version bump fall through `detect`
|
do nothing.
|
||||||
and do nothing.
|
|
||||||
|
|
||||||
### Codeberg direct-download channel
|
|
||||||
|
|
||||||
Alongside F-Droid, each release is mirrored to the Codeberg repo
|
|
||||||
(`jlmakiola/calendula`) as a plain download for users who don't want F-Droid.
|
|
||||||
Gitea already **push-mirrors** branches and tags to Codeberg, but releases
|
|
||||||
aren't git objects and don't sync, so the pipeline creates the release over the
|
|
||||||
Codeberg API and attaches `calendula_v<version>.apk` + its `.sha256`. It's the
|
|
||||||
same APK the F-Droid repo serves (same **app key**), so it adds no trust
|
|
||||||
surface. The step is best-effort: a Codeberg outage never fails an
|
|
||||||
already-published F-Droid release, and it skips cleanly if `CODEBERG_RELEASE_TOKEN` is
|
|
||||||
unset. One-time setup: the repo's **Releases** unit must be enabled and a
|
|
||||||
`CODEBERG_RELEASE_TOKEN` secret (Codeberg access token, `write:repository` scope) added
|
|
||||||
to Gitea Actions.
|
|
||||||
|
|
||||||
### Manual re-sign / recovery
|
### Manual re-sign / recovery
|
||||||
|
|
||||||
@@ -118,7 +103,6 @@ for key rotation or repo recovery without publishing a new app version.
|
|||||||
| `FDROID_CONFIG_BASE64` | F-Droid `config.yml` (base64) — repo metadata + keystore passwords. |
|
| `FDROID_CONFIG_BASE64` | F-Droid `config.yml` (base64) — repo metadata + keystore passwords. |
|
||||||
| `HETZNER_HOST`, `HETZNER_USER`, `HETZNER_PASS` | Upload target for the F-Droid repo. |
|
| `HETZNER_HOST`, `HETZNER_USER`, `HETZNER_PASS` | Upload target for the F-Droid repo. |
|
||||||
| `GITHUB_TOKEN` | Provided by Gitea Actions; used to create the release + attach assets. |
|
| `GITHUB_TOKEN` | Provided by Gitea Actions; used to create the release + attach assets. |
|
||||||
| `CODEBERG_RELEASE_TOKEN` | Codeberg access token (`write:repository` scope) — creates the mirrored Codeberg release + uploads the APK/checksum. Best-effort; if unset the Codeberg step skips. |
|
|
||||||
|
|
||||||
The two keys are independent: the **app key** signs APKs; the **repo key**
|
The two keys are independent: the **app key** signs APKs; the **repo key**
|
||||||
signs the index (its fingerprint is what users pin). Neither key nor the
|
signs the index (its fingerprint is what users pin). Neither key nor the
|
||||||
|
|||||||
Reference in New Issue
Block a user