Files
at-mintel/packages/infra/gitea/deploy-action.yml
Marc Mintel 83b4ea8807
Some checks failed
Code Quality / lint-and-build (push) Failing after 52s
Release Packages / release (push) Failing after 32s
feat: introduce Gatekeeper application, Directus utilities, and monorepo configuration for linting, testing, and husky hooks.
2026-02-01 21:23:34 +01:00

262 lines
12 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
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: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🔍 Environment & Version ermitteln
id: determine
run: |
TAG="${{ github.ref_name }}"
SHORT_SHA="${{ github.sha }}"
SHORT_SHA="${SHORT_SHA:0: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}" # Note: Host() backticks usually needed in compose
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" >> $GITHUB_OUTPUT
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
echo "env_file=$ENV_FILE" >> $GITHUB_OUTPUT
echo "traefik_host=$TRAEFIK_HOST" >> $GITHUB_OUTPUT
echo "next_public_base_url=$NEXT_PUBLIC_BASE_URL" >> $GITHUB_OUTPUT
echo "directus_url=$DIRECTUS_URL" >> $GITHUB_OUTPUT
echo "directus_host=$DIRECTUS_HOST" >> $GITHUB_OUTPUT
echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT
echo "is_prod=$IS_PROD" >> $GITHUB_OUTPUT
echo "gotify_title=$GOTIFY_TITLE" >> $GITHUB_OUTPUT
echo "gotify_priority=$GOTIFY_PRIORITY" >> $GITHUB_OUTPUT
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
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
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: 🧪 Run Checks
if: github.event.inputs.skip_long_checks != 'true'
run: |
npm run lint
npm run typecheck
npm run test
# ──────────────────────────────────────────────────────────────────────────────
# JOB 3: Build & Push
# ──────────────────────────────────────────────────────────────────────────────
build:
name: 🏗️ Build
needs: prepare
if: needs.prepare.outputs.target != 'skip'
runs-on: docker
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 🔐 Registry Login
run: |
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: 🏗️ Docker Build & Push
env:
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_base_url }}
run: |
docker buildx build \
--pull \
--platform linux/arm64 \
--build-arg NEXT_PUBLIC_BASE_URL="$NEXT_PUBLIC_BASE_URL" \
-t registry.infra.mintel.me/mintel/${{ github.event.repository.name }}:$IMAGE_TAG \
--push .
# ──────────────────────────────────────────────────────────────────────────────
# JOB 4: Deploy
# ──────────────────────────────────────────────────────────────────────────────
deploy:
name: 🚀 Deploy
needs: [prepare, build, qa]
if: needs.prepare.outputs.target != 'skip'
runs-on: docker
env:
TARGET: ${{ needs.prepare.outputs.target }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 🚀 Deploy via SSH
env:
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
ENV_FILE: ${{ needs.prepare.outputs.env_file }}
TRAEFIK_HOST: ${{ needs.prepare.outputs.traefik_host }}
PROJECT_NAME: ${{ needs.prepare.outputs.project_name }}
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=$TRAEFIK_HOST
PROJECT_NAME=$PROJECT_NAME
ENV_FILE=$ENV_FILE
# App Config
NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
# 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 }}"
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=168h"
EOF
# ──────────────────────────────────────────────────────────────────────────────
# JOB 5: Notifications
# ──────────────────────────────────────────────────────────────────────────────
notifications:
name: 🔔 Notifications
needs: [prepare, deploy]
if: always()
runs-on: docker
steps:
- name: 🔔 Gotify
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 }}**" \
-F "priority=4" || true