Publish a fixed-URL "latest" AppImage for static-URL updaters
Gitea has no /releases/latest/download/ redirect and release URLs always include the tag, so updaters that poll a constant URL (Shelly) cannot use them. After publishing a vX.Y.Z release, replace the asset on a rolling pre-release tagged "latest": .../releases/download/latest/WhatsThatMusic-latest-linux-x86_64.AppImage Replacing the asset changes its ETag and Last-Modified, which Shelly compares. The pre-release flag keeps Gitea's own "latest release" on the newest vX.Y.Z. The logic lives in .gitea/scripts/update-latest.sh so it can be run against the API outside CI. The publish job now checks out first, since checkout cleans the workspace and would delete downloaded artifacts. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
# Maintains a rolling "latest" release whose AppImage always has the same
|
||||
# download URL, for updaters that poll a static URL (e.g. Shelly) instead of
|
||||
# understanding version tags:
|
||||
#
|
||||
# <server>/<owner>/<repo>/releases/download/latest/WhatsThatMusic-latest-linux-x86_64.AppImage
|
||||
#
|
||||
# Gitea serves ETag and Last-Modified on release assets, and replacing the
|
||||
# asset changes both, which is how those updaters detect a new build.
|
||||
# Gitea has no GitHub-style /releases/latest/download/ redirect, hence this.
|
||||
#
|
||||
# Marked as a pre-release so Gitea's own "latest release" stays the newest vX.Y.Z.
|
||||
#
|
||||
# Required env: TOKEN, API (…/api/v1/repos/<owner>/<repo>), GITHUB_REF_NAME, GITHUB_SHA
|
||||
# Optional env: ARTIFACTS_DIR (default: artifacts)
|
||||
set -euo pipefail
|
||||
|
||||
: "${TOKEN:?TOKEN is required}" "${API:?API is required}"
|
||||
: "${GITHUB_REF_NAME:?GITHUB_REF_NAME is required}" "${GITHUB_SHA:?GITHUB_SHA is required}"
|
||||
ARTIFACTS_DIR="${ARTIFACTS_DIR:-artifacts}"
|
||||
LATEST_TAG="latest"
|
||||
LATEST_NAME="WhatsThatMusic-latest-linux-x86_64.AppImage"
|
||||
|
||||
api() {
|
||||
local method="$1" path="$2"; shift 2
|
||||
local out code body
|
||||
out=$(curl -s -w '\n%{http_code}' -X "$method" -H "Authorization: token $TOKEN" "$API$path" "$@")
|
||||
code=$(printf '%s' "$out" | tail -n1)
|
||||
body=$(printf '%s' "$out" | sed '$d')
|
||||
if [ "$code" -ge 400 ]; then
|
||||
echo "$method $path -> HTTP $code" >&2
|
||||
echo "$body" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s' "$body"
|
||||
}
|
||||
|
||||
appimage=$(find "$ARTIFACTS_DIR" -type f -name '*.AppImage' | sort | head -n1)
|
||||
if [ -z "$appimage" ]; then
|
||||
echo "No AppImage under $ARTIFACTS_DIR; cannot update the $LATEST_TAG release." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
body_json=$(node -e '
|
||||
console.log(JSON.stringify({
|
||||
body: "Rolling pointer to the newest AppImage, currently " + process.argv[1] + ". " +
|
||||
"The download URL for this asset never changes; versioned downloads are on each vX.Y.Z release.",
|
||||
}));' "$GITHUB_REF_NAME")
|
||||
|
||||
rolling_id=$(api GET "/releases/tags/$LATEST_TAG" 2>/dev/null \
|
||||
| node -pe "JSON.parse(require('fs').readFileSync(0,'utf8')).id" 2>/dev/null || true)
|
||||
|
||||
if [ -z "${rolling_id:-}" ]; then
|
||||
echo "No '$LATEST_TAG' release yet; creating it."
|
||||
create_json=$(node -e '
|
||||
console.log(JSON.stringify({
|
||||
tag_name: "latest", name: "Latest (rolling)", target_commitish: process.argv[1],
|
||||
prerelease: true, body: JSON.parse(process.argv[2]).body,
|
||||
}));' "$GITHUB_SHA" "$body_json")
|
||||
rolling_id=$(api POST "/releases" -H "Content-Type: application/json" -d "$create_json" \
|
||||
| node -pe "JSON.parse(require('fs').readFileSync(0,'utf8')).id")
|
||||
else
|
||||
api PATCH "/releases/$rolling_id" -H "Content-Type: application/json" -d "$body_json" >/dev/null
|
||||
fi
|
||||
|
||||
# Replace the asset: the new upload gets a new ETag and Last-Modified.
|
||||
old_id=$(api GET "/releases/$rolling_id/assets" \
|
||||
| node -pe "((JSON.parse(require('fs').readFileSync(0,'utf8'))).find(a => a.name === process.argv[1]) || {}).id || ''" "$LATEST_NAME")
|
||||
if [ -n "$old_id" ]; then
|
||||
api DELETE "/releases/$rolling_id/assets/$old_id" >/dev/null
|
||||
fi
|
||||
|
||||
echo "Uploading $LATEST_NAME from $(basename "$appimage") ($(du -h "$appimage" | cut -f1))"
|
||||
api POST "/releases/$rolling_id/assets?name=$LATEST_NAME" -F "attachment=@$appimage" >/dev/null
|
||||
echo "Updated '$LATEST_TAG' release ($rolling_id) to $GITHUB_REF_NAME"
|
||||
Reference in New Issue
Block a user