Files
at-mintel/packages/infra/gitea/deploy-action.yml
Marc Mintel 0096c18098
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 27s
Monorepo Pipeline / 🧪 Test (push) Failing after 24s
Monorepo Pipeline / 🏗️ Build (push) Failing after 22s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m4s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
fix(infra): correct registry data path and enable untagged manifest deletion
2026-02-11 12:39:50 +01:00

312 lines
14 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Build & Deploy
on:
push:
branches:
- main
tags:
- 'v*'
workflow_dispatch:
inputs:
skip_long_checks:
description: 'Skip tests? (true/false)'
required: false
default: 'false'
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
# ──────────────────────────────────────────────────────────────────────────────
# JOB 1: Prepare & Determine Environment
# ──────────────────────────────────────────────────────────────────────────────
prepare:
name: 🔍 Prepare Environment
runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
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 }}
next_public_base_url: ${{ steps.determine.outputs.next_public_base_url }}
directus_url: ${{ steps.determine.outputs.directus_url }}
directus_host: ${{ steps.determine.outputs.directus_host }}
project_name: ${{ steps.determine.outputs.project_name }}
is_prod: ${{ steps.determine.outputs.is_prod }}
gotify_title: ${{ steps.determine.outputs.gotify_title }}
gotify_priority: ${{ steps.determine.outputs.gotify_priority }}
short_sha: ${{ steps.determine.outputs.short_sha }}
commit_msg: ${{ steps.determine.outputs.commit_msg }}
steps:
- name: 🧹 Maintenance (High Density Cleanup)
shell: bash
run: |
echo "Purging old build layers and dangling images..."
docker image prune -f
docker builder prune -f --filter "until=6h"
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 🔍 Environment & Version ermitteln
id: determine
run: |
TAG="${{ github.ref_name }}"
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-9)
COMMIT_MSG=$(git log -1 --pretty=%s || echo "No commit message available")
# Base Domain (e.g. example.com)
DOMAIN_BASE=$(echo "${{ secrets.NEXT_PUBLIC_BASE_URL }}" | sed -E 's|https?://||' | sed -E 's|/.*||')
PRJ_ID="${{ github.event.repository.name }}"
if [[ "${{ github.ref_type }}" == "branch" && "$TAG" == "main" ]]; then
if [[ "$COMMIT_MSG" =~ ^chore: ]]; then
TARGET="skip"
GOTIFY_TITLE=" Skip Deploy (Chore)"
GOTIFY_PRIORITY=2
else
TARGET="testing"
IMAGE_TAG="main-${SHORT_SHA}"
ENV_FILE=".env.testing"
TRAEFIK_HOST="\`testing.\${DOMAIN_BASE}\`"
NEXT_PUBLIC_BASE_URL="https://testing.\${DOMAIN_BASE}"
DIRECTUS_URL="https://cms.testing.\${DOMAIN_BASE}"
DIRECTUS_HOST="\`cms.testing.\${DOMAIN_BASE}\`"
PROJECT_NAME="\${PRJ_ID}-testing"
IS_PROD="false"
GOTIFY_TITLE="🧪 Testing-Deploy"
GOTIFY_PRIORITY=4
fi
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
TARGET="production"
IMAGE_TAG="$TAG"
ENV_FILE=".env.prod"
TRAEFIK_HOST="\`\${DOMAIN_BASE}\`, \`www.\${DOMAIN_BASE}\`"
NEXT_PUBLIC_BASE_URL="https://\${DOMAIN_BASE}"
DIRECTUS_URL="https://cms.\${DOMAIN_BASE}"
DIRECTUS_HOST="\`cms.\${DOMAIN_BASE}\`"
PROJECT_NAME="\${PRJ_ID}-prod"
IS_PROD="true"
GOTIFY_TITLE="🚀 Production-Release"
GOTIFY_PRIORITY=6
elif [[ "$TAG" =~ -rc || "$TAG" =~ -beta || "$TAG" =~ -alpha ]]; then
TARGET="staging"
IMAGE_TAG="$TAG"
ENV_FILE=".env.staging"
TRAEFIK_HOST="\`staging.\${DOMAIN_BASE}\`"
NEXT_PUBLIC_BASE_URL="https://staging.\${DOMAIN_BASE}"
DIRECTUS_URL="https://cms.staging.\${DOMAIN_BASE}"
DIRECTUS_HOST="\`cms.staging.\${DOMAIN_BASE}\`"
PROJECT_NAME="\${PRJ_ID}-staging"
IS_PROD="false"
GOTIFY_TITLE="🧪 Staging-Deploy (Pre-Release)"
GOTIFY_PRIORITY=5
else
TARGET="skip"
fi
else
TARGET="skip"
fi
{
echo "target=$TARGET"
echo "image_tag=$IMAGE_TAG"
echo "env_file=$ENV_FILE"
echo "traefik_host=$TRAEFIK_HOST"
echo "next_public_base_url=$NEXT_PUBLIC_BASE_URL"
echo "directus_url=$DIRECTUS_URL"
echo "directus_host=$DIRECTUS_HOST"
echo "project_name=$PROJECT_NAME"
echo "is_prod=$IS_PROD"
echo "gotify_title=$GOTIFY_TITLE"
echo "gotify_priority=$GOTIFY_PRIORITY"
echo "short_sha=$SHORT_SHA"
echo "commit_msg=$COMMIT_MSG"
} >> "$GITHUB_OUTPUT"
# ──────────────────────────────────────────────────────────────────────────────
# JOB 2: Quality Assurance (Lint & 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 Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: 🧪 Run Checks in Parallel
if: github.event.inputs.skip_long_checks != 'true'
run: |
npm run lint &
LINT_PID=$!
npm run typecheck &
TYPE_PID=$!
npm run test &
TEST_PID=$!
wait $LINT_PID || exit 1
wait $TYPE_PID || exit 1
wait $TEST_PID || exit 1
# ──────────────────────────────────────────────────────────────────────────────
# JOB 3: Build & Push
# ──────────────────────────────────────────────────────────────────────────────
build:
name: 🏗️ Build
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: 🐳 Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 🔐 Registry Login
uses: docker/login-action@v3
with:
registry: registry.infra.mintel.me
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASS }}
- name: 🏗️ Docker Build & Push
uses: docker/build-push-action@v5
with:
context: .
file: packages/infra/docker/Dockerfile.nextjs
platforms: linux/arm64
pull: true
build-args: |
NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
NEXT_PUBLIC_TARGET=${{ needs.prepare.outputs.target }}
push: true
secrets: |
NPM_TOKEN=${{ secrets.NPM_TOKEN }}
tags: registry.infra.mintel.me/mintel/${{ github.event.repository.name }}:${{ needs.prepare.outputs.image_tag }}
cache-from: type=registry,ref=registry.infra.mintel.me/mintel/${{ github.event.repository.name }}:buildcache
cache-to: type=registry,ref=registry.infra.mintel.me/mintel/${{ github.event.repository.name }}:buildcache,mode=max
# ──────────────────────────────────────────────────────────────────────────────
# JOB 4: Deploy
# ──────────────────────────────────────────────────────────────────────────────
deploy:
name: 🚀 Deploy
needs: [prepare, build, qa]
if: needs.prepare.outputs.target != 'skip'
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 }}
ENV_FILE: ${{ needs.prepare.outputs.env_file }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 🚀 Deploy via SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
# Generate .env from secrets
cat > /tmp/app.env << EOF
# Generated by CI - $TARGET - $(date -u)
NODE_ENV=production
IMAGE_TAG=$IMAGE_TAG
TRAEFIK_HOST=${{ needs.prepare.outputs.traefik_host }}
PROJECT_NAME=$PROJECT_NAME
ENV_FILE=$ENV_FILE
# App Config
NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
NEXT_PUBLIC_TARGET=$TARGET
# Directus Config
DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }}
DIRECTUS_HOST=${{ needs.prepare.outputs.directus_host }}
DIRECTUS_KEY=${{ secrets.DIRECTUS_KEY }}
DIRECTUS_SECRET=${{ secrets.DIRECTUS_SECRET }}
DIRECTUS_ADMIN_EMAIL=${{ secrets.DIRECTUS_ADMIN_EMAIL }}
DIRECTUS_ADMIN_PASSWORD=${{ secrets.DIRECTUS_ADMIN_PASSWORD }}
DIRECTUS_DB_PASSWORD=${{ secrets.DIRECTUS_DB_PASSWORD }}
# Gatekeeper
GATEKEEPER_PASSWORD=${{ secrets.GATEKEEPER_PASSWORD || 'mintel' }}
AUTH_MIDDLEWARE=$( [[ "$TARGET" == "production" ]] && echo "compress" || echo "$PROJECT_NAME-auth,compress" )
EOF
APP_DIR="/home/deploy/sites/${{ github.event.repository.name }}"
ssh root@${{ secrets.SSH_HOST }} "mkdir -p $APP_DIR"
scp /tmp/app.env root@${{ secrets.SSH_HOST }}:$APP_DIR/$ENV_FILE
scp docker-compose.yml root@${{ secrets.SSH_HOST }}:$APP_DIR/docker-compose.yml
ssh root@${{ secrets.SSH_HOST }} IMAGE_TAG="$IMAGE_TAG" ENV_FILE="$ENV_FILE" PROJECT_NAME="$PROJECT_NAME" bash << 'EOF'
set -e
cd "/home/deploy/sites/${{ github.event.repository.name }}"
chmod 600 "$ENV_FILE"
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" pull
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" up -d --remove-orphans
docker system prune -f --filter "until=24h"
EOF
- name: 🧹 Post-Deploy Cleanup (Runner)
if: always()
run: docker builder prune -f --filter "until=1h"
# ──────────────────────────────────────────────────────────────────────────────
# JOB 5: Notifications
# ──────────────────────────────────────────────────────────────────────────────
notifications:
name: 🔔 Notifications
needs: [prepare, deploy]
if: always()
runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: 🔔 Gotify - Success
if: needs.deploy.result == 'success'
run: |
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
-F "title=${{ needs.prepare.outputs.gotify_title }}" \
-F "message=Erfolgreich deployt auf **${{ needs.prepare.outputs.target }}**\n\nVersion: **${{ needs.prepare.outputs.image_tag }}**\nCommit: ${{ needs.prepare.outputs.short_sha }}\nRun: ${{ github.run_id }}" \
-F "priority=4" || true
- name: 🔔 Gotify - Failure
if: |
needs.prepare.result == 'failure' ||
needs.qa.result == 'failure' ||
needs.build.result == 'failure' ||
needs.deploy.result == 'failure'
run: |
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
-F "title=❌ Deployment FEHLGESCHLAGEN ${{ github.event.repository.name }}" \
-F "message=**Fehler beim Deploy auf ${{ needs.prepare.outputs.target || 'unknown' }}**\n\nRun: ${{ github.run_id }}\nBitte Logs prüfen!" \
-F "priority=8" || true