Durable `leader_acked` frontier in `ShardReplica` tracks the highest seqno acked under `ack=leader` (journal-only, un-replicated); `decide_join` now quarantines on THIS node's own frontier rather than comparing stream numbers across stream boundaries — eliminates false-quarantine churn on rolling restarts. `SHUTDOWN_HANDOFF_WAIT` (3s) drains the leader's tail to quorum before step-down so the next leader inherits a clean prefix. New `load_leader_acked`/`persist_leader_acked` helpers; `cluster_reseed.rs` gains the divergence-fix regression suite; `replication_ops.rs` threads the signal. Soak-eval: `tidal_stress::soak_eval` + `soak-eval` binary implement the 30-night streak (ledger.tsv × restarts.tsv → streak.tsv); monitor and nightly CronJob k8s YAMLs updated; phase-9 doc clarifies the dual-stream streak definition (ledger PASS AND zero pod restarts in window). `run-reliability.sh` gates the election-divergence suite before any k8s push. Release tooling: `docker/release/` multi-stage Dockerfile + DR image; `scripts/build-release.sh` single repeatable cross-compile+buildx path.
111 lines
5.1 KiB
Bash
Executable File
111 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# build-release.sh — the one committed, repeatable tidalDB release path.
|
|
#
|
|
# Replaces the ad-hoc hand-typed cross-compile + buildx + push with a single
|
|
# command on a clean checkout:
|
|
#
|
|
# ./scripts/build-release.sh <tag> [server|dr|stress|all]
|
|
#
|
|
# It HOST cross-compiles (mac-arm64 -> x86_64-unknown-linux-gnu, glibc 2.41 via the
|
|
# homebrew toolchain), packages the binaries into the committed docker/release/*
|
|
# Dockerfiles via the amd64 buildx builder, pushes, and prints the resulting
|
|
# @sha256 digest for each image (pin that digest in k8s/cluster/statefulset.yaml).
|
|
#
|
|
# The project bans CI/CD pipelines (orchard9-k3sf/CLAUDE.md) — this is a SCRIPT,
|
|
# run manually or from an operator's shell, never a pipeline. Registry creds come
|
|
# from the Docker daemon's existing login (run `docker login registry.threesix.ai`
|
|
# first); NO secret is embedded here.
|
|
set -euo pipefail
|
|
|
|
TAG="${1:-}"
|
|
COMPONENT="${2:-all}"
|
|
REGISTRY="${TIDAL_REGISTRY:-registry.threesix.ai/tidal}"
|
|
TARGET="x86_64-unknown-linux-gnu"
|
|
BUILDER="${TIDAL_BUILDX_BUILDER:-amd64builder}"
|
|
|
|
die() { echo "build-release: $*" >&2; exit 1; }
|
|
|
|
[ -n "$TAG" ] || die "usage: build-release.sh <tag> [server|dr|stress|all]"
|
|
case "$COMPONENT" in server|dr|stress|all) ;; *) die "component must be server|dr|stress|all" ;; esac
|
|
|
|
# ── Toolchain preflight (fail fast, no half-built image) ─────────────────────
|
|
command -v cargo >/dev/null || die "cargo not on PATH"
|
|
command -v docker >/dev/null || die "docker not on PATH"
|
|
command -v "${TARGET}-gcc" >/dev/null || die "missing ${TARGET}-gcc (brew install ${TARGET})"
|
|
PROTOC_BIN="${PROTOC:-$(command -v protoc || true)}"
|
|
[ -n "$PROTOC_BIN" ] || die "protoc not found (brew install protobuf), or set PROTOC"
|
|
rustup target list --installed 2>/dev/null | grep -qx "$TARGET" \
|
|
|| die "rust target $TARGET not installed (rustup target add $TARGET)"
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
# ── Pinned cross-compile environment (the documented recipe) ─────────────────
|
|
export CC_x86_64_unknown_linux_gnu="${TARGET}-gcc"
|
|
export CXX_x86_64_unknown_linux_gnu="${TARGET}-g++"
|
|
export AR_x86_64_unknown_linux_gnu="${TARGET}-ar"
|
|
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER="${TARGET}-gcc"
|
|
export PROTOC="$PROTOC_BIN"
|
|
|
|
# Which crates to cross-compile for the selected component(s).
|
|
PKGS=()
|
|
case "$COMPONENT" in
|
|
server) PKGS=(-p tidal-server) ;;
|
|
dr) PKGS=(-p tidalctl -p tidal-server) ;;
|
|
stress) PKGS=() ;; # stress builds IN-container (pure Rust, no engine deps)
|
|
all) PKGS=(-p tidal-server -p tidalctl) ;;
|
|
esac
|
|
|
|
if [ "${#PKGS[@]}" -gt 0 ]; then
|
|
echo "==> cross-compiling ${PKGS[*]} for $TARGET (release)"
|
|
cargo build "${PKGS[@]}" --release --target "$TARGET" --locked
|
|
fi
|
|
|
|
# ── buildx amd64 builder (idempotent) ────────────────────────────────────────
|
|
if ! docker buildx inspect "$BUILDER" >/dev/null 2>&1; then
|
|
echo "==> creating buildx builder $BUILDER"
|
|
docker buildx create --name "$BUILDER" --driver docker-container >/dev/null
|
|
fi
|
|
|
|
BIN_DIR="target/$TARGET/release"
|
|
STAGE="$(mktemp -d)"
|
|
trap 'rm -rf "$STAGE"' EXIT
|
|
|
|
# Build one image from a staged context; print its pushed digest.
|
|
build_image() { # $1=image_name $2=dockerfile $3=stage_subdir
|
|
local image="$REGISTRY/$1:$TAG" dockerfile="$2" ctx="$STAGE/$3"
|
|
echo "==> building $image"
|
|
docker buildx build --builder "$BUILDER" --platform linux/amd64 \
|
|
-f "$dockerfile" -t "$image" --push "$ctx"
|
|
local digest
|
|
digest="$(docker buildx imagetools inspect "$image" --format '{{.Manifest.Digest}}' 2>/dev/null || true)"
|
|
echo "RELEASE $1: $REGISTRY/$1@${digest:-<digest-unavailable>} (tag $TAG)"
|
|
}
|
|
|
|
if [ "$COMPONENT" = server ] || [ "$COMPONENT" = all ]; then
|
|
mkdir -p "$STAGE/server"
|
|
cp "$BIN_DIR/tidal-server" "$STAGE/server/"
|
|
cp -a docker/release/config "$STAGE/server/config"
|
|
cp docker/release/Dockerfile "$STAGE/server/Dockerfile"
|
|
build_image server "$STAGE/server/Dockerfile" server
|
|
fi
|
|
|
|
if [ "$COMPONENT" = dr ] || [ "$COMPONENT" = all ]; then
|
|
mkdir -p "$STAGE/dr"
|
|
cp "$BIN_DIR/tidalctl" "$BIN_DIR/tidal-server" "$STAGE/dr/"
|
|
cp docker/release/dr.Dockerfile "$STAGE/dr/Dockerfile"
|
|
build_image tidalctl "$STAGE/dr/Dockerfile" dr
|
|
fi
|
|
|
|
if [ "$COMPONENT" = stress ] || [ "$COMPONENT" = all ]; then
|
|
# Pure-Rust, in-container build (includes the soak-eval binary). Context = repo
|
|
# root so the workspace manifests resolve; the .dockerignore prunes it.
|
|
echo "==> building $REGISTRY/stress:$TAG (in-container, includes soak-eval)"
|
|
docker buildx build --builder "$BUILDER" --platform linux/amd64 \
|
|
-f docker/stress/Dockerfile -t "$REGISTRY/stress:$TAG" --push .
|
|
d="$(docker buildx imagetools inspect "$REGISTRY/stress:$TAG" --format '{{.Manifest.Digest}}' 2>/dev/null || true)"
|
|
echo "RELEASE stress: $REGISTRY/stress@${d:-<digest-unavailable>} (tag $TAG)"
|
|
fi
|
|
|
|
echo "==> done. Pin the printed @sha256 digest(s) in k8s/cluster/statefulset.yaml and the DR/soak manifests."
|