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 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=${{ steps.branch.outputs.BRANCH }}" \ --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=${{ 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: | for i in {1..10}; do if curl -f -k https://staging.michaelschiemer.de/health; then echo "✅ Health check passed" exit 0 fi echo "âŗ Waiting for staging service... (attempt $i/10)" sleep 10 done echo "❌ Health check failed" 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 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 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=${{ steps.branch.outputs.BRANCH }}" \ --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=${{ 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: | for i in {1..10}; do if curl -f -k https://michaelschiemer.de/health; then echo "✅ Health check passed" exit 0 fi echo "âŗ Waiting for production service... (attempt $i/10)" sleep 10 done echo "❌ Health check failed" 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 }}"