#!/usr/bin/env bash # Upload release files to the Gitea release vVERSION, creating the release # (and its tag at HEAD) when missing. # GITEA_TOKEN=... scripts/release.sh FILE... # Each FILE's FILE.sha256 is uploaded too when it exists; assets of the same # name are replaced. Several CI jobs may call this at once: the first creates # the release, the others find it. The token is only read from the # environment and handed to curl on stdin: it never appears in argv, on disk # or in the output. # # CI: set GITEA_TOKEN (or GITHUB_TOKEN), CST_GITEA, CST_REPO. The remote-branch # check is skipped under GITEA_ACTIONS / GITHUB_ACTIONS / CI. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" # shellcheck source=lib_release.sh . "$ROOT/scripts/lib_release.sh" SERVER="${CST_GITEA:-${GITHUB_SERVER_URL:-https://git.whcself.xyz}}" REPO="${CST_REPO:-${GITHUB_REPOSITORY:-WHC/CST-agent}}" API="$SERVER/api/v1/repos/$REPO" TOKEN="${GITEA_TOKEN:-${GITHUB_TOKEN:-}}" [[ -n "$TOKEN" ]] || { echo "set GITEA_TOKEN (a Gitea token with write:repository)" >&2 exit 1 } [[ $# -gt 0 ]] || { echo "usage: $0 FILE..." >&2; exit 1; } command -v python3 >/dev/null || { echo "missing python3" >&2; exit 1; } files=() for f in "$@"; do [[ -f "$f" ]] || { echo "missing $f" >&2; exit 1; } f="$(cd "$(dirname "$f")" && pwd)/$(basename "$f")" files+=("$f") [[ "$f" != *.sha256 && -f "$f.sha256" ]] && files+=("$f.sha256") done cd "$ROOT" ver="$(cst_version)" tag="v$ver" # If this job was triggered by a version tag, it must match cst_version.h. if [[ "${GITHUB_REF:-}" == refs/tags/v* ]]; then ref_tag="${GITHUB_REF#refs/tags/}" if [[ "$ref_tag" != "$tag" ]]; then echo "pushed tag $ref_tag != cst_version.h $tag; bump include/cst_version.h" >&2 exit 1 fi fi api() { # METHOD PATH [curl args...] local m="$1" p="$2" shift 2 printf 'Authorization: token %s\n' "$TOKEN" | curl -fsS -X "$m" -H @- "$API$p" "$@" } json() { # CODE [ARGS...]: d = the JSON on stdin, ARGS in sys.argv[1:] local code="$1" shift python3 -c "import json,sys; d=json.load(sys.stdin); $code" "$@" } BODY="Hermit $ver Linux (static, musl): hermit-$ver-linux-{x86_64,x86,aarch64}.tar.gz — extract and run ./hermit (cst-agent symlink included). Legacy cst-agent-*.tar.gz aliases may ship for one cycle. Windows x86_64: hermit-$ver-windows-x86_64-setup.exe / .zip (cst-agent.exe symlink or copy when present). Env: HERMIT_* preferred; CST_* still accepted. Config: ~/.config/hermit (falls back to ~/.config/cst-agent). Every file has a .sha256 next to it." if rel="$(api GET "/releases/tags/$tag" 2>/dev/null)"; then echo "==> release $tag exists" else head="$(git rev-parse HEAD)" if [[ -z "${CI:-}${GITEA_ACTIONS:-}${GITHUB_ACTIONS:-}" ]]; then if ! git branch -r --contains "$head" 2>/dev/null | grep -q .; then echo "HEAD $head is not on any remote branch; push it first" >&2 exit 1 fi fi echo "==> creating release $tag at $head" body="$(python3 -c 'import json,sys; print(json.dumps({"tag_name": sys.argv[1], "target_commitish": sys.argv[2], "name": "CST-Agent " + sys.argv[1], "body": sys.argv[3]}))' "$tag" "$head" "$BODY")" if ! rel="$(api POST /releases -H 'Content-Type: application/json' -d "$body" 2>/dev/null)"; then # Another job created it first (409). rel="" for _ in 1 2 3 4 5 6; do sleep 5 rel="$(api GET "/releases/tags/$tag" 2>/dev/null)" && break rel="" done [[ -n "$rel" ]] || { echo "could not create or find release $tag" >&2; exit 1; } echo "==> release $tag created by another job" fi fi id="$(json 'print(d["id"])' <<<"$rel")" for f in "${files[@]}"; do name="$(basename "$f")" old="$(json "print(' '.join(str(a['id']) for a in d.get('assets') or [] if a['name'] == sys.argv[1]))" "$name" <<<"$rel")" for aid in $old; do echo "==> replacing $name" api DELETE "/releases/$id/assets/$aid" >/dev/null done echo "==> upload $name" api POST "/releases/$id/assets?name=$name" -F "attachment=@$f" >/dev/null done echo "$SERVER/$REPO/releases/tag/$tag"