mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 23:21:08 +02:00
Two bugs made the Mac Mini auto-deploy silently miss everything on a multi-commit push: 1. Diff range was HEAD~1..HEAD, so a push with N commits only checked the tip. Now uses github.event.before..sha, with a safe fallback to HEAD~1 when the before SHA is absent (first push, force reset). 2. Service list was still the legacy per-product web/backend apps (todo-web, chat-web, calendar-web, …) that were consolidated into `mana-web` + `mana-api` months ago. The unified services didn't exist in the workflow, so a push touching apps/mana/apps/web or apps/api never rebuilt them. Rewrite: - Collapse per-service outputs into one `services` output driven by a SERVICE_SOURCES array (add a new service by adding one line). - Expanded service surface: mana-ai, mana-research, mana-events, mana-user, mana-subscriptions, mana-analytics, mana-llm, mana-api, mana-web, mana-credits, mana-geocoding, manavoxel-web — alongside the Go services + memoro + landing-builder. - Removed dead entries: todo/chat/calendar/clock/contacts/music/ storage/memoro-web variants. - Expanded sveltekit-base trigger (any commit to shared-pwa / shared-vite-config / root Dockerfile / pnpm-lock forces a base rebuild — those were invisible before). - Updated health-check URLs from the running containers' actual host ports (PORT_SCHEMA.md prose + table disagreed; docker ps wins). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
551 lines
22 KiB
YAML
551 lines
22 KiB
YAML
# CD Pipeline: Auto-deploy to Mac Mini on push to main
|
|
#
|
|
# Requires a self-hosted GitHub Actions runner on the Mac Mini.
|
|
# Setup: see docs/MAC_MINI_RUNNER_SETUP.md
|
|
#
|
|
# Flow:
|
|
# Push → main : Detects changed services across the FULL push range
|
|
# (github.event.before..sha), rebuilds & restarts only
|
|
# those containers.
|
|
#
|
|
# Service → path mapping lives in one array (SERVICE_SOURCES) — add a
|
|
# new service by adding one line. The Deploy job reads a single
|
|
# `services` output, so no per-service wiring in two places.
|
|
|
|
name: CD Mac Mini
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
inputs:
|
|
service:
|
|
description: 'Service to deploy (or "all" for everything)'
|
|
required: false
|
|
default: 'all'
|
|
type: choice
|
|
options:
|
|
- all
|
|
- mana-auth
|
|
- mana-ai
|
|
- mana-credits
|
|
- mana-research
|
|
- mana-events
|
|
- mana-geocoding
|
|
- mana-user
|
|
- mana-subscriptions
|
|
- mana-analytics
|
|
- mana-search
|
|
- mana-sync
|
|
- mana-notify
|
|
- mana-crawler
|
|
- mana-api-gateway
|
|
- mana-media
|
|
- mana-llm
|
|
- mana-landing-builder
|
|
- mana-web
|
|
- mana-api
|
|
- manavoxel-web
|
|
- memoro-server
|
|
- memoro-audio-server
|
|
|
|
concurrency:
|
|
group: cd-macmini
|
|
cancel-in-progress: false # Don't cancel in-progress deploys
|
|
|
|
env:
|
|
PROJECT_DIR: /Users/mana/projects/mana-monorepo
|
|
COMPOSE_FILE: docker-compose.macmini.yml
|
|
ENV_FILE: .env.macmini
|
|
DOCKER_BUILDKIT: 1
|
|
PATH: /usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
|
|
|
jobs:
|
|
# ===========================================
|
|
# Detect what changed
|
|
# ===========================================
|
|
detect-changes:
|
|
name: Detect Changes
|
|
runs-on: self-hosted
|
|
if: github.event_name == 'push'
|
|
outputs:
|
|
services: ${{ steps.changes.outputs.services }}
|
|
any-changes: ${{ steps.changes.outputs.any-changes }}
|
|
steps:
|
|
- name: Check for changes
|
|
id: changes
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
|
|
# Compare the full push range, not just the tip. github.event.before
|
|
# is the commit main pointed at BEFORE this push — so a push of N
|
|
# commits lands with before = HEAD~N. Previously we only looked at
|
|
# HEAD~1 HEAD, which made pushes of more than one commit silently
|
|
# drop all-but-the-tip's changes.
|
|
BEFORE="${{ github.event.before }}"
|
|
AFTER="${{ github.sha }}"
|
|
if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
|
|
# First push to the branch / force-push with no shared history.
|
|
# Fall back to the last commit only — better than failing open.
|
|
echo "No valid 'before' SHA (first push or force reset), using HEAD~1"
|
|
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
|
|
else
|
|
# Make sure we actually have the 'before' commit locally. The
|
|
# self-hosted runner works in a long-lived clone that usually does,
|
|
# but a forced push to a rebased branch can leave it missing.
|
|
if ! git cat-file -e "$BEFORE^{commit}" 2>/dev/null; then
|
|
echo "Fetching origin to resolve $BEFORE..."
|
|
git fetch origin main --no-tags --quiet || true
|
|
fi
|
|
CHANGED=$(git diff --name-only "$BEFORE" "$AFTER" 2>/dev/null || git diff --name-only HEAD~1 HEAD)
|
|
fi
|
|
|
|
# Shared packages trigger rebuilds for all web services that import
|
|
# them. Go services use packages/shared-go/ and are handled per-svc.
|
|
SHARED_CHANGED="false"
|
|
if echo "$CHANGED" | grep -qE "^packages/(shared-ui|shared-theme|shared-icons|shared-tailwind|shared-auth|shared-branding|shared-i18n|shared-utils|shared-types|shared-pwa|shared-vite-config)/"; then
|
|
SHARED_CHANGED="true"
|
|
fi
|
|
|
|
# Root-level SvelteKit base Dockerfile or pnpm-lock also force a
|
|
# web-app rebuild — the per-app Dockerfiles do FROM sveltekit-base.
|
|
WEB_BASE_CHANGED="false"
|
|
if echo "$CHANGED" | grep -qE "^(docker/Dockerfile.sveltekit-base|pnpm-lock.yaml)$"; then
|
|
WEB_BASE_CHANGED="true"
|
|
fi
|
|
|
|
# Service → source paths. One line per compose service. Space-
|
|
# separated paths are OR'd: any of them matching triggers a rebuild.
|
|
# Keep names aligned with `docker compose config --services`.
|
|
SERVICE_SOURCES=(
|
|
"mana-auth|services/mana-auth/"
|
|
"mana-ai|services/mana-ai/"
|
|
"mana-credits|services/mana-credits/"
|
|
"mana-research|services/mana-research/"
|
|
"mana-events|services/mana-events/"
|
|
"mana-geocoding|services/mana-geocoding/"
|
|
"mana-user|services/mana-user/"
|
|
"mana-subscriptions|services/mana-subscriptions/"
|
|
"mana-analytics|services/mana-analytics/"
|
|
"mana-search|services/mana-search/ packages/shared-go/"
|
|
"mana-sync|services/mana-sync/ packages/shared-go/"
|
|
"mana-notify|services/mana-notify/ packages/shared-go/"
|
|
"mana-crawler|services/mana-crawler/ packages/shared-go/"
|
|
"mana-api-gateway|services/mana-api-gateway/ packages/shared-go/"
|
|
"mana-media|services/mana-media/"
|
|
"mana-llm|services/mana-llm/"
|
|
"mana-landing-builder|services/mana-landing-builder/ packages/shared-landing-ui/ packages/shared-types/"
|
|
"mana-web|apps/mana/apps/web/ apps/mana/packages/"
|
|
"mana-api|apps/api/"
|
|
"manavoxel-web|apps/manavoxel/apps/web/ apps/manavoxel/packages/"
|
|
"memoro-server|apps/memoro/apps/server/ apps/memoro/packages/"
|
|
"memoro-audio-server|apps/memoro/apps/audio-server/"
|
|
)
|
|
|
|
echo "Changed files (first 40):"
|
|
echo "$CHANGED" | head -40
|
|
echo ""
|
|
echo "Shared web packages changed: $SHARED_CHANGED"
|
|
echo "SvelteKit base changed: $WEB_BASE_CHANGED"
|
|
echo ""
|
|
|
|
SERVICES=""
|
|
for entry in "${SERVICE_SOURCES[@]}"; do
|
|
name="${entry%%|*}"
|
|
paths="${entry#*|}"
|
|
|
|
hit="false"
|
|
for path in $paths; do
|
|
if echo "$CHANGED" | grep -q "^$path"; then
|
|
hit="true"
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Web services also rebuild on shared-package or base-image churn
|
|
case "$name" in
|
|
*-web|mana-api|mana-landing-builder)
|
|
if [ "$SHARED_CHANGED" = "true" ] || [ "$WEB_BASE_CHANGED" = "true" ]; then
|
|
hit="true"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [ "$hit" = "true" ]; then
|
|
SERVICES="$SERVICES $name"
|
|
echo " ✓ $name"
|
|
fi
|
|
done
|
|
|
|
# Trim leading whitespace
|
|
SERVICES="${SERVICES# }"
|
|
ANY="false"
|
|
[ -n "$SERVICES" ] && ANY="true"
|
|
|
|
echo ""
|
|
echo "Deploy list: ${SERVICES:-<none>}"
|
|
echo "services=$SERVICES" >> "$GITHUB_OUTPUT"
|
|
echo "any-changes=$ANY" >> "$GITHUB_OUTPUT"
|
|
|
|
# ===========================================
|
|
# Deploy changed services
|
|
# ===========================================
|
|
deploy:
|
|
name: Deploy
|
|
runs-on: self-hosted
|
|
needs: [detect-changes]
|
|
if: |
|
|
always() &&
|
|
(needs.detect-changes.result == 'success' && needs.detect-changes.outputs.any-changes == 'true') ||
|
|
github.event_name == 'workflow_dispatch'
|
|
steps:
|
|
- name: Pull latest code
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
git pull origin main
|
|
|
|
- name: Init deploy tracking
|
|
id: init
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
source scripts/deploy-metrics.sh
|
|
deploy_timer_start
|
|
echo "start_epoch=$DEPLOY_START_EPOCH" >> $GITHUB_OUTPUT
|
|
ensure_deploy_schema
|
|
|
|
- name: Ensure env vars exist
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
# Add CALENDAR_ENCRYPTION_KEY if not present
|
|
if ! grep -q "CALENDAR_ENCRYPTION_KEY" "${{ env.ENV_FILE }}" 2>/dev/null; then
|
|
echo "CALENDAR_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> "${{ env.ENV_FILE }}"
|
|
echo "Added CALENDAR_ENCRYPTION_KEY to ${{ env.ENV_FILE }}"
|
|
fi
|
|
|
|
- name: Determine services to deploy
|
|
id: services
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
|
INPUT="${{ inputs.service }}"
|
|
if [ "$INPUT" == "all" ]; then
|
|
echo "Manual deploy: all services"
|
|
echo "deploy-all=true" >> $GITHUB_OUTPUT
|
|
echo "services=" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
echo "services=$INPUT" >> $GITHUB_OUTPUT
|
|
echo "deploy-all=false" >> $GITHUB_OUTPUT
|
|
echo "Services to deploy: $INPUT"
|
|
else
|
|
SERVICES="${{ needs.detect-changes.outputs.services }}"
|
|
echo "services=$SERVICES" >> $GITHUB_OUTPUT
|
|
echo "deploy-all=false" >> $GITHUB_OUTPUT
|
|
echo "Services to deploy: $SERVICES"
|
|
fi
|
|
|
|
- name: Build shared base image
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
SERVICES="${{ steps.services.outputs.services }}"
|
|
DEPLOY_ALL="${{ steps.services.outputs.deploy-all }}"
|
|
|
|
NEEDS_WEB_BASE=false
|
|
if [ "$DEPLOY_ALL" == "true" ]; then
|
|
NEEDS_WEB_BASE=true
|
|
else
|
|
for svc in $SERVICES; do
|
|
case "$svc" in *-web|mana-api|mana-landing-builder) NEEDS_WEB_BASE=true; break ;; esac
|
|
done
|
|
fi
|
|
|
|
if [ "$NEEDS_WEB_BASE" == "true" ]; then
|
|
echo "=== Building shared SvelteKit base image ==="
|
|
docker build -f docker/Dockerfile.sveltekit-base -t sveltekit-base:local . 2>&1 | tail -5
|
|
echo "SvelteKit base image built"
|
|
else
|
|
echo "No web apps to deploy, skipping SvelteKit base image"
|
|
fi
|
|
|
|
- name: Build and deploy services
|
|
id: build
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
source scripts/deploy-metrics.sh
|
|
|
|
DEPLOY_ALL="${{ steps.services.outputs.deploy-all }}"
|
|
SERVICES="${{ steps.services.outputs.services }}"
|
|
|
|
# Determine final service list
|
|
if [ "$DEPLOY_ALL" == "true" ]; then
|
|
# Get all service names from compose file
|
|
SERVICES=$(docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" config --services | tr '\n' ' ')
|
|
echo "=== Rebuilding ALL services ==="
|
|
elif [ -z "$SERVICES" ]; then
|
|
echo "No services to deploy"
|
|
echo "build-times=" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
else
|
|
echo "=== Rebuilding: $SERVICES ==="
|
|
fi
|
|
|
|
# Build each service individually to capture build times
|
|
BUILD_TIMES=""
|
|
for svc in $SERVICES; do
|
|
echo "--- Building $svc ---"
|
|
build_start=$(date +%s)
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build "$svc" 2>&1 || true
|
|
build_end=$(date +%s)
|
|
build_dur=$(( build_end - build_start ))
|
|
BUILD_TIMES="$BUILD_TIMES $svc:$build_dur"
|
|
echo " $svc built in ${build_dur}s"
|
|
done
|
|
|
|
# Start all services at once (no rebuild, images already built)
|
|
echo "=== Starting services ==="
|
|
if [ "$DEPLOY_ALL" == "true" ]; then
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d
|
|
else
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --no-deps $SERVICES
|
|
fi
|
|
echo "=== Waiting for services to start ==="
|
|
sleep 10
|
|
|
|
echo "build-times=$BUILD_TIMES" >> $GITHUB_OUTPUT
|
|
|
|
- name: Health checks
|
|
id: health
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
source scripts/deploy-metrics.sh
|
|
|
|
# Service → internal health URL (localhost, post-deploy curl on the
|
|
# Mac Mini). Services without an entry here get a "no health endpoint
|
|
# configured" skip — check_health_timed handles it gracefully.
|
|
health_url_for() {
|
|
case "$1" in
|
|
mana-auth) echo "http://localhost:3001/health" ;;
|
|
mana-ai) echo "http://localhost:3067/health" ;;
|
|
mana-credits) echo "http://localhost:3002/health" ;;
|
|
mana-research) echo "http://localhost:3068/health" ;;
|
|
mana-geocoding) echo "http://localhost:3018/health" ;;
|
|
mana-user) echo "http://localhost:3062/health" ;;
|
|
mana-subscriptions) echo "http://localhost:3063/health" ;;
|
|
mana-analytics) echo "http://localhost:3064/health" ;;
|
|
mana-search) echo "http://localhost:3012/health" ;;
|
|
mana-sync) echo "http://localhost:3050/health" ;;
|
|
mana-notify) echo "http://localhost:3013/health" ;;
|
|
mana-crawler) echo "http://localhost:3014/health" ;;
|
|
mana-api-gateway) echo "http://localhost:3016/health" ;;
|
|
mana-media) echo "http://localhost:3011/health" ;;
|
|
mana-llm) echo "http://localhost:3025/health" ;;
|
|
mana-events) echo "http://localhost:3065/health" ;;
|
|
mana-api) echo "http://localhost:3060/health" ;;
|
|
mana-web) echo "http://localhost:5000/health" ;;
|
|
manavoxel-web) echo "http://localhost:5028/health" ;;
|
|
memoro-server) echo "http://localhost:3015/health" ;;
|
|
*) echo "" ;;
|
|
esac
|
|
}
|
|
|
|
DEPLOY_ALL="${{ steps.services.outputs.deploy-all }}"
|
|
SERVICES="${{ steps.services.outputs.services }}"
|
|
|
|
# On "deploy all", health-check a representative set (add more as
|
|
# endpoints stabilise). Per-push deploys only check what was rebuilt.
|
|
if [ "$DEPLOY_ALL" == "true" ]; then
|
|
SERVICES="mana-auth mana-ai mana-credits mana-research mana-user mana-subscriptions mana-analytics mana-sync mana-api-gateway mana-api mana-web"
|
|
fi
|
|
|
|
HEALTH_RESULTS=""
|
|
echo "=== Health Checks ==="
|
|
|
|
for svc in $SERVICES; do
|
|
url=$(health_url_for "$svc")
|
|
if [ -z "$url" ]; then
|
|
echo " - $svc: no health endpoint configured"
|
|
HEALTH_RESULTS="$HEALTH_RESULTS $svc:skipped:0:0"
|
|
continue
|
|
fi
|
|
|
|
result=$(check_health_timed "$svc" "$url" 2>/dev/null) || true
|
|
status=$(echo "$result" | awk '{print $1}')
|
|
elapsed=$(echo "$result" | awk '{print $2}')
|
|
http_code=$(echo "$result" | awk '{print $3}')
|
|
|
|
if [ -z "$status" ]; then
|
|
status="skipped"
|
|
elapsed="0"
|
|
http_code="0"
|
|
fi
|
|
|
|
if [ "$status" = "ok" ]; then
|
|
echo " ✓ $svc: OK (${elapsed}s)"
|
|
else
|
|
echo " ✗ $svc: $status (HTTP $http_code, ${elapsed}s)"
|
|
fi
|
|
|
|
HEALTH_RESULTS="$HEALTH_RESULTS $svc:$status:$http_code:$elapsed"
|
|
done
|
|
|
|
echo "health-results=$HEALTH_RESULTS" >> $GITHUB_OUTPUT
|
|
|
|
- name: Record deploy metrics
|
|
if: always()
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
source scripts/deploy-metrics.sh
|
|
|
|
START_EPOCH="${{ steps.init.outputs.start_epoch }}"
|
|
NOW=$(date +%s)
|
|
DURATION=$(( NOW - START_EPOCH ))
|
|
|
|
# Determine overall status
|
|
STATUS="success"
|
|
if [ "${{ job.status }}" != "success" ]; then
|
|
STATUS="failure"
|
|
fi
|
|
|
|
# Determine services list
|
|
DEPLOY_ALL="${{ steps.services.outputs.deploy-all }}"
|
|
SERVICES="${{ steps.services.outputs.services }}"
|
|
if [ "$DEPLOY_ALL" == "true" ]; then
|
|
SERVICES_CSV="all"
|
|
else
|
|
SERVICES_CSV=$(echo "$SERVICES" | tr ' ' ',')
|
|
fi
|
|
|
|
COMMIT_MSG=$(git log -1 --pretty=%s 2>/dev/null | head -c 200 || echo "unknown")
|
|
BRANCH="${{ github.ref_name }}"
|
|
|
|
# Insert deployment row
|
|
DEPLOY_ID=$(insert_deployment \
|
|
"${{ github.run_id }}" \
|
|
"${{ github.run_attempt }}" \
|
|
"${{ github.sha }}" \
|
|
"$COMMIT_MSG" \
|
|
"$BRANCH" \
|
|
"${{ github.event_name }}" \
|
|
"${{ github.actor }}" \
|
|
"$SERVICES_CSV" \
|
|
"$STATUS" 2>/dev/null) || DEPLOY_ID=""
|
|
|
|
if [ -n "$DEPLOY_ID" ]; then
|
|
# Finalise with duration
|
|
finalise_deployment "$DEPLOY_ID" "$STATUS" "$DURATION" 2>/dev/null || true
|
|
|
|
# Helper: lookup value from "key:val key2:val2" string
|
|
# Usage: lookup "key" "key:val key2:val2" [field_index] (default: 2nd field)
|
|
lookup() {
|
|
local needle="$1" haystack="$2" field="${3:-2}"
|
|
for item in $haystack; do
|
|
if [ "${item%%:*}" = "$needle" ]; then
|
|
echo "$item" | cut -d: -f"$field"
|
|
return
|
|
fi
|
|
done
|
|
echo ""
|
|
}
|
|
|
|
BUILD_TIMES="${{ steps.build.outputs.build-times }}"
|
|
HEALTH_RESULTS="${{ steps.health.outputs.health-results }}"
|
|
|
|
# Collect unique service names from both build and health data
|
|
ALL_SVCS=$(echo "$BUILD_TIMES $HEALTH_RESULTS" | tr ' ' '\n' | cut -d: -f1 | sort -u | tr '\n' ' ')
|
|
for svc in $ALL_SVCS; do
|
|
[ -z "$svc" ] && continue
|
|
build_dur=$(lookup "$svc" "$BUILD_TIMES" 2)
|
|
build_dur="${build_dur:-0}"
|
|
img_mb=$(get_image_size_mb "$svc" 2>/dev/null || echo "0")
|
|
startup=$(lookup "$svc" "$HEALTH_RESULTS" 4)
|
|
startup="${startup:-0}"
|
|
health=$(lookup "$svc" "$HEALTH_RESULTS" 2)
|
|
health="${health:-skipped}"
|
|
http_code=$(lookup "$svc" "$HEALTH_RESULTS" 3)
|
|
http_code="${http_code:-0}"
|
|
|
|
insert_deploy_service "$DEPLOY_ID" "$svc" "$build_dur" "$img_mb" "$startup" "$health" "$http_code" 2>/dev/null || true
|
|
push_service_metrics "$svc" "$build_dur" "$img_mb" "$health" 2>/dev/null || true
|
|
done
|
|
fi
|
|
|
|
# Push overall metrics to Pushgateway
|
|
push_deploy_metrics "$STATUS" "$DURATION" "$BRANCH" 2>/dev/null || true
|
|
echo "Deploy tracking recorded: status=$STATUS duration=${DURATION}s"
|
|
|
|
- name: Notify on failure
|
|
if: failure()
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
SERVICES="${{ steps.services.outputs.services }}"
|
|
[ "${{ steps.services.outputs.deploy-all }}" == "true" ] && SERVICES="all"
|
|
COMMIT_MSG=$(git log -1 --pretty=%s 2>/dev/null | head -c 100)
|
|
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
|
|
MSG="⚠️ Deploy failed: ${SERVICES} (commit ${COMMIT_MSG} by ${{ github.actor }}) — ${RUN_URL}"
|
|
echo "$MSG"
|
|
|
|
- name: Cleanup old images
|
|
if: always()
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
echo "=== Pruning dangling images ==="
|
|
docker image prune -f 2>/dev/null || true
|
|
echo "=== Pruning unused images older than 7 days ==="
|
|
docker image prune -a -f --filter "until=168h" 2>/dev/null || true
|
|
|
|
- name: Summary
|
|
if: always()
|
|
run: |
|
|
cd "${{ env.PROJECT_DIR }}"
|
|
|
|
START_EPOCH="${{ steps.init.outputs.start_epoch }}"
|
|
NOW=$(date +%s)
|
|
DURATION=$(( NOW - START_EPOCH ))
|
|
|
|
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Duration:** ${DURATION}s" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
if [ "${{ steps.services.outputs.deploy-all }}" == "true" ]; then
|
|
echo "**Services:** All" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "**Services:** ${{ steps.services.outputs.services }}" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
# Build times table
|
|
BUILD_TIMES="${{ steps.build.outputs.build-times }}"
|
|
if [ -n "$BUILD_TIMES" ]; then
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### Build Times" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Service | Duration |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|---------|----------|" >> $GITHUB_STEP_SUMMARY
|
|
for entry in $BUILD_TIMES; do
|
|
svc="${entry%%:*}"
|
|
dur="${entry#*:}"
|
|
echo "| $svc | ${dur}s |" >> $GITHUB_STEP_SUMMARY
|
|
done
|
|
fi
|
|
|
|
# Health results table
|
|
HEALTH_RESULTS="${{ steps.health.outputs.health-results }}"
|
|
if [ -n "$HEALTH_RESULTS" ]; then
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### Health Checks" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Service | Status | HTTP | Startup |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|---------|--------|------|---------|" >> $GITHUB_STEP_SUMMARY
|
|
for entry in $HEALTH_RESULTS; do
|
|
svc=$(echo "$entry" | cut -d: -f1)
|
|
h_status=$(echo "$entry" | cut -d: -f2)
|
|
h_code=$(echo "$entry" | cut -d: -f3)
|
|
h_time=$(echo "$entry" | cut -d: -f4)
|
|
icon="✓"
|
|
[ "$h_status" != "ok" ] && icon="✗"
|
|
echo "| $svc | $icon $h_status | $h_code | ${h_time}s |" >> $GITHUB_STEP_SUMMARY
|
|
done
|
|
fi
|