#!/usr/bin/env bash set -euo pipefail # Load .env if present if [ -f "$(dirname "$0")/../.env" ]; then set -a # shellcheck disable=SC1091 . "$(dirname "$0")/../.env" set +a fi API_URL=${GITEA_API_URL:-"https://gitea.elpatron.me/api/v1"} OWNER=${GITEA_OWNER:-"elpatron"} REPO=${GITEA_REPO:-"octo-funnel"} TOKEN=${GITEA_API_TOKEN:-""} TAG="" NAME="" ASSET_PATH="" BODY_FILE="" usage() { echo "Usage: $0 -t -a [-n ] [-b ]" >&2 } while getopts ":t:a:n:b:" opt; do case $opt in t) TAG="$OPTARG" ;; a) ASSET_PATH="$OPTARG" ;; n) NAME="$OPTARG" ;; b) BODY_FILE="$OPTARG" ;; *) usage; exit 2 ;; esac done if [ -z "$TAG" ] || [ -z "$ASSET_PATH" ]; then usage; exit 2 fi if [ -z "$NAME" ]; then NAME="$TAG"; fi if [ ! -f "$ASSET_PATH" ]; then echo "Asset not found: $ASSET_PATH" >&2; exit 1 fi if [ -z "$TOKEN" ]; then echo "GITEA_API_TOKEN not set (in .env)." >&2; exit 1 fi BODY="Tailscale Funnel Plugin ${TAG}\n\nAutomated release." if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then BODY=$(cat "$BODY_FILE") fi # Build JSON payload via python (robust quoting) create_payload=$(REL_TAG="$TAG" REL_NAME="$NAME" REL_BODY_TXT="$BODY" python3 - <<'PY' import json, os payload = { "tag_name": os.environ["REL_TAG"], "name": os.environ["REL_NAME"], "body": os.environ["REL_BODY_TXT"], "draft": False, "prerelease": False } print(json.dumps(payload)) PY ) create_resp=$(curl -sS -X POST \ -H 'Content-Type: application/json' \ -H "Authorization: token ${TOKEN}" \ -d "$create_payload" \ "${API_URL}/repos/${OWNER}/${REPO}/releases" || true) # Extract id; if missing, fetch by tag rel_id=$(python3 - <<'PY' import sys, json data=sys.stdin.read().strip() if not data: print("") else: try: obj=json.loads(data) print(obj.get('id','')) except Exception: print("") PY <<<"$create_resp") if [ -z "$rel_id" ]; then get_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/tags/${TAG}") rel_id=$(python3 - <<'PY' import sys, json obj=json.loads(sys.stdin.read()) print(obj.get('id','')) PY <<<"$get_resp") fi if [ -z "$rel_id" ]; then echo "Failed to get create/fetch release id" >&2 echo "$create_resp" | head -c 400 >&2 exit 1 fi # Delete existing asset with same name (if any) assets_json=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets") asset_id=$(python3 - <<'PY' import sys, json, os name=os.environ['ASSET_NAME'] try: arr=json.loads(sys.stdin.read()) for a in arr: if a.get('name')==name: print(a.get('id','')) break except Exception: pass PY ASSET_NAME="$(basename "$ASSET_PATH")" <<<"$assets_json") if [ -n "$asset_id" ]; then curl -sS -X DELETE -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets/${asset_id}" >/dev/null || true fi # Upload asset upload_resp=$(curl -sS -H "Authorization: token ${TOKEN}" -F attachment=@"${ASSET_PATH}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets?name=$(basename "$ASSET_PATH")") html_url=$(python3 - <<'PY' import sys, json try: print(json.loads(sys.stdin.read()).get('browser_download_url','')) except Exception: print("") PY <<<"$upload_resp") echo "Release ${TAG} created (id=${rel_id}). Asset uploaded: ${html_url}"