mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 22:35:42 +00:00
The existing cleanup only removed dangling images. Now also removes images older than 24h to prevent disk space exhaustion while keeping recent images available for rollback. Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
410 lines
18 KiB
YAML
410 lines
18 KiB
YAML
name: Docker Build and Push
|
|
|
|
on:
|
|
push:
|
|
branches: [ "main" ]
|
|
paths:
|
|
# Note: C++ changes trigger shardok_arm64_build.yml instead
|
|
# Note: Auth changes trigger auth_build.yml instead
|
|
# Note: Windows installer changes trigger installer_build.yml instead
|
|
- 'src/main/go/**'
|
|
- '!src/main/go/net/eagle0/authservice/**'
|
|
- '!src/main/go/net/eagle0/authcli/**'
|
|
- '!src/main/go/net/eagle0/clients/**'
|
|
- 'src/main/scala/**'
|
|
- 'src/main/protobuf/**'
|
|
- '!src/main/protobuf/net/eagle0/eagle/api/auth.proto'
|
|
- '!src/main/protobuf/net/eagle0/eagle/api/admin/**'
|
|
- '!src/main/protobuf/net/eagle0/eagle/internal/auth_internal.proto'
|
|
- 'src/main/resources/**'
|
|
- 'ci/BUILD.bazel'
|
|
- 'MODULE.bazel'
|
|
- 'docker-compose.prod.yml'
|
|
- 'nginx/**'
|
|
- '.github/workflows/docker_build.yml'
|
|
workflow_dispatch:
|
|
inputs:
|
|
push_images:
|
|
description: 'Push images to container registry'
|
|
required: true
|
|
default: 'false'
|
|
type: boolean
|
|
|
|
# Only allow one deployment at a time to prevent race conditions
|
|
concurrency:
|
|
group: docker-build-deploy
|
|
cancel-in-progress: false # Don't cancel running deployments, queue new ones
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
# Single consolidated build job - builds all images with one bazel invocation
|
|
# This uses 1 runner slot instead of 4, and Bazel parallelizes internally
|
|
# NOTE: Must run on a runner with the bazel label to ensure consistent Bazel cache
|
|
build-all:
|
|
runs-on: [self-hosted, bazel]
|
|
outputs:
|
|
eagle_image_tag: ${{ steps.push-images.outputs.eagle_image_tag }}
|
|
admin_image_tag: ${{ steps.push-images.outputs.admin_image_tag }}
|
|
jfr_sidecar_image_tag: ${{ steps.push-images.outputs.jfr_sidecar_image_tag }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
lfs: false
|
|
|
|
- name: Build all Docker images
|
|
id: build-all
|
|
run: |
|
|
set -ex
|
|
|
|
# Build ALL images in a single bazel command - Bazel parallelizes internally
|
|
# Note: Shardok is built separately for ARM64 and deployed to Hetzner
|
|
echo "=== Building Docker images ==="
|
|
bazel build \
|
|
--platforms=//:linux_x86_64 \
|
|
--extra_toolchains=@llvm_toolchain_linux//:all \
|
|
//ci:eagle_server_image \
|
|
//ci:admin_server_image \
|
|
//ci:jfr_sidecar_image \
|
|
//ci:warmup_tar
|
|
|
|
# Extract warmup binary from tar for deployment
|
|
mkdir -p scripts/bin
|
|
tar -xf bazel-bin/ci/warmup_tar.tar -C scripts/bin --strip-components=1
|
|
|
|
# Save all image paths before any other bazel command changes bazel-bin symlink
|
|
EAGLE_PATH=$(readlink -f bazel-bin/ci/eagle_server_image)
|
|
ADMIN_PATH=$(readlink -f bazel-bin/ci/admin_server_image)
|
|
JFR_PATH=$(readlink -f bazel-bin/ci/jfr_sidecar_image)
|
|
|
|
echo "eagle_path=$EAGLE_PATH" >> $GITHUB_OUTPUT
|
|
echo "admin_path=$ADMIN_PATH" >> $GITHUB_OUTPUT
|
|
echo "jfr_path=$JFR_PATH" >> $GITHUB_OUTPUT
|
|
|
|
echo "=== Image paths ==="
|
|
echo "Eagle: $EAGLE_PATH"
|
|
echo "Admin: $ADMIN_PATH"
|
|
echo "JFR Sidecar: $JFR_PATH"
|
|
|
|
- name: Login to DigitalOcean Container Registry
|
|
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
|
|
env:
|
|
DO_TOKEN: ${{ secrets.DO_REGISTRY_TOKEN }}
|
|
run: |
|
|
mkdir -p ~/.docker
|
|
AUTH=$(echo -n "${DO_TOKEN}:${DO_TOKEN}" | base64)
|
|
echo "{\"auths\":{\"registry.digitalocean.com\":{\"auth\":\"${AUTH}\"}}}" > ~/.docker/config.json
|
|
mkdir -p .docker
|
|
cp ~/.docker/config.json .docker/
|
|
|
|
- name: Push all images to DO registry
|
|
id: push-images
|
|
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
|
|
env:
|
|
DOCKER_CONFIG: ${{ github.workspace }}/.docker
|
|
run: |
|
|
set -ex
|
|
|
|
GIT_SHA=$(git rev-parse --short=8 HEAD)
|
|
|
|
# Get crane from push target runfiles
|
|
bazel build //ci:eagle_server_push
|
|
CRANE="bazel-bin/ci/push_eagle_server_push.sh.runfiles/rules_oci~~oci~oci_crane_darwin_arm64/crane"
|
|
|
|
if [ ! -e "$CRANE" ]; then
|
|
# Fallback: find any Darwin crane
|
|
RUNFILES="bazel-bin/ci/push_eagle_server_push.sh.runfiles"
|
|
CRANE=$(find "$RUNFILES" -path "*darwin*" -name crane 2>/dev/null | head -1)
|
|
fi
|
|
|
|
if [ -z "$CRANE" ] || [ ! -e "$CRANE" ]; then
|
|
echo "ERROR: crane not found"
|
|
exit 1
|
|
fi
|
|
echo "Using crane: $CRANE"
|
|
|
|
# Push Eagle image
|
|
EAGLE_IMAGE="${{ steps.build-all.outputs.eagle_path }}"
|
|
EAGLE_TAG="registry.digitalocean.com/eagle0/eagle-server:${GIT_SHA}"
|
|
echo "Pushing Eagle: $EAGLE_TAG"
|
|
$CRANE push "$EAGLE_IMAGE" "$EAGLE_TAG"
|
|
$CRANE copy "$EAGLE_TAG" "registry.digitalocean.com/eagle0/eagle-server:latest"
|
|
echo "eagle_image_tag=$EAGLE_TAG" >> $GITHUB_OUTPUT
|
|
|
|
# Push Admin image
|
|
ADMIN_IMAGE="${{ steps.build-all.outputs.admin_path }}"
|
|
ADMIN_TAG="registry.digitalocean.com/eagle0/admin-server:${GIT_SHA}"
|
|
echo "Pushing Admin: $ADMIN_TAG"
|
|
$CRANE push "$ADMIN_IMAGE" "$ADMIN_TAG"
|
|
$CRANE copy "$ADMIN_TAG" "registry.digitalocean.com/eagle0/admin-server:latest"
|
|
echo "admin_image_tag=$ADMIN_TAG" >> $GITHUB_OUTPUT
|
|
|
|
# Push JFR Sidecar image
|
|
JFR_IMAGE="${{ steps.build-all.outputs.jfr_path }}"
|
|
JFR_TAG="registry.digitalocean.com/eagle0/jfr-sidecar:${GIT_SHA}"
|
|
echo "Pushing JFR Sidecar: $JFR_TAG"
|
|
$CRANE push "$JFR_IMAGE" "$JFR_TAG"
|
|
$CRANE copy "$JFR_TAG" "registry.digitalocean.com/eagle0/jfr-sidecar:latest"
|
|
echo "jfr_sidecar_image_tag=$JFR_TAG" >> $GITHUB_OUTPUT
|
|
|
|
echo "=== All images pushed successfully ==="
|
|
|
|
deploy:
|
|
runs-on: [self-hosted, bazel]
|
|
needs: [build-all]
|
|
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
|
|
environment: production
|
|
env:
|
|
EAGLE_IMAGE: ${{ needs.build-all.outputs.eagle_image_tag }}
|
|
ADMIN_IMAGE: ${{ needs.build-all.outputs.admin_image_tag }}
|
|
JFR_SIDECAR_IMAGE: ${{ needs.build-all.outputs.jfr_sidecar_image_tag }}
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
GPT_MODEL_NAME: ${{ secrets.GPT_MODEL_NAME }}
|
|
EAGLE_ENABLE_S3: ${{ secrets.EAGLE_ENABLE_S3 }}
|
|
DO_SPACES_ACCESS_KEY: ${{ secrets.DO_SPACES_ACCESS_KEY }}
|
|
DO_SPACES_SECRET_KEY: ${{ secrets.DO_SPACES_SECRET_KEY }}
|
|
JWT_PRIVATE_KEY: ${{ secrets.JWT_PRIVATE_KEY }}
|
|
DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }}
|
|
DISCORD_CLIENT_SECRET: ${{ secrets.DISCORD_CLIENT_SECRET }}
|
|
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
|
|
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
|
|
GH_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }}
|
|
GH_OAUTH_CLIENT_SECRET: ${{ secrets.GH_OAUTH_CLIENT_SECRET }}
|
|
APPLE_SIGNIN_CLIENT_ID: ${{ secrets.APPLE_SIGNIN_CLIENT_ID }}
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
APPLE_SIGNIN_KEY_ID: ${{ secrets.APPLE_SIGNIN_KEY_ID }}
|
|
APPLE_SIGNIN_PRIVATE_KEY: ${{ secrets.APPLE_SIGNIN_PRIVATE_KEY }}
|
|
TWITCH_CLIENT_ID: ${{ secrets.TWITCH_CLIENT_ID }}
|
|
TWITCH_CLIENT_SECRET: ${{ secrets.TWITCH_CLIENT_SECRET }}
|
|
SHARDOK_ADDRESS: ${{ secrets.SHARDOK_ADDRESS }}
|
|
SHARDOK_AUTH_TOKEN: ${{ secrets.SHARDOK_AUTH_TOKEN }}
|
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
|
FASTMAIL_API_TOKEN: ${{ secrets.FASTMAIL_API_TOKEN }}
|
|
FASTMAIL_FROM_EMAIL: ${{ secrets.FASTMAIL_FROM_EMAIL }}
|
|
FASTMAIL_FROM_NAME: ${{ secrets.FASTMAIL_FROM_NAME }}
|
|
DO_DROPLET_IP: ${{ secrets.DO_DROPLET_IP }}
|
|
DO_REGISTRY_TOKEN: ${{ secrets.DO_REGISTRY_TOKEN }}
|
|
NOTIFY_SECRET: ${{ secrets.EAGLE_NOTIFY_SECRET }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup SSH key
|
|
run: |
|
|
mkdir -p ~/.ssh
|
|
echo "${{ secrets.DO_SSH_KEY }}" > ~/.ssh/deploy_key
|
|
chmod 600 ~/.ssh/deploy_key
|
|
ssh-keyscan -H "$DO_DROPLET_IP" >> ~/.ssh/known_hosts 2>/dev/null || true
|
|
|
|
- name: Build warmup tool
|
|
run: |
|
|
bazel build //ci:warmup_tar
|
|
mkdir -p scripts/bin
|
|
tar -xf bazel-bin/ci/warmup_tar.tar -C scripts/bin --strip-components=1
|
|
|
|
- name: Copy config files to droplet
|
|
run: |
|
|
# Create directory structure on remote
|
|
ssh -i ~/.ssh/deploy_key deploy@"$DO_DROPLET_IP" bash -s << 'SETUP_DIRS'
|
|
set -e
|
|
mkdir -p /opt/eagle0/scripts/bin /opt/eagle0/nginx
|
|
rm -f /opt/eagle0/scripts/bin/warmup
|
|
SETUP_DIRS
|
|
|
|
# Copy files
|
|
scp -i ~/.ssh/deploy_key docker-compose.prod.yml deploy@"$DO_DROPLET_IP":/opt/eagle0/
|
|
scp -i ~/.ssh/deploy_key nginx/nginx.conf deploy@"$DO_DROPLET_IP":/opt/eagle0/nginx/
|
|
scp -i ~/.ssh/deploy_key scripts/deploy-blue-green.sh scripts/warmup-eagle.sh scripts/eagle-exec.sh scripts/eagle-logs.sh deploy@"$DO_DROPLET_IP":/opt/eagle0/scripts/
|
|
scp -i ~/.ssh/deploy_key scripts/bin/warmup deploy@"$DO_DROPLET_IP":/opt/eagle0/scripts/bin/
|
|
|
|
- name: Deploy to production droplet
|
|
run: |
|
|
ssh -i ~/.ssh/deploy_key deploy@"$DO_DROPLET_IP" bash -s << DEPLOY_SCRIPT
|
|
set -ex
|
|
cd /opt/eagle0
|
|
|
|
# =================================================================
|
|
# CRITICAL: Validate environment variables before proceeding
|
|
# This catches GitHub Actions secret store hiccups early
|
|
# =================================================================
|
|
validate_env() {
|
|
local var_name="\$1"
|
|
local var_value="\$2"
|
|
local default_value="\${3:-}"
|
|
|
|
if [ -z "\${var_value}" ]; then
|
|
echo "ERROR: \${var_name} is empty. GitHub Actions secrets may have failed to load."
|
|
echo "Please retry the workflow."
|
|
return 1
|
|
fi
|
|
|
|
# Check if we got a default value instead of the real secret
|
|
if [ -n "\${default_value}" ] && [ "\${var_value}" = "\${default_value}" ]; then
|
|
echo "ERROR: \${var_name} has default value '\${default_value}' instead of the actual secret."
|
|
echo "This indicates GitHub Actions secrets failed to load. Please retry the workflow."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
echo "Validating critical environment variables..."
|
|
|
|
# These are the raw values from GitHub Actions (before export)
|
|
# We check them before exporting to catch issues early
|
|
VALIDATION_FAILED=0
|
|
|
|
validate_env "SHARDOK_ADDRESS" "${SHARDOK_ADDRESS}" "" || VALIDATION_FAILED=1
|
|
validate_env "EAGLE_IMAGE" "${EAGLE_IMAGE}" "" || VALIDATION_FAILED=1
|
|
validate_env "JWT_PRIVATE_KEY" "${JWT_PRIVATE_KEY}" "" || VALIDATION_FAILED=1
|
|
validate_env "DO_REGISTRY_TOKEN" "${DO_REGISTRY_TOKEN}" "" || VALIDATION_FAILED=1
|
|
|
|
if [ "\${VALIDATION_FAILED}" -eq 1 ]; then
|
|
echo ""
|
|
echo "========================================="
|
|
echo "DEPLOYMENT ABORTED: Missing critical secrets"
|
|
echo "This is likely a transient GitHub Actions issue."
|
|
echo "Please retry the workflow."
|
|
echo "========================================="
|
|
exit 1
|
|
fi
|
|
|
|
echo "All critical environment variables validated successfully."
|
|
|
|
# =================================================================
|
|
# Export environment variables for docker compose
|
|
# These are passed via heredoc and exported so child processes (docker compose) can access them
|
|
export EAGLE_IMAGE="${EAGLE_IMAGE}"
|
|
export ADMIN_IMAGE="${ADMIN_IMAGE}"
|
|
export JFR_SIDECAR_IMAGE="${JFR_SIDECAR_IMAGE}"
|
|
export OPENAI_API_KEY="${OPENAI_API_KEY}"
|
|
export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY}"
|
|
export GEMINI_API_KEY="${GEMINI_API_KEY}"
|
|
export GPT_MODEL_NAME="${GPT_MODEL_NAME:-gpt-4o}"
|
|
export EAGLE_ENABLE_S3="${EAGLE_ENABLE_S3:-false}"
|
|
export DO_SPACES_ACCESS_KEY="${DO_SPACES_ACCESS_KEY}"
|
|
export DO_SPACES_SECRET_KEY="${DO_SPACES_SECRET_KEY}"
|
|
export JWT_PRIVATE_KEY="${JWT_PRIVATE_KEY}"
|
|
export DISCORD_CLIENT_ID="${DISCORD_CLIENT_ID}"
|
|
export DISCORD_CLIENT_SECRET="${DISCORD_CLIENT_SECRET}"
|
|
export GOOGLE_CLIENT_ID="${GOOGLE_CLIENT_ID}"
|
|
export GOOGLE_CLIENT_SECRET="${GOOGLE_CLIENT_SECRET}"
|
|
export GH_OAUTH_CLIENT_ID="${GH_OAUTH_CLIENT_ID}"
|
|
export GH_OAUTH_CLIENT_SECRET="${GH_OAUTH_CLIENT_SECRET}"
|
|
export APPLE_SIGNIN_CLIENT_ID="${APPLE_SIGNIN_CLIENT_ID}"
|
|
export APPLE_TEAM_ID="${APPLE_TEAM_ID}"
|
|
export APPLE_SIGNIN_KEY_ID="${APPLE_SIGNIN_KEY_ID}"
|
|
export APPLE_SIGNIN_PRIVATE_KEY="${APPLE_SIGNIN_PRIVATE_KEY}"
|
|
export TWITCH_CLIENT_ID="${TWITCH_CLIENT_ID}"
|
|
export TWITCH_CLIENT_SECRET="${TWITCH_CLIENT_SECRET}"
|
|
export SHARDOK_ADDRESS="${SHARDOK_ADDRESS}"
|
|
export SHARDOK_AUTH_TOKEN="${SHARDOK_AUTH_TOKEN}"
|
|
export SENTRY_DSN="${SENTRY_DSN}"
|
|
export FASTMAIL_API_TOKEN="${FASTMAIL_API_TOKEN}"
|
|
export FASTMAIL_FROM_EMAIL="${FASTMAIL_FROM_EMAIL}"
|
|
export FASTMAIL_FROM_NAME="${FASTMAIL_FROM_NAME}"
|
|
export DO_REGISTRY_TOKEN="${DO_REGISTRY_TOKEN}"
|
|
export NOTIFY_SECRET="${NOTIFY_SECRET}"
|
|
|
|
# Check Docker has IPv6 support
|
|
if ! cat /etc/docker/daemon.json 2>/dev/null | grep -q '"ip6tables"'; then
|
|
echo "WARNING: Docker IPv6 not configured. Eagle may not reach Hetzner Shardok."
|
|
fi
|
|
|
|
# Login to registry
|
|
echo "\$DO_REGISTRY_TOKEN" | docker login registry.digitalocean.com -u "\$DO_REGISTRY_TOKEN" --password-stdin
|
|
|
|
echo "Using images: \$EAGLE_IMAGE, \$ADMIN_IMAGE, \$JFR_SIDECAR_IMAGE"
|
|
|
|
# Install crane for pulling OCI images
|
|
echo "Installing crane..."
|
|
rm -f crane
|
|
curl -sL https://github.com/google/go-containerregistry/releases/download/v0.20.2/go-containerregistry_Linux_x86_64.tar.gz | tar xzf - crane
|
|
chmod +x crane
|
|
|
|
# Pull and load all images
|
|
echo "Pulling Eagle image..."
|
|
./crane pull "\${EAGLE_IMAGE}" eagle.tar && docker load -i eagle.tar && rm eagle.tar
|
|
|
|
echo "Pulling Admin image..."
|
|
./crane pull "\${ADMIN_IMAGE}" admin.tar && docker load -i admin.tar && rm admin.tar
|
|
# Tag as :latest locally so docker-compose fallback uses correct image
|
|
docker tag "\${ADMIN_IMAGE}" registry.digitalocean.com/eagle0/admin-server:latest
|
|
|
|
echo "Pulling JFR Sidecar image..."
|
|
./crane pull "\${JFR_SIDECAR_IMAGE}" jfr-sidecar.tar && docker load -i jfr-sidecar.tar && rm jfr-sidecar.tar
|
|
|
|
# Pull other compose images
|
|
docker pull nginx:alpine || true
|
|
docker pull certbot/certbot || true
|
|
|
|
echo "All images pulled successfully"
|
|
|
|
# =================================================================
|
|
# Verify Shardok connectivity before proceeding with deployment
|
|
# This catches network/firewall issues early
|
|
# =================================================================
|
|
echo "Verifying Shardok connectivity..."
|
|
SHARDOK_HOST=\$(echo "\${SHARDOK_ADDRESS}" | cut -d: -f1)
|
|
SHARDOK_PORT=\$(echo "\${SHARDOK_ADDRESS}" | cut -d: -f2)
|
|
|
|
# Try to connect to Shardok (timeout after 10 seconds)
|
|
if nc -z -w 10 "\${SHARDOK_HOST}" "\${SHARDOK_PORT}" 2>/dev/null; then
|
|
echo "Shardok connectivity verified: \${SHARDOK_ADDRESS} is reachable"
|
|
else
|
|
echo ""
|
|
echo "========================================="
|
|
echo "ERROR: Cannot reach Shardok at \${SHARDOK_ADDRESS}"
|
|
echo "This may indicate:"
|
|
echo " - Shardok server is not running on Hetzner"
|
|
echo " - Network/firewall issues between DigitalOcean and Hetzner"
|
|
echo " - Incorrect SHARDOK_ADDRESS configuration"
|
|
echo ""
|
|
echo "DEPLOYMENT ABORTED: Shardok must be reachable for battles to work."
|
|
echo "========================================="
|
|
exit 1
|
|
fi
|
|
|
|
# Stop local shardok container if running (now runs on Hetzner)
|
|
docker stop shardok-server 2>/dev/null || true
|
|
docker rm shardok-server 2>/dev/null || true
|
|
|
|
# Deploy Eagle with blue-green (handles eagle, nginx, admin, jfr-sidecar)
|
|
# Note: Shardok runs on Hetzner, deployed separately via shardok_arm64_build.yml
|
|
# Note: Auth is deployed separately via auth_build.yml - do NOT touch auth here
|
|
chmod +x /opt/eagle0/scripts/*.sh
|
|
[ -f "/opt/eagle0/scripts/bin/warmup" ] && chmod +x /opt/eagle0/scripts/bin/warmup
|
|
GIT_SHA=\$(echo "\${EAGLE_IMAGE}" | sed 's/.*://')
|
|
/opt/eagle0/scripts/deploy-blue-green.sh "\${GIT_SHA}"
|
|
|
|
# Verify
|
|
sleep 10
|
|
docker compose -f docker-compose.prod.yml ps
|
|
docker compose -f docker-compose.prod.yml images
|
|
|
|
# Verify admin container is running the correct image
|
|
echo "=== Verifying admin container image ==="
|
|
ADMIN_RUNNING_DIGEST=\$(docker inspect admin-server --format '{{.Image}}')
|
|
ADMIN_EXPECTED_DIGEST=\$(docker inspect "\${ADMIN_IMAGE}" --format '{{.Id}}')
|
|
echo "Expected image: \${ADMIN_IMAGE}"
|
|
echo "Expected digest: \${ADMIN_EXPECTED_DIGEST}"
|
|
echo "Running digest: \${ADMIN_RUNNING_DIGEST}"
|
|
if [ "\${ADMIN_RUNNING_DIGEST}" != "\${ADMIN_EXPECTED_DIGEST}" ]; then
|
|
echo "ERROR: Admin container is running wrong image!"
|
|
echo "Container entrypoint:"
|
|
docker inspect admin-server --format '{{.Config.Entrypoint}}'
|
|
exit 1
|
|
fi
|
|
echo "Admin image verification passed"
|
|
|
|
# Cleanup - remove old images to prevent disk full
|
|
docker container prune -f
|
|
docker image prune -f
|
|
# Remove images older than 24h (keeps recent images for rollback)
|
|
docker image prune -a -f --filter "until=24h" || true
|
|
DEPLOY_SCRIPT
|