fix(release): harden publish curl against Cloudflare HTTP/2 stream errors
All checks were successful
ci / test (push) Successful in 6m1s

The dispatched v0.1.1 build succeeded a third time (2m06s, valid ARM ELF)
and then died at the first API call inside the publish step with curl exit
92 (HTTP/2 stream error) — job containers reach Forgejo through Cloudflare,
where that is intermittent.

- all API calls go through api_curl: --http1.1 --retry 5
  --retry-all-errors --retry-delay 3 --connect-timeout 20 --max-time 300
- HTTP/1.1 is the documented workaround for this instance's HTTP/2 resets

Job containers cannot reach http://forgejo:3000 (isolated GITEA-ACTIONS
network, name unresolvable, container IP unroutable — measured), so the
public hostname plus retries is the available fix.

Verified locally: create + idempotent re-run against a throwaway tag, both
clean; test release and tag deleted.
This commit is contained in:
Sirius DevOps 2026-09-10 14:08:39 -04:00
parent 9441bccf58
commit cbbf3f6baa
No known key found for this signature in database

View file

@ -12,6 +12,15 @@
# same-named assets instead of failing with 409.
set -euo pipefail
# Every API call goes through here. Publishing runs from CI containers that
# reach Forgejo through Cloudflare, where a bare HTTP/2 request intermittently
# dies with curl exit 92 (stream error) — retries plus forcing HTTP/1.1 make
# that a non-event.
api_curl() {
curl -sS --http1.1 --retry 5 --retry-all-errors --retry-delay 3 \
--connect-timeout 20 --max-time 300 "$@"
}
tag="${1:?usage: publish-release.sh <tag> <name> <body-file> <asset>...}"
name="${2:?missing release name}"
body_file="${3:?missing body file}"
@ -41,18 +50,18 @@ if sys.argv[3]:
print(json.dumps(payload))
PY
code=$(curl -s -o /tmp/release-rel.json -w '%{http_code}' \
code=$(api_curl -o /tmp/release-rel.json -w '%{http_code}' \
-H "Authorization: Bearer $FORGEJO_TOKEN" "$api/releases/tags/$tag")
if [ "$code" = "404" ]; then
echo "publish: creating release $tag"
curl -sf -X POST "$api/releases" \
api_curl -sf -X POST "$api/releases" \
-H "Authorization: Bearer $FORGEJO_TOKEN" \
-H 'Content-Type: application/json' \
--data @/tmp/release-body.json -o /tmp/release-rel.json
elif [ "$code" = "200" ]; then
echo "publish: release $tag exists, updating"
rid=$(jqp "d['id']" < /tmp/release-rel.json)
curl -sf -X PATCH "$api/releases/$rid" \
api_curl -sf -X PATCH "$api/releases/$rid" \
-H "Authorization: Bearer $FORGEJO_TOKEN" \
-H 'Content-Type: application/json' \
--data @/tmp/release-body.json -o /tmp/release-rel.json
@ -67,7 +76,7 @@ echo "publish: release id $rid ($tag)"
for f in "$@"; do
[ -f "$f" ] || { echo "publish: missing asset $f" >&2; exit 1; }
asset=$(basename "$f")
curl -s -H "Authorization: Bearer $FORGEJO_TOKEN" "$api/releases/$rid/assets" \
api_curl -s -H "Authorization: Bearer $FORGEJO_TOKEN" "$api/releases/$rid/assets" \
| python3 -c "
import json,sys
for a in json.load(sys.stdin):
@ -75,11 +84,11 @@ for a in json.load(sys.stdin):
" | while read -r aid aname; do
if [ "$aname" = "$asset" ]; then
echo "publish: deleting stale asset $aname ($aid)"
curl -sf -X DELETE -H "Authorization: Bearer $FORGEJO_TOKEN" \
api_curl -sf -X DELETE -H "Authorization: Bearer $FORGEJO_TOKEN" \
"$api/releases/$rid/assets/$aid" > /dev/null
fi
done
curl -sf -X POST "$api/releases/$rid/assets?name=$asset" \
api_curl -sf -X POST "$api/releases/$rid/assets?name=$asset" \
-H "Authorization: Bearer $FORGEJO_TOKEN" \
-F "attachment=@$f" -o /tmp/release-asset.json
python3 -c "
@ -87,7 +96,7 @@ import json;d=json.load(open('/tmp/release-asset.json'))
print('publish: uploaded', d['name'], d['size'], 'bytes')"
done
curl -s -H "Authorization: Bearer $FORGEJO_TOKEN" "$api/releases/$rid/assets" \
api_curl -s -H "Authorization: Bearer $FORGEJO_TOKEN" "$api/releases/$rid/assets" \
| python3 -c "
import json,sys
for a in json.load(sys.stdin):