archy/scripts/publish-release-assets.sh

103 lines
3.5 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# Publish an Archipelago OTA release to a Gitea remote and verify downloads.
set -euo pipefail
VERSION="${1:-}"
REMOTE="${2:-gitea-vps2}"
if [ -z "$VERSION" ]; then
echo "Usage: $0 VERSION [remote]"
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
VERSION_DIR="$PROJECT_ROOT/releases/v${VERSION}"
BACKEND="$VERSION_DIR/archipelago"
FRONTEND="$VERSION_DIR/archipelago-frontend-${VERSION}.tar.gz"
fail() { echo "Error: $*" >&2; exit 1; }
[ -f "$PROJECT_ROOT/releases/manifest.json" ] || fail "releases/manifest.json missing"
[ -f "$BACKEND" ] || fail "backend artifact missing: $BACKEND"
[ -f "$FRONTEND" ] || fail "frontend artifact missing: $FRONTEND"
"$SCRIPT_DIR/check-release-manifest.sh"
remote_url=$(git -C "$PROJECT_ROOT" remote get-url "$REMOTE")
case "$remote_url" in
http://*@*) ;;
*) fail "$REMOTE must be an authenticated http:// Gitea remote URL for API uploads" ;;
esac
auth=${remote_url#http://}
auth=${auth%@*}
host_path=${remote_url#http://$auth@}
host=${host_path%%/*}
repo_path=${host_path#*/}
repo_path=${repo_path%.git}
api="http://$host/api/v1/repos/$repo_path"
release_url="$api/releases/tags/v${VERSION}"
echo "Pushing main and v${VERSION} to $REMOTE..."
git -C "$PROJECT_ROOT" push "$REMOTE" main "refs/tags/v${VERSION}"
release_json=$(curl -fsS -u "$auth" "$release_url" || true)
if [ -z "$release_json" ]; then
echo "Creating Gitea release v${VERSION}..."
release_body=$(python3 - "$VERSION" <<'PY'
import json
import sys
version = sys.argv[1]
print(json.dumps({
"tag_name": f"v{version}",
"target_commitish": "main",
"name": f"v{version}",
"body": f"Archipelago v{version} release artifacts for OTA updates.",
"draft": False,
"prerelease": True,
}))
PY
)
release_json=$(curl -fsS -u "$auth" -H 'Content-Type: application/json' -d "$release_body" "$api/releases")
fi
release_id=$(printf '%s' "$release_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
asset_names=$(curl -fsS -u "$auth" "$api/releases/$release_id/assets" | python3 -c 'import json,sys; print("\n".join(a["name"] for a in json.load(sys.stdin)))')
upload_asset() {
local path="$1"
local name="$2"
if printf '%s\n' "$asset_names" | grep -Fxq "$name"; then
echo "Asset $name already exists; leaving it in place."
return
fi
echo "Uploading $name..."
curl --fail --show-error --silent --http1.1 --connect-timeout 20 --max-time 900 \
-u "$auth" \
-F "attachment=@$path" \
"$api/releases/$release_id/assets?name=$name" >/dev/null
asset_names=$(printf '%s\n%s\n' "$asset_names" "$name")
}
upload_asset "$BACKEND" "archipelago"
upload_asset "$FRONTEND" "archipelago-frontend-${VERSION}.tar.gz"
echo "Verifying public download URLs from manifest..."
python3 - "$PROJECT_ROOT/releases/manifest.json" <<'PY' | while read -r url size; do
import json
import sys
manifest = json.load(open(sys.argv[1]))
for component in manifest["components"]:
print(component["download_url"], component["size_bytes"])
PY
headers=$(curl -fsSI -L --max-time 60 "$url") || fail "download URL failed: $url"
actual_size=$(printf '%s\n' "$headers" | awk 'tolower($1)=="content-length:" { size=$2 } END { gsub("\r", "", size); print size }')
[ "$actual_size" = "$size" ] || fail "download size mismatch for $url (expected $size, got ${actual_size:-missing})"
done
echo "Release v${VERSION} published and verified on $REMOTE."