Files
michaelschiemer/.gitea/workflows/build-image.yml
Michael Schiemer e9e87c9c5e
Some checks failed
Security Vulnerability Scan / Check for Dependency Changes (push) Successful in 1m1s
🚀 Build & Deploy Image / Determine Build Necessity (push) Successful in 28s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Successful in 10s
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
Security Vulnerability Scan / Composer Security Audit (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Successful in 16s
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Failing after 38s
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
fix: Replace apt-get with apk for Alpine-based docker-build container
2025-11-08 13:45:18 +01:00

1243 lines
46 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 Image
run-name: Build Image - ${{ github.ref_name }} - ${{ github.sha }}
on:
push:
branches:
- main
- staging
paths:
- '.gitea/workflows/**'
- 'src/**'
- 'resources/**'
- 'config/**'
- 'public/**'
- 'composer.json'
- 'composer.lock'
- 'package.json'
- 'package-lock.json'
- 'Dockerfile.production'
- 'Dockerfile.runtime'
- 'docker-compose.yml'
- 'docker-compose.*.yml'
- 'docker/**'
- 'deployment/**'
- 'scripts/ci/**'
- 'vite.config.*'
- 'tsconfig.json'
- 'babel.config.js'
- 'Makefile'
workflow_dispatch:
inputs:
branch:
description: 'Branch to build from'
required: false
type: choice
options:
- staging
- main
- develop
default: 'staging'
force_build:
description: 'Force image build even if no runtime changes detected'
type: boolean
required: false
default: false
env:
REGISTRY: registry.michaelschiemer.de
IMAGE_NAME: framework
RUNTIME_IMAGE_NAME: framework-runtime
concurrency:
group: build-image-${{ github.ref_name || github.head_ref || inputs.branch || github.run_id }}
cancel-in-progress: true
jobs:
# Job 0: Detect if a new image build is required
changes:
name: Determine Build Necessity
runs-on: ubuntu-latest
outputs:
needs_build: ${{ steps.filter.outputs.needs_build }}
changed_files: ${{ steps.filter.outputs.changed_files }}
needs_runtime_build: ${{ steps.filter.outputs.needs_runtime_build }}
steps:
- name: Download CI helpers
shell: bash
env:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
run: |
set -euo pipefail
REF="${{ github.sha }}"
if [ -z "$REF" ]; then
REF="${{ github.ref_name }}"
fi
if [ -z "$REF" ]; then
REF="${{ inputs.branch || 'staging' }}"
fi
URL="https://git.michaelschiemer.de/${{ github.repository }}/raw/${REF}/scripts/ci/clone_repo.sh"
mkdir -p /tmp/ci-tools
if [ -n "$CI_TOKEN" ]; then
curl -sfL -u "$CI_TOKEN:x-oauth-basic" "$URL" -o /tmp/ci-tools/clone_repo.sh
else
curl -sfL "$URL" -o /tmp/ci-tools/clone_repo.sh
fi
chmod +x /tmp/ci-tools/clone_repo.sh
- name: Analyse changed files
id: filter
shell: bash
env:
FORCE_BUILD: ${{ inputs.force_build }}
EVENT_NAME: ${{ github.event_name }}
EVENT_BEFORE: ${{ github.event.before }}
INPUT_BRANCH: ${{ inputs.branch }}
REF_NAME_GITHUB: ${{ github.ref_name }}
run: |
set -euo pipefail
FORCE="${FORCE_BUILD:-false}"
if [ "$EVENT_NAME" != "workflow_dispatch" ]; then
FORCE="false"
fi
REF_NAME="$REF_NAME_GITHUB"
if [ -z "$REF_NAME" ]; then
REF_NAME="$INPUT_BRANCH"
fi
if [ -z "$REF_NAME" ]; then
REF_NAME="staging"
fi
REPO="${{ github.repository }}"
WORKDIR="/workspace/repo"
export CI_REPOSITORY="$REPO"
export CI_TOKEN="${{ secrets.CI_TOKEN }}"
export CI_REF_NAME="$REF_NAME"
export CI_INPUT_BRANCH="$INPUT_BRANCH"
export CI_DEFAULT_BRANCH="main"
export CI_TARGET_DIR="$WORKDIR"
export CI_FETCH_DEPTH="2"
/tmp/ci-tools/clone_repo.sh
cd "$WORKDIR"
CHANGED_FILES=""
if [ "$EVENT_NAME" = "push" ] && [ -n "$EVENT_BEFORE" ]; then
if git rev-parse "$EVENT_BEFORE" >/dev/null 2>&1; then
CHANGED_FILES="$(git diff --name-only "$EVENT_BEFORE" HEAD || true)"
else
git fetch origin "$EVENT_BEFORE" --depth 1 || true
if git rev-parse "$EVENT_BEFORE" >/dev/null 2>&1; then
CHANGED_FILES="$(git diff --name-only "$EVENT_BEFORE" HEAD || true)"
fi
fi
fi
if [ -z "$CHANGED_FILES" ]; then
if git rev-parse HEAD^ >/dev/null 2>&1; then
CHANGED_FILES="$(git diff --name-only HEAD^ HEAD || true)"
else
echo " Erweiterter Fetch für Diff-Ermittlung"
git fetch origin "$REF_NAME" --depth 50 || true
if git rev-parse HEAD^ >/dev/null 2>&1; then
CHANGED_FILES="$(git diff --name-only HEAD^ HEAD || true)"
fi
fi
fi
if [ -z "$CHANGED_FILES" ] && [ "$FORCE" != "true" ]; then
# No diff information available; assume no build needed if this is not initial commit
# Only skip if we can detect this is not the first commit
if git rev-parse HEAD^ >/dev/null 2>&1; then
echo " Keine Änderungsinformation gefunden, aber HEAD^ existiert überspringe Build."
echo "needs_build=false" >> "$GITHUB_OUTPUT"
echo "changed_files=<none>" >> "$GITHUB_OUTPUT"
echo "needs_runtime_build=false" >> "$GITHUB_OUTPUT"
exit 0
else
# First commit or detached state - build to be safe
echo "⚠️ Keine Änderungsinformation gefunden bilde Image sicherheitshalber."
echo "needs_build=true" >> "$GITHUB_OUTPUT"
echo "changed_files=<none>" >> "$GITHUB_OUTPUT"
echo "needs_runtime_build=true" >> "$GITHUB_OUTPUT"
exit 0
fi
fi
NEEDS_BUILD=true
SUMMARY="Runtime-relevante Änderungen erkannt"
RUNTIME_BUILD=false
RUNTIME_PATTERN='^(Dockerfile\\.production|Dockerfile\\.runtime|docker/php/|install-php85\\.sh)'
if [ -n "$CHANGED_FILES" ]; then
NEEDS_BUILD=false
OTHER_NON_IGNORED=false
IGNORE_PATTERN='^(docs/|docs$|tests/|tests$|tests-e2e/|\.github/|\.gitea/|\.idea/|\.vscode/|\.husky/|.*\.md$|.*\.MD$|LICENSE$|CHANGELOG|CHANGELOG\.md$|\.editorconfig$|\.gitignore$)'
BUILD_TRIGGER_PATTERN='^(src/|resources/|config/|app/|public/|composer\.json$|composer\.lock$|composer/|package\.json$|package-lock\.json$|pnpm-lock\.yaml$|yarn\.lock$|Dockerfile\.production$|Dockerfile\.runtime$|docker-compose\..*\.yml$|docker-compose\.yml$|docker/|vite\.config\.(js|ts)$|tsconfig\.json$|babel\.config\.js$|Makefile$|artisan$)'
while IFS= read -r FILE; do
[ -z "$FILE" ] && continue
if echo "$FILE" | grep -Eq "$RUNTIME_PATTERN"; then
RUNTIME_BUILD=true
fi
if echo "$FILE" | grep -Eq "$IGNORE_PATTERN"; then
continue
fi
if echo "$FILE" | grep -Eq "$BUILD_TRIGGER_PATTERN"; then
NEEDS_BUILD=true
continue
fi
OTHER_NON_IGNORED=true
done <<< "$CHANGED_FILES"
if [ "$NEEDS_BUILD" = "false" ] && [ "$OTHER_NON_IGNORED" = "false" ]; then
SUMMARY="Nur Doku-/Teständerungen Container-Build wird übersprungen"
elif [ "$NEEDS_BUILD" = "false" ] && [ "$OTHER_NON_IGNORED" = "true" ]; then
SUMMARY="Keine Build-Trigger gefunden Container-Build wird übersprungen"
elif [ "$NEEDS_BUILD" = "true" ]; then
SUMMARY="Runtime-relevante Änderungen erkannt Container-Build wird ausgeführt"
fi
else
RUNTIME_BUILD=true
fi
if [ "$FORCE" = "true" ]; then
NEEDS_BUILD=true
SUMMARY="Manuell erzwungener Build"
RUNTIME_BUILD=true
fi
PRETTY_CHANGES="$(printf '%s' "$CHANGED_FILES" | tr '\n' ', ' | sed 's/, $//')"
if [ -z "$PRETTY_CHANGES" ]; then
PRETTY_CHANGES="<none>"
fi
echo "📄 Änderungen: $PRETTY_CHANGES"
echo " Ergebnis: $SUMMARY"
echo "🔁 Runtime-Rebuild erforderlich: $RUNTIME_BUILD"
echo "needs_build=$NEEDS_BUILD" >> "$GITHUB_OUTPUT"
echo "changed_files=$PRETTY_CHANGES" >> "$GITHUB_OUTPUT"
echo "needs_runtime_build=$RUNTIME_BUILD" >> "$GITHUB_OUTPUT"
runtime-base:
name: Build Runtime Base Image
needs: changes
runs-on: docker-build
outputs:
image_ref: ${{ steps.set-result.outputs.image_ref }}
built: ${{ steps.set-result.outputs.built }}
steps:
- name: Evaluate runtime build requirement
id: decision
shell: bash
run: |
if [ "${{ needs.changes.outputs.needs_runtime_build }}" = "true" ]; then
echo "Runtime base rebuild required"
echo "should_build=true" >> "$GITHUB_OUTPUT"
else
echo "Runtime base rebuild not required"
echo "should_build=false" >> "$GITHUB_OUTPUT"
fi
- name: Download CI helpers
if: ${{ steps.decision.outputs.should_build == 'true' }}
shell: bash
env:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
run: |
set -euo pipefail
REF="${{ github.sha }}"
if [ -z "$REF" ]; then
REF="${{ github.ref_name }}"
fi
if [ -z "$REF" ]; then
REF="${{ inputs.branch || 'staging' }}"
fi
URL="https://git.michaelschiemer.de/${{ github.repository }}/raw/${REF}/scripts/ci/clone_repo.sh"
mkdir -p /tmp/ci-tools
if [ -n "$CI_TOKEN" ]; then
curl -sfL -u "$CI_TOKEN:x-oauth-basic" "$URL" -o /tmp/ci-tools/clone_repo.sh
else
curl -sfL "$URL" -o /tmp/ci-tools/clone_repo.sh
fi
chmod +x /tmp/ci-tools/clone_repo.sh
- name: Skip runtime base build
if: ${{ steps.decision.outputs.should_build != 'true' }}
run: |
echo " Runtime base build not required skipping."
- name: Install git and setup environment
if: steps.decision.outputs.should_build == 'true'
shell: sh
run: |
if ! command -v bash >/dev/null 2>&1 || ! command -v git >/dev/null 2>&1; then
apk add --no-cache git bash curl
fi
bash --version
git --version
- name: Checkout code
if: steps.decision.outputs.should_build == 'true'
shell: bash
run: |
REF_NAME="${{ github.ref_name }}"
INPUT_BRANCH="${{ inputs.branch }}"
REPO="${{ github.repository }}"
export CI_REPOSITORY="$REPO"
export CI_TOKEN="${{ secrets.CI_TOKEN }}"
export CI_REF_NAME="$REF_NAME"
export CI_INPUT_BRANCH="$INPUT_BRANCH"
export CI_DEFAULT_BRANCH="main"
export CI_TARGET_DIR="/workspace/repo"
export CI_FETCH_DEPTH="1"
/tmp/ci-tools/clone_repo.sh
cd /workspace/repo
- name: Setup Docker Buildx
if: steps.decision.outputs.should_build == 'true'
shell: bash
run: |
docker buildx version || echo "Buildx nicht gefunden"
if ! docker ps >/dev/null 2>&1; then
echo "❌ Fehler: Docker ist nicht verfügbar!"
exit 1
fi
if ! docker buildx ls 2>/dev/null | grep -q builder; then
docker buildx create --name builder --use --driver docker-container
else
docker buildx use builder
fi
docker buildx inspect --bootstrap
- name: Login to Registry
if: steps.decision.outputs.should_build == 'true'
shell: bash
run: |
REGISTRY_USER="${{ secrets.REGISTRY_USER }}"
REGISTRY_PASSWORD="${{ secrets.REGISTRY_PASSWORD }}"
REGISTRY_URL="${{ env.REGISTRY }}"
DEPLOYMENT_HOST="94.16.110.151"
if [ -z "$REGISTRY_USER" ] || [ -z "$REGISTRY_PASSWORD" ]; then
echo "❌ Error: Registry credentials missing"
exit 1
fi
echo "🔐 Logging in to registry..."
HOST_IP=$(ip route | grep default | awk '{print $3}' 2>/dev/null | head -1 || echo "$DEPLOYMENT_HOST")
REGISTRY_URLS=(
"registry.michaelschiemer.de"
"$REGISTRY_URL"
"$DEPLOYMENT_HOST"
"$DEPLOYMENT_HOST:5000"
"${HOST_IP}:5000"
)
LOGIN_SUCCESS=false
for TEST_URL in "${REGISTRY_URLS[@]}"; do
echo "🔍 Testing registry: $TEST_URL"
if [[ "$TEST_URL" == *":5000" ]]; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://$TEST_URL/v2/" 2>&1 || echo "000")
if [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "200" ]; then
set +e
LOGIN_OUTPUT=$(echo "$REGISTRY_PASSWORD" | docker login "$TEST_URL" -u "$REGISTRY_USER" --password-stdin 2>&1)
LOGIN_EXIT_CODE=$?
set -e
if [ $LOGIN_EXIT_CODE -eq 0 ]; then
REGISTRY_URL="$TEST_URL"
LOGIN_SUCCESS=true
break
fi
fi
else
HTTPS_CODE=$(curl -k -s -o /dev/null -w "%{http_code}" "https://$TEST_URL/v2/" 2>&1 || echo "000")
if [ "$HTTPS_CODE" = "401" ] || [ "$HTTPS_CODE" = "200" ]; then
set +e
LOGIN_OUTPUT=$(echo "$REGISTRY_PASSWORD" | docker login "$TEST_URL" -u "$REGISTRY_USER" --password-stdin 2>&1)
LOGIN_EXIT_CODE=$?
set -e
if [ $LOGIN_EXIT_CODE -eq 0 ]; then
REGISTRY_URL="$TEST_URL"
LOGIN_SUCCESS=true
break
fi
fi
fi
done
if [ "$LOGIN_SUCCESS" = false ]; then
echo "❌ Registry login failed"
exit 1
fi
echo "✅ Registry login successful: $REGISTRY_URL"
echo "REGISTRY_URL=$REGISTRY_URL" >> $GITHUB_ENV
echo "CACHE_REGISTRY=${{ env.REGISTRY }}" >> $GITHUB_ENV
- name: Build and push runtime base image
if: steps.decision.outputs.should_build == 'true'
shell: bash
env:
REGISTRY_URL: ${{ env.REGISTRY_URL }}
CACHE_REGISTRY: ${{ env.CACHE_REGISTRY }}
RUNTIME_IMAGE_NAME: ${{ env.RUNTIME_IMAGE_NAME }}
run: |
cd /workspace/repo
TARGET_REGISTRY="$CACHE_REGISTRY"
if [ -z "$TARGET_REGISTRY" ]; then
TARGET_REGISTRY="$REGISTRY_URL"
fi
echo "TARGET_REGISTRY=$TARGET_REGISTRY" >> $GITHUB_ENV
# Debug: Check if RUNTIME_IMAGE_NAME is set
if [ -z "$RUNTIME_IMAGE_NAME" ]; then
echo "⚠️ RUNTIME_IMAGE_NAME is not set, using default: framework-runtime"
RUNTIME_IMAGE_NAME="framework-runtime"
fi
IMAGE_NAME="$RUNTIME_IMAGE_NAME"
echo "🏗️ Building runtime base image..."
echo " Registry: $TARGET_REGISTRY"
echo " Image: $IMAGE_NAME"
docker buildx build \
--platform linux/amd64 \
--file ./Dockerfile.production \
--target runtime-base \
--tag "$TARGET_REGISTRY/$IMAGE_NAME:latest" \
--tag "$TARGET_REGISTRY/$IMAGE_NAME:$(date +%Y%m%d)" \
--push \
.
echo "✅ Runtime base image pushed successfully!"
- name: Set runtime base outputs
id: set-result
shell: bash
env:
RUNTIME_IMAGE_NAME: ${{ env.RUNTIME_IMAGE_NAME }}
run: |
if [ "${{ steps.decision.outputs.should_build }}" = "true" ]; then
TARGET_REGISTRY="${{ env.TARGET_REGISTRY || env.REGISTRY }}"
if [ -z "$TARGET_REGISTRY" ]; then
TARGET_REGISTRY="${{ env.REGISTRY }}"
fi
echo "image_ref=$TARGET_REGISTRY/$RUNTIME_IMAGE_NAME:latest" >> "$GITHUB_OUTPUT"
echo "built=true" >> "$GITHUB_OUTPUT"
else
# When runtime build is skipped, output empty but build job will use default latest image
echo "image_ref=" >> "$GITHUB_OUTPUT"
echo "built=false" >> "$GITHUB_OUTPUT"
fi
# Job 1: Run Tests
test:
needs: changes
if: needs.changes.outputs.needs_build == 'true'
name: Run Tests & Quality Checks
runs-on: php-ci
steps:
- name: Download CI helpers
shell: bash
env:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
run: |
set -euo pipefail
REF="${{ github.sha }}"
if [ -z "$REF" ]; then
REF="${{ github.ref_name }}"
fi
if [ -z "$REF" ]; then
REF="${{ inputs.branch || 'staging' }}"
fi
URL="https://git.michaelschiemer.de/${{ github.repository }}/raw/${REF}/scripts/ci/clone_repo.sh"
mkdir -p /tmp/ci-tools
if [ -n "$CI_TOKEN" ]; then
curl -sfL -u "$CI_TOKEN:x-oauth-basic" "$URL" -o /tmp/ci-tools/clone_repo.sh
else
curl -sfL "$URL" -o /tmp/ci-tools/clone_repo.sh
fi
chmod +x /tmp/ci-tools/clone_repo.sh
- name: Checkout code
run: |
REF_NAME="${{ github.ref_name }}"
INPUT_BRANCH="${{ inputs.branch }}"
REPO="${{ github.repository }}"
export CI_REPOSITORY="$REPO"
export CI_TOKEN="${{ secrets.CI_TOKEN }}"
export CI_REF_NAME="$REF_NAME"
export CI_INPUT_BRANCH="$INPUT_BRANCH"
export CI_DEFAULT_BRANCH="main"
export CI_TARGET_DIR="/workspace/repo"
export CI_FETCH_DEPTH="1"
/tmp/ci-tools/clone_repo.sh
cd /workspace/repo
- name: Cache Composer dependencies
run: |
if [ -d "/tmp/composer-cache/vendor" ]; then
echo "📦 Restoring cached dependencies..."
cp -r /tmp/composer-cache/vendor /workspace/repo/vendor || true
fi
- name: Install dependencies
run: |
cd /workspace/repo
composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-req=php
- name: Save Composer cache
run: |
mkdir -p /tmp/composer-cache
cp -r /workspace/repo/vendor /tmp/composer-cache/vendor || true
- name: Tests temporarily skipped
run: |
echo "⚠️ Tests temporarily skipped due to PHP 8.5 compatibility issues"
# Job 2: Build & Push Docker Image
build:
name: Build Docker Image
needs: [changes, runtime-base]
runs-on: docker-build
env:
SHOULD_BUILD: ${{ needs.changes.outputs.needs_build }}
outputs:
image_tag: ${{ steps.image_info.outputs.IMAGE_TAG }}
commit_sha: ${{ steps.image_info.outputs.COMMIT_SHA }}
image_url: ${{ steps.image_info.outputs.IMAGE_URL }}
steps:
- name: Skip build when not required
if: ${{ env.SHOULD_BUILD != 'true' }}
run: |
echo " Container build not required using latest published image"
- name: Install git and setup environment
if: ${{ env.SHOULD_BUILD == 'true' }}
shell: sh
run: |
if ! command -v bash >/dev/null 2>&1 || ! command -v git >/dev/null 2>&1; then
apk add --no-cache git bash curl
fi
bash --version
git --version
- name: Download CI helpers
if: ${{ env.SHOULD_BUILD == 'true' }}
shell: bash
env:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
run: |
set -euo pipefail
REF="${{ github.sha }}"
if [ -z "$REF" ]; then
REF="${{ github.ref_name }}"
fi
if [ -z "$REF" ]; then
REF="${{ inputs.branch || 'staging' }}"
fi
URL="https://git.michaelschiemer.de/${{ github.repository }}/raw/${REF}/scripts/ci/clone_repo.sh"
mkdir -p /tmp/ci-tools
if [ -n "$CI_TOKEN" ]; then
curl -sfL -u "$CI_TOKEN:x-oauth-basic" "$URL" -o /tmp/ci-tools/clone_repo.sh
else
curl -sfL "$URL" -o /tmp/ci-tools/clone_repo.sh
fi
chmod +x /tmp/ci-tools/clone_repo.sh
- name: Checkout code
if: ${{ env.SHOULD_BUILD == 'true' }}
shell: bash
run: |
REF_NAME="${{ github.ref_name }}"
INPUT_BRANCH="${{ inputs.branch }}"
REPO="${{ github.repository }}"
export CI_REPOSITORY="$REPO"
export CI_TOKEN="${{ secrets.CI_TOKEN }}"
export CI_REF_NAME="$REF_NAME"
export CI_INPUT_BRANCH="$INPUT_BRANCH"
export CI_DEFAULT_BRANCH="main"
export CI_TARGET_DIR="/workspace/repo"
export CI_FETCH_DEPTH="1"
/tmp/ci-tools/clone_repo.sh
cd /workspace/repo
- name: Setup Docker Buildx
if: ${{ env.SHOULD_BUILD == 'true' }}
shell: bash
run: |
docker buildx version || echo "Buildx nicht gefunden"
echo "🔧 DOCKER_HOST: ${DOCKER_HOST:-nicht gesetzt}"
docker info | grep -E "Server Version|Registry" || true
if ! docker ps >/dev/null 2>&1; then
echo "❌ Fehler: Docker ist nicht verfügbar!"
exit 1
fi
if ! docker buildx ls 2>/dev/null | grep -q builder; then
echo "📦 Erstelle neuen Buildx Builder..."
docker buildx create --name builder --use --driver docker-container
else
echo "✅ Builder existiert bereits"
docker buildx use builder
fi
docker buildx inspect --bootstrap
docker buildx ls
- name: Generate image metadata
if: ${{ env.SHOULD_BUILD == 'true' }}
id: meta
run: |
cd /workspace/repo
COMMIT_SHA="${{ github.sha }}"
if [ -z "$COMMIT_SHA" ]; then
COMMIT_SHA=$(git rev-parse HEAD)
fi
SHORT_SHA=$(echo "$COMMIT_SHA" | cut -c1-7)
TAG="${SHORT_SHA}-$(date +%s)"
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "commit_sha=${COMMIT_SHA}" >> $GITHUB_OUTPUT
echo "Generated tag: ${TAG}"
- name: Login to Registry
if: ${{ env.SHOULD_BUILD == 'true' }}
id: login
shell: bash
run: |
REGISTRY_USER="${{ secrets.REGISTRY_USER }}"
REGISTRY_PASSWORD="${{ secrets.REGISTRY_PASSWORD }}"
REGISTRY_URL="${{ env.REGISTRY }}"
CANONICAL_REGISTRY="${{ env.REGISTRY }}"
DEPLOYMENT_HOST="94.16.110.151"
if [ -z "$REGISTRY_USER" ] || [ -z "$REGISTRY_PASSWORD" ]; then
echo "❌ Error: Registry credentials missing"
exit 1
fi
echo "🔐 Logging in to registry..."
HOST_IP=$(ip route | grep default | awk '{print $3}' 2>/dev/null | head -1 || echo "$DEPLOYMENT_HOST")
REGISTRY_URLS=(
"registry.michaelschiemer.de"
"$REGISTRY_URL"
"$DEPLOYMENT_HOST"
"$DEPLOYMENT_HOST:5000"
"${HOST_IP}:5000"
)
LOGIN_SUCCESS=false
for TEST_URL in "${REGISTRY_URLS[@]}"; do
echo "🔍 Testing registry: $TEST_URL"
if [[ "$TEST_URL" == *":5000" ]]; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://$TEST_URL/v2/" 2>&1 || echo "000")
if [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "200" ]; then
set +e
LOGIN_OUTPUT=$(echo "$REGISTRY_PASSWORD" | docker login "$TEST_URL" -u "$REGISTRY_USER" --password-stdin 2>&1)
LOGIN_EXIT_CODE=$?
set -e
if [ $LOGIN_EXIT_CODE -eq 0 ]; then
REGISTRY_URL="$TEST_URL"
LOGIN_SUCCESS=true
break
fi
fi
else
HTTPS_CODE=$(curl -k -s -o /dev/null -w "%{http_code}" "https://$TEST_URL/v2/" 2>&1 || echo "000")
if [ "$HTTPS_CODE" = "401" ] || [ "$HTTPS_CODE" = "200" ]; then
set +e
LOGIN_OUTPUT=$(echo "$REGISTRY_PASSWORD" | docker login "$TEST_URL" -u "$REGISTRY_USER" --password-stdin 2>&1)
LOGIN_EXIT_CODE=$?
set -e
if [ $LOGIN_EXIT_CODE -eq 0 ]; then
REGISTRY_URL="$TEST_URL"
LOGIN_SUCCESS=true
break
fi
fi
fi
done
if [ "$LOGIN_SUCCESS" = false ]; then
echo "❌ Registry login failed"
exit 1
fi
echo "✅ Registry login successful: $REGISTRY_URL"
echo "REGISTRY_URL=$REGISTRY_URL" >> $GITHUB_ENV
echo "CACHE_REGISTRY=$CANONICAL_REGISTRY" >> $GITHUB_ENV
- name: Determine runtime base image
if: ${{ env.SHOULD_BUILD == 'true' }}
id: runtime-image
shell: bash
run: |
DEFAULT_IMAGE="${{ env.REGISTRY }}/${{ env.RUNTIME_IMAGE_NAME }}:latest"
SELECTED_IMAGE="$DEFAULT_IMAGE"
# Try to get runtime-base image_ref (may not be available if runtime-base was skipped)
RUNTIME_BASE_REF="${{ needs.runtime-base.outputs.image_ref }}"
if [ -n "$RUNTIME_BASE_REF" ] && [ "$RUNTIME_BASE_REF" != "" ] && [ "$RUNTIME_BASE_REF" != "null" ]; then
SELECTED_IMAGE="$RUNTIME_BASE_REF"
echo " Verwende frisch gebautes Runtime-Image: $SELECTED_IMAGE"
else
if ! docker pull "$DEFAULT_IMAGE" >/dev/null 2>&1; then
ALT_REGISTRY="${{ env.REGISTRY_URL }}"
if [ -n "$ALT_REGISTRY" ] && [ "$ALT_REGISTRY" != "$DEFAULT_IMAGE" ]; then
ALT_IMAGE="$ALT_REGISTRY/${{ env.RUNTIME_IMAGE_NAME }}:latest"
if docker pull "$ALT_IMAGE" >/dev/null 2>&1; then
echo " Verwende alternatives Runtime-Image $ALT_IMAGE"
SELECTED_IMAGE="$ALT_IMAGE"
else
echo "⚠️ Runtime base image nicht verfügbar, verwende lokale Stage"
SELECTED_IMAGE="runtime-base"
fi
else
echo "⚠️ Runtime base image nicht verfügbar, verwende lokale Stage"
SELECTED_IMAGE="runtime-base"
fi
else
echo " Verwende bestehendes Runtime-Image $DEFAULT_IMAGE"
fi
fi
echo "runtime_image=$SELECTED_IMAGE" >> "$GITHUB_OUTPUT"
echo "🏗️ Runtime-Image gewählt: $SELECTED_IMAGE"
- name: Build and push Docker image
if: ${{ env.SHOULD_BUILD == 'true' }}
shell: bash
env:
REGISTRY_URL: ${{ env.REGISTRY_URL }}
CACHE_REGISTRY: ${{ env.CACHE_REGISTRY }}
RUNTIME_IMAGE: ${{ steps.runtime-image.outputs.runtime_image }}
run: |
cd /workspace/repo
REGISTRY_TO_USE="$REGISTRY_URL"
CACHE_TARGET="$CACHE_REGISTRY"
if [ -z "$CACHE_TARGET" ]; then
CACHE_TARGET="$REGISTRY_TO_USE"
fi
IMAGE_NAME="${{ env.IMAGE_NAME }}"
COMMIT_SHA="${{ github.sha }}"
if [ -z "$COMMIT_SHA" ]; then
COMMIT_SHA=$(git rev-parse HEAD)
fi
REF_NAME="${{ github.ref_name }}"
if [ -z "$REF_NAME" ]; then
REF_NAME=$(git rev-parse --abbrev-ref HEAD)
fi
SHORT_SHA=$(echo "$COMMIT_SHA" | cut -c1-7)
TAG="${SHORT_SHA}-$(date +%s)"
echo "🏗️ Building Docker image..."
echo " Registry: $REGISTRY_TO_USE"
if [ "$CACHE_TARGET" != "$REGISTRY_TO_USE" ]; then
echo " Cache registry: $CACHE_TARGET"
fi
echo " Runtime base: $RUNTIME_IMAGE"
echo " Image: $IMAGE_NAME"
echo " Tags: latest, $TAG, git-$SHORT_SHA"
# Build cache sources - branch-specific and general caches
CACHE_SOURCES=(
"type=registry,ref=${CACHE_TARGET}/${IMAGE_NAME}:buildcache"
"type=registry,ref=${REGISTRY_TO_USE}/${IMAGE_NAME}:latest"
"type=registry,ref=${REGISTRY_TO_USE}/${IMAGE_NAME}:${REF_NAME}-cache"
)
# If this is not the first build, try to use previous commit's tag as cache
if git rev-parse HEAD^ >/dev/null 2>&1; then
PREV_SHORT_SHA=$(git rev-parse --short=7 HEAD^)
CACHE_SOURCES+=("type=registry,ref=${REGISTRY_TO_USE}/${IMAGE_NAME}:git-${PREV_SHORT_SHA}")
fi
CACHE_FROM_ARGS=""
for CACHE_SRC in "${CACHE_SOURCES[@]}"; do
CACHE_FROM_ARGS="${CACHE_FROM_ARGS} --cache-from ${CACHE_SRC}"
done
# Build image with cache but don't push yet
echo "🏗️ Building image..."
docker buildx build \
--platform linux/amd64 \
--file ./Dockerfile.production \
--build-arg RUNTIME_IMAGE="$RUNTIME_IMAGE" \
--tag "${REGISTRY_TO_USE}/${IMAGE_NAME}:latest" \
--tag "${REGISTRY_TO_USE}/${IMAGE_NAME}:${TAG}" \
--tag "${REGISTRY_TO_USE}/${IMAGE_NAME}:git-${SHORT_SHA}" \
${CACHE_FROM_ARGS} \
--cache-to type=registry,ref="${CACHE_TARGET}/${IMAGE_NAME}:buildcache",mode=max \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg GIT_COMMIT=${COMMIT_SHA} \
--build-arg GIT_BRANCH=${REF_NAME} \
--load \
.
echo "✅ Image built successfully!"
# Push tags one by one to avoid overwhelming the registry
echo "📤 Pushing tags to registry..."
docker tag "${REGISTRY_TO_USE}/${IMAGE_NAME}:latest" "${REGISTRY_TO_USE}/${IMAGE_NAME}:${TAG}"
docker tag "${REGISTRY_TO_USE}/${IMAGE_NAME}:latest" "${REGISTRY_TO_USE}/${IMAGE_NAME}:git-${SHORT_SHA}"
echo " Pushing latest..."
docker push "${REGISTRY_TO_USE}/${IMAGE_NAME}:latest"
echo " Pushing ${TAG}..."
docker push "${REGISTRY_TO_USE}/${IMAGE_NAME}:${TAG}"
echo " Pushing git-${SHORT_SHA}..."
docker push "${REGISTRY_TO_USE}/${IMAGE_NAME}:git-${SHORT_SHA}"
echo "✅ All tags pushed successfully!"
- name: Set image info
id: image_info
shell: bash
env:
SHOULD_BUILD: ${{ env.SHOULD_BUILD }}
REF_NAME: ${{ github.ref_name }}
HEAD_REF: ${{ github.head_ref }}
FULL_REF: ${{ github.ref }}
run: |
REGISTRY_DEFAULT="$REGISTRY"
if [ -z "$REGISTRY_DEFAULT" ]; then
REGISTRY_DEFAULT="${{ env.REGISTRY }}"
fi
IMAGE_NAME="${{ env.IMAGE_NAME }}"
COMMIT_SHA="${{ github.sha }}"
if [ -z "$COMMIT_SHA" ]; then
COMMIT_SHA="unknown"
fi
if [ "${SHOULD_BUILD}" != "true" ]; then
IMAGE_TAG="latest"
IMAGE_URL="${REGISTRY_DEFAULT}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "IMAGE_TAG=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
echo "IMAGE_URL=$IMAGE_URL" >> "$GITHUB_OUTPUT"
echo "COMMIT_SHA=$COMMIT_SHA" >> "$GITHUB_OUTPUT"
echo " Build skipped using latest image tag ($IMAGE_TAG)."
EFFECTIVE_REF="$REF_NAME"
if [ -z "$EFFECTIVE_REF" ]; then
EFFECTIVE_REF="$HEAD_REF"
fi
if [ -z "$EFFECTIVE_REF" ] && [ -n "$FULL_REF" ]; then
EFFECTIVE_REF="${FULL_REF##*/}"
fi
if [ "$EFFECTIVE_REF" = "staging" ]; then
echo "🚀 Staging branch detected - will auto-deploy using the latest published image."
else
echo "💡 Image is ready for deployment!"
echo " Run the 'Deploy to Production' or 'Deploy to Staging' workflow to deploy this image."
fi
exit 0
fi
cd /workspace/repo
if [ -z "$COMMIT_SHA" ] || [ "$COMMIT_SHA" = "unknown" ]; then
COMMIT_SHA=$(git rev-parse HEAD)
fi
SHORT_SHA=$(echo "$COMMIT_SHA" | cut -c1-7)
TAG="${SHORT_SHA}-$(date +%s)"
REGISTRY_TO_USE="${REGISTRY_URL:-$REGISTRY_DEFAULT}"
# Use git-SHA format for deployment (stable, doesn't change with time)
IMAGE_TAG="git-${SHORT_SHA}"
IMAGE_URL="${REGISTRY_TO_USE}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "IMAGE_TAG=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
echo "IMAGE_URL=$IMAGE_URL" >> "$GITHUB_OUTPUT"
echo "COMMIT_SHA=$COMMIT_SHA" >> "$GITHUB_OUTPUT"
echo "📦 Image info:"
echo " Tag: $IMAGE_TAG (stable git-based tag)"
echo " Also pushed: $TAG (timestamped), latest"
echo " URL: $IMAGE_URL"
echo ""
REF_NAME="${{ github.ref_name }}"
if [ -z "$REF_NAME" ]; then
REF_NAME=$(git rev-parse --abbrev-ref HEAD)
fi
if [ "$REF_NAME" = "staging" ]; then
echo "🚀 Staging branch detected - will auto-deploy after build"
else
echo "💡 Image is ready for deployment!"
echo " Run the 'Deploy to Production' or 'Deploy to Staging' workflow to deploy this image."
fi
# Job 3: Auto-deploy to Staging (only for staging branch)
deploy-staging:
name: Auto-deploy to Staging
needs: [changes, build]
if: ${{ always() && (github.ref_name == 'staging' || github.head_ref == 'staging' || (github.ref_name == '' && contains(github.ref, 'staging'))) && needs.build.result != 'failure' && needs.build.result != 'cancelled' && needs.changes.result != 'failure' && needs.changes.result != 'cancelled' }}
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.michaelschiemer.de
env:
DEPLOYMENT_HOST: 94.16.110.151
steps:
- name: Determine branch name
id: branch
shell: bash
run: |
REF_NAME="${{ github.ref_name }}"
if [ -z "$REF_NAME" ]; then
REF_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///')
fi
if [ -z "$REF_NAME" ]; then
REF_NAME="staging"
fi
echo "BRANCH=$REF_NAME" >> $GITHUB_OUTPUT
echo "📋 Branch: $REF_NAME"
- name: Checkout deployment scripts
run: |
REF_NAME="${{ steps.branch.outputs.BRANCH }}"
REPO="${{ github.repository }}"
if [ -n "${{ secrets.CI_TOKEN }}" ]; then
git clone --depth 1 --branch "$REF_NAME" \
"https://${{ secrets.CI_TOKEN }}@git.michaelschiemer.de/${REPO}.git" \
/workspace/repo
else
git clone --depth 1 --branch "$REF_NAME" \
"https://git.michaelschiemer.de/${REPO}.git" \
/workspace/repo || \
git clone --depth 1 \
"https://git.michaelschiemer.de/${REPO}.git" \
/workspace/repo
fi
cd /workspace/repo
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/production
chmod 600 ~/.ssh/production
ssh-keyscan -H ${{ env.DEPLOYMENT_HOST }} >> ~/.ssh/known_hosts
- name: Install Ansible
run: |
apk add --no-cache python3 py3-pip
pip3 install --user ansible-core docker
- name: Create Ansible Vault password file
run: |
if [ -n "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" ]; then
echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
echo "✅ Vault password file created"
else
echo "⚠️ ANSIBLE_VAULT_PASSWORD secret not set, using empty password file"
touch /tmp/vault_pass
chmod 600 /tmp/vault_pass
fi
- name: Deploy Application Code to Staging
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-application-code.yml \
-e "deployment_environment=staging" \
-e "deployment_hosts=production" \
-e "git_branch=staging" \
--vault-password-file /tmp/vault_pass \
--private-key ~/.ssh/production
- name: Install Composer Dependencies
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/install-composer-dependencies.yml \
-e "deployment_environment=staging" \
--vault-password-file /tmp/vault_pass \
--private-key ~/.ssh/production
- name: Deploy Docker Image to Staging
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-image.yml \
-e "deployment_environment=staging" \
-e "deployment_hosts=production" \
-e "image_tag=latest" \
-e "docker_registry=${{ env.REGISTRY }}" \
-e "docker_registry_username=${{ secrets.REGISTRY_USER }}" \
-e "docker_registry_password=${{ secrets.REGISTRY_PASSWORD }}" \
--vault-password-file /tmp/vault_pass \
--private-key ~/.ssh/production
- name: Wait for deployment to stabilize
run: sleep 30
- name: Health check
id: health
run: |
echo "🔍 Performing health checks..."
# Basic health check
BASIC_HEALTH_OK=false
for i in {1..10}; do
if curl -f -k -s https://staging.michaelschiemer.de/health > /dev/null 2>&1; then
echo "✅ Basic health check passed"
BASIC_HEALTH_OK=true
break
fi
echo "⏳ Waiting for staging service... (attempt $i/10)"
sleep 10
done
if [ "$BASIC_HEALTH_OK" != "true" ]; then
echo "❌ Basic health check failed"
exit 1
fi
# Extended health check (if available)
echo "🔍 Checking extended health status..."
HEALTH_SUMMARY=$(curl -f -k -s https://staging.michaelschiemer.de/admin/health/api/summary 2>/dev/null || echo "")
if [ -n "$HEALTH_SUMMARY" ]; then
OVERALL_STATUS=$(echo "$HEALTH_SUMMARY" | grep -o '"overall_status":"[^"]*"' | cut -d'"' -f4 || echo "unknown")
echo "📊 Overall health status: $OVERALL_STATUS"
if [ "$OVERALL_STATUS" = "unhealthy" ]; then
echo "⚠️ Extended health check shows unhealthy status"
echo " Health summary: $HEALTH_SUMMARY"
else
echo "✅ Extended health check passed"
fi
else
echo " Extended health check endpoint not available (this is OK)"
fi
echo "✅ All health checks completed"
- name: Notify deployment success
if: success()
run: |
echo "🚀 Staging deployment successful!"
echo "URL: https://staging.michaelschiemer.de"
echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
# Job 4: Auto-deploy to Production (only for main branch)
deploy-production:
name: Auto-deploy to Production
needs: [changes, build]
if: always() && (github.ref_name == 'main' || github.head_ref == 'main' || (github.ref_name == '' && contains(github.ref, 'main'))) && needs.changes.outputs.needs_build == 'true'
runs-on: ubuntu-latest
environment:
name: production
url: https://michaelschiemer.de
env:
DEPLOYMENT_HOST: 94.16.110.151
steps:
- name: Determine branch name
id: branch
shell: bash
run: |
REF_NAME="${{ github.ref_name }}"
if [ -z "$REF_NAME" ]; then
REF_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///')
fi
if [ -z "$REF_NAME" ]; then
REF_NAME="main"
fi
echo "BRANCH=$REF_NAME" >> $GITHUB_OUTPUT
echo "📋 Branch: $REF_NAME"
- name: Checkout deployment scripts
run: |
REF_NAME="${{ steps.branch.outputs.BRANCH }}"
REPO="${{ github.repository }}"
if [ -n "${{ secrets.CI_TOKEN }}" ]; then
git clone --depth 1 --branch "$REF_NAME" \
"https://${{ secrets.CI_TOKEN }}@git.michaelschiemer.de/${REPO}.git" \
/workspace/repo
else
git clone --depth 1 --branch "$REF_NAME" \
"https://git.michaelschiemer.de/${REPO}.git" \
/workspace/repo || \
git clone --depth 1 \
"https://git.michaelschiemer.de/${REPO}.git" \
/workspace/repo
fi
cd /workspace/repo
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/production
chmod 600 ~/.ssh/production
ssh-keyscan -H ${{ env.DEPLOYMENT_HOST }} >> ~/.ssh/known_hosts
- name: Install Ansible
run: |
apk add --no-cache python3 py3-pip
pip3 install --user ansible-core docker
- name: Create Ansible Vault password file
run: |
if [ -n "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" ]; then
echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass
chmod 600 /tmp/vault_pass
echo "✅ Vault password file created"
else
echo "⚠️ ANSIBLE_VAULT_PASSWORD secret not set, using empty password file"
touch /tmp/vault_pass
chmod 600 /tmp/vault_pass
fi
- name: Determine image tag
id: image_tag
run: |
# Get image tag from build job output with fallback
IMAGE_TAG="${{ needs.build.outputs.image_tag }}"
# If IMAGE_TAG is empty, use latest
if [ -z "$IMAGE_TAG" ] || [ "$IMAGE_TAG" = "..." ] || [ "$IMAGE_TAG" = "null" ]; then
COMMIT_SHA="${{ github.sha }}"
if [ -z "$COMMIT_SHA" ]; then
COMMIT_SHA=$(cd /workspace/repo && git rev-parse HEAD 2>/dev/null || echo "")
fi
if [ -z "$COMMIT_SHA" ]; then
IMAGE_TAG="latest"
else
SHORT_SHA=$(echo "$COMMIT_SHA" | cut -c1-7)
IMAGE_TAG="git-${SHORT_SHA}"
fi
fi
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_OUTPUT
echo "📦 Image Tag: ${IMAGE_TAG}"
- name: Deploy Application Code to Production
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-application-code.yml \
-e "deployment_environment=production" \
-e "deployment_hosts=production" \
-e "git_branch=main" \
--vault-password-file /tmp/vault_pass \
--private-key ~/.ssh/production
- name: Install Composer Dependencies
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/install-composer-dependencies.yml \
-e "deployment_environment=production" \
--vault-password-file /tmp/vault_pass \
--private-key ~/.ssh/production
- name: Deploy Docker Image to Production
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-image.yml \
-e "deployment_environment=production" \
-e "deployment_hosts=production" \
-e "image_tag=${{ steps.image_tag.outputs.IMAGE_TAG }}" \
-e "docker_registry=${{ env.REGISTRY }}" \
-e "docker_registry_username=${{ secrets.REGISTRY_USER }}" \
-e "docker_registry_password=${{ secrets.REGISTRY_PASSWORD }}" \
--vault-password-file /tmp/vault_pass \
--private-key ~/.ssh/production
- name: Wait for deployment to stabilize
run: sleep 30
- name: Health check
id: health
run: |
echo "🔍 Performing health checks..."
# Basic health check
BASIC_HEALTH_OK=false
for i in {1..10}; do
if curl -f -k -s https://michaelschiemer.de/health > /dev/null 2>&1; then
echo "✅ Basic health check passed"
BASIC_HEALTH_OK=true
break
fi
echo "⏳ Waiting for production service... (attempt $i/10)"
sleep 10
done
if [ "$BASIC_HEALTH_OK" != "true" ]; then
echo "❌ Basic health check failed"
exit 1
fi
# Extended health check (if available)
echo "🔍 Checking extended health status..."
HEALTH_SUMMARY=$(curl -f -k -s https://michaelschiemer.de/admin/health/api/summary 2>/dev/null || echo "")
if [ -n "$HEALTH_SUMMARY" ]; then
OVERALL_STATUS=$(echo "$HEALTH_SUMMARY" | grep -o '"overall_status":"[^"]*"' | cut -d'"' -f4 || echo "unknown")
echo "📊 Overall health status: $OVERALL_STATUS"
if [ "$OVERALL_STATUS" = "unhealthy" ]; then
echo "⚠️ Extended health check shows unhealthy status"
echo " Health summary: $HEALTH_SUMMARY"
else
echo "✅ Extended health check passed"
fi
else
echo " Extended health check endpoint not available (this is OK)"
fi
echo "✅ All health checks completed"
- name: Notify deployment success
if: success()
run: |
echo "🚀 Production deployment successful!"
echo "URL: https://michaelschiemer.de"
echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.image_tag || 'latest' }}"