7581f8140a
gates / gates (push) Successful in 7s
All three are the reporting and release path misreporting its own work. No
customer machine, no backup, no restore, no data. The restore-test itself and
when it runs are unchanged.
R-189 — a passing restore-test no longer vanishes on a restart. restore_tests[]
came only from the in-memory store, whose comment ("lost on restart; the cadence
re-populates") was true under a timer and stopped being true when R-86 made the
agent refuse to re-test a proven archive: the proof is then not repeated for a
whole archive generation. Observed live — a 14.5 GB offsite PASS reached no
host-report because the agent was restarted 2m43s later. RestoreTestState now
carries tier + verified beside the archive and renders reportable entries; the
collector merges them, one per tier, newest by TestedAt. It refuses to lie: a
record missing archive-or-tier produces no entry, and run mechanics are not
re-invented. Only successes are persisted, and the asymmetry is now written where
it will be read.
R-188 — a correct release stops emailing a failure. Only the tag PUSH moved
(build -> tag locally -> publish -> push tag): the push wakes CI, and a tag
visible before its package made the gate correctly fail a correct release about
half the time. The old order's invariant is asserted directly instead — the gate
now refuses a published version with no tag, as a bounded probe that prints its
own coverage, because the package listing api is still 401 without a token.
R-186 — a released binary can be verified by rebuilding it. -trimpath
-buildvcs=false: same source, same bytes, tag or no tag. Measured. publish-agent's
fallback also forced CGO_ENABLED=0 and produced a 74 KB different binary for the
same version; both paths now build identically. CLAUDE.md records the command.
96 lines
5.0 KiB
Bash
Executable File
96 lines
5.0 KiB
Bash
Executable File
#!/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/<ver>/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=<token> ./publish-agent.sh <version> [binary-path]
|
|
#
|
|
# <version> 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=<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=<version>
|
|
# AGENT_SHA256=<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 <version> [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}"
|