Some checks failed
Deploy Application / deploy (push) Has been cancelled
287 lines
9.9 KiB
YAML
287 lines
9.9 KiB
YAML
name: 🚀 Manual Deployment
|
||
|
||
run-name: Manual Deploy - ${{ inputs.environment }} - ${{ inputs.image_tag || 'latest' }}
|
||
|
||
on:
|
||
workflow_dispatch:
|
||
inputs:
|
||
environment:
|
||
description: 'Deployment environment'
|
||
required: true
|
||
type: choice
|
||
options:
|
||
- staging
|
||
- production
|
||
image_tag:
|
||
description: 'Image tag to deploy (e.g. abc1234-1696234567, git-abc1234). Leave empty for latest'
|
||
required: false
|
||
type: string
|
||
default: ''
|
||
branch:
|
||
description: 'Branch to checkout (default: main for production, staging for staging)'
|
||
required: false
|
||
type: string
|
||
default: ''
|
||
|
||
env:
|
||
REGISTRY: registry.michaelschiemer.de
|
||
IMAGE_NAME: framework
|
||
DEPLOYMENT_HOST: 94.16.110.151
|
||
|
||
jobs:
|
||
determine-image:
|
||
name: Determine Deployment Image
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
image_url: ${{ steps.image.outputs.image_url }}
|
||
image_tag: ${{ steps.image.outputs.image_tag }}
|
||
registry_host: ${{ env.REGISTRY }}
|
||
image_name: ${{ env.IMAGE_NAME }}
|
||
steps:
|
||
- name: Determine image to deploy
|
||
id: image
|
||
shell: bash
|
||
run: |
|
||
REGISTRY="${{ env.REGISTRY }}"
|
||
IMAGE_NAME="${{ env.IMAGE_NAME }}"
|
||
INPUT_TAG="${{ inputs.image_tag }}"
|
||
|
||
if [ -z "$INPUT_TAG" ] || [ "$INPUT_TAG" = "" ]; then
|
||
IMAGE_TAG="latest"
|
||
else
|
||
IMAGE_TAG="$INPUT_TAG"
|
||
fi
|
||
|
||
IMAGE_URL="${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
|
||
|
||
echo "image_url=${IMAGE_URL}" >> "$GITHUB_OUTPUT"
|
||
echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"
|
||
|
||
echo "📦 Deployment Image:"
|
||
echo " URL: ${IMAGE_URL}"
|
||
echo " Tag: ${IMAGE_TAG}"
|
||
echo ""
|
||
echo "ℹ️ Image will be validated during deployment"
|
||
|
||
deploy-staging:
|
||
name: Deploy to Staging
|
||
needs: determine-image
|
||
if: inputs.environment == 'staging'
|
||
runs-on: ubuntu-latest
|
||
environment:
|
||
name: staging
|
||
url: https://staging.michaelschiemer.de
|
||
steps:
|
||
- name: Determine branch name
|
||
id: branch
|
||
shell: bash
|
||
run: |
|
||
INPUT_BRANCH="${{ inputs.branch }}"
|
||
if [ -z "$INPUT_BRANCH" ] || [ "$INPUT_BRANCH" = "" ]; then
|
||
REF_NAME="staging"
|
||
else
|
||
REF_NAME="$INPUT_BRANCH"
|
||
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: 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 to Staging (Complete)
|
||
run: |
|
||
cd /workspace/repo/deployment/ansible
|
||
ansible-playbook -i inventory/production.yml \
|
||
playbooks/deploy-complete.yml \
|
||
-e "deployment_environment=staging" \
|
||
-e "deployment_hosts=production" \
|
||
-e "git_branch=${{ steps.branch.outputs.BRANCH }}" \
|
||
-e "image_tag=${{ needs.determine-image.outputs.image_tag }}" \
|
||
-e "docker_registry=${{ needs.determine-image.outputs.registry_host }}" \
|
||
-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 with exponential backoff..."
|
||
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 "✅ Health check passed (attempt $i/$MAX_ATTEMPTS)"
|
||
exit 0
|
||
fi
|
||
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
|
||
echo "❌ Health check failed after $MAX_ATTEMPTS attempts"
|
||
exit 1
|
||
|
||
- name: Notify deployment success
|
||
if: success()
|
||
run: |
|
||
echo "🚀 Staging deployment successful!"
|
||
echo "URL: https://staging.michaelschiemer.de"
|
||
echo "Image: ${{ needs.determine-image.outputs.image_url }}"
|
||
|
||
deploy-production:
|
||
name: Deploy to Production
|
||
needs: determine-image
|
||
if: inputs.environment == 'production'
|
||
runs-on: ubuntu-latest
|
||
concurrency:
|
||
group: deploy-production
|
||
cancel-in-progress: false
|
||
environment:
|
||
name: production
|
||
url: https://michaelschiemer.de
|
||
steps:
|
||
- name: Determine branch name
|
||
id: branch
|
||
shell: bash
|
||
run: |
|
||
INPUT_BRANCH="${{ inputs.branch }}"
|
||
if [ -z "$INPUT_BRANCH" ] || [ "$INPUT_BRANCH" = "" ]; then
|
||
REF_NAME="main"
|
||
else
|
||
REF_NAME="$INPUT_BRANCH"
|
||
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: 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 to Production (Complete)
|
||
run: |
|
||
cd /workspace/repo/deployment/ansible
|
||
ansible-playbook -i inventory/production.yml \
|
||
playbooks/deploy-complete.yml \
|
||
-e "deployment_environment=production" \
|
||
-e "deployment_hosts=production" \
|
||
-e "git_branch=${{ steps.branch.outputs.BRANCH }}" \
|
||
-e "image_tag=${{ needs.determine-image.outputs.image_tag }}" \
|
||
-e "docker_registry=${{ needs.determine-image.outputs.registry_host }}" \
|
||
-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 with exponential backoff..."
|
||
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 "✅ Health check passed (attempt $i/$MAX_ATTEMPTS)"
|
||
exit 0
|
||
fi
|
||
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
|
||
echo "❌ Health check failed after $MAX_ATTEMPTS attempts"
|
||
exit 1
|
||
|
||
- name: Notify deployment success
|
||
if: success()
|
||
run: |
|
||
echo "🚀 Production deployment successful!"
|
||
echo "URL: https://michaelschiemer.de"
|
||
echo "Image: ${{ needs.determine-image.outputs.image_url }}"
|
||
|