Compare commits

...

8 Commits

Author SHA1 Message Date
2d8df53e36 refactor: Replace hardcoded Traefik service names with a dynamic project name variable.
All checks were successful
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m25s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Successful in 2m52s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Successful in 29s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 2s
2026-02-01 14:26:44 +01:00
6f49dbc56c ci: Update Gitea deploy workflow configuration.
All checks were successful
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 20s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m24s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Successful in 2m55s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Successful in 30s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 1s
2026-02-01 14:17:09 +01:00
ad2a477636 perf: Add Docker build cache mounts for npm ci and npm run build commands.
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m24s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Successful in 5m48s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Failing after 12s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 3s
2026-02-01 14:00:44 +01:00
77a1067820 build: include .next/cache in Docker build context.
All checks were successful
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m22s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Successful in 5m42s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Successful in 44s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 2s
2026-02-01 13:43:42 +01:00
ea3076b4ec fix: Remove quotes from environment variables in Traefik host rules within docker-compose.yml.
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m28s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Has been cancelled
Build & Deploy KLZ Cables / 🔔 Notifications (push) Has been cancelled
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Has been cancelled
2026-02-01 13:38:35 +01:00
17fe0d7107 fix: Update Traefik host rules in docker-compose.yml to use properly quoted environment variables.
All checks were successful
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 2m32s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Successful in 4m26s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Successful in 49s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 1s
2026-02-01 13:26:25 +01:00
38b512973b ci: update Gitea deploy workflow configuration.
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m17s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Successful in 4m52s
Build & Deploy KLZ Cables / 🔔 Notifications (push) Has been cancelled
Build & Deploy KLZ Cables / 🚀 Deploy (push) Has been cancelled
2026-02-01 13:18:06 +01:00
4f73838c21 ci: update Gitea deploy workflow configuration.
Some checks failed
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Has been cancelled
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Has been cancelled
Build & Deploy KLZ Cables / 🚀 Deploy (push) Has been cancelled
Build & Deploy KLZ Cables / 🔔 Notifications (push) Has been cancelled
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Has been cancelled
2026-02-01 13:15:34 +01:00
4 changed files with 93 additions and 41 deletions

View File

@@ -1,5 +1,6 @@
node_modules
.next
!.next/cache
.git
.DS_Store
.env

View File

@@ -6,6 +6,16 @@ on:
- 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:
# ──────────────────────────────────────────────────────────────────────────────
@@ -19,6 +29,10 @@ jobs:
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 }}
@@ -42,7 +56,11 @@ jobs:
TARGET="testing"
IMAGE_TAG="main-${SHORT_SHA}"
ENV_FILE=".env.testing"
TRAEFIK_HOST="\`testing.klz-cables.com\`"
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
@@ -51,7 +69,11 @@ jobs:
TARGET="production"
IMAGE_TAG="$TAG"
ENV_FILE=".env.prod"
TRAEFIK_HOST="\`klz-cables.com\`, \`www.klz-cables.com\`"
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
@@ -59,7 +81,11 @@ jobs:
TARGET="staging"
IMAGE_TAG="$TAG"
ENV_FILE=".env.staging"
TRAEFIK_HOST="\`staging.klz-cables.com\`"
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
@@ -76,6 +102,10 @@ jobs:
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
@@ -100,23 +130,38 @@ jobs:
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: 🔍 Lint & Typecheck
- name: 🧪 Run Checks in Parallel
if: github.event.inputs.skip_long_checks != 'true'
run: |
npm run lint
npm run typecheck
npm run lint &
LINT_PID=$!
npm run typecheck &
TYPE_PID=$!
npm run test &
TEST_PID=$!
- name: 🧪 Test
run: npm run test
# 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, qa]
needs: prepare
runs-on: docker
steps:
- name: Checkout repository
@@ -130,10 +175,10 @@ jobs:
env:
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
TARGET: ${{ needs.prepare.outputs.target }}
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.target == 'production' && secrets.NEXT_PUBLIC_BASE_URL || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.TESTING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }}
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.target == 'production' && 'https://cms.klz-cables.com' || (needs.prepare.outputs.target == 'staging' && 'https://cms-staging.klz-cables.com' || 'https://cms-testing.klz-cables.com') }}
DIRECTUS_URL: ${{ needs.prepare.outputs.directus_url }}
run: |
echo "🏗️ Building → $TARGET / $IMAGE_TAG"
docker buildx build \
@@ -144,21 +189,24 @@ jobs:
--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 .
# ──────────────────────────────────────────────────────────────────────────────
# JOB 4: Deploy via SSH
# ──────────────────────────────────────────────────────────────────────────────
deploy:
name: 🚀 Deploy
needs: [prepare, build]
needs: [prepare, build, qa]
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.target == 'production' && secrets.NEXT_PUBLIC_BASE_URL || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.TESTING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }}
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) }}
@@ -168,7 +216,9 @@ jobs:
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
MAIL_FROM: ${{ secrets.MAIL_FROM }}
MAIL_RECIPIENTS: ${{ secrets.MAIL_RECIPIENTS }}
DIRECTUS_URL: ${{ needs.prepare.outputs.target == 'production' && 'https://cms.klz-cables.com' || (needs.prepare.outputs.target == 'staging' && 'https://cms-staging.klz-cables.com' || 'https://cms-testing.klz-cables.com') }}
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 }}
@@ -206,6 +256,7 @@ jobs:
# Directus
DIRECTUS_URL=$DIRECTUS_URL
DIRECTUS_HOST=$DIRECTUS_HOST
DIRECTUS_KEY=$DIRECTUS_KEY
DIRECTUS_SECRET=$DIRECTUS_SECRET
DIRECTUS_ADMIN_EMAIL=$DIRECTUS_ADMIN_EMAIL
@@ -223,24 +274,24 @@ jobs:
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" TRAEFIK_HOST="$TRAEFIK_HOST" bash << 'EOF'
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 --env-file "$ENV_FILE" pull
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" pull
echo "→ Starting containers..."
docker compose --env-file "$ENV_FILE" up -d
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 --env-file "$ENV_FILE" ps
if ! docker compose --env-file "$ENV_FILE" ps | grep -q "Up"; then
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 --env-file "$ENV_FILE" logs --tail=150
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" logs --tail=150
exit 1
fi
@@ -271,7 +322,7 @@ jobs:
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 }}" \
-F "priority=${{ needs.prepare.outputs.gotify_priority }}" || true
-F "priority=4" || true
- name: 🔔 Gotify - Failure
if: needs.deploy.result == 'failure' || needs.build.result == 'failure' || needs.qa.result == 'failure'

View File

@@ -8,7 +8,7 @@ WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json package-lock.json* ./
RUN npm ci
RUN --mount=type=cache,target=/root/.npm npm ci
# Rebuild the source code only when needed
@@ -37,7 +37,7 @@ ENV DIRECTUS_URL=$DIRECTUS_URL
# Validate environment variables during build
RUN npx tsx scripts/validate-env.ts
RUN npm run build
RUN --mount=type=cache,target=/app/.next/cache npm run build
# Production image, copy all the files and run next
FROM base AS runner

View File

@@ -9,22 +9,22 @@ services:
labels:
- "traefik.enable=true"
# HTTP ⇒ HTTPS redirect
- "traefik.http.routers.klz-cables-web.rule=Host(${TRAEFIK_HOST}) && !PathPrefix(`/.well-known/acme-challenge/`)"
- "traefik.http.routers.klz-cables-web.entrypoints=web"
- "traefik.http.routers.klz-cables-web.middlewares=redirect-https"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-web.rule=Host(${TRAEFIK_HOST}) && !PathPrefix(`/.well-known/acme-challenge/`)"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-web.entrypoints=web"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-web.middlewares=redirect-https"
# HTTPS router
- "traefik.http.routers.klz-cables.rule=Host(${TRAEFIK_HOST})"
- "traefik.http.routers.klz-cables.entrypoints=websecure"
- "traefik.http.routers.klz-cables.tls.certresolver=le"
- "traefik.http.routers.klz-cables.tls=true"
- "traefik.http.routers.klz-cables.service=klz-cables"
- "traefik.http.services.klz-cables.loadbalancer.server.port=3000"
- "traefik.http.services.klz-cables.loadbalancer.server.scheme=http"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.rule=Host(${TRAEFIK_HOST})"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.entrypoints=websecure"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.tls.certresolver=le"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.tls=true"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.service=${PROJECT_NAME:-klz-cables}"
- "traefik.http.services.${PROJECT_NAME:-klz-cables}.loadbalancer.server.port=3000"
- "traefik.http.services.${PROJECT_NAME:-klz-cables}.loadbalancer.server.scheme=http"
# Forwarded Headers
- "traefik.http.middlewares.klz-forward.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.middlewares.klz-forward.headers.customrequestheaders.X-Forwarded-Ssl=on"
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-forward.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-forward.headers.customrequestheaders.X-Forwarded-Ssl=on"
# Middlewares
- "traefik.http.routers.klz-cables.middlewares=klz-forward,compress"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.middlewares=${PROJECT_NAME:-klz-cables}-forward,compress"
directus:
image: directus/directus:11
@@ -51,11 +51,11 @@ services:
- ./directus/extensions:/directus/extensions
labels:
- "traefik.enable=true"
- "traefik.http.routers.klz-directus.rule=Host(`cms.klz-cables.com`) || Host(`cms-staging.klz-cables.com`)"
- "traefik.http.routers.klz-directus.entrypoints=websecure"
- "traefik.http.routers.klz-directus.tls.certresolver=le"
- "traefik.http.routers.klz-directus.tls=true"
- "traefik.http.services.klz-directus.loadbalancer.server.port=8055"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-directus.rule=Host(${DIRECTUS_HOST})"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-directus.entrypoints=websecure"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-directus.tls.certresolver=le"
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-directus.tls=true"
- "traefik.http.services.${PROJECT_NAME:-klz-cables}-directus.loadbalancer.server.port=8055"
directus-db:
image: postgres:15-alpine