Files
security-game/tools/publish_release.sh
T
zwitschi b4d189c325
Full build / Import + headless tests (push) Successful in 36s
Full build / Full desktop build + optional release (push) Successful in 3m37s
Publish releases with the runner's checkout credentials
The v0.2.10 tag build still failed to publish: its env dump showed no token at all,
because this Gitea/act runner does not provide `github.token`, and the repo has no
GITEA_TOKEN secret.

publish_release.sh now resolves credentials in order: GITEA_TOKEN,
GITEA_AUTH_HEADER, ~/.git-credentials, then the local git extraheader that
actions/checkout writes for the job. That header carries the runner's basic-auth
credentials for the same server, so tag builds publish with no repo secret.

Verified by adding the extraheader locally and publishing v0.2.10 (all five
artifacts: Linux/Windows/macOS ports, NSIS installer, AppImage), then removing it.

Docs: RELEASING describes the four credential sources and warns that github.token is
not available here; MEMORY records both failed attempts and the final fix; TODO keeps
the "verify on the next tag" follow-up. Version 0.2.11.
2026-10-31 21:30:24 +01:00

107 lines
3.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Publish a release to the Gitea/Forgejo host, uploading the built artifacts.
#
# Requires:
# - GITEA_TOKEN (token with write access) OR credentials usable for basic auth
# (e.g. the same ones stored in ~/.git-credentials by `git push`)
# - build artifacts from tools/build_releases.sh (+ optional installer/AppImage)
#
# Usage:
# GITEA_TOKEN=xxx ./tools/publish_release.sh [version]
# (version defaults to config/version from project.godot)
set -euo pipefail
REPO="${REPO:-zwitschi/security-game}"
HOST="${HOST:-https://git.allucanget.biz}"
VERSION="${1:-$(grep -oP 'config/version="\K[^"]+' project.godot)}"
TAG="v$VERSION"
# AUTH is a curl argument list. Populated by the first available method:
# 1. GITEA_TOKEN explicit token (repo secret in CI)
# 2. GITEA_AUTH_HEADER a ready-made "Authorization: ..." header
# 3. ~/.git-credentials basic auth for HOST
# 4. the checkout's git extraheader (Gitea Actions runners inject one for the
# job; this is what makes tag builds publish without any repo secret)
AUTH=()
if [[ -n "${GITEA_TOKEN:-}" ]]; then
AUTH=(-H "Authorization: token $GITEA_TOKEN")
elif [[ -n "${GITEA_AUTH_HEADER:-}" ]]; then
AUTH=(-H "$GITEA_AUTH_HEADER")
elif [[ -f "$HOME/.git-credentials" ]]; then
CRED="$(python3 -c "
import os
for l in open(os.path.expanduser('~/.git-credentials')):
if '$HOST'.split('//')[1] in l:
print(l.split('://',1)[1].split('@')[0]); break")"
if [[ -z "$CRED" ]]; then
echo "error: no credentials for $HOST in ~/.git-credentials" >&2
exit 1
fi
# curl basic auth. (Previously only CRED was set, so ${AUTH[@]} was unbound
# under `set -u` and every API call failed silently.)
AUTH=(-u "$CRED")
fi
if [[ ${#AUTH[@]} -eq 0 ]]; then
# Gitea Actions: actions/checkout configures a local extraheader with the
# runner's credentials. Reuse it for the API (the same server, so the header is
# valid for $HOST too).
GIT_HEADER="$(git config --local --get-regexp '^http\..*\.extraheader$' 2>/dev/null | head -n1 | cut -d' ' -f2- || true)"
if [[ -n "${GIT_HEADER:-}" ]]; then
AUTH=(-H "$GIT_HEADER")
fi
fi
if [[ ${#AUTH[@]} -eq 0 ]]; then
echo "error: no credentials. Set GITEA_TOKEN, GITEA_AUTH_HEADER, or provide ~/.git-credentials." >&2
exit 1
fi
# Artifacts that must exist (run tools/build_releases.sh first).
portables=(
"build/step-line-linux-$VERSION.x86_64"
"build/step-line-windows-$VERSION.exe"
"build/step-line-macos-$VERSION.zip"
)
extras=(
"build/step-line-setup-$VERSION.exe"
"build/step-line-$VERSION-x86_64.AppImage"
)
all_artifacts=("${portables[@]}")
for f in "${extras[@]}"; do
if [[ -f "$f" ]]; then
all_artifacts+=("$f")
else
echo "note: optional artifact missing (skipping): $f"
fi
done
API="$HOST/api/v1/repos/$REPO/releases"
echo "==> Creating release $TAG on $HOST/$REPO"
RELEASE_ID="$(curl -s -X POST "$API" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":\"Step Line $VERSION — playable prototype. See README for controls.\",\"draft\":false,\"prerelease\":false}" \
| python3 -c "import sys,json;print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)"
# If the release already exists (tag already pushed), reuse it.
if [[ -z "$RELEASE_ID" ]]; then
RELEASE_ID="$(curl -s "${AUTH[@]}" "$API/tags/$TAG" | python3 -c "import sys,json;print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)"
fi
if [[ -z "$RELEASE_ID" ]]; then
echo "error: could not create or find release $TAG" >&2
exit 1
fi
echo "Release id: $RELEASE_ID"
for f in "${all_artifacts[@]}"; do
if [[ ! -f "$f" ]]; then
echo "error: missing artifact $f — run ./tools/build_releases.sh first" >&2
exit 1
fi
echo "==> Uploading $(basename "$f")"
curl -s -X POST "$API/$RELEASE_ID/assets" "${AUTH[@]}" -F "attachment=@$f" >/dev/null
done
echo ""
echo "Release live: $HOST/$REPO/releases/tag/$TAG"