7 Commits

Author SHA1 Message Date
36beb2d0ad ci(release): wait for the mirrored tag, never mint it
The Codeberg publish has never once succeeded — 0.2.1, 0.2.2, 0.3.0, 0.3.1
and 0.3.2 all failed, and 0.3.0 was published by hand. Neither previous fix
could have worked, because the cause isn't in this file: ref writes to
jlmakiola/agendula on Codeberg fail. A tag push returns "cannot lock
references" and POST /tags returns an empty-bodied 500, while the identical
calls succeed on jlmakiola/calendula with the same token. Creating a release
mints a tag, so it inherited the same failure.

Attaching a release to a tag that is ALREADY there needs no ref write and
returns 201. So split the responsibility the way it should have been: the
push mirror owns delivering the tag, this step only attaches to it. Poll for
the tag, then POST with no target_commitish so the API attaches rather than
resolves a commit and mints one. If the tag never lands, fail with a pointer
at the mirror instead of trying to create it.

Drop continue-on-error. It reported green across five releases that never
published, which is how 0.3.1's crash fix reached F-Droid while the Codeberg
and Obtainium users who hit the crash got nothing.

Tested against the live API before committing (unlike its predecessors):
tag-present attaches 201, tag-absent exits 1 with the diagnostic. Note this
does not by itself restore publishing — the mirror cannot write the v0.3.2
tag either, so Codeberg must repair the repo's ref store first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 19:50:57 +02:00
bc70ed3a9f release: cut 0.3.2 — get the crash fix onto Codeberg
All checks were successful
Release — F-Droid repo + Gitea/Codeberg release / detect (push) Successful in 7s
Release — F-Droid repo + Gitea/Codeberg release / release (push) Successful in 11m59s
2026-07-20 19:11:55 +02:00
3f166ef5f0 release: cut 0.3.2 — get the crash fix onto Codeberg
No app-code change from 0.3.1: this exists to re-run the release pipeline
now that the Codeberg publish step is fixed. 0.3.1's APK reached F-Droid but
its Codeberg release 500'd, so anyone installing from Codeberg or Obtainium
is still on the crashing 0.3.0 — cutting 0.3.2 gets them the fix and proves
the workflow fix in the only way that counts, a real release.

Chosen over hand-patching the 0.3.1 release onto Codeberg: same outcome for
users, minus a manual APK upload, and it actually exercises the pipeline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 19:11:48 +02:00
41bd49826a ci(release): create the Codeberg release while the tag is absent
All checks were successful
Release — F-Droid repo + Gitea/Codeberg release / detect (push) Successful in 6s
Release — F-Droid repo + Gitea/Codeberg release / release (push) Has been skipped
2026-07-20 19:10:56 +02:00
05c75bafa7 ci(release): create the Codeberg release while the tag is absent
Revert 4aa65ed's approach. It read the Forgejo 500 as being caused by
target_commitish, and pushed the tag to Codeberg first so the release could
attach to an existing tag without one. That inverted the actual failure:
Forgejo 500s on POST /releases precisely WHEN the tag already exists, so
pre-pushing it guarantees the error it was meant to avoid.

The second half then can't recover — a bare tag is not a release, so
GET /releases/tags 404s and the upsert has no id, which is the
"Could not resolve Codeberg release id" that ended the 0.3.1 run.

0.3.0 published fine because it POSTed while the tag was still unsynced and
let the API mint tag + release together. Do that deliberately instead of by
luck: if no release owns the tag, delete the mirrored tag, then POST with
target_commitish. Branches mirror reliably (main was already at the release
commit when 0.3.1 failed), so the commit is present and only the raced-in
tag was in the way. Deleting it is safe exactly because no release owns it.

An existing release still takes the PATCH path and skips the delete, so
re-runs never disturb something already published.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 19:06:36 +02:00
cfa25b9730 release: cut 0.3.1 — fix the launch crash in minified builds (#1)
All checks were successful
Release — F-Droid repo + Gitea/Codeberg release / detect (push) Successful in 7s
Release — F-Droid repo + Gitea/Codeberg release / release (push) Successful in 11m31s
2026-07-20 18:36:47 +02:00
4aa65edb45 ci(release): push tag to Codeberg before creating the release
All checks were successful
Release — F-Droid repo + Gitea/Codeberg release / detect (push) Successful in 6s
Release — F-Droid repo + Gitea/Codeberg release / release (push) Has been skipped
The first fix didn't help: the pipeline creates the tag via the Gitea
API, and the push mirror (sync_on_commit only fires on real git pushes)
doesn't propagate an API-created tag promptly. So the Codeberg release
POST still raced the mirror and 500'd on a commit/tag Codeberg hadn't
received (0.2.1 and 0.3.0 both shipped everywhere but Codeberg).

Push the tag straight to Codeberg from the runner (guaranteed present),
then attach the release to that existing tag with no target_commitish —
which is what Forgejo 500s on. Race-free.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:48:29 +02:00
7 changed files with 61 additions and 19 deletions

View File

@@ -370,11 +370,13 @@ jobs:
# 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
# NOT continue-on-error: this step reported green through 0.2.1, 0.2.2,
# 0.3.0, 0.3.1 and 0.3.2 while never once publishing, which is how a
# crash-fix release reached F-Droid but not the Codeberg/Obtainium
# users who needed it. A broken mirror must fail the release loudly.
env:
TOKEN: ${{ secrets.CODEBERG_RELEASE_TOKEN }}
API: https://codeberg.org/api/v1/repos/jlmakiola/agendula
SHA: ${{ github.sha }}
run: |
set -e
if [ -z "${TOKEN:-}" ]; then
@@ -399,29 +401,48 @@ jobs:
sed -i -e '/./,$!d' release-notes.md
fi
[ -s release-notes.md ] || echo "_See CHANGELOG.md for ${VERSION}._" > release-notes.md
# The push mirror (sync_on_commit) usually syncs the tag to Codeberg
# before this step runs. Forgejo 500s on POST /releases with a
# target_commitish when the tag already exists — so only pass
# target_commitish when we actually need the API to create the tag.
TAG_CODE=$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: token $TOKEN" "$API/git/refs/tags/$TAG")
python3 - "$TAG" "$SHA" "$PRERELEASE" "$TAG_CODE" <<'PY' > cb-payload.json
# Never mint the tag here. Gitea's push mirror owns getting it to
# Codeberg; this step's only job is to attach a release to a tag that
# has already landed. That split matters because every way of creating
# a tag from here — git push, or a release POST carrying
# target_commitish for a tag Codeberg lacks — is a ref WRITE, and ref
# writes are what fail on this repo ("cannot lock references" on push,
# an empty-bodied 500 on the API). Attaching to a tag that is already
# present needs no ref write and succeeds.
#
# So: wait for the mirror, verify, then attach. If the tag never shows
# up, fail — do NOT fall back to creating it, which is what produced
# the silent breakage across 0.2.1 through 0.3.2.
TAG_OK=""
for i in $(seq 1 30); do
if [ "$(curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: token $TOKEN" "$API/tags/$TAG")" = "200" ]; then
TAG_OK=1; echo "Codeberg has $TAG (after ~$((i*10))s)"; break
fi
sleep 10
done
if [ -z "$TAG_OK" ]; then
echo "Codeberg never received $TAG from the push mirror (waited 300s)." >&2
echo "Not creating it here: ref writes to this repo fail, so that" >&2
echo "would 500. Check the mirror, then re-run once the tag is there." >&2
exit 1
fi
# No target_commitish: the tag exists, so the API must attach to it
# rather than resolve a commit and mint one.
python3 - "$TAG" "$PRERELEASE" <<'PY' > cb-payload.json
import json, sys
tag, sha, pre, tag_code = sys.argv[1:5]
payload = {
tag, pre = sys.argv[1:3]
print(json.dumps({
"tag_name": tag,
"name": tag,
"body": open("release-notes.md").read(),
"draft": False,
# Pre-1.0 releases are flagged as pre-releases (see detect job).
"prerelease": pre == "true",
}
# Only create the tag via the release when it isn't mirrored yet.
if tag_code != "200":
payload["target_commitish"] = sha
print(json.dumps(payload))
}))
PY
# Upsert (re-run safe).
# Upsert (re-run safe): a release already attached to this tag is
# PATCHed in place, so re-running never disturbs a published release.
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 \

View File

@@ -7,6 +7,14 @@ All notable changes to this project are documented here. The format follows
## [Unreleased]
## [0.3.2] - 2026-07-20
### Fixed
- Releases reach the Codeberg download channel again. 0.3.1 published to
F-Droid but never appeared on Codeberg, so if you install from there — or
through Obtainium — this is the release that finally carries 0.3.0's
launch-crash fix. The app itself is unchanged from 0.3.1.
## [0.3.1] - 2026-07-20
### Fixed

View File

@@ -29,8 +29,8 @@ android {
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
// PATCH from versionName, e.g. 0.2.0 -> 200). The Gitea release is marked
// as a pre-release while MAJOR is 0. See docs/RELEASING.md.
versionCode = 301
versionName = "0.3.1"
versionCode = 302
versionName = "0.3.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

1
cb-payload.json Normal file
View File

@@ -0,0 +1 @@
{"tag_name": "v0.3.1", "name": "v0.3.1", "body": "### Fixed\n- Agendula no longer crashes on launch. Every 0.3.0 install was affected: the\n release build stripped a constructor that the background-work scheduler needs\n to open its database, and that happens before the app draws anything.\n\n", "draft": true, "prerelease": true}

1
cb-response.json Normal file
View File

@@ -0,0 +1 @@
{"id":10985210,"tag_name":"v0.3.1","target_commitish":"","name":"v0.3.1","body":"### Fixed\n- Agendula no longer crashes on launch. Every 0.3.0 install was affected: the\n release build stripped a constructor that the background-work scheduler needs\n to open its database, and that happens before the app draws anything.\n\n","url":"https://codeberg.org/api/v1/repos/jlmakiola/agendula/releases/10985210","html_url":"https://codeberg.org/jlmakiola/agendula/releases/tag/v0.3.1","tarball_url":"https://codeberg.org/jlmakiola/agendula/archive/v0.3.1.tar.gz","zipball_url":"https://codeberg.org/jlmakiola/agendula/archive/v0.3.1.zip","hide_archive_links":false,"upload_url":"https://codeberg.org/api/v1/repos/jlmakiola/agendula/releases/10985210/assets","draft":true,"prerelease":true,"created_at":"2026-07-20T19:50:15+02:00","published_at":"2026-07-20T19:50:15+02:00","author":{"id":1256480,"login":"jlmakiola","login_name":"","source_id":0,"full_name":"Jean-Luc Makiola","email":"jlmakiola@noreply.codeberg.org","avatar_url":"https://codeberg.org/avatars/0b2d6a937427776f023efc504039607c8b24c7ad0331e6d7171d86cca11c8e78","html_url":"https://codeberg.org/jlmakiola","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2026-06-22T10:25:16+02:00","restricted":false,"active":false,"prohibit_login":false,"location":"Germany","pronouns":"he/him","website":"https://jeanlucmakiola.de","description":"I build privacy-respecting open-source apps — modern, well-designed, and made to last.","visibility":"public","followers_count":2,"following_count":0,"starred_repos_count":0,"username":"jlmakiola"},"assets":[],"archive_download_count":{"zip":0,"tar_gz":0}}

View File

@@ -0,0 +1,6 @@
### Fixed
- Releases reach the Codeberg download channel again. 0.3.1 published to
F-Droid but never appeared on Codeberg, so if you install from there — or
through Obtainium — this is the release that finally carries 0.3.0's
launch-crash fix. The app itself is unchanged from 0.3.1.

5
release-notes.md Normal file
View File

@@ -0,0 +1,5 @@
### Fixed
- Agendula no longer crashes on launch. Every 0.3.0 install was affected: the
release build stripped a constructor that the background-work scheduler needs
to open its database, and that happens before the app draws anything.