CI: publish a fixed-URL latest AppImage for Shelly #7

Merged
jknapp merged 1 commits from ci/latest-appimage-url into main 2026-10-04 19:19:40 +00:00
2 changed files with 87 additions and 0 deletions
+75
View File
@@ -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"
+12
View File
@@ -129,6 +129,9 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
# Checkout first: it cleans the workspace, which would delete downloaded artifacts.
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
with:
path: artifacts
@@ -198,3 +201,12 @@ jobs:
exit 1
fi
echo "Attached $found artifacts to release $release_id"
# Constant-URL copy of the AppImage for updaters that poll a static URL
# (e.g. Shelly). Runs after the versioned release exists, so a failure here
# cannot leave a tag without its assets.
- name: Update rolling "latest" AppImage
env:
TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITEA_TOKEN }}
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
run: bash .gitea/scripts/update-latest.sh