All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 42s
Build & Deploy / 🧪 QA (push) Successful in 1m33s
Build & Deploy / 🏗️ Build (push) Successful in 3m10s
Build & Deploy / 🚀 Deploy (push) Successful in 35s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
1. Added native Gitea/GitHub Actions concurrency group to automatically cancel outdated/stale deploy runs for the same branch (fixes 'alte deploys durchjagen'). 2. Replaced the buggy 'github.ref_type' condition with explicit 'github.ref' matching. Gitea often failed to populate ref_type, causing staging tags to be downgraded to 'branch' target, making them equal priority to feature branches where the newer run would win (fixes 'feature branches wichtiger als staging').
482 lines
22 KiB
YAML
482 lines
22 KiB
YAML
name: Build & Deploy
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- '**'
|
|
tags:
|
|
- 'v*'
|
|
workflow_dispatch:
|
|
inputs:
|
|
skip_checks:
|
|
description: 'Skip tests? (true/false)'
|
|
required: false
|
|
default: 'false'
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
PUPPETEER_SKIP_DOWNLOAD: "true"
|
|
COREPACK_NPM_REGISTRY: "https://registry.npmmirror.com"
|
|
|
|
|
|
jobs:
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# JOB 1: Prepare Environment
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
prepare:
|
|
name: 🔍 Prepare
|
|
runs-on: docker
|
|
outputs:
|
|
target: ${{ steps.determine.outputs.target }}
|
|
image_tag: ${{ steps.determine.outputs.image_tag }}
|
|
env_file: ${{ steps.determine.outputs.env_file }}
|
|
traefik_host: ${{ steps.determine.outputs.traefik_host }}
|
|
traefik_rule: ${{ steps.determine.outputs.traefik_rule }}
|
|
next_public_url: ${{ steps.determine.outputs.next_public_url }}
|
|
project_name: ${{ steps.determine.outputs.project_name }}
|
|
short_sha: ${{ steps.determine.outputs.short_sha }}
|
|
container:
|
|
image: catthehacker/ubuntu:act-latest
|
|
steps:
|
|
- name: 🧹 Maintenance (High Density Cleanup)
|
|
shell: bash
|
|
run: |
|
|
echo "=== System Disk Usage ==="
|
|
df -h || true
|
|
echo "=== Tool Cache Usage ==="
|
|
du -sh /opt/hostedtoolcache/* || true
|
|
du -sh /opt/hostedtoolcache/.[!.]* || true
|
|
echo "Purging old tool caches..."
|
|
rm -rf /opt/hostedtoolcache/* || true
|
|
rm -rf /opt/hostedtoolcache/.[!.]* || true
|
|
echo "=== Host Disk Usage ==="
|
|
docker run --rm -v /:/host-root alpine df -h || true
|
|
echo "=== Host Inode Usage ==="
|
|
docker run --rm -v /:/host-root alpine df -i || true
|
|
echo "=== Running Host Containers ==="
|
|
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" || true
|
|
echo "=== Host Docker Info ==="
|
|
docker info || true
|
|
echo "Pruning host Buildkit builder caches aggressively..."
|
|
for container in $(docker ps --filter "name=buildx_buildkit_builder" --format "{{.Names}}"); do
|
|
echo "Pruning Buildkit inside container: $container"
|
|
docker exec -i "$container" buildctl prune --all || true
|
|
done
|
|
echo "Removing buildkit builder containers to clear overlay storage..."
|
|
containers=$(docker ps -a --filter "name=buildx_buildkit_builder" -q)
|
|
if [ -n "$containers" ]; then
|
|
docker rm -f $containers || true
|
|
fi
|
|
echo "=== Truncating host Docker container log files ==="
|
|
docker run --rm -v /:/host-root alpine sh -c "truncate -s 0 /host-root/mnt/HC_Volume_104796416/docker/containers/*/*.log" || true
|
|
echo "Purging old build layers, unused volumes and images..."
|
|
docker builder prune -a -f || true
|
|
docker volume prune -f || true
|
|
docker system prune -a -f --volumes || true
|
|
echo "=== System Disk Usage After Prune ==="
|
|
df -h || true
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 2
|
|
|
|
- name: 🔍 Environment ermitteln
|
|
id: determine
|
|
shell: bash
|
|
run: |
|
|
REF="${{ github.ref_name }}"
|
|
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
|
|
DOMAIN="e-tib.com"
|
|
PRJ="etib"
|
|
|
|
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
|
TARGET="testing"
|
|
IMAGE_TAG="main-${SHORT_SHA}"
|
|
ENV_FILE=".env.testing"
|
|
TRAEFIK_HOST="test.${DOMAIN}"
|
|
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
|
if [[ "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
TARGET="production"
|
|
IMAGE_TAG="$REF"
|
|
ENV_FILE=".env.production"
|
|
TRAEFIK_HOST="${DOMAIN}, www.${DOMAIN}"
|
|
else
|
|
TARGET="staging"
|
|
IMAGE_TAG="$REF"
|
|
ENV_FILE=".env.staging"
|
|
TRAEFIK_HOST="staging.${DOMAIN}"
|
|
fi
|
|
else
|
|
TARGET="branch"
|
|
SLUG=$(echo "$REF" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
|
|
IMAGE_TAG="branch-${SLUG}-${SHORT_SHA}"
|
|
ENV_FILE=".env.branch-${SLUG}"
|
|
TRAEFIK_HOST="${SLUG}.branch.mintel.me"
|
|
fi
|
|
|
|
# Standardize Traefik Rule
|
|
if [[ "$TRAEFIK_HOST" == *","* ]]; then
|
|
TRAEFIK_RULE=$(echo "$TRAEFIK_HOST" | sed 's/,/ /g' | awk '{for(i=1;i<=NF;i++) printf "Host(\x60%s\x60)%s", $i, (i==NF?"":" || ")}')
|
|
PRIMARY_HOST=$(echo "$TRAEFIK_HOST" | cut -d',' -f1 | sed 's/ //g')
|
|
else
|
|
TRAEFIK_RULE='Host(`'"$TRAEFIK_HOST"'`)'
|
|
PRIMARY_HOST="$TRAEFIK_HOST"
|
|
fi
|
|
|
|
GATEKEEPER_HOST="gatekeeper.$PRIMARY_HOST"
|
|
|
|
{
|
|
echo "target=$TARGET"
|
|
echo "image_tag=$IMAGE_TAG"
|
|
echo "env_file=$ENV_FILE"
|
|
echo "traefik_host=$PRIMARY_HOST"
|
|
echo "traefik_rule=$TRAEFIK_RULE"
|
|
echo "gatekeeper_host=$GATEKEEPER_HOST"
|
|
echo "next_public_url=https://$PRIMARY_HOST"
|
|
if [[ "$TARGET" == "production" ]]; then
|
|
echo "project_name=etib-production"
|
|
elif [[ "$TARGET" == "branch" ]]; then
|
|
echo "project_name=$PRJ-branch-$SLUG"
|
|
else
|
|
echo "project_name=$PRJ-$TARGET"
|
|
fi
|
|
echo "short_sha=$SHORT_SHA"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
- name: 🚦 Priority Gate
|
|
env:
|
|
GITEA_PAT: ${{ secrets.GITEA_PAT }}
|
|
CURRENT_RUN_ID: ${{ github.run_id }}
|
|
CURRENT_TARGET: ${{ steps.determine.outputs.target }}
|
|
run: bash scripts/priority-gate.sh
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# JOB 2: QA (Lint, Typecheck, Test)
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
qa:
|
|
name: 🧪 QA
|
|
needs: prepare
|
|
if: needs.prepare.outputs.target != 'skip'
|
|
runs-on: docker
|
|
container:
|
|
image: catthehacker/ubuntu:act-latest
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v3
|
|
with:
|
|
version: 10
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
|
|
- name: 🔍 Secret Diagnostics
|
|
run: |
|
|
echo "Checking available secrets..."
|
|
[ -n "${{ secrets.NPM_TOKEN }}" ] && echo "✅ NPM_TOKEN is set" || echo "❌ NPM_TOKEN is empty"
|
|
[ -n "${{ secrets.GITEA_PAT }}" ] && echo "✅ GITEA_PAT is set" || echo "❌ GITEA_PAT is empty"
|
|
[ -n "${{ secrets.MINTEL_PRIVATE_TOKEN }}" ] && echo "✅ MINTEL_PRIVATE_TOKEN is set" || echo "❌ MINTEL_PRIVATE_TOKEN is empty"
|
|
[ -n "${{ secrets.MINTEL_REGISTRY_TOKEN }}" ] && echo "✅ MINTEL_REGISTRY_TOKEN is set" || echo "❌ MINTEL_REGISTRY_TOKEN is empty"
|
|
|
|
- name: 🔐 Registry Auth
|
|
env:
|
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
GITEA_PAT: ${{ secrets.GITEA_PAT }}
|
|
MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }}
|
|
MINTEL_REGISTRY_TOKEN: ${{ secrets.MINTEL_REGISTRY_TOKEN }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: bash scripts/registry-auth.sh
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
pnpm store prune
|
|
pnpm install --no-frozen-lockfile
|
|
|
|
- name: 🧪 QA Checks
|
|
if: github.event.inputs.skip_checks != 'true'
|
|
env:
|
|
TURBO_TELEMETRY_DISABLED: "1"
|
|
run: npx turbo run lint typecheck test --cache-dir=".turbo"
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# JOB 3: Build & Push
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
build:
|
|
name: 🏗️ Build
|
|
needs: [prepare, qa]
|
|
if: needs.prepare.outputs.target != 'skip'
|
|
runs-on: docker
|
|
container:
|
|
image: catthehacker/ubuntu:act-latest
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🔐 Registry Auth
|
|
id: auth
|
|
env:
|
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
GITEA_PAT: ${{ secrets.GITEA_PAT }}
|
|
MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }}
|
|
MINTEL_REGISTRY_TOKEN: ${{ secrets.MINTEL_REGISTRY_TOKEN }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: bash scripts/registry-auth.sh
|
|
|
|
- name: 🔐 Registry Login
|
|
run: echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
|
- name: 🏗️ Build and Push (Native Docker Buildkit)
|
|
env:
|
|
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
|
NEXT_PUBLIC_TARGET: ${{ needs.prepare.outputs.target }}
|
|
NEXT_PUBLIC_APP_VERSION: ${{ needs.prepare.outputs.image_tag }}
|
|
UMAMI_WEBSITE_ID: ${{ secrets.UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID }}
|
|
UMAMI_API_ENDPOINT: ${{ secrets.UMAMI_API_ENDPOINT || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }}
|
|
NPM_TOKEN: ${{ steps.auth.outputs.working_token }}
|
|
NPM_DOMAIN: ${{ steps.auth.outputs.working_domain }}
|
|
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
|
|
run: |
|
|
echo "$NPM_TOKEN" > npm_token.txt
|
|
DOCKER_BUILDKIT=1 docker build \
|
|
--secret id=NPM_TOKEN,src=npm_token.txt \
|
|
--build-arg NEXT_PUBLIC_BASE_URL="$NEXT_PUBLIC_BASE_URL" \
|
|
--build-arg NEXT_PUBLIC_TARGET="$NEXT_PUBLIC_TARGET" \
|
|
--build-arg NEXT_PUBLIC_APP_VERSION="$IMAGE_TAG" \
|
|
--build-arg UMAMI_WEBSITE_ID="$UMAMI_WEBSITE_ID" \
|
|
--build-arg UMAMI_API_ENDPOINT="$UMAMI_API_ENDPOINT" \
|
|
--build-arg NPM_TOKEN="$NPM_TOKEN" \
|
|
--build-arg NPM_DOMAIN="$NPM_DOMAIN" \
|
|
-t registry.infra.mintel.me/mintel/e-tib.com:"$IMAGE_TAG" \
|
|
.
|
|
docker push registry.infra.mintel.me/mintel/e-tib.com:"$IMAGE_TAG"
|
|
rm -f npm_token.txt
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# JOB 4: Deploy
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
deploy:
|
|
name: 🚀 Deploy
|
|
needs: [prepare, build, qa]
|
|
runs-on: docker
|
|
container:
|
|
image: catthehacker/ubuntu:act-latest
|
|
env:
|
|
TARGET: ${{ needs.prepare.outputs.target }}
|
|
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
|
|
PROJECT_NAME: ${{ needs.prepare.outputs.project_name }}
|
|
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
|
TRAEFIK_HOST: ${{ needs.prepare.outputs.traefik_host }}
|
|
GATEKEEPER_HOST: ${{ needs.prepare.outputs.gatekeeper_host }}
|
|
|
|
# Secrets mapping (Mail)
|
|
MAIL_HOST: ${{ secrets.SMTP_HOST || vars.SMTP_HOST }}
|
|
MAIL_PORT: ${{ secrets.SMTP_PORT || vars.SMTP_PORT || '587' }}
|
|
MAIL_USERNAME: ${{ secrets.SMTP_USER || vars.SMTP_USER }}
|
|
MAIL_PASSWORD: ${{ secrets.SMTP_PASS || vars.SMTP_PASS }}
|
|
MAIL_FROM: ${{ secrets.SMTP_FROM || vars.SMTP_FROM || 'noreply@e-tib.com' }}
|
|
MAIL_RECIPIENTS: ${{ secrets.CONTACT_RECIPIENT || vars.CONTACT_RECIPIENT || 'info@e-tib.com' }}
|
|
|
|
# Monitoring
|
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN || vars.SENTRY_DSN }}
|
|
|
|
# Gatekeeper
|
|
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }}
|
|
|
|
# Analytics
|
|
UMAMI_WEBSITE_ID: ${{ secrets.UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID }}
|
|
UMAMI_API_ENDPOINT: ${{ secrets.UMAMI_API_ENDPOINT || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
- name: 📝 Generate Environment
|
|
shell: bash
|
|
env:
|
|
TRAEFIK_RULE: ${{ needs.prepare.outputs.traefik_rule }}
|
|
ENV_FILE: ${{ needs.prepare.outputs.env_file }}
|
|
run: |
|
|
LOG_LEVEL=$( [[ "$TARGET" == "testing" || "$TARGET" == "development" ]] && echo "debug" || echo "info" )
|
|
COOKIE_DOMAIN=.$(echo $NEXT_PUBLIC_BASE_URL | sed 's|https://||')
|
|
STD_MW="${PROJECT_NAME}-ratelimit,${PROJECT_NAME}-forward,${PROJECT_NAME}-compress"
|
|
|
|
if [[ "$TARGET" == "production" ]]; then
|
|
AUTH_MIDDLEWARE="$STD_MW"
|
|
COMPOSE_PROFILES=""
|
|
else
|
|
AUTH_MIDDLEWARE="${PROJECT_NAME}-ratelimit,${PROJECT_NAME}-forward,${PROJECT_NAME}-auth,${PROJECT_NAME}-compress"
|
|
COMPOSE_PROFILES="gatekeeper"
|
|
fi
|
|
AUTH_MIDDLEWARE_UNPROTECTED="$STD_MW"
|
|
|
|
GATEKEEPER_ORIGIN="${NEXT_PUBLIC_BASE_URL}/gatekeeper"
|
|
|
|
if [[ "$TARGET" == "production" ]]; then
|
|
INTERNAL_SUBNET="10.199.10.0/24"
|
|
elif [[ "$TARGET" == "testing" ]]; then
|
|
INTERNAL_SUBNET="10.199.11.0/24"
|
|
elif [[ "$TARGET" == "staging" ]]; then
|
|
INTERNAL_SUBNET="10.199.12.0/24"
|
|
else
|
|
HEX_SHA="${{ needs.prepare.outputs.short_sha }}"
|
|
DEC_SHA=$((16#$HEX_SHA))
|
|
SUBNET_NUM=$(( 13 + (DEC_SHA % 230) ))
|
|
INTERNAL_SUBNET="10.199.${SUBNET_NUM}.0/24"
|
|
fi
|
|
|
|
{
|
|
echo "# Generated by CI - $TARGET"
|
|
echo "IMAGE_TAG=$IMAGE_TAG"
|
|
echo "NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL"
|
|
echo "GATEKEEPER_ORIGIN=$GATEKEEPER_ORIGIN"
|
|
echo "SENTRY_DSN=$SENTRY_DSN"
|
|
echo "LOG_LEVEL=$LOG_LEVEL"
|
|
echo "MAIL_HOST=$MAIL_HOST"
|
|
echo "MAIL_PORT=$MAIL_PORT"
|
|
echo "MAIL_USERNAME=$MAIL_USERNAME"
|
|
echo "MAIL_PASSWORD=$MAIL_PASSWORD"
|
|
echo "MAIL_FROM=$MAIL_FROM"
|
|
echo "MAIL_RECIPIENTS=$MAIL_RECIPIENTS"
|
|
echo ""
|
|
echo "# Gatekeeper"
|
|
echo "GATEKEEPER_PASSWORD=$GATEKEEPER_PASSWORD"
|
|
echo "AUTH_COOKIE_NAME=etib_gatekeeper_session"
|
|
echo "COOKIE_DOMAIN=$COOKIE_DOMAIN"
|
|
echo ""
|
|
echo "# Analytics"
|
|
echo "UMAMI_WEBSITE_ID=$UMAMI_WEBSITE_ID"
|
|
echo "UMAMI_API_ENDPOINT=$UMAMI_API_ENDPOINT"
|
|
echo ""
|
|
echo "TARGET=$TARGET"
|
|
echo "SENTRY_ENVIRONMENT=$TARGET"
|
|
echo "PROJECT_NAME=$PROJECT_NAME"
|
|
printf 'TRAEFIK_HOST_RULE=%s\n' "$TRAEFIK_RULE"
|
|
echo "TRAEFIK_HOST=$TRAEFIK_HOST"
|
|
echo "GATEKEEPER_HOST=$GATEKEEPER_HOST"
|
|
echo "TRAEFIK_ENTRYPOINT=websecure"
|
|
echo "TRAEFIK_TLS=true"
|
|
echo "TRAEFIK_CERT_RESOLVER=le"
|
|
echo "ENV_FILE=$ENV_FILE"
|
|
echo "COMPOSE_PROFILES=$COMPOSE_PROFILES"
|
|
echo "AUTH_MIDDLEWARE=$AUTH_MIDDLEWARE"
|
|
echo "AUTH_MIDDLEWARE_UNPROTECTED=$AUTH_MIDDLEWARE_UNPROTECTED"
|
|
echo "INTERNAL_SUBNET=$INTERNAL_SUBNET"
|
|
} > .env.deploy
|
|
|
|
- name: 🚀 SSH Deploy
|
|
shell: bash
|
|
env:
|
|
ENV_FILE: ${{ needs.prepare.outputs.env_file }}
|
|
run: |
|
|
SLUG=$(echo "${{ github.ref_name }}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
|
|
mkdir -p ~/.ssh
|
|
echo "${{ secrets.ALPHA_SSH_KEY }}" > ~/.ssh/id_ed25519
|
|
chmod 600 ~/.ssh/id_ed25519
|
|
ssh-keyscan -H alpha.mintel.me >> ~/.ssh/known_hosts 2>/dev/null
|
|
|
|
if [[ "$TARGET" == "production" ]]; then
|
|
SITE_DIR="/home/deploy/sites/e-tib.com"
|
|
elif [[ "$TARGET" == "testing" ]]; then
|
|
SITE_DIR="/home/deploy/sites/test.e-tib.com"
|
|
elif [[ "$TARGET" == "staging" ]]; then
|
|
SITE_DIR="/home/deploy/sites/staging.e-tib.com"
|
|
else
|
|
SITE_DIR="/home/deploy/sites/branch.e-tib.com/$SLUG"
|
|
fi
|
|
|
|
ssh root@alpha.mintel.me "mkdir -p $SITE_DIR"
|
|
scp .env.deploy root@alpha.mintel.me:$SITE_DIR/$ENV_FILE
|
|
scp docker-compose.yml root@alpha.mintel.me:$SITE_DIR/docker-compose.yml
|
|
|
|
ssh root@alpha.mintel.me "docker system prune -f"
|
|
ssh root@alpha.mintel.me "docker rm -f \$(docker ps -a -q --filter name=${SLUG}-etib-) 2>/dev/null || true"
|
|
ssh root@alpha.mintel.me "cd $SITE_DIR && echo '${{ secrets.REGISTRY_PASS }}' | docker login registry.infra.mintel.me -u '${{ secrets.REGISTRY_USER }}' --password-stdin"
|
|
ssh root@alpha.mintel.me "cd $SITE_DIR && docker compose -p '${{ needs.prepare.outputs.project_name }}' --env-file '$ENV_FILE' pull"
|
|
ssh root@alpha.mintel.me "cd $SITE_DIR && docker compose -p '${{ needs.prepare.outputs.project_name }}' --env-file '$ENV_FILE' up -d --remove-orphans"
|
|
ssh root@alpha.mintel.me "docker system prune -f --filter 'until=24h'"
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# JOB 5: Post-Deploy Verification
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
post_deploy_checks:
|
|
name: 🧪 Post-Deploy Verification
|
|
needs: [prepare, deploy]
|
|
if: needs.deploy.result == 'success' && needs.prepare.outputs.target != 'branch'
|
|
runs-on: docker
|
|
container:
|
|
image: catthehacker/ubuntu:act-latest
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v3
|
|
with:
|
|
version: 10
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
- name: 🔍 Secret Diagnostics
|
|
run: |
|
|
echo "Checking available secrets..."
|
|
[ -n "${{ secrets.NPM_TOKEN }}" ] && echo "✅ NPM_TOKEN is set" || echo "❌ NPM_TOKEN is empty"
|
|
[ -n "${{ secrets.GITEA_PAT }}" ] && echo "✅ GITEA_PAT is set" || echo "❌ GITEA_PAT is empty"
|
|
[ -n "${{ secrets.MINTEL_PRIVATE_TOKEN }}" ] && echo "✅ MINTEL_PRIVATE_TOKEN is set" || echo "❌ MINTEL_PRIVATE_TOKEN is empty"
|
|
[ -n "${{ secrets.MINTEL_REGISTRY_TOKEN }}" ] && echo "✅ MINTEL_REGISTRY_TOKEN is set" || echo "❌ MINTEL_REGISTRY_TOKEN is empty"
|
|
|
|
- name: 🔐 Registry Auth
|
|
env:
|
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
GITEA_PAT: ${{ secrets.GITEA_PAT }}
|
|
MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }}
|
|
MINTEL_REGISTRY_TOKEN: ${{ secrets.MINTEL_REGISTRY_TOKEN }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: bash scripts/registry-auth.sh
|
|
- name: Install dependencies
|
|
run: pnpm install --no-frozen-lockfile
|
|
- name: 🌐 Run Smoke Test
|
|
env:
|
|
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
|
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }}
|
|
run: npx tsx scripts/smoke.ts || echo "⚠️ Smoke test failed"
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# JOB 6: Notifications
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
notifications:
|
|
name: 🔔 Notify
|
|
needs: [prepare, deploy, post_deploy_checks]
|
|
if: always()
|
|
runs-on: docker
|
|
container:
|
|
image: catthehacker/ubuntu:act-latest
|
|
steps:
|
|
- name: 🔔 Gotify
|
|
shell: bash
|
|
run: |
|
|
DEPLOY="${{ needs.deploy.result }}"
|
|
TARGET="${{ needs.prepare.outputs.target }}"
|
|
VERSION="${{ needs.prepare.outputs.image_tag }}"
|
|
URL="${{ needs.prepare.outputs.next_public_url }}"
|
|
|
|
if [[ "$DEPLOY" == "success" ]]; then
|
|
PRIORITY=2
|
|
EMOJI="✅"
|
|
STATUS_LINE="Deploy Success"
|
|
else
|
|
PRIORITY=10
|
|
EMOJI="🚨"
|
|
STATUS_LINE="DEPLOY FAILED"
|
|
fi
|
|
|
|
TITLE="$EMOJI e-tib.com $VERSION → $TARGET"
|
|
MESSAGE="$STATUS_LINE\n$URL"
|
|
|
|
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
|
|
-F "title=$TITLE" \
|
|
-F "message=$MESSAGE" \
|
|
-F "priority=$PRIORITY" || true
|