chore(ci): remove legacy Docker workflows (moved to Nix)
Docker was decommissioned 2026-08-02 but docker-build-push.yml and deploy-docker.yml were left behind. They still listened to repository_dispatch: [submodule-updated], so every app push queued a redundant Docker build alongside the intended Nix deploy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
722709f6fb
commit
b7b60e9125
@@ -1,239 +0,0 @@
|
|||||||
name: Deploy Docker to VPS
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ['Build and Push Docker Images']
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- 'infra/**'
|
|
||||||
- '.github/workflows/deploy-docker.yml'
|
|
||||||
- '.github/workflows/docker-build-push.yml'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# Prevent multiple deployments from running simultaneously
|
|
||||||
concurrency:
|
|
||||||
group: deploy-vps
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 30
|
|
||||||
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.workflow_run.conclusion == 'success'
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
submodules: false
|
|
||||||
|
|
||||||
- name: Deploy to VPS
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
VPS_HOST: ${{ secrets.VPS_HOST }}
|
|
||||||
VPS_USER: ${{ secrets.VPS_USER }}
|
|
||||||
VPS_TARGET_DIR: ${{ secrets.VPS_TARGET_DIR }}
|
|
||||||
ENV_FILE_PRODUCTION: ${{ secrets.ENV_FILE_PRODUCTION }}
|
|
||||||
GHCR_USERNAME: ${{ github.actor }}
|
|
||||||
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
echo "Deploy event: ${{ github.event_name }}"
|
|
||||||
echo "Deploy ref: ${{ github.ref }}"
|
|
||||||
echo "Deploy sha: ${{ github.sha }}"
|
|
||||||
|
|
||||||
if [ -z "${SSH_PRIVATE_KEY:-}" ] || [ -z "${VPS_HOST:-}" ] || [ -z "${VPS_USER:-}" ] || [ -z "${VPS_TARGET_DIR:-}" ]; then
|
|
||||||
echo "❌ Deployment secrets are not fully configured. Please set SSH_PRIVATE_KEY, VPS_HOST, VPS_USER, and VPS_TARGET_DIR."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
|
||||||
chmod 600 ~/.ssh/id_rsa
|
|
||||||
ssh-keyscan -H -t ed25519,rsa "$VPS_HOST" >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
# Use SSH multiplexing for faster subsequent commands
|
|
||||||
SSH_OPTS=(-o ControlMaster=auto -o ControlPath=/tmp/ssh-%r@%h:%p -o ControlPersist=600 -o StrictHostKeyChecking=yes)
|
|
||||||
|
|
||||||
ssh "${SSH_OPTS[@]}" "$VPS_USER@$VPS_HOST" "mkdir -p $VPS_TARGET_DIR && mkdir -p $VPS_TARGET_DIR/infra/compose"
|
|
||||||
echo "$ENV_FILE_PRODUCTION" > .env.prod
|
|
||||||
scp "${SSH_OPTS[@]}" .env.prod "$VPS_USER@$VPS_HOST:$VPS_TARGET_DIR/.env"
|
|
||||||
|
|
||||||
echo "🔐 Logging in to GitHub Container Registry..."
|
|
||||||
printf '%s' "$GHCR_TOKEN" | ssh "${SSH_OPTS[@]}" "$VPS_USER@$VPS_HOST" "docker login ghcr.io -u '$GHCR_USERNAME' --password-stdin"
|
|
||||||
|
|
||||||
ssh "${SSH_OPTS[@]}" "$VPS_USER@$VPS_HOST" "export VPS_TARGET_DIR=$VPS_TARGET_DIR; bash -s" <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
cd "$VPS_TARGET_DIR"
|
|
||||||
|
|
||||||
# Ensure shared network exists
|
|
||||||
docker network inspect app-shared-net >/dev/null 2>&1 || docker network create app-shared-net
|
|
||||||
|
|
||||||
echo "🔄 Synchronizing repository..."
|
|
||||||
if [ ! -d ".git" ]; then
|
|
||||||
echo "Initializing git repository..."
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/asepharyana/asepharyana-hub.git
|
|
||||||
fi
|
|
||||||
git fetch origin main --depth=1 || true
|
|
||||||
|
|
||||||
# Detect changed files before resetting
|
|
||||||
ALL_COMPOSE_FILES="infra/compose/traefik.yml infra/compose/shared.yml infra/compose/scraper.yml infra/compose/hub.yml infra/compose/tools.yml infra/compose/llm-api.yml infra/compose/nats.yml infra/compose/dapr.yml infra/compose/observability.yml"
|
|
||||||
TRAEFIK_DYNAMIC_DIR="infra/traefik/dynamic"
|
|
||||||
|
|
||||||
if git rev-parse HEAD >/dev/null 2>&1; then
|
|
||||||
BEFORE_REV=$(git rev-parse HEAD)
|
|
||||||
git reset --hard FETCH_HEAD
|
|
||||||
AFTER_REV=$(git rev-parse HEAD)
|
|
||||||
|
|
||||||
if [ "$BEFORE_REV" = "$AFTER_REV" ]; then
|
|
||||||
echo "ℹ️ No new commits detected. Using full file list for safety."
|
|
||||||
TARGET_COMPOSE=""
|
|
||||||
else
|
|
||||||
CHANGED=$(git diff --name-only "$BEFORE_REV" "$AFTER_REV" || true)
|
|
||||||
echo "📄 Changed files:"
|
|
||||||
echo "$CHANGED"
|
|
||||||
|
|
||||||
# Detect compose stack changes
|
|
||||||
CHANGED_COMPOSE=$(echo "$CHANGED" | grep '^infra/compose/.*\.yml$' || true)
|
|
||||||
TARGET_COMPOSE=""
|
|
||||||
for f in $CHANGED_COMPOSE; do
|
|
||||||
case " $ALL_COMPOSE_FILES " in
|
|
||||||
*" $f "*) TARGET_COMPOSE="$TARGET_COMPOSE $f" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
TARGET_COMPOSE=$(printf '%s' "$TARGET_COMPOSE" | xargs || true)
|
|
||||||
if [ -n "$TARGET_COMPOSE" ]; then
|
|
||||||
echo "🎯 Detected compose stack changes in: $TARGET_COMPOSE"
|
|
||||||
else
|
|
||||||
echo "ℹ️ No stack compose files changed."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect Traefik dynamic config changes
|
|
||||||
CHANGED_TRAEFIK=$(echo "$CHANGED" | grep "^$TRAEFIK_DYNAMIC_DIR/" || true)
|
|
||||||
if [ -n "$CHANGED_TRAEFIK" ]; then
|
|
||||||
echo "🎯 Detected Traefik dynamic config changes:"
|
|
||||||
echo "$CHANGED_TRAEFIK"
|
|
||||||
RELOAD_TRAEFIK="true"
|
|
||||||
else
|
|
||||||
echo "ℹ️ No Traefik dynamic config changes."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect infra file changes (Dockerfiles, config, traefik static)
|
|
||||||
CHANGED_INFRA=$(echo "$CHANGED" | grep '^infra/' | grep -v '^infra/compose/' || true)
|
|
||||||
if [ -n "$CHANGED_INFRA" ]; then
|
|
||||||
echo "📦 Detected other infra file changes:"
|
|
||||||
echo "$CHANGED_INFRA"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
git reset --hard FETCH_HEAD
|
|
||||||
TARGET_COMPOSE=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v "docker" >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
|
||||||
COMPOSE_CMD="docker compose"
|
|
||||||
elif command -v docker-compose >/dev/null 2>&1; then
|
|
||||||
COMPOSE_CMD="docker-compose"
|
|
||||||
else
|
|
||||||
echo "❌ docker compose is not installed on the remote host."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Always include ALL compose files for dependency resolution
|
|
||||||
COMPOSE_ARGS=""
|
|
||||||
for f in $ALL_COMPOSE_FILES; do
|
|
||||||
if [ -f "$f" ]; then
|
|
||||||
COMPOSE_ARGS="$COMPOSE_ARGS -f $f"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -n "$TARGET_COMPOSE" ]; then
|
|
||||||
# Extract service names from target compose file(s) for selective up
|
|
||||||
TARGET_SERVICES=""
|
|
||||||
for f in $TARGET_COMPOSE; do
|
|
||||||
if [ -f "$f" ]; then
|
|
||||||
svcs=$($COMPOSE_CMD -f "$f" config --services 2>/dev/null | tr '\n' ' ' | xargs)
|
|
||||||
TARGET_SERVICES="$TARGET_SERVICES $svcs"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
TARGET_SERVICES=$(echo "$TARGET_SERVICES" | xargs) # trim whitespace
|
|
||||||
echo "🎯 Selective update for services: $TARGET_SERVICES"
|
|
||||||
else
|
|
||||||
echo "🚀 Performing full deployment of all services..."
|
|
||||||
TARGET_SERVICES=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "📥 Pulling images for target services..."
|
|
||||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
|
||||||
PULL_SUCCESS=false
|
|
||||||
# Retry pull up to 3 times to handle transient Docker attestation lease errors
|
|
||||||
for attempt in 1 2 3; do
|
|
||||||
echo "Pull attempt $attempt/3..."
|
|
||||||
if $COMPOSE_CMD $COMPOSE_ARGS --env-file .env pull $TARGET_SERVICES; then
|
|
||||||
echo "✅ Pull succeeded on attempt $attempt"
|
|
||||||
PULL_SUCCESS=true
|
|
||||||
break
|
|
||||||
else
|
|
||||||
echo "⚠️ Pull attempt $attempt failed. Retrying in 5s..."
|
|
||||||
sleep 5
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [ "$PULL_SUCCESS" != "true" ]; then
|
|
||||||
echo "❌ Failed to pull images after 3 attempts."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🧹 Clearing Git locks..."
|
|
||||||
rm -f .git/shallow.lock || true
|
|
||||||
|
|
||||||
echo "🧹 Removing stale target containers by container_name..."
|
|
||||||
# Extract all explicitly defined container_names from compose files and remove them to prevent conflicts
|
|
||||||
if [ -n "$TARGET_COMPOSE" ]; then
|
|
||||||
for f in $TARGET_COMPOSE; do
|
|
||||||
if [ -f "$f" ]; then
|
|
||||||
grep "container_name:" "$f" | awk '{print $2}' | while read -r cname; do
|
|
||||||
docker rm -f "$cname" >/dev/null 2>&1 || true
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
for f in $ALL_COMPOSE_FILES; do
|
|
||||||
if [ -f "$f" ]; then
|
|
||||||
grep "container_name:" "$f" | awk '{print $2}' | while read -r cname; do
|
|
||||||
docker rm -f "$cname" >/dev/null 2>&1 || true
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🆙 Starting services..."
|
|
||||||
echo "🔍 Debug: Current docker containers:"
|
|
||||||
docker ps -a
|
|
||||||
if [ -n "$TARGET_SERVICES" ]; then
|
|
||||||
$COMPOSE_CMD $COMPOSE_ARGS --env-file .env up -d $TARGET_SERVICES
|
|
||||||
else
|
|
||||||
$COMPOSE_CMD $COMPOSE_ARGS --env-file .env up -d --remove-orphans
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Traefik reload ──
|
|
||||||
if [ "${RELOAD_TRAEFIK:-false}" = "true" ]; then
|
|
||||||
echo "🔄 Traefik dynamic config changed — reloading Traefik..."
|
|
||||||
# Traefik watches the dynamic config dir (providers.file.watch=true),
|
|
||||||
# but send SIGHUP as insurance
|
|
||||||
docker kill --signal HUP traefik 2>/dev/null || docker exec traefik kill -HUP 1 2>/dev/null || true
|
|
||||||
echo "✅ Traefik reload signal sent"
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
name: Build and Push Docker Images
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- 'apps/scraper/**'
|
|
||||||
- 'apps/hub/**'
|
|
||||||
- 'apps/tools/**'
|
|
||||||
- 'apps/llm-api/**'
|
|
||||||
- '.github/workflows/docker-build-push.yml'
|
|
||||||
- 'infra/**'
|
|
||||||
- '!infra/compose/**'
|
|
||||||
repository_dispatch:
|
|
||||||
types: [submodule-updated]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
IMAGE_NAME_PREFIX: asepharyana/asepharyana-hub
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# Phase 1: Detect which services have changed
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
changes:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
||||||
scraper-api: ${{ steps.filter.outputs['scraper-api'] == 'true' || steps.dispatch.outputs['scraper-api'] == 'true' || github.event_name == 'workflow_dispatch' }}
|
|
||||||
hub: ${{ steps.filter.outputs['hub'] == 'true' || steps.dispatch.outputs['hub'] == 'true' || github.event_name == 'workflow_dispatch' }}
|
|
||||||
tools: ${{ steps.filter.outputs['tools'] == 'true' || steps.dispatch.outputs['tools'] == 'true' || github.event_name == 'workflow_dispatch' }}
|
|
||||||
llm-api: ${{ steps.filter.outputs['llm-api'] == 'true' || steps.dispatch.outputs['llm-api'] == 'true' || github.event_name == 'workflow_dispatch' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
submodules: false
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: Detect changed services
|
|
||||||
id: filter
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
env:
|
|
||||||
BEFORE: ${{ github.event.before }}
|
|
||||||
AFTER: ${{ github.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -z "${BEFORE:-}" ] || [[ "$BEFORE" =~ ^0+$ ]]; then
|
|
||||||
CHANGED_FILES=$(git ls-files)
|
|
||||||
else
|
|
||||||
git fetch --no-tags --depth=2 origin "$BEFORE" || true
|
|
||||||
CHANGED_FILES=$(git diff --name-only "$BEFORE" "$AFTER")
|
|
||||||
fi
|
|
||||||
|
|
||||||
changed() {
|
|
||||||
printf '%s\n' "$CHANGED_FILES" | grep -Eq "$1" && echo true || echo false
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "scraper-api=$(changed '^(apps/scraper(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/scraper\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "hub=$(changed '^(apps/hub(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/hub\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "tools=$(changed '^(apps/tools(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/tools\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "llm-api=$(changed '^(apps/llm-api(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/llm-api\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Parse repository_dispatch payload
|
|
||||||
id: dispatch
|
|
||||||
if: github.event_name == 'repository_dispatch'
|
|
||||||
env:
|
|
||||||
SERVICE: ${{ github.event.client_payload.service }}
|
|
||||||
SHA: ${{ github.event.client_payload.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
if [ -z "${SERVICE:-}" ]; then
|
|
||||||
echo "::error::repository_dispatch payload missing service"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "${SHA:-}" ]; then
|
|
||||||
echo "::error::repository_dispatch payload missing sha"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$SERVICE" in
|
|
||||||
scraper-api|hub|tools|llm-api) ;;
|
|
||||||
*)
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if ! [[ "$SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then echo "::error::Invalid sha '$SHA'. Expected 40 hex characters"; fi
|
|
||||||
|
|
||||||
SERVICES=(scraper-api hub tools llm-api)
|
|
||||||
for svc in "${SERVICES[@]}"; do
|
|
||||||
if [ "$SERVICE" = "$svc" ]; then
|
|
||||||
echo "${svc}=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "${svc}=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Set matrix
|
|
||||||
id: set-matrix
|
|
||||||
run: |
|
|
||||||
SERVICES=()
|
|
||||||
add_service() {
|
|
||||||
SERVICES+=("{\"id\":\"$1\",\"target\":\"$2\",\"path\":\"$3\"}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "${{ steps.filter.outputs['scraper-api'] == 'true' || steps.dispatch.outputs['scraper-api'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "scraper-api" "docker-scraper" "apps/scraper"; fi
|
|
||||||
if [ "${{ steps.filter.outputs['hub'] == 'true' || steps.dispatch.outputs['hub'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "hub" "docker-hub" "apps/hub"; fi
|
|
||||||
if [ "${{ steps.filter.outputs['tools'] == 'true' || steps.dispatch.outputs['tools'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "tools" "docker-tools" "apps/tools"; fi
|
|
||||||
if [ "${{ steps.filter.outputs['llm-api'] == 'true' || steps.dispatch.outputs['llm-api'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "llm-api" "docker-llm-api" "apps/llm-api"; fi
|
|
||||||
|
|
||||||
JSON_ARRAY="[$(IFS=,; echo "${SERVICES[*]}")]"
|
|
||||||
echo "matrix=$JSON_ARRAY" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
wait-submodule-ref:
|
|
||||||
needs: [changes]
|
|
||||||
if: github.event_name == 'repository_dispatch'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
|
||||||
- name: Wait for submodule ref
|
|
||||||
env:
|
|
||||||
SERVICE: ${{ github.event.client_payload.service }}
|
|
||||||
SHA: ${{ github.event.client_payload.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
case "$SERVICE" in
|
|
||||||
"scraper-api") REPO="https://github.com/asepharyana/asepharyana-hub-scraper.git" ;;
|
|
||||||
"hub") REPO="https://github.com/asepharyana/asepharyana-hub-hub.git" ;;
|
|
||||||
"tools") echo "tools is built from monorepo, no submodule wait needed"; exit 0 ;;
|
|
||||||
"llm-api") REPO="https://github.com/asepharyana/asepharyana-hub-llm-api.git" ;;
|
|
||||||
*)
|
|
||||||
echo "::error::Unsupported service '$SERVICE'"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "Waiting for $SERVICE commit $SHA in $REPO"
|
|
||||||
TMPDIR=$(mktemp -d)
|
|
||||||
git init "$TMPDIR/probe" >/dev/null
|
|
||||||
git -C "$TMPDIR/probe" remote add origin "$REPO"
|
|
||||||
for attempt in {1..30}; do
|
|
||||||
if git -C "$TMPDIR/probe" fetch --depth=1 origin "$SHA" >/dev/null 2>&1; then
|
|
||||||
echo "Submodule commit $SHA is fetchable for $SERVICE"
|
|
||||||
rm -rf "$TMPDIR"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Attempt $attempt/30: $SHA not fetchable yet; waiting 10s"
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
rm -rf "$TMPDIR"
|
|
||||||
|
|
||||||
echo "::error::Submodule commit $SHA for $SERVICE was not fetchable after 300s"
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────
|
|
||||||
# Phase 2: Build and Push Images (Matrix)
|
|
||||||
# ─────────────────────────────────────────────────
|
|
||||||
build:
|
|
||||||
needs: [changes, wait-submodule-ref]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 30
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include: ${{ fromJson(needs.changes.outputs.matrix) }}
|
|
||||||
if: |
|
|
||||||
always() &&
|
|
||||||
needs.changes.result == 'success' &&
|
|
||||||
(needs.wait-submodule-ref.result == 'success' || needs.wait-submodule-ref.result == 'skipped') &&
|
|
||||||
needs.changes.outputs.matrix != '[]'
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
submodules: false
|
|
||||||
|
|
||||||
- name: Sync submodule locally
|
|
||||||
env:
|
|
||||||
EVENT_NAME: ${{ github.event_name }}
|
|
||||||
DISPATCH_SHA: ${{ github.event.client_payload.sha }}
|
|
||||||
SUBMODULE_PATH: ${{ matrix.path }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
git submodule update --init --recursive "$SUBMODULE_PATH"
|
|
||||||
if [ "$EVENT_NAME" = "repository_dispatch" ] && [ -n "${DISPATCH_SHA:-}" ]; then
|
|
||||||
cd "$SUBMODULE_PATH"
|
|
||||||
git fetch origin "$DISPATCH_SHA"
|
|
||||||
git checkout "$DISPATCH_SHA"
|
|
||||||
cd "${GITHUB_WORKSPACE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- uses: docker/login-action@v4
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v4
|
|
||||||
|
|
||||||
- name: Docker metadata
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
SVC_NAME="${{ matrix.id }}"
|
|
||||||
SHORT=${GITHUB_SHA:0:7}
|
|
||||||
echo "image=${REGISTRY}/${IMAGE_NAME_PREFIX}/${SVC_NAME}" >> $GITHUB_OUTPUT
|
|
||||||
echo "tags=${REGISTRY}/${IMAGE_NAME_PREFIX}/${SVC_NAME}:sha-${SHORT}" >> $GITHUB_OUTPUT
|
|
||||||
echo "cache-registry=${REGISTRY}/${IMAGE_NAME_PREFIX}/${SVC_NAME}:buildcache" >> $GITHUB_OUTPUT
|
|
||||||
case "$SVC_NAME" in
|
|
||||||
"scraper-api") echo "dockerfile=infra/docker/scraper.Dockerfile" >> $GITHUB_OUTPUT ;;
|
|
||||||
"hub") echo "dockerfile=infra/docker/hub.Dockerfile" >> $GITHUB_OUTPUT ;;
|
|
||||||
"tools") echo "dockerfile=infra/docker/tools.Dockerfile" >> $GITHUB_OUTPUT ;;
|
|
||||||
"llm-api") echo "dockerfile=infra/docker/llm-api.Dockerfile" >> $GITHUB_OUTPUT ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
- name: Build and Push Docker image
|
|
||||||
uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ${{ steps.meta.outputs.dockerfile }}
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
build-args: |
|
|
||||||
COMMIT_COUNT=${{ env.NR_COMMIT_COUNT || github.run_number }}
|
|
||||||
COMMIT_SHA=${{ env.NR_COMMIT_SHA || github.sha }}
|
|
||||||
cache-from: type=registry,ref=${{ steps.meta.outputs['cache-registry'] }}
|
|
||||||
cache-to: type=registry,ref=${{ steps.meta.outputs['cache-registry'] }},mode=max
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
# Phase 3: Update Manifests and Submodule Refs
|
|
||||||
# ──────────────────────────────────────────────
|
|
||||||
update-manifest:
|
|
||||||
needs: [changes, wait-submodule-ref, build]
|
|
||||||
if: |
|
|
||||||
always() &&
|
|
||||||
needs.changes.result == 'success' &&
|
|
||||||
(needs.wait-submodule-ref.result == 'success' || needs.wait-submodule-ref.result == 'skipped') &&
|
|
||||||
(needs.build.result == 'success' || needs.build.result == 'skipped')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
submodules: false
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Update tags and submodules
|
|
||||||
run: |
|
|
||||||
SHORT_SHA=${GITHUB_SHA:0:7}
|
|
||||||
TAG="sha-$SHORT_SHA"
|
|
||||||
CHANGED=false
|
|
||||||
|
|
||||||
declare -A SERVICES
|
|
||||||
SERVICES["scraper-api"]="scraper.yml"
|
|
||||||
SERVICES["hub"]="hub.yml"
|
|
||||||
SERVICES["tools"]="tools.yml"
|
|
||||||
SERVICES["llm-api"]="llm-api.yml"
|
|
||||||
|
|
||||||
declare -A PATHS
|
|
||||||
PATHS["scraper-api"]="apps/scraper"
|
|
||||||
PATHS["hub"]="apps/hub"
|
|
||||||
PATHS["tools"]="apps/tools"
|
|
||||||
PATHS["llm-api"]="apps/llm-api"
|
|
||||||
|
|
||||||
# Use git config for possible commits
|
|
||||||
git config --local user.email "action@github.com"
|
|
||||||
git config --local user.name "GitHub Action"
|
|
||||||
|
|
||||||
for id in "${!SERVICES[@]}"; do
|
|
||||||
SHOULD_HAVE_RUN=false
|
|
||||||
if [ "${{ needs.changes.outputs['scraper-api'] }}" == "true" ] && [ "$id" == "scraper-api" ]; then SHOULD_HAVE_RUN=true; fi
|
|
||||||
if [ "${{ needs.changes.outputs['hub'] }}" == "true" ] && [ "$id" == "hub" ]; then SHOULD_HAVE_RUN=true; fi
|
|
||||||
if [ "${{ needs.changes.outputs['tools'] }}" == "true" ] && [ "$id" == "tools" ]; then SHOULD_HAVE_RUN=true; fi
|
|
||||||
if [ "${{ needs.changes.outputs['llm-api'] }}" == "true" ] && [ "$id" == "llm-api" ]; then SHOULD_HAVE_RUN=true; fi
|
|
||||||
|
|
||||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then SHOULD_HAVE_RUN=true; fi
|
|
||||||
|
|
||||||
if [ "$SHOULD_HAVE_RUN" == "true" ]; then
|
|
||||||
COMPOSE_FILE="infra/compose/${SERVICES[$id]}"
|
|
||||||
if [ -f "$COMPOSE_FILE" ]; then
|
|
||||||
echo "Updating $COMPOSE_FILE to $TAG"
|
|
||||||
sed -i "s|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_PREFIX }}/$id:.*|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_PREFIX }}/$id:$TAG|g" "$COMPOSE_FILE"
|
|
||||||
git add "$COMPOSE_FILE"
|
|
||||||
CHANGED=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If it's a repository_dispatch for this specific service, update its submodule pointer
|
|
||||||
if [ "${{ github.event_name }}" == "repository_dispatch" ] && [ "${{ github.event.client_payload.service }}" == "$id" ]; then
|
|
||||||
SHA_DISPATCH="${{ github.event.client_payload.sha }}"
|
|
||||||
SUB_PATH="${PATHS[$id]}"
|
|
||||||
if [ -n "$SHA_DISPATCH" ]; then
|
|
||||||
echo "Updating submodule $SUB_PATH to $SHA_DISPATCH"
|
|
||||||
git submodule update --init "$SUB_PATH"
|
|
||||||
git -C "$SUB_PATH" fetch origin "$SHA_DISPATCH"
|
|
||||||
git -C "$SUB_PATH" checkout "$SHA_DISPATCH"
|
|
||||||
git add "$SUB_PATH"
|
|
||||||
CHANGED=true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$CHANGED" == "true" ]; then
|
|
||||||
git commit -m "chore: update manifests and submodules [skip ci]"
|
|
||||||
|
|
||||||
for attempt in {1..3}; do
|
|
||||||
if git pull --rebase origin main && git push origin main; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Manifest push attempt $attempt/3 failed; retrying"
|
|
||||||
git rebase --abort || true
|
|
||||||
git pull --rebase origin main || true
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "::error::Failed to push manifest update after 3 attempts"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "No changes detected."
|
|
||||||
fi
|
|
||||||
Reference in New Issue
Block a user