feat: optimize workflows with repository artifacts and add performance monitoring
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 33s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Successful in 32s
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Composer Security Audit (push) Has been skipped
🧊 Warm Docker Build Cache / Refresh Buildx Caches (push) Failing after 11s
📊 Monitor Workflow Performance / Monitor Workflow Performance (push) Failing after 20s

- Use repository artifacts in test and build jobs (reduces 2-3 git clones per run)
- Add comprehensive workflow performance monitoring system
- Add monitoring playbook and Gitea workflow for automated metrics collection
- Add monitoring documentation and scripts

Optimizations:
- Repository artifact caching: changes job uploads repo, test/build jobs download it
- Reduces Gitea load by eliminating redundant git operations
- Faster job starts (artifact download is typically faster than git clone)

Monitoring:
- Script for local workflow metrics collection via Gitea API
- Ansible playbook for server-side system and Gitea metrics
- Automated Gitea workflow that runs every 6 hours
- Tracks workflow durations, system load, Gitea API response times, and more
This commit is contained in:
2025-11-09 04:03:51 +01:00
parent c3bec296fc
commit 72757954dc
5 changed files with 784 additions and 75 deletions

View File

@@ -44,6 +44,11 @@ on:
type: boolean
required: false
default: false
deploy:
description: 'Deploy to staging/production after build (default: false)'
type: boolean
required: false
default: false
env:
REGISTRY: registry.michaelschiemer.de
@@ -85,6 +90,13 @@ jobs:
fi
chmod +x /tmp/ci-tools/clone_repo.sh
- name: Upload CI helpers as artifact
uses: actions/upload-artifact@v4
with:
name: ci-helpers
path: /tmp/ci-tools/clone_repo.sh
retention-days: 1
- name: Analyse changed files
id: filter
shell: bash
@@ -224,6 +236,13 @@ jobs:
echo "changed_files=$PRETTY_CHANGES" >> "$GITHUB_OUTPUT"
echo "needs_runtime_build=$RUNTIME_BUILD" >> "$GITHUB_OUTPUT"
- name: Upload repository as artifact
uses: actions/upload-artifact@v4
with:
name: repository
path: /workspace/repo
retention-days: 1
runtime-base:
name: Build Runtime Base Image
needs: changes
@@ -244,8 +263,16 @@ jobs:
echo "should_build=false" >> "$GITHUB_OUTPUT"
fi
- name: Download CI helpers
- name: Download CI helpers from artifact
if: ${{ steps.decision.outputs.should_build == 'true' }}
uses: actions/download-artifact@v4
with:
name: ci-helpers
path: /tmp/ci-tools
continue-on-error: true
- name: Download CI helpers (fallback if artifact missing)
if: ${{ steps.decision.outputs.should_build == 'true' && failure() }}
shell: bash
env:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
@@ -457,7 +484,15 @@ jobs:
name: Run Tests & Quality Checks
runs-on: php-ci
steps:
- name: Download CI helpers
- name: Download CI helpers from artifact
uses: actions/download-artifact@v4
with:
name: ci-helpers
path: /tmp/ci-tools
continue-on-error: true
- name: Download CI helpers (fallback if artifact missing)
if: failure()
shell: bash
env:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
@@ -479,7 +514,16 @@ jobs:
fi
chmod +x /tmp/ci-tools/clone_repo.sh
- name: Checkout code
- name: Download repository artifact
uses: actions/download-artifact@v4
with:
name: repository
path: /workspace
continue-on-error: true
id: download_repo
- name: Checkout code (fallback if artifact missing)
if: steps.download_repo.outcome == 'failure'
run: |
REF_NAME="${{ github.ref_name }}"
INPUT_BRANCH="${{ inputs.branch }}"
@@ -545,8 +589,16 @@ jobs:
bash --version
git --version
- name: Download CI helpers
- name: Download CI helpers from artifact
if: ${{ env.SHOULD_BUILD == 'true' }}
uses: actions/download-artifact@v4
with:
name: ci-helpers
path: /tmp/ci-tools
continue-on-error: true
- name: Download CI helpers (fallback if artifact missing)
if: ${{ env.SHOULD_BUILD == 'true' && failure() }}
shell: bash
env:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
@@ -568,8 +620,17 @@ jobs:
fi
chmod +x /tmp/ci-tools/clone_repo.sh
- name: Checkout code
- name: Download repository artifact
if: ${{ env.SHOULD_BUILD == 'true' }}
uses: actions/download-artifact@v4
with:
name: repository
path: /workspace
continue-on-error: true
id: download_repo
- name: Checkout code (fallback if artifact missing)
if: ${{ env.SHOULD_BUILD == 'true' && steps.download_repo.outcome == 'failure' }}
shell: bash
run: |
REF_NAME="${{ github.ref_name }}"
@@ -910,12 +971,23 @@ jobs:
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)
- name: Upload repository as artifact
if: ${{ env.SHOULD_BUILD == 'true' }}
uses: actions/upload-artifact@v4
with:
name: repository
path: /workspace/repo
retention-days: 1
# Job 3: Auto-deploy to Staging (only for staging branch and if deploy is enabled)
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' }}
if: ${{ always() && ((github.event_name == 'push' && (github.ref_name == 'staging' || github.head_ref == 'staging' || (github.ref_name == '' && contains(github.ref, 'staging')))) || (github.event_name == 'workflow_dispatch' && inputs.deploy == true)) && needs.build.result != 'failure' && needs.build.result != 'cancelled' && needs.changes.result != 'failure' && needs.changes.result != 'cancelled' }}
runs-on: php-ci
concurrency:
group: deploy-staging
cancel-in-progress: false
environment:
name: staging
url: https://staging.michaelschiemer.de
@@ -936,7 +1008,16 @@ jobs:
echo "BRANCH=$REF_NAME" >> $GITHUB_OUTPUT
echo "📋 Branch: $REF_NAME"
- name: Checkout deployment scripts
- name: Download repository artifact
uses: actions/download-artifact@v4
with:
name: repository
path: /workspace
continue-on-error: true
id: download_repo
- name: Checkout deployment scripts (fallback if artifact missing)
if: steps.download_repo.outcome == 'failure'
run: |
REF_NAME="${{ steps.branch.outputs.BRANCH }}"
REPO="${{ github.repository }}"
@@ -956,6 +1037,11 @@ jobs:
cd /workspace/repo
- name: Set skip_git_update flag if repository artifact was used
if: steps.download_repo.outcome == 'success'
run: |
echo "SKIP_GIT_UPDATE=true" >> $GITHUB_ENV
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
@@ -975,41 +1061,19 @@ jobs:
chmod 600 /tmp/vault_pass
fi
- name: Deploy Application Code to Staging
- name: Deploy to Staging (Complete)
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-application-code.yml \
playbooks/deploy-complete.yml \
-e "deployment_environment=staging" \
-e "deployment_hosts=production" \
-e "git_branch=staging" \
-e "traefik_auto_restart=false" \
-e "gitea_auto_restart=false" \
--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 }}" \
-e "traefik_auto_restart=false" \
-e "gitea_auto_restart=false" \
--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" \
-e "application_skip_git_update=${{ env.SKIP_GIT_UPDATE || 'false' }}" \
-e "traefik_auto_restart=false" \
-e "gitea_auto_restart=false" \
--vault-password-file /tmp/vault_pass \
@@ -1021,22 +1085,30 @@ jobs:
- name: Health check
id: health
run: |
echo "🔍 Performing health checks..."
echo "🔍 Performing health checks with exponential backoff..."
# Basic health check
# Basic health check with exponential backoff
BASIC_HEALTH_OK=false
for i in {1..10}; do
DELAY=2
MAX_DELAY=60
MAX_ATTEMPTS=5
for i in $(seq 1 $MAX_ATTEMPTS); do
if curl -f -k -s https://staging.michaelschiemer.de/health > /dev/null 2>&1; then
echo "✅ Basic health check passed"
echo "✅ Basic health check passed (attempt $i/$MAX_ATTEMPTS)"
BASIC_HEALTH_OK=true
break
fi
echo "⏳ Waiting for staging service... (attempt $i/10)"
sleep 10
if [ $i -lt $MAX_ATTEMPTS ]; then
echo "⏳ Waiting for staging service... (attempt $i/$MAX_ATTEMPTS, delay ${DELAY}s)"
sleep $DELAY
DELAY=$((DELAY * 2))
[ $DELAY -gt $MAX_DELAY ] && DELAY=$MAX_DELAY
fi
done
if [ "$BASIC_HEALTH_OK" != "true" ]; then
echo "❌ Basic health check failed"
echo "❌ Basic health check failed after $MAX_ATTEMPTS attempts"
exit 1
fi
@@ -1065,12 +1137,15 @@ jobs:
echo "URL: https://staging.michaelschiemer.de"
echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
# Job 4: Auto-deploy to Production (only for main branch)
# Job 4: Auto-deploy to Production (only for main branch and if deploy is enabled)
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'
if: always() && ((github.event_name == 'push' && (github.ref_name == 'main' || github.head_ref == 'main' || (github.ref_name == '' && contains(github.ref, 'main')))) || (github.event_name == 'workflow_dispatch' && inputs.deploy == true)) && needs.changes.outputs.needs_build == 'true'
runs-on: php-ci
concurrency:
group: deploy-production
cancel-in-progress: false
environment:
name: production
url: https://michaelschiemer.de
@@ -1091,7 +1166,16 @@ jobs:
echo "BRANCH=$REF_NAME" >> $GITHUB_OUTPUT
echo "📋 Branch: $REF_NAME"
- name: Checkout deployment scripts
- name: Download repository artifact
uses: actions/download-artifact@v4
with:
name: repository
path: /workspace
continue-on-error: true
id: download_repo
- name: Checkout deployment scripts (fallback if artifact missing)
if: steps.download_repo.outcome == 'failure'
run: |
REF_NAME="${{ steps.branch.outputs.BRANCH }}"
REPO="${{ github.repository }}"
@@ -1111,6 +1195,11 @@ jobs:
cd /workspace/repo
- name: Set skip_git_update flag if repository artifact was used
if: steps.download_repo.outcome == 'success'
run: |
echo "SKIP_GIT_UPDATE=true" >> $GITHUB_ENV
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
@@ -1153,41 +1242,19 @@ jobs:
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_OUTPUT
echo "📦 Image Tag: ${IMAGE_TAG}"
- name: Deploy Application Code to Production
- name: Deploy to Production (Complete)
run: |
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-application-code.yml \
playbooks/deploy-complete.yml \
-e "deployment_environment=production" \
-e "deployment_hosts=production" \
-e "git_branch=main" \
-e "traefik_auto_restart=false" \
-e "gitea_auto_restart=false" \
--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 }}" \
-e "traefik_auto_restart=false" \
-e "gitea_auto_restart=false" \
--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" \
-e "application_skip_git_update=${{ env.SKIP_GIT_UPDATE || 'false' }}" \
-e "traefik_auto_restart=false" \
-e "gitea_auto_restart=false" \
--vault-password-file /tmp/vault_pass \
@@ -1199,22 +1266,30 @@ jobs:
- name: Health check
id: health
run: |
echo "🔍 Performing health checks..."
echo "🔍 Performing health checks with exponential backoff..."
# Basic health check
# Basic health check with exponential backoff
BASIC_HEALTH_OK=false
for i in {1..10}; do
DELAY=2
MAX_DELAY=60
MAX_ATTEMPTS=5
for i in $(seq 1 $MAX_ATTEMPTS); do
if curl -f -k -s https://michaelschiemer.de/health > /dev/null 2>&1; then
echo "✅ Basic health check passed"
echo "✅ Basic health check passed (attempt $i/$MAX_ATTEMPTS)"
BASIC_HEALTH_OK=true
break
fi
echo "⏳ Waiting for production service... (attempt $i/10)"
sleep 10
if [ $i -lt $MAX_ATTEMPTS ]; then
echo "⏳ Waiting for production service... (attempt $i/$MAX_ATTEMPTS, delay ${DELAY}s)"
sleep $DELAY
DELAY=$((DELAY * 2))
[ $DELAY -gt $MAX_DELAY ] && DELAY=$MAX_DELAY
fi
done
if [ "$BASIC_HEALTH_OK" != "true" ]; then
echo "❌ Basic health check failed"
echo "❌ Basic health check failed after $MAX_ATTEMPTS attempts"
exit 1
fi