build(confidence-engine): add manual Jenkins deployment
This commit is contained in:
Executable
+189
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env bash
|
||||
# Confidence Engine — Production Deployment Script
|
||||
#
|
||||
# Executes on CT 112 at /opt/confidence-engine.
|
||||
# Input: $1 = exact commit SHA (validated)
|
||||
#
|
||||
# Environment:
|
||||
# DEPLOY_DIR - target repository path (defaults /opt/confidence-engine)
|
||||
# HEALTH_URL - health check endpoint (defaults http://127.0.0.1:3000/api/health)
|
||||
#
|
||||
# Reads deploy.env from $DEPLOY_DIR/deploy.env for runtime values.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Parameters ──────────────────────────────────────────────────────
|
||||
|
||||
DEPLOY_SHA="${1:?Error: missing commit SHA argument}"
|
||||
DEPLOY_DIR="${DEPLOY_DIR:-/opt/confidence-engine}"
|
||||
HEALTH_URL="${HEALTH_URL:-http://127.0.0.1:3000/api/health}"
|
||||
|
||||
# ── Validation ──────────────────────────────────────────────────────
|
||||
|
||||
if [[ ${#DEPLOY_SHA} -lt 7 ]]; then
|
||||
echo "ERROR: SHA must be at least 7 hex characters."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! echo "$DEPLOY_SHA" | grep -qE '^[0-9a-fA-F]+$'; then
|
||||
echo "ERROR: SHA contains non-hex characters."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deploying SHA: $DEPLOY_SHA"
|
||||
|
||||
cd "$DEPLOY_DIR" || { echo "ERROR: Cannot cd to $DEPLOY_DIR"; exit 1; }
|
||||
|
||||
# ── Safety checks ───────────────────────────────────────────────────
|
||||
|
||||
# deploy.env must exist on the host (not tracked by Git)
|
||||
if [[ ! -f deploy.env ]]; then
|
||||
echo "DEPLOYMENT BLOCKED - deploy.env not found at deploy.env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for uncommitted changes that would block safe checkout
|
||||
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
|
||||
echo "DEPLOYMENT BLOCKED - target working tree has uncommitted changes"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$(git ls-files --others --exclude-standard)" ]]; then
|
||||
echo "DEPLOYMENT BLOCKED - target working tree has untracked files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Fetch and verify SHA ────────────────────────────────────────────
|
||||
|
||||
echo "Fetching latest refs from origin..."
|
||||
git fetch origin >/dev/null 2>&1 || {
|
||||
echo "ERROR: git fetch origin failed."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ! git rev-parse --verify "$DEPLOY_SHA" >/dev/null 2>&1; then
|
||||
echo "ERROR: SHA $DEPLOY_SHA not found in repository after fetch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Detached checkout (never modifies working tree or remote) ───────
|
||||
|
||||
echo "Checking out exactly $DEPLOY_SHA..."
|
||||
git checkout --detach "$DEPLOY_SHA" >/dev/null 2>&1 || {
|
||||
echo "ERROR: Could not checkout SHA $DEPLOY_SHA."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verify the detached HEAD matches our deploy SHA
|
||||
CURRENT_SHA="$(git rev-parse HEAD)"
|
||||
if [[ "${CURRENT_SHA%"${CURRENT_SHA#?}"}" != "${DEPLOY_SHA%"${DEPLOY_SHA#?}"}" ]] || \
|
||||
[[ "$CURRENT_SHA" != "$DEPLOY_SHA" && "${CURRENT_SHA:0:7}" != "${DEPLOY_SHA:0:7}" ]]; then
|
||||
echo "ERROR: Checked out SHA $CURRENT_SHA does not match requested $DEPLOY_SHA."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Load runtime environment ────────────────────────────────────────
|
||||
|
||||
if [[ -f deploy.env ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source deploy.env
|
||||
set +a
|
||||
else
|
||||
echo "ERROR: deploy.env not found at $DEPLOY_DIR/deploy.env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Build Docker image ──────────────────────────────────────────────
|
||||
|
||||
IMAGE_TAG="confidence-engine:${DEPLOY_SHA}"
|
||||
|
||||
echo "Building Docker image ${IMAGE_TAG}..."
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_SUPABASE_URL="${NEXT_PUBLIC_SUPABASE_URL}" \
|
||||
--build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY="${NEXT_PUBLIC_SUPABASE_ANON_KEY}" \
|
||||
-t "${IMAGE_TAG}" \
|
||||
.
|
||||
|
||||
echo "Image ${IMAGE_TAG} built successfully."
|
||||
|
||||
# ── Container replacement ───────────────────────────────────────────
|
||||
|
||||
CONTAINER_NAME="confidence-engine"
|
||||
|
||||
# Record previous image before replacing container
|
||||
PREV_IMAGE=""
|
||||
if docker ps -a --format '{{.Names}}' | grep -qx "$CONTAINER_NAME"; then
|
||||
PREV_IMAGE="$(docker inspect --format='{{.Config.Image}}' "$CONTAINER_NAME" 2>/dev/null || echo "")"
|
||||
fi
|
||||
|
||||
echo "Stopping existing container (if running)..."
|
||||
docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
|
||||
echo "Starting new container from ${IMAGE_TAG}..."
|
||||
docker run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--restart unless-stopped \
|
||||
-p 3000:3000 \
|
||||
-e OLLAMA_BASE_URL="${OLLAMA_BASE_URL}" \
|
||||
-e OLLAMA_MODEL="${OLLAMA_MODEL}" \
|
||||
"${IMAGE_TAG}"
|
||||
|
||||
echo "Container $CONTAINER_NAME started."
|
||||
|
||||
# ── Health check polling ────────────────────────────────────────────
|
||||
|
||||
HEALTH_CHECK_TIMEOUT=60
|
||||
HEALTH_CHECK_INTERVAL=3
|
||||
ELAPSED=0
|
||||
|
||||
echo "Waiting for health check at ${HEALTH_URL}..."
|
||||
|
||||
while [[ $ELAPSED -lt $HEALTH_CHECK_TIMEOUT ]]; do
|
||||
if curl -sf "$HEALTH_URL" | grep -q '"healthy"[[:space:]]*:[[:space:]]*true'; then
|
||||
echo ""
|
||||
echo "============================================="
|
||||
echo "DEPLOYMENT SUCCEEDED"
|
||||
echo "Deployed SHA: $DEPLOY_SHA"
|
||||
echo "Current image: $(docker inspect --format='{{.Config.Image}}' "$CONTAINER_NAME" 2>/dev/null || echo 'unknown')"
|
||||
echo "============================================="
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep "$HEALTH_CHECK_INTERVAL"
|
||||
ELAPSED=$((ELAPSED + HEALTH_CHECK_INTERVAL))
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "============================================="
|
||||
echo "DEPLOYMENT FAILED - Health check timed out after ${HEALTH_CHECK_TIMEOUT}s"
|
||||
echo "============================================="
|
||||
|
||||
# ── Rollback (one attempt) ──────────────────────────────────────────
|
||||
|
||||
if [[ -n "$PREV_IMAGE" ]]; then
|
||||
echo ""
|
||||
echo "Attempting rollback to previous image: $PREV_IMAGE"
|
||||
docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
|
||||
docker run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--restart unless-stopped \
|
||||
-p 3000:3000 \
|
||||
-e OLLAMA_BASE_URL="${OLLAMA_BASE_URL}" \
|
||||
-e OLLAMA_MODEL="${OLLAMA_MODEL}" \
|
||||
"$PREV_IMAGE"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "DEPLOYMENT FAILED - ROLLED BACK to $PREV_IMAGE"
|
||||
else
|
||||
echo "DEPLOYMENT FAILED - ROLLBACK ALSO FAILED"
|
||||
fi
|
||||
else
|
||||
echo "No previous image available for rollback."
|
||||
fi
|
||||
|
||||
echo "DEPLOYMENT FAILED - MANUAL RECOVERY REQUIRED"
|
||||
exit 1
|
||||
Reference in New Issue
Block a user