#!/usr/bin/env bash # publish-agent.sh — build (optional) + publish the felhom-agent binary to Gitea as a generic package. # # Part of the BUNDLE slice: the host-bootstrap script fetches the agent binary from Gitea # (/api/packages/admin/generic/felhom-agent//felhom-agent) and verifies its sha256 against the # hub-vouched artifact manifest before installing it. This script PUTs that binary and prints the # version + sha256 the operator records in the hub (Configs → "Day-0 artifacts"). # # The binary is PINNED to a version (never :latest). The published path encodes the version. # # Usage: # GITEA_USER=admin GITEA_TOKEN= ./publish-agent.sh [binary-path] # # bare semver, e.g. 0.43.0 (MUST match the binary's `--version`) # [binary-path] path to a prebuilt felhom-agent binary. If omitted, the script builds one with # `go build -ldflags "-X main.version="` from the repo (needs a Go toolchain). # # Env: # GITEA_USER / GITEA_TOKEN Gitea credentials with package write (the build-server's admin creds). # Falls back to REGISTRY_USER / REGISTRY_TOKEN if those are unset. # GITEA_BASE Gitea base URL (default https://gitea.dooplex.hu) # GITEA_OWNER package owner (default admin) # # Output (stdout, machine-greppable): # AGENT_VERSION= # AGENT_SHA256= set -euo pipefail GITEA_BASE="${GITEA_BASE:-https://gitea.dooplex.hu}" GITEA_OWNER="${GITEA_OWNER:-admin}" GITEA_USER="${GITEA_USER:-${REGISTRY_USER:-}}" GITEA_TOKEN="${GITEA_TOKEN:-${REGISTRY_TOKEN:-}}" die() { echo "[publish-agent] ERROR: $*" >&2; exit 1; } log() { echo "[publish-agent] $*" >&2; } VERSION="${1:-}" BIN="${2:-}" [[ -n "$VERSION" ]] || die "version required (usage: GITEA_USER=.. GITEA_TOKEN=.. $0 [binary-path])" [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "version must be bare semver X.Y.Z (got '$VERSION')" [[ -n "$GITEA_USER" && -n "$GITEA_TOKEN" ]] || die "GITEA_USER + GITEA_TOKEN (or REGISTRY_USER/REGISTRY_TOKEN) required" # Resolve repo root from this script's location (scripts/ lives at the repo root). SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # Build the binary if no prebuilt path was given. CLEANUP_BIN="" if [[ -z "$BIN" ]]; then command -v go >/dev/null || die "no binary-path given and no 'go' toolchain to build one" BIN="$(mktemp -t felhom-agent.XXXXXX)" CLEANUP_BIN="$BIN" log "building felhom-agent $VERSION from $REPO_ROOT …" # These flags MUST match release-agent.sh's build exactly — see the long comment there (R-186). # They used to differ: this line forced CGO_ENABLED=0 and produced a binary 74 KB smaller than # the one the release path built for the same version. One version name must mean one binary # whichever entry point produced it. ( cd "$REPO_ROOT" && go build -trimpath -buildvcs=false -ldflags "-X main.version=${VERSION}" -o "$BIN" ./cmd/felhom-agent ) fi [[ -f "$BIN" ]] || die "binary not found: $BIN" trap '[[ -n "$CLEANUP_BIN" ]] && rm -f "$CLEANUP_BIN"' EXIT # Sanity: the binary's self-reported version MUST match the publish version (catch a stale rebuild). BIN_VER="$("$BIN" --version 2>/dev/null | awk '{print $2}' || true)" if [[ -n "$BIN_VER" && "$BIN_VER" != "$VERSION" ]]; then die "binary --version is '$BIN_VER' but publishing as '$VERSION' — rebuild with the right ldflags" fi SHA256="$(sha256sum "$BIN" | awk '{print $1}')" [[ -n "$SHA256" ]] || die "failed to compute sha256" URL="${GITEA_BASE}/api/packages/${GITEA_OWNER}/generic/felhom-agent/${VERSION}/felhom-agent" log "publishing $BIN ($(wc -c < "$BIN") bytes, sha256 ${SHA256:0:16}…) → $URL" # Generic packages reject re-upload of an existing version+file with 409/400. Delete-then-put makes # re-publishing a version idempotent (so a rebuild of the same version overwrites cleanly). code="$(curl -fsS -o /dev/null -w '%{http_code}' -u "${GITEA_USER}:${GITEA_TOKEN}" -X DELETE "$URL" 2>/dev/null || true)" log "pre-delete existing artifact: HTTP ${code} (404/204 expected)" code="$(curl -sS -o /dev/null -w '%{http_code}' -u "${GITEA_USER}:${GITEA_TOKEN}" -X PUT --upload-file "$BIN" "$URL")" [[ "$code" == "201" || "$code" == "200" ]] || die "upload failed: HTTP $code" log "upload OK (HTTP $code)" # GET round-trip: re-fetch and confirm the stored bytes hash to the same sha256 (proves fetchable + # intact end-to-end, the same path the host-install script will take). TMP_GET="$(mktemp -t felhom-agent-get.XXXXXX)" trap '[[ -n "$CLEANUP_BIN" ]] && rm -f "$CLEANUP_BIN"; rm -f "$TMP_GET"' EXIT curl -fsS -u "${GITEA_USER}:${GITEA_TOKEN}" -o "$TMP_GET" "$URL" || die "round-trip GET failed" GOT_SHA="$(sha256sum "$TMP_GET" | awk '{print $1}')" [[ "$GOT_SHA" == "$SHA256" ]] || die "round-trip sha256 mismatch (put $SHA256, got $GOT_SHA)" log "round-trip GET verified (sha256 matches)" echo "AGENT_VERSION=${VERSION}" echo "AGENT_SHA256=${SHA256}" log "DONE. Record in the hub operator UI (Configs → Day-0 artifacts): agent ${VERSION} / ${SHA256}"