Files
klz-cables.com/.gitea/workflows/deploy.yml
Marc Mintel 73c32c6d31
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m30s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Successful in 2m51s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Successful in 39s
Build & Deploy KLZ Cables / ⚡ PageSpeed (push) Failing after 1m6s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 2s
fix(ci): robust chromium installation to avoid snap issues on arm64
2026-02-01 19:46:41 +01:00

420 lines
21 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 KLZ Cables
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
shell: bash
run: |
TAG="${{ github.ref_name }}"
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-9)
IMAGE_TAG="sha-${SHORT_SHA}"
COMMIT_MSG=$(git log -1 --pretty=%s || echo "No commit message available")
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.klz-cables.com`'
NEXT_PUBLIC_BASE_URL="https://testing.klz-cables.com"
DIRECTUS_URL="https://cms.testing.klz-cables.com"
DIRECTUS_HOST='`cms.testing.klz-cables.com`'
PROJECT_NAME="klz-cables-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='`klz-cables.com`, `www.klz-cables.com`'
NEXT_PUBLIC_BASE_URL="https://klz-cables.com"
DIRECTUS_URL="https://cms.klz-cables.com"
DIRECTUS_HOST='`cms.klz-cables.com`'
PROJECT_NAME="klz-cables-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.klz-cables.com`'
NEXT_PUBLIC_BASE_URL="https://staging.klz-cables.com"
DIRECTUS_URL="https://cms.staging.klz-cables.com"
DIRECTUS_HOST='`cms.staging.klz-cables.com`'
PROJECT_NAME="klz-cables-staging"
IS_PROD="false"
GOTIFY_TITLE="🧪 Staging-Deploy (Pre-Release)"
GOTIFY_PRIORITY=5
else
TARGET="skip"
GOTIFY_TITLE="❓ Unbekannter Tag"
GOTIFY_PRIORITY=3
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: 🧪 Quality Assurance
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: 📦 Restore npm cache
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- 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 for all and fail if any fail
wait $LINT_PID || exit 1
wait $TYPE_PID || exit 1
wait $TEST_PID || exit 1
# ──────────────────────────────────────────────────────────────────────────────
# JOB 3: Build & Push Docker Image
# ──────────────────────────────────────────────────────────────────────────────
build:
name: 🏗️ Build & Push
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 Image bauen & pushen
env:
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
TARGET: ${{ needs.prepare.outputs.target }}
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_base_url }}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ needs.prepare.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.TESTING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }}
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ needs.prepare.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.TESTING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }}
DIRECTUS_URL: ${{ needs.prepare.outputs.directus_url }}
run: |
echo "🏗️ Building → $TARGET / $IMAGE_TAG"
docker buildx build \
--pull \
--platform linux/arm64 \
--build-arg NEXT_PUBLIC_BASE_URL="$NEXT_PUBLIC_BASE_URL" \
--build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="$NEXT_PUBLIC_UMAMI_WEBSITE_ID" \
--build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL="$NEXT_PUBLIC_UMAMI_SCRIPT_URL" \
--build-arg DIRECTUS_URL="$DIRECTUS_URL" \
-t registry.infra.mintel.me/mintel/klz-cables.com:$IMAGE_TAG \
--cache-from type=registry,ref=registry.infra.mintel.me/mintel/klz-cables.com:buildcache \
--cache-to type=registry,ref=registry.infra.mintel.me/mintel/klz-cables.com:buildcache,mode=max \
--push .
- name: 🏗️ Gatekeeper bauen & pushen
env:
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
run: |
docker buildx build \
--pull \
--platform linux/arm64 \
-t registry.infra.mintel.me/mintel/klz-cables-gatekeeper:$IMAGE_TAG \
--push ./gatekeeper
# ──────────────────────────────────────────────────────────────────────────────
# JOB 4: Deploy via SSH
# ──────────────────────────────────────────────────────────────────────────────
deploy:
name: 🚀 Deploy
needs: [prepare, build, qa]
if: ${{ needs.prepare.outputs.target != 'skip' }}
runs-on: docker
env:
TARGET: ${{ needs.prepare.outputs.target }}
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
ENV_FILE: ${{ needs.prepare.outputs.env_file }}
TRAEFIK_HOST: ${{ needs.prepare.outputs.traefik_host }}
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_base_url }}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ needs.prepare.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.TESTING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }}
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ needs.prepare.outputs.target == 'production' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.TESTING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }}
SENTRY_DSN: ${{ needs.prepare.outputs.target == 'production' && secrets.SENTRY_DSN || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_SENTRY_DSN || secrets.TESTING_SENTRY_DSN || secrets.SENTRY_DSN) }}
MAIL_HOST: ${{ secrets.MAIL_HOST }}
MAIL_PORT: ${{ secrets.MAIL_PORT }}
MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }}
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
MAIL_FROM: ${{ secrets.MAIL_FROM }}
MAIL_RECIPIENTS: ${{ secrets.MAIL_RECIPIENTS }}
DIRECTUS_URL: ${{ needs.prepare.outputs.directus_url }}
DIRECTUS_HOST: ${{ needs.prepare.outputs.directus_host }}
PROJECT_NAME: ${{ needs.prepare.outputs.project_name }}
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_NAME: ${{ secrets.DIRECTUS_DB_NAME || 'directus' }}
DIRECTUS_DB_USER: ${{ secrets.DIRECTUS_DB_USER || 'directus' }}
DIRECTUS_DB_PASSWORD: ${{ secrets.DIRECTUS_DB_PASSWORD }}
DIRECTUS_API_TOKEN: ${{ secrets.DIRECTUS_API_TOKEN }}
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 🚀 Deploy to ${{ env.TARGET }}
run: |
echo "Deploying $TARGET → $IMAGE_TAG"
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
cat > /tmp/klz-cables.env << EOF
# Generated by CI - $TARGET - $(date -u)
NODE_ENV=production
NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL
SENTRY_DSN=$SENTRY_DSN
MAIL_HOST=$MAIL_HOST
MAIL_PORT=$MAIL_PORT
MAIL_USERNAME=$MAIL_USERNAME
MAIL_PASSWORD=$MAIL_PASSWORD
MAIL_FROM=$MAIL_FROM
MAIL_RECIPIENTS=$MAIL_RECIPIENTS
# Directus
DIRECTUS_URL=$DIRECTUS_URL
DIRECTUS_HOST=$DIRECTUS_HOST
DIRECTUS_KEY=$DIRECTUS_KEY
DIRECTUS_SECRET=$DIRECTUS_SECRET
DIRECTUS_ADMIN_EMAIL=$DIRECTUS_ADMIN_EMAIL
DIRECTUS_ADMIN_PASSWORD=$DIRECTUS_ADMIN_PASSWORD
DIRECTUS_DB_NAME=$DIRECTUS_DB_NAME
DIRECTUS_DB_USER=$DIRECTUS_DB_USER
DIRECTUS_DB_PASSWORD=$DIRECTUS_DB_PASSWORD
DIRECTUS_API_TOKEN=$DIRECTUS_API_TOKEN
GATEKEEPER_PASSWORD=$GATEKEEPER_PASSWORD
IMAGE_TAG=$IMAGE_TAG
TRAEFIK_HOST=$TRAEFIK_HOST
ENV_FILE=$ENV_FILE
AUTH_MIDDLEWARE=$( [[ "$TARGET" == "production" ]] && echo "compress" || echo "${PROJECT_NAME}-auth,compress" )
EOF
scp -o StrictHostKeyChecking=accept-new /tmp/klz-cables.env root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/$ENV_FILE
scp -o StrictHostKeyChecking=accept-new docker-compose.yml root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/docker-compose.yml
ssh -o StrictHostKeyChecking=accept-new root@alpha.mintel.me IMAGE_TAG="$IMAGE_TAG" ENV_FILE="$ENV_FILE" PROJECT_NAME="$PROJECT_NAME" bash << 'EOF'
set -e
cd /home/deploy/sites/klz-cables.com
chmod 600 "$ENV_FILE"
chown deploy:deploy "$ENV_FILE"
echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
echo "→ Pulling image: $IMAGE_TAG"
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" pull
echo "→ Starting containers..."
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" up -d --remove-orphans
docker system prune -f --filter "until=168h"
echo "→ Waiting 15s for warmup..."
sleep 15
echo "→ Container status:"
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" ps
if ! docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" ps | grep -q "Up"; then
echo "❌ Fehler: Container nicht Up!"
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" logs --tail=150
exit 1
fi
# ──────────────────────────────────────────────────────────────────────────────
# JOB 5: PageSpeed Test
# ──────────────────────────────────────────────────────────────────────────────
pagespeed:
name: ⚡ PageSpeed
needs: [prepare, deploy]
if: |
always() &&
needs.prepare.outputs.target != 'skip' &&
needs.deploy.result == 'success' &&
github.event.inputs.skip_long_checks != 'true'
runs-on: docker
outputs:
report_url: ${{ steps.save.outputs.report_url }}
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: 🔍 Install Chromium (Native)
run: |
apt-get update
apt-get install -y gnupg wget
# Manually add xtradeb PPA (avoids software-properties-common dependency)
# Works for Ubuntu; for Debian it might fail, so we continue-on-error
export CODENAME=$(. /etc/os-release && echo $VERSION_CODENAME)
echo "deb http://ppa.launchpad.net/xtradeb/apps/ubuntu $CODENAME main" > /etc/apt/sources.list.d/xtradeb-ppa.list
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 290D73D2240900B5
apt-get update
apt-get install -y chromium
continue-on-error: true
- name: 🧪 Run PageSpeed (Lighthouse)
env:
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_base_url }}
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
PAGESPEED_LIMIT: 8
PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium
CHROME_PATH: /usr/bin/chromium
run: npm run pagespeed:test
- name: 💾 Save Report URL
id: save
if: always()
run: |
if [ -f pagespeed-report-url.txt ]; then
URL=$(cat pagespeed-report-url.txt)
echo "report_url=$URL" >> $GITHUB_OUTPUT
echo "✅ Report URL found: $URL"
fi
# ──────────────────────────────────────────────────────────────────────────────
# JOB 6: Notifications
# ──────────────────────────────────────────────────────────────────────────────
notifications:
name: 🔔 Notifications
needs: [prepare, qa, build, deploy, pagespeed]
if: always()
runs-on: docker
steps:
- name: 📊 Deployment Summary
run: |
echo "┌──────────────────────────────┐"
echo "│ Deployment Summary │"
echo "├──────────────────────────────┤"
echo "│ Status: ${{ needs.deploy.result }} │"
echo "│ Umgebung: ${{ needs.prepare.outputs.target || 'skipped' }} │"
echo "│ Version: ${{ needs.prepare.outputs.image_tag }} │"
echo "│ Commit: ${{ needs.prepare.outputs.short_sha }} │"
echo "│ Message: ${{ needs.prepare.outputs.commit_msg }} │"
echo "└──────────────────────────────┘"
- name: 🔔 Gotify - Success
if: needs.deploy.result == 'success'
run: |
REPORT_MSG=""
if [ -n "${{ needs.pagespeed.outputs.report_url }}" ]; then
REPORT_MSG="\n\n⚡ **PageSpeed Report:**\n${{ needs.pagespeed.outputs.report_url }}"
fi
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 }} (${{ needs.prepare.outputs.commit_msg }})\nVon: ${{ github.actor }}\nRun: ${{ github.run_id }}${REPORT_MSG}" \
-F "priority=4" || true
- name: 🔔 Gotify - Failure
if: needs.deploy.result == 'failure' || needs.build.result == 'failure' || needs.qa.result == 'failure'
run: |
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
-F "title=❌ Deployment FEHLGESCHLAGEN ${{ needs.prepare.outputs.target || 'unknown' }}" \
-F "message=**Fehler beim Deploy auf ${{ needs.prepare.outputs.target }}**\n\nVersion: ${{ needs.prepare.outputs.image_tag || '?' }}\nCommit: ${{ needs.prepare.outputs.short_sha || '?' }}\nVon: ${{ github.actor }}\nRun: ${{ github.run_id }}\n\nBitte Logs prüfen!" \
-F "priority=8" || true