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
+40 -6
View File
@@ -245,7 +245,38 @@ jobs:
echo "Created new release for ${TAG}"
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:
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
REPO: ${{ gitea.repository }}
@@ -255,7 +286,8 @@ jobs:
: "${ARCHIVE:?ARCHIVE missing from packaging step}"
API="https://git.linuxhg.com/api/v1/repos/${REPO}/releases"
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')"
if [ -z "${RID}" ]; then
@@ -264,18 +296,20 @@ jobs:
fi
# 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}")"
AID="$(curl -sS -H "${AUTH}" "${API}/${RID}/assets" | jq -r --arg n "${ANAME}" '.[] | select(.name==$n) | .id // empty')"
if [ -n "${AID}" ]; then
curl -sS -X DELETE -H "${AUTH}" "${API}/${RID}/assets/${AID}" >/dev/null
echo "Deleted existing asset id=${AID} (${ARCHIVE})"
echo "Deleted existing asset id=${AID} (${ANAME})"
fi
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)"
rbody="$(printf '%s' "${resp}" | sed '$d')"
if [ "${code}" -ge 400 ]; then
echo "::error::Asset upload ${code}: ${rbody}" >&2
exit 1
fi
echo "Uploaded ${ARCHIVE} to release id=${RID}"
echo "Uploaded ${ANAME} to release id=${RID}"
done