chore: initial hub repo structure

This commit is contained in:
asepharyana
2026-07-09 22:08:26 +07:00
commit b31fe9d188
83 changed files with 7969 additions and 0 deletions
+253
View File
@@ -0,0 +1,253 @@
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
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.workflow_run.conclusion == 'success'
steps:
- name: Checkout repository
uses: actions/checkout@v6
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/elysia.yml infra/compose/react.yml infra/compose/rust-auth.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
# Submodule update removed for faster VPS deployments
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
# Construct compose arguments
docker rm -f imphenbot-app || true
if [ -n "$TARGET_COMPOSE" ]; then
COMPOSE_ARGS=""
for f in $TARGET_COMPOSE; do
if [ -f "$f" ]; then
COMPOSE_ARGS="$COMPOSE_ARGS -f $f"
fi
done
UP_FLAGS="-d" # No --remove-orphans for selective updates to avoid killing other services
else
echo "🚀 Performing full deployment of all services..."
COMPOSE_ARGS=""
for f in $ALL_COMPOSE_FILES; do
COMPOSE_ARGS="$COMPOSE_ARGS -f $f"
done
UP_FLAGS="-d --remove-orphans"
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; 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
# Cooldown to allow daemon to settle (optional but kept for safety)
# echo "⏳ Waiting for Docker daemon to settle..."
# sleep 2
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
$COMPOSE_CMD $COMPOSE_ARGS --env-file .env up $UP_FLAGS
# ── 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
# - name: Restart coolify-proxy (delay 1 min)
# env:
# SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
# VPS_HOST: ${{ secrets.VPS_HOST }}
# VPS_USER: ${{ secrets.VPS_USER }}
# run: |
# mkdir -p ~/.ssh
# echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
# chmod 600 ~/.ssh/id_rsa
# ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
#
# echo "⏳ Waiting 60s for containers to settle before restarting proxy..."
# sleep 60
#
# echo "🔄 Restarting coolify-proxy..."
# ssh "${VPS_USER}@${VPS_HOST}" "docker restart coolify-proxy"
# echo "✅ coolify-proxy restarted."