ci(release): publish signed updater assets alongside tarball

After packaging, stage the bare AniTrack-linux-amd64 binary (the updater swaps os.Executable in place; the user tarball single top-level directory would fail the single-entry rule), sign it with wails3 updater sign using UPDATER_SIGNING_KEY (missing secret is a hard error, key shredded after use), split the output into .sha512/.sig sidecars, and extend the idempotent replace-upload loop from 1 asset to all 4. Tarball flow untouched; rc tags get signed assets too, which the beta-channel live proof needs.
This commit is contained in:
John O'Keefe
2026-09-16 08:52:09 -04:00
parent 52fc656669
commit 0149b24a33
+56 -22
View File
@@ -245,7 +245,38 @@ jobs:
echo "Created new release for ${TAG}" echo "Created new release for ${TAG}"
fi fi
- name: Upload release archive - name: Build updater asset and sign
env:
UPDATER_SIGNING_KEY: ${{ secrets.UPDATER_SIGNING_KEY }}
run: |
set -euo pipefail
: "${VERSION:?VERSION missing from version-guard step}"
export PATH="${HOME}/go/bin:${PATH}"
: "${UPDATER_SIGNING_KEY:?Missing secret UPDATER_SIGNING_KEY. Add the updater private key under Settings → Secrets → Actions.}"
# Bare updater binary: the updater swaps os.Executable() in place,
# so it takes the single binary, not the user tarball (whose
# top-level directory would fail the single-entry rule).
mkdir -p updater-dist
cp build/bin/AniTrack updater-dist/AniTrack-linux-amd64
printf '%s' "${UPDATER_SIGNING_KEY}" > updater-dist/updater.key
chmod 600 updater-dist/updater.key
wails3 updater sign -key updater-dist/updater.key updater-dist/AniTrack-linux-amd64 > updater-dist/sign.json
# Split the sign output into the sidecar assets the Gitea provider
# fetches: <name>.sha512 (sha512sum format) + <name>.sig (base64).
python3 - <<'EOF'
import base64, binascii, json
entries = json.load(open('updater-dist/sign.json'))
entry = next(e for e in entries if e['filename'].endswith('AniTrack-linux-amd64'))
assert entry['digestAlgo'] == 'sha512', entry
assert entry['signatureAlgo'] == 'ed25519ph', entry
digest_hex = binascii.hexlify(base64.b64decode(entry['digest'])).decode()
open('updater-dist/AniTrack-linux-amd64.sha512', 'w').write(f"{digest_hex} AniTrack-linux-amd64\n")
open('updater-dist/AniTrack-linux-amd64.sig', 'w').write(entry['signature'].strip() + '\n')
EOF
shred -u updater-dist/updater.key
ls -la updater-dist/
- name: Upload release assets
env: env:
TOKEN: ${{ secrets.REGISTRY_TOKEN }} TOKEN: ${{ secrets.REGISTRY_TOKEN }}
REPO: ${{ gitea.repository }} REPO: ${{ gitea.repository }}
@@ -255,27 +286,30 @@ jobs:
: "${ARCHIVE:?ARCHIVE missing from packaging step}" : "${ARCHIVE:?ARCHIVE missing from packaging step}"
API="https://git.linuxhg.com/api/v1/repos/${REPO}/releases" API="https://git.linuxhg.com/api/v1/repos/${REPO}/releases"
AUTH="Authorization: token ${TOKEN}" AUTH="Authorization: token ${TOKEN}"
test -f "${ARCHIVE}" || { echo "::error::${ARCHIVE} not found" >&2; exit 1; } for ASSET in "${ARCHIVE}" updater-dist/AniTrack-linux-amd64 updater-dist/AniTrack-linux-amd64.sha512 updater-dist/AniTrack-linux-amd64.sig; do
test -f "${ASSET}" || { echo "::error::${ASSET} not found" >&2; exit 1; }
RID="$(curl -sS -H "${AUTH}" "${API}/tags/${TAG}" | jq -r '.id // empty')" RID="$(curl -sS -H "${AUTH}" "${API}/tags/${TAG}" | jq -r '.id // empty')"
if [ -z "${RID}" ]; then if [ -z "${RID}" ]; then
echo "::error::No release found for tag ${TAG} after create step" >&2 echo "::error::No release found for tag ${TAG} after create step" >&2
exit 1 exit 1
fi fi
# Replace a same-named asset so re-runs stay idempotent. # Replace a same-named asset so re-runs stay idempotent.
AID="$(curl -sS -H "${AUTH}" "${API}/${RID}/assets" | jq -r --arg n "${ARCHIVE}" '.[] | select(.name==$n) | .id // empty')" ANAME="$(basename "${ASSET}")"
if [ -n "${AID}" ]; then AID="$(curl -sS -H "${AUTH}" "${API}/${RID}/assets" | jq -r --arg n "${ANAME}" '.[] | select(.name==$n) | .id // empty')"
curl -sS -X DELETE -H "${AUTH}" "${API}/${RID}/assets/${AID}" >/dev/null if [ -n "${AID}" ]; then
echo "Deleted existing asset id=${AID} (${ARCHIVE})" curl -sS -X DELETE -H "${AUTH}" "${API}/${RID}/assets/${AID}" >/dev/null
fi echo "Deleted existing asset id=${AID} (${ANAME})"
fi
resp="$(curl -sS -w '\n%{http_code}' -X POST -H "${AUTH}" \ resp="$(curl -sS -w '\n%{http_code}' -X POST -H "${AUTH}" \
-F "attachment=@${ARCHIVE}" "${API}/${RID}/assets?name=${ARCHIVE}")" -F "attachment=@${ASSET}" "${API}/${RID}/assets?name=${ANAME}")"
code="$(printf '%s' "${resp}" | tail -n1)" code="$(printf '%s' "${resp}" | tail -n1)"
rbody="$(printf '%s' "${resp}" | sed '$d')" rbody="$(printf '%s' "${resp}" | sed '$d')"
if [ "${code}" -ge 400 ]; then if [ "${code}" -ge 400 ]; then
echo "::error::Asset upload ${code}: ${rbody}" >&2 echo "::error::Asset upload ${code}: ${rbody}" >&2
exit 1 exit 1
fi fi
echo "Uploaded ${ARCHIVE} to release id=${RID}" echo "Uploaded ${ANAME} to release id=${RID}"
done