ci(release): split canary/stable tracks + unified Gitea Releases
ci / rust (push) Failing after 37s
apple / swift (push) Successful in 56s
ci / web (push) Successful in 42s
ci / docs-site (push) Failing after 27m33s
android / android (push) Failing after 28m53s
windows-host / package (push) Failing after 28m55s
deb / build-publish (push) Successful in 2m28s
decky / build-publish (push) Successful in 23s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
ci / bench (push) Successful in 4m34s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 46s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m20s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Failing after 4m4s
flatpak / build-publish (push) Successful in 4m19s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 7m38s
release / apple (push) Successful in 4m36s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m48s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m25s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 50s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m6s
ci / rust (push) Failing after 37s
apple / swift (push) Successful in 56s
ci / web (push) Successful in 42s
ci / docs-site (push) Failing after 27m33s
android / android (push) Failing after 28m53s
windows-host / package (push) Failing after 28m55s
deb / build-publish (push) Successful in 2m28s
decky / build-publish (push) Successful in 23s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
ci / bench (push) Successful in 4m34s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 46s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m20s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Failing after 4m4s
flatpak / build-publish (push) Successful in 4m19s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 7m38s
release / apple (push) Successful in 4m36s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m48s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m25s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 50s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m6s
A push to main publishes canary builds to canary channels (fast iteration,
unchanged); a single vX.Y.Z tag releases every platform at one version to the
stable channels and attaches all artifacts (.deb/.rpm/.msix/.apk/.aab/.dmg +
flatpak/decky/host-installer) to one Gitea Release. Collapses the
host-v*/win-v*/host-win-v* tag namespaces into v* — the channel split makes the
version-shadow bug structurally impossible (canary and stable are separate repos,
never a shared version line).
- scripts/ci/gitea-release.{sh,ps1}: one idempotent release helper
(create-or-fetch + delete-before-upload), replacing 3 copy-pasted inline blocks
and fixing their latent 409-on-reupload bug; prerelease flag auto-derived from
the tag (an -rc tag won't shadow "Latest")
- channels: apt canary/stable distributions; rpm *-canary/base groups; flatpak
canary/stable OSTree branches + a 2nd .Canary.flatpakref; generic-registry
canary/ vs latest/ aliases; Play internal/alpha; Apple TestFlight vs notarized DMG
- android versionName threaded through gradle (versionCode stays run_number);
Apple canary = TestFlight-only (no DMG/tvOS); canary base bumped to 0.3.0
- docs: new docs-site channels.md (subscribe table + cut-a-release runbook +
box migration), refreshed ci.md workflow table + packaging READMEs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# shellcheck shell=bash
|
||||
# Shared Gitea Release helpers for the punktfunk CI workflows (Linux + macOS runners).
|
||||
#
|
||||
# Source this file, then call ensure_release / upsert_asset. It replaces the three
|
||||
# copy-pasted inline blocks that used to live in release.yml / flatpak.yml / decky.yml,
|
||||
# and fixes a latent bug those had: the bare asset POST returns 409 if an asset with the
|
||||
# same name already exists, so re-running a workflow — or reusing the rolling `canary`
|
||||
# release with stable filenames — would fail. upsert_asset deletes the old asset first.
|
||||
#
|
||||
# Callers run under Gitea Actions' default `bash -eo pipefail`, so a non-zero return from
|
||||
# these functions aborts the step (the desired behaviour on a real failure).
|
||||
#
|
||||
# Env (Gitea Actions sets the first two automatically in every step):
|
||||
# GITHUB_SERVER_URL e.g. https://git.unom.io
|
||||
# GITHUB_REPOSITORY e.g. unom/punktfunk
|
||||
# GITEA_TOKEN a PAT with repository (release) write scope — set from secrets.REGISTRY_TOKEN
|
||||
# (the same PAT the package uploads use; it must carry `write:repository`,
|
||||
# not only `write:package`, or the release-asset POST 403s)
|
||||
#
|
||||
# Requires: curl + python3 (python3 is already a proven dependency on every runner that
|
||||
# attaches releases today — macOS, the fedora flatpak container, the node:bookworm decky
|
||||
# image; the .deb runner installs it alongside its other apt deps).
|
||||
|
||||
_gitea_api() { printf '%s/api/v1/repos/%s' "${GITHUB_SERVER_URL:?}" "${GITHUB_REPOSITORY:?}"; }
|
||||
|
||||
# Tiny JSON / URL helpers. python3 reads the TOP-LEVEL "id" only, so there is no ambiguity
|
||||
# with the nested author.id / assets[].id fields a string-grep would trip over.
|
||||
_json_id() { python3 -c 'import json,sys;print(json.load(sys.stdin).get("id",""))' 2>/dev/null; }
|
||||
_json_asset_id() {
|
||||
python3 -c 'import json,sys
|
||||
want=sys.argv[1]
|
||||
for a in json.load(sys.stdin):
|
||||
if a.get("name")==want:
|
||||
print(a.get("id",""));break' "$1" 2>/dev/null
|
||||
}
|
||||
_urlencode() { python3 -c 'import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=""))' "$1"; }
|
||||
|
||||
# ensure_release TAG NAME PRERELEASE [TARGET_COMMITISH]
|
||||
# Idempotently create (or fetch) the release for TAG; prints its numeric id on stdout.
|
||||
# PRERELEASE is "true", "false", or "auto" — auto marks it a prerelease iff TAG carries a
|
||||
# `-` pre-release suffix (e.g. v0.2.0-rc1), so an rc never becomes the repo's "Latest" release
|
||||
# (Gitea's /releases/latest surfaces the newest non-prerelease). TARGET_COMMITISH (optional)
|
||||
# creates the git tag if it does not exist yet — for a `vX.Y.Z` release the tag already exists
|
||||
# (it is the trigger), so TARGET is omitted and create-vs-fetch hinges on the release object.
|
||||
ensure_release() {
|
||||
local tag="${1:?tag}" name="${2:?name}" prerelease="${3:?prerelease}" target="${4:-}"
|
||||
local api body id
|
||||
if [ "$prerelease" = auto ]; then
|
||||
case "$tag" in *-*) prerelease=true ;; *) prerelease=false ;; esac
|
||||
fi
|
||||
api="$(_gitea_api)"
|
||||
if [ -n "$target" ]; then
|
||||
body=$(printf '{"tag_name":"%s","name":"%s","prerelease":%s,"target_commitish":"%s"}' \
|
||||
"$tag" "$name" "$prerelease" "$target")
|
||||
else
|
||||
body=$(printf '{"tag_name":"%s","name":"%s","prerelease":%s}' "$tag" "$name" "$prerelease")
|
||||
fi
|
||||
# Try to create. On any failure (almost always "release already exists"), fall back to
|
||||
# fetching it by tag. Either path MUST yield an id, or we error loudly — so a 401/scope
|
||||
# problem can't masquerade as a successful no-op.
|
||||
id=$(curl -fsS -X POST "$api/releases" \
|
||||
-H "Authorization: token ${GITEA_TOKEN:?}" -H 'Content-Type: application/json' \
|
||||
-d "$body" 2>/dev/null | _json_id || true)
|
||||
if [ -z "$id" ]; then
|
||||
id=$(curl -fsS "$api/releases/tags/$tag" \
|
||||
-H "Authorization: token ${GITEA_TOKEN:?}" 2>/dev/null | _json_id || true)
|
||||
fi
|
||||
if [ -z "$id" ]; then
|
||||
echo "gitea-release: could not create or find a release for tag '$tag'" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s' "$id"
|
||||
}
|
||||
|
||||
# upsert_asset RELEASE_ID FILE [NAME]
|
||||
# Attach FILE to the release, replacing any existing asset of the same name first so that
|
||||
# re-runs and rolling canary re-uploads are idempotent (a plain POST 409s on a dup name).
|
||||
upsert_asset() {
|
||||
local rid="${1:?release id}" file="${2:?file}" name="${3:-}"
|
||||
local api existing
|
||||
[ -n "$name" ] || name="$(basename "$file")"
|
||||
[ -f "$file" ] || { echo "gitea-release: asset file not found: $file" >&2; return 1; }
|
||||
api="$(_gitea_api)"
|
||||
existing=$(curl -fsS "$api/releases/$rid/assets" \
|
||||
-H "Authorization: token ${GITEA_TOKEN:?}" 2>/dev/null \
|
||||
| _json_asset_id "$name" || true)
|
||||
if [ -n "$existing" ]; then
|
||||
curl -fsS -o /dev/null -X DELETE "$api/releases/$rid/assets/$existing" \
|
||||
-H "Authorization: token ${GITEA_TOKEN:?}" || true
|
||||
fi
|
||||
curl -fsS -o /dev/null -X POST "$api/releases/$rid/assets?name=$(_urlencode "$name")" \
|
||||
-H "Authorization: token ${GITEA_TOKEN:?}" \
|
||||
-F "attachment=@$file"
|
||||
echo "gitea-release: uploaded '$name' -> release $rid"
|
||||
}
|
||||
Reference in New Issue
Block a user