Retry a failed upload instead of losing the release

Publishing 1.2.0 put the first package up and then failed the second with
"invalid username, password or token" — the same token, seconds later, and the
identical command succeeded on the next run. A flake at 118 MB should not cost a
rebuild, so each upload gets up to three attempts, and the attachment already on
the release is dropped before every attempt so a retry cannot leave two copies.

The shipped bundle was checked rather than assumed: unpacked from the release zip,
`codesign --verify --deep --strict` is valid on disk and satisfies its designated
requirement, and the app drives the real store — ten cards, the menu populated,
the categories counted. With the quarantine flag set it is killed on launch
(exit 137), which is the ad-hoc-signing limitation the README already documents.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-18 14:46:34 +02:00
co-authored by Claude Opus 5
parent cd5361e222
commit a25acf6e35
2 changed files with 39 additions and 12 deletions
+10
View File
@@ -174,6 +174,16 @@ Release notes come from `RELEASE_NOTES.md` when the file is present, otherwise t
release gets a one-line note. The repository is read from `origin`, so a fork
publishes to the fork.
Each upload is retried up to three times, and the existing attachment is dropped
before every attempt so a retry cannot leave two copies. A 100 MB upload does fail
on its own: publishing 1.2.0 got *"invalid username, password or token"* on the
second package while the first had just gone up with the same token, and the same
command succeeded immediately afterwards.
Package names contain a space — `WarpEngine Store-1.2.0-arm64.dmg` — so the list of
files is passed one path per line rather than as one string; splitting it on
whitespace is what broke the first attempt at publishing 1.1.0.
It needs `tea` installed and logged in — the devarea repo has `make tea` for that.
Overridable: `TAG`, `REPO`, `TEA_LOGIN`, `NOTES`, `DIST`.
+28 -11
View File
@@ -99,27 +99,44 @@ RELEASE_ID="$(release_id)"
[ -n "$RELEASE_ID" ] || die "the release $TAG could not be created or found"
# --- the attachments -------------------------------------------------------
while IFS= read -r asset; do
[ -n "$asset" ] || continue
[ -f "$asset" ] || die "no such file: $asset"
name="$(basename "$asset")"
# Replacing rather than refusing: a second run after a rebuild should land.
old="$(tea api "/repos/$REPO/releases/$RELEASE_ID/assets" | python3 -c "
# Anything already attached under this name, dropped: replacing rather than
# refusing is what makes a rebuild-and-upload repeatable. Called before every
# attempt, so a retry cannot leave two copies behind.
drop_existing() {
ids="$(tea api "/repos/$REPO/releases/$RELEASE_ID/assets" | python3 -c "
import json, sys
name = sys.argv[1]
for a in json.load(sys.stdin):
if a['name'] == name:
print(a['id'])
" "$name")"
for id in $old; do
say "replacing $name"
" "$1")"
for id in $ids; do
say "replacing $1"
tea api -X DELETE "/repos/$REPO/releases/$RELEASE_ID/assets/$id" >/dev/null
done
}
while IFS= read -r asset; do
[ -n "$asset" ] || continue
[ -f "$asset" ] || die "no such file: $asset"
name="$(basename "$asset")"
size="$(python3 -c "import os,sys; print(f'{os.path.getsize(sys.argv[1])/1e6:.0f} MB')" "$asset")"
# Retried, because a 100 MB upload does fail on its own: publishing 1.2.0 got
# "invalid username, password or token" on the second package while the first
# had just gone up with the same token, and the identical command succeeded on
# the next run. One flake should not cost a rebuild.
attempt=1
while :; do
drop_existing "$name"
say "uploading $name ($size) — large packages take a few minutes"
tea releases assets create --login "$LOGIN" --repo "$REPO" "$TAG" "$asset" >/dev/null
if tea releases assets create --login "$LOGIN" --repo "$REPO" "$TAG" "$asset" >/dev/null; then
break
fi
[ "$attempt" -lt 3 ] || die "$name could not be uploaded after $attempt attempts"
attempt=$((attempt + 1))
say "that failed — attempt $attempt of 3"
done
done < "$LIST"
say "done:"