feat: update deployment configuration and encrypted env loader

- Update Ansible playbooks and roles for application deployment
- Add new Gitea/Traefik troubleshooting playbooks
- Update Docker Compose configurations (base, local, staging, production)
- Enhance EncryptedEnvLoader with improved error handling
- Add deployment scripts (autossh setup, migration, secret testing)
- Update CI/CD workflows and documentation
- Add Semaphore stack configuration
This commit is contained in:
2025-11-02 20:38:06 +01:00
parent 7b7f0b41d2
commit 24cbbccf4c
44 changed files with 5280 additions and 276 deletions

View File

@@ -123,12 +123,22 @@ jobs:
fi
if [ -z "$CHANGED_FILES" ] && [ "$FORCE" != "true" ]; then
# No diff information available; fall back to building to stay 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
# 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
@@ -160,6 +170,8 @@ jobs:
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
@@ -187,7 +199,7 @@ jobs:
runtime-base:
name: Build Runtime Base Image
needs: changes
if: always()
if: needs.changes.outputs.needs_runtime_build == 'true'
runs-on: docker-build
outputs:
image_ref: ${{ steps.set-result.outputs.image_ref }}
@@ -396,6 +408,7 @@ jobs:
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
@@ -727,6 +740,24 @@ jobs:
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
docker buildx build \
--platform linux/amd64 \
--file ./Dockerfile.production \
@@ -734,9 +765,9 @@ jobs:
--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 type=registry,ref="${CACHE_TARGET}/${IMAGE_NAME}:buildcache" \
--cache-from type=registry,ref="${REGISTRY_TO_USE}/${IMAGE_NAME}:latest" \
${CACHE_FROM_ARGS} \
--cache-to type=registry,ref="${CACHE_TARGET}/${IMAGE_NAME}:buildcache",mode=max \
--cache-to type=registry,ref="${REGISTRY_TO_USE}/${IMAGE_NAME}:${REF_NAME}-cache",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} \
@@ -787,7 +818,9 @@ jobs:
deploy-staging:
name: Auto-deploy to Staging
needs: [changes, build, runtime-base]
if: github.ref_name == 'staging' || github.head_ref == 'staging' || (github.ref_name == '' && contains(github.ref, 'staging'))
if: |
(github.ref_name == 'staging' || github.head_ref == 'staging' || (github.ref_name == '' && contains(github.ref, 'staging'))) &&
(needs.build.result == 'success' || needs.build.result == 'skipped')
runs-on: ubuntu-latest
environment:
name: staging
@@ -952,21 +985,29 @@ jobs:
fi
fi
# If docker-compose.yml doesn't exist, it will be created from repo
if [ ! -f docker-compose.yml ]; then
echo "⚠️ docker-compose.yml not found, copying from repo..."
cp /workspace/repo/deployment/stacks/staging/docker-compose.yml . || {
echo "❌ Failed to copy docker-compose.yml"
# Copy base and staging docker-compose files if they don't exist
if [ ! -f docker-compose.base.yml ]; then
echo "⚠️ docker-compose.base.yml not found, copying from repo..."
cp /workspace/repo/docker-compose.base.yml . || {
echo "❌ Failed to copy docker-compose.base.yml"
exit 1
}
fi
# Update docker-compose.yml with new image tag
echo "📝 Updating docker-compose.yml..."
sed -i "s|image:.*/${IMAGE_NAME}:.*|image: ${DEPLOY_IMAGE}|g" docker-compose.yml
if [ ! -f docker-compose.staging.yml ]; then
echo "⚠️ docker-compose.staging.yml not found, copying from repo..."
cp /workspace/repo/docker-compose.staging.yml . || {
echo "❌ Failed to copy docker-compose.staging.yml"
exit 1
}
fi
echo "✅ Updated docker-compose.yml:"
grep "image:" docker-compose.yml | head -5
# Update docker-compose.staging.yml with new image tag
echo "📝 Updating docker-compose.staging.yml with new image tag..."
sed -i "s|image:.*/${IMAGE_NAME}:.*|image: ${DEPLOY_IMAGE}|g" docker-compose.staging.yml
echo "✅ Updated docker-compose.staging.yml:"
grep "image:" docker-compose.staging.yml | head -5
# Ensure networks exist
echo "🔗 Ensuring Docker networks exist..."
@@ -974,7 +1015,8 @@ jobs:
docker network create staging-internal 2>/dev/null || true
echo "🔄 Starting/updating services..."
docker compose up -d --pull always --force-recreate || {
# Use --pull missing instead of --pull always since we already pulled the specific image
docker compose -f docker-compose.base.yml -f docker-compose.staging.yml up -d --pull missing --force-recreate || {
echo "❌ Failed to start services"
exit 1
}
@@ -982,27 +1024,32 @@ jobs:
echo "⏳ Waiting for services to start..."
sleep 15
# Force containers to pull latest code from Git repository
echo "🔄 Pulling latest code from Git repository in staging-app container..."
docker compose exec -T staging-app bash -c "cd /var/www/html && git -c safe.directory=/var/www/html fetch origin staging && git -c safe.directory=/var/www/html reset --hard origin/staging && git -c safe.directory=/var/www/html clean -fd" || echo "⚠️ Git pull failed, container will sync on next restart"
# Pull latest code from Git repository only if image was actually rebuilt
# Skip if build was skipped (no changes detected) - container already has latest code
if [ "${{ needs.build.result }}" = "success" ] && [ -n "${{ needs.build.outputs.image_url }}" ] && [ "${{ needs.build.outputs.image_url }}" != "null" ]; then
echo "🔄 Pulling latest code from Git repository in staging-app container (image was rebuilt)..."
docker compose -f docker-compose.base.yml -f docker-compose.staging.yml exec -T staging-app bash -c "cd /var/www/html && git -c safe.directory=/var/www/html fetch origin staging && git -c safe.directory=/var/www/html reset --hard origin/staging && git -c safe.directory=/var/www/html clean -fd" || echo "⚠️ Git pull failed, container will sync on next restart"
else
echo " Skipping Git pull - no new image built, container already has latest code"
fi
# Also trigger a restart to ensure entrypoint script runs
echo "🔄 Restarting staging-app to ensure all services are up-to-date..."
docker compose restart staging-app || echo "⚠️ Failed to restart staging-app"
docker compose -f docker-compose.base.yml -f docker-compose.staging.yml restart staging-app || echo "⚠️ Failed to restart staging-app"
# Fix nginx upstream configuration - critical fix for 502 errors
# sites-available/default uses 127.0.0.1:9000 but PHP-FPM runs in staging-app container
echo "🔧 Fixing nginx PHP-FPM upstream configuration (post-deploy fix)..."
sleep 5
docker compose exec -T staging-nginx sed -i '/upstream php-upstream {/,/}/s|server 127.0.0.1:9000;|server staging-app:9000;|g' /etc/nginx/sites-available/default || echo "⚠️ Upstream fix (127.0.0.1) failed"
docker compose exec -T staging-nginx sed -i '/upstream php-upstream {/,/}/s|server localhost:9000;|server staging-app:9000;|g' /etc/nginx/sites-available/default || echo "⚠️ Upstream fix (localhost) failed"
docker compose exec -T staging-nginx nginx -t && docker compose restart staging-nginx || echo "⚠️ Nginx config test or restart failed"
docker compose -f docker-compose.base.yml -f docker-compose.staging.yml exec -T staging-nginx sed -i '/upstream php-upstream {/,/}/s|server 127.0.0.1:9000;|server staging-app:9000;|g' /etc/nginx/sites-available/default || echo "⚠️ Upstream fix (127.0.0.1) failed"
docker compose -f docker-compose.base.yml -f docker-compose.staging.yml exec -T staging-nginx sed -i '/upstream php-upstream {/,/}/s|server localhost:9000;|server staging-app:9000;|g' /etc/nginx/sites-available/default || echo "⚠️ Upstream fix (localhost) failed"
docker compose -f docker-compose.base.yml -f docker-compose.staging.yml exec -T staging-nginx nginx -t && docker compose -f docker-compose.base.yml -f docker-compose.staging.yml restart staging-nginx || echo "⚠️ Nginx config test or restart failed"
echo "✅ Nginx configuration fixed and reloaded"
echo "⏳ Waiting for services to stabilize..."
sleep 10
echo "📊 Container status:"
docker compose ps
docker compose -f docker-compose.base.yml -f docker-compose.staging.yml ps
echo "✅ Staging deployment completed!"
EOF
@@ -1137,15 +1184,33 @@ jobs:
exit 1
}
echo "📝 Updating docker-compose.yml..."
sed -i "s|image:.*/${IMAGE_NAME}:.*|image: ${FULL_IMAGE}|g" docker-compose.yml
sed -i "s|image:.*/${IMAGE_NAME}@.*|image: ${FULL_IMAGE}|g" docker-compose.yml
# Copy base and production docker-compose files if they don't exist
if [ ! -f docker-compose.base.yml ]; then
echo "⚠️ docker-compose.base.yml not found, copying from repo..."
cp /workspace/repo/docker-compose.base.yml . || {
echo "❌ Failed to copy docker-compose.base.yml"
exit 1
}
fi
echo "✅ Updated docker-compose.yml:"
grep "image:" docker-compose.yml | head -5
if [ ! -f docker-compose.production.yml ]; then
echo "⚠️ docker-compose.production.yml not found, copying from repo..."
cp /workspace/repo/docker-compose.production.yml . || {
echo "❌ Failed to copy docker-compose.production.yml"
exit 1
}
fi
echo "📝 Updating docker-compose.production.yml with new image tag..."
sed -i "s|image:.*/${IMAGE_NAME}:.*|image: ${FULL_IMAGE}|g" docker-compose.production.yml
sed -i "s|image:.*/${IMAGE_NAME}@.*|image: ${FULL_IMAGE}|g" docker-compose.production.yml
echo "✅ Updated docker-compose.production.yml:"
grep "image:" docker-compose.production.yml | head -5
echo "🔄 Restarting services..."
docker compose up -d --pull always --force-recreate || {
# Use --pull missing instead of --pull always since we already pulled the specific image
docker compose -f docker-compose.base.yml -f docker-compose.production.yml up -d --pull missing --force-recreate || {
echo "❌ Failed to restart services"
exit 1
}
@@ -1154,7 +1219,7 @@ jobs:
sleep 10
echo "📊 Container status:"
docker compose ps
docker compose -f docker-compose.base.yml -f docker-compose.production.yml ps
echo "✅ Production deployment completed!"
EOF