108 lines
3.4 KiB
Bash
Executable file
108 lines
3.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Publish a built APK to the self-hosted Forgejo release page.
|
|
#
|
|
# Usage:
|
|
# FORGEJO_TOKEN=... scripts/publish-forgejo.sh 1.7.62 [path/to.apk]
|
|
# FORGEJO_PASSWORD=... FORGEJO_USER=skatespots scripts/publish-forgejo.sh 1.7.62
|
|
#
|
|
# This uploads the APK and its .sha256 file. It does not push the git tag or
|
|
# source; do that separately (the Forgejo repo is a mirror, not an auto-push
|
|
# remote). Keep credentials in your shell/environment, never in this repo.
|
|
set -euo pipefail
|
|
|
|
VERSION="${1:?usage: publish-forgejo.sh <version> [apk]}"
|
|
APK="${2:-android/app/build/outputs/apk/release/app-release.apk}"
|
|
TAG="${FORGEJO_TAG:-v$VERSION}"
|
|
BASE="${FORGEJO_URL:-https://git.skate-spots.org}"
|
|
OWNER="${FORGEJO_OWNER:-skatespots}"
|
|
REPO="${FORGEJO_REPO:-SkateSpots}"
|
|
API="$BASE/api/v1/repos/$OWNER/$REPO"
|
|
|
|
command -v curl >/dev/null || { echo "curl is required" >&2; exit 1; }
|
|
command -v python3 >/dev/null || { echo "python3 is required" >&2; exit 1; }
|
|
[ -f "$APK" ] || { echo "APK not found: $APK" >&2; exit 1; }
|
|
|
|
AUTH=()
|
|
if [ -n "${FORGEJO_TOKEN:-}" ]; then
|
|
AUTH=(-H "Authorization: token $FORGEJO_TOKEN")
|
|
elif [ -n "${FORGEJO_PASSWORD:-}" ]; then
|
|
AUTH=(-u "${FORGEJO_USER:-skatespots}:$FORGEJO_PASSWORD")
|
|
else
|
|
echo "set FORGEJO_TOKEN (preferred) or FORGEJO_PASSWORD" >&2
|
|
exit 1
|
|
fi
|
|
|
|
tmpdir="$(mktemp -d)"
|
|
trap 'rm -rf "$tmpdir"' EXIT
|
|
|
|
sha="$(sha256sum "$APK" | awk '{print $1}')"
|
|
body="$tmpdir/release.md"
|
|
cat >"$body" <<EOF
|
|
## SkateSpots $VERSION
|
|
|
|
Signed Android release. SHA-256: \`$sha\`
|
|
|
|
- Source: $BASE/$OWNER/$REPO
|
|
- APK signing key: same as the Zapstore release.
|
|
EOF
|
|
|
|
python3 - "$TAG" "$VERSION" "$body" >"$tmpdir/payload.json" <<'PY'
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
tag, version, body_path = sys.argv[1:]
|
|
print(json.dumps({
|
|
"tag_name": tag,
|
|
"name": version,
|
|
"body": Path(body_path).read_text(),
|
|
"draft": False,
|
|
"prerelease": False,
|
|
}))
|
|
PY
|
|
|
|
create_code="$(curl -sS -o "$tmpdir/release.json" -w '%{http_code}' \
|
|
"${AUTH[@]}" -H 'Content-Type: application/json' \
|
|
-X POST "$API/releases" --data-binary @"$tmpdir/payload.json" || true)"
|
|
|
|
if [ "$create_code" = "201" ]; then
|
|
release_id="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$tmpdir/release.json")"
|
|
elif [ "$create_code" = "409" ] || [ "$create_code" = "422" ]; then
|
|
curl -fsS "${AUTH[@]}" "$API/releases/tags/$TAG" -o "$tmpdir/release.json"
|
|
release_id="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$tmpdir/release.json")"
|
|
else
|
|
echo "Forgejo release creation failed (HTTP $create_code)" >&2
|
|
cat "$tmpdir/release.json" >&2
|
|
exit 1
|
|
fi
|
|
|
|
sha_file="$tmpdir/SkateSpots-$VERSION.apk.sha256"
|
|
printf '%s SkateSpots-%s.apk\n' "$sha" "$VERSION" >"$sha_file"
|
|
|
|
existing_assets="$(python3 - "$tmpdir/release.json" <<'PY'
|
|
import json
|
|
import sys
|
|
|
|
data = json.load(open(sys.argv[1]))
|
|
print("\n".join(asset["name"] for asset in data.get("assets", [])))
|
|
PY
|
|
)"
|
|
|
|
for file in "$APK" "$sha_file"; do
|
|
name="$(basename "$file")"
|
|
if printf '%s\n' "$existing_assets" | grep -Fqx "$name"; then
|
|
echo "already present: $name"
|
|
continue
|
|
fi
|
|
code="$(curl -sS -o "$tmpdir/asset.json" -w '%{http_code}' \
|
|
"${AUTH[@]}" -X POST "$API/releases/$release_id/assets?name=$name" \
|
|
-F "attachment=@$file" || true)"
|
|
case "$code" in
|
|
201|202) echo "uploaded $name" ;;
|
|
*) echo "warning: upload of $name returned HTTP $code" >&2 ;;
|
|
esac
|
|
done
|
|
|
|
echo "release: $BASE/$OWNER/$REPO/releases/tag/$TAG"
|
|
echo "latest: $BASE/$OWNER/$REPO/releases/latest"
|