Skip to content

refresh latest (workflow_dispatch) #56

refresh latest (workflow_dispatch)

refresh latest (workflow_dispatch) #56

name: Refresh latest release
run-name: refresh latest (${{ github.event.head_commit.message || inputs.reason || github.event_name }})
# Keeps a rolling `latest` GitHub Release whose zip asset always matches the
# shipped files on `main` (RBF + ARM binary + _handler.sh + README).
#
# Triggers:
# * push to main touching a shipped file -> catches manual RBF / handler /
# README rolls (a dev push, so it fires normally).
# * workflow_dispatch -> build.yml calls this via the PAT right after it
# commits a rebuilt ARM binary back to main. That commit-back push uses the
# default GITHUB_TOKEN, which by design does NOT re-trigger `on: push`, so
# the explicit dispatch is how an ARM-binary-only change (RBF unchanged)
# gets picked up. Mirrors build.yml's DB-rebuild dispatch pattern.
#
# The work is gated on a CONTENT SIGNATURE (sha256 over each shipped file's
# hash -- independent of zip compression / runner image), stored as a
# `content-sig:` line in the release body. A redundant double-fire is a cheap
# no-op. Hand-cut version tags (v1.1, ...) are never touched -- only the
# rolling `latest` pointer moves. update_all / db.json is unaffected (it tracks
# main); this only serves pinning tools + manual zip downloaders.
# Rule: [[release-tag-asset-currency]].
on:
push:
branches: [main]
paths:
- '_Other/*.rbf'
- 'games/**'
- 'docs/**'
workflow_dispatch:
inputs:
reason:
description: 'Why (shown as the run title in the Actions tab)'
required: false
type: string
permissions:
contents: write
concurrency:
group: refresh-latest-release
cancel-in-progress: false
jobs:
refresh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build release zip + content signature from current main
run: |
python3 - <<'PY'
import zipfile, os, glob, hashlib
BIN = "games/OpenBOR/OpenBOR_7533"
FILES = [
BIN,
"games/OpenBOR/_handler.sh",
"docs/OpenBOR/README.md",
]
rbfs = sorted(glob.glob("_Other/*.rbf"))
assert len(rbfs) == 1, "expected exactly one RBF, got %r" % rbfs
FILES += rbfs
EXE = {BIN, "games/OpenBOR/_handler.sh"}
entries = sorted((p.replace(os.sep, "/"), p) for p in FILES)
# Content signature: sha256 over "arcname:filehash" lines. Depends
# ONLY on shipped file contents -> stable across runners / zlib
# versions (unlike the compressed zip bytes).
sig_parts = []
out = "MiSTer-OpenBOR-release.zip"
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for arc, path in entries:
assert os.path.isfile(path), "missing shipped file: " + path
data = open(path, "rb").read()
sig_parts.append(arc + ":" + hashlib.sha256(data).hexdigest())
zi = zipfile.ZipInfo(arc, date_time=(1980, 1, 1, 0, 0, 0))
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = (0o755 if path in EXE else 0o644) << 16
z.writestr(zi, data)
sig = hashlib.sha256("\n".join(sig_parts).encode()).hexdigest()
open("content.sig", "w").write(sig)
print("built", out, os.path.getsize(out), "bytes")
print("content-sig", sig)
PY
- name: Refresh rolling 'latest' release when content changed
env:
GH_TOKEN: ${{ github.token }}
run: |
set -e
REPO="${{ github.repository }}"
ZIP="MiSTer-OpenBOR-release.zip"
NEW=$(cat content.sig)
CUR=$(gh release view latest -R "$REPO" --json body \
-q '.body' 2>/dev/null | sed -n 's/^content-sig: //p' | head -1)
echo "current-main content-sig = $NEW"
echo "published latest sig = ${CUR:-<none>}"
if [ "$NEW" = "$CUR" ]; then
echo "latest already matches current main -- no refresh needed."
exit 0
fi
git config user.email "ci@misterorganize"
git config user.name "MiSTer Organize CI"
# Move the rolling pointer tag to current main HEAD (never touches
# hand-cut version tags).
git tag -f latest
git push -f origin latest
NOTES=$(printf '%s\n' \
'Rolling snapshot - always matches the current main branch.' \
'' \
'Auto-refreshed by CI whenever the RBF, ARM binary, _handler.sh, or README changes on main. For a frozen build, pin a version tag (e.g. v1.1) instead of latest.' \
'' \
'MiSTer Frontier auto-installs and updates through the update_all database; this zip is only a manual-install snapshot.' \
'' \
"content-sig: $NEW")
if gh release view latest -R "$REPO" >/dev/null 2>&1; then
gh release upload latest "$ZIP" --clobber -R "$REPO"
gh release edit latest -R "$REPO" --target "$GITHUB_SHA" \
--title "latest (tracks main)" --notes "$NOTES"
else
gh release create latest "$ZIP" -R "$REPO" --target "$GITHUB_SHA" \
--title "latest (tracks main)" --notes "$NOTES"
fi
# Confirm the release now advertises the new signature.
POST=$(gh release view latest -R "$REPO" --json body -q '.body' \
| sed -n 's/^content-sig: //p' | head -1)
[ "$POST" = "$NEW" ] || { echo "ERROR: published sig != built sig"; exit 1; }
echo "latest refreshed OK (content-sig $NEW)"