@@ -6,7 +6,7 @@ name: Deploy Docker Compose
66# - lib/common.sh: Logging, validation, and utility functions
77# - health-check.sh: Service health verification with stack-specific counting
88# - deploy-stacks.sh: Parallel deployment with comprehensive error handling
9- # - detect-removed-stacks .sh: Multi-method stack removal detection
9+ # - detect-stack-changes .sh: Multi-method detection for removed/existing/new stacks
1010# - cleanup-stack.sh: Individual stack cleanup helper
1111# - rollback-stacks.sh: Automated rollback with stack discovery
1212#
@@ -112,7 +112,7 @@ jobs:
112112 previous_sha : ${{ steps.backup.outputs.previous_sha }}
113113 deployment_needed : ${{ steps.backup.outputs.deployment_needed }}
114114 deleted_files : ${{ steps.changed-files.outputs.deleted_files }}
115- deploy_status : ${{ steps.deploy.outcome }}
115+ deploy_status : ${{ ( steps.deploy-existing .outcome == 'success' || steps.deploy-existing.outcome == 'skipped') && (steps.deploy-new.outcome == 'success' || steps.deploy-new.outcome == 'skipped') && 'success' || 'failure' }}
116116 health_status : ${{ steps.health.outcome }}
117117 cleanup_status : ${{ steps.cleanup.outcome }}
118118 rollback_status : ${{ steps.rollback.outcome }}
@@ -130,8 +130,12 @@ jobs:
130130 rollback_total_containers : ${{ steps.rollback-health.outputs.rollback_total_containers }}
131131 rollback_running_containers : ${{ steps.rollback-health.outputs.rollback_running_containers }}
132132 rollback_success_rate : ${{ steps.rollback-health.outputs.rollback_success_rate }}
133- removed_stacks : ${{ steps.cleanup-removed.outputs.removed_stacks }}
134- has_removed_stacks : ${{ steps.cleanup-removed.outputs.has_removed_stacks }}
133+ removed_stacks : ${{ steps.detect-changes.outputs.removed_stacks }}
134+ existing_stacks : ${{ steps.detect-changes.outputs.existing_stacks }}
135+ new_stacks : ${{ steps.detect-changes.outputs.new_stacks }}
136+ has_removed_stacks : ${{ steps.detect-changes.outputs.has_removed_stacks }}
137+ has_existing_stacks : ${{ steps.detect-changes.outputs.has_existing_stacks }}
138+ has_new_stacks : ${{ steps.detect-changes.outputs.has_new_stacks }}
135139 steps :
136140 - name : Validate and sanitize inputs
137141 run : |
@@ -473,28 +477,29 @@ jobs:
473477 #
474478 # Design: docs/plans/2025-12-06-enhanced-stack-removal-detection-design.md
475479
476- - name : Detect and clean up removed stacks
477- id : cleanup-removed
480+ - name : Detect stack changes ( removed/existing/new)
481+ id : detect-changes
478482 if : steps.backup.outputs.deployment_needed == 'true'
479483 continue-on-error : false
480484 run : |
481- ./.compose-workflow/scripts/deployment/detect-removed-stacks .sh \
485+ ./.compose-workflow/scripts/deployment/detect-stack-changes .sh \
482486 --current-sha "${{ steps.backup.outputs.previous_sha }}" \
483487 --target-ref "${{ inputs.target-ref }}" \
484- --deleted-files '${{ steps.changed-files.outputs.deleted_files }}' \
488+ --input-stacks '${{ inputs.stacks }}' \
489+ --removed-files '${{ steps.changed-files.outputs.deleted_files }}' \
485490 --ssh-user "${{ secrets.SSH_USER }}" \
486491 --ssh-host "${{ secrets.SSH_HOST }}"
487492
488493 - name : Notify removed stacks cleanup
489- if : steps.cleanup-removed .outputs.has_removed_stacks == 'true'
494+ if : steps.detect-changes .outputs.has_removed_stacks == 'true'
490495 run : |
491496 echo "📢 Sending cleanup notification to Discord..."
492497
493498 # Get webhook URL from 1Password
494499 WEBHOOK_URL=$(op read "${{ inputs.webhook-url }}")
495500
496501 # Build removed stacks list and create JSON payload using jq for proper escaping
497- REMOVED_STACKS='${{ steps.cleanup-removed .outputs.removed_stacks }}'
502+ REMOVED_STACKS='${{ steps.detect-changes .outputs.removed_stacks }}'
498503 STACK_LIST=$(echo "$REMOVED_STACKS" | jq -r '.[] | "- " + .')
499504 TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
500505
@@ -553,13 +558,34 @@ jobs:
553558 --startup-timeout "${{ inputs.service-startup-timeout }}" \
554559 --compose-args "${{ inputs.args || '' }}"
555560
556- - name : Deploy All Stacks
557- id : deploy
558- if : steps.backup.outputs.deployment_needed == 'true' && (inputs.has-dockge == false || steps.deploy-dockge.outcome == 'success')
561+ - name : Deploy Existing Stacks
562+ id : deploy-existing
563+ if : steps.backup.outputs.deployment_needed == 'true' && steps.detect-changes.outputs.has_existing_stacks == 'true' && (inputs.has-dockge == false || steps.deploy-dockge.outcome == 'success')
559564 continue-on-error : true
560565 run : |
566+ echo "🔄 Deploying existing stacks (updates)..."
561567 ./.compose-workflow/scripts/deployment/deploy-stacks.sh \
562- --stacks "${{ join(fromJSON(inputs.stacks), ' ') }}" \
568+ --stacks "${{ join(fromJSON(steps.detect-changes.outputs.existing_stacks), ' ') }}" \
569+ --target-ref "${{ inputs.target-ref }}" \
570+ --compose-args "${{ inputs.args || '' }}" \
571+ --ssh-user "${{ secrets.SSH_USER }}" \
572+ --ssh-host "${{ secrets.SSH_HOST }}" \
573+ --op-token "${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}" \
574+ --git-fetch-timeout "${{ inputs.git-fetch-timeout }}" \
575+ --git-checkout-timeout "${{ inputs.git-checkout-timeout }}" \
576+ --image-pull-timeout "${{ inputs.image-pull-timeout }}" \
577+ --service-startup-timeout "${{ inputs.service-startup-timeout }}" \
578+ --validation-env-timeout "${{ inputs.validation-env-timeout }}" \
579+ --validation-syntax-timeout "${{ inputs.validation-syntax-timeout }}"
580+
581+ - name : Deploy New Stacks
582+ id : deploy-new
583+ if : steps.backup.outputs.deployment_needed == 'true' && steps.detect-changes.outputs.has_new_stacks == 'true' && (inputs.has-dockge == false || steps.deploy-dockge.outcome == 'success') && (steps.detect-changes.outputs.has_existing_stacks == 'false' || steps.deploy-existing.outcome == 'success')
584+ continue-on-error : true
585+ run : |
586+ echo "✨ Deploying new stacks (fresh deployments)..."
587+ ./.compose-workflow/scripts/deployment/deploy-stacks.sh \
588+ --stacks "${{ join(fromJSON(steps.detect-changes.outputs.new_stacks), ' ') }}" \
563589 --target-ref "${{ inputs.target-ref }}" \
564590 --compose-args "${{ inputs.args || '' }}" \
565591 --ssh-user "${{ secrets.SSH_USER }}" \
@@ -574,7 +600,7 @@ jobs:
574600
575601 - name : Health Check All Services
576602 id : health
577- if : steps.backup.outputs.deployment_needed == 'true' && steps.deploy.outcome == 'success'
603+ if : steps.backup.outputs.deployment_needed == 'true' && ( steps.deploy-existing .outcome == 'success' || steps.deploy-existing.outcome == 'skipped') && (steps.deploy-new.outcome == 'success' || steps.deploy-new.outcome == 'skipped')
578604 continue-on-error : true
579605 run : |
580606 # Use auto-detected critical stacks if enabled, otherwise use manual input
@@ -596,7 +622,7 @@ jobs:
596622
597623 - name : Cleanup unused images
598624 id : cleanup
599- if : steps.backup.outputs.deployment_needed == 'true' && steps.deploy.outcome == 'success' && steps.health.outcome == 'success'
625+ if : steps.backup.outputs.deployment_needed == 'true' && ( steps.deploy-existing .outcome == 'success' || steps.deploy-existing.outcome == 'skipped') && (steps.deploy-new.outcome == 'success' || steps.deploy-new.outcome == 'skipped') && steps.health.outcome == 'success'
600626 continue-on-error : true
601627 run : |
602628 echo "::group::Cleaning up unused Docker images"
@@ -609,7 +635,7 @@ jobs:
609635
610636 - name : Rollback Dockge
611637 id : rollback-dockge
612- if : steps.backup.outputs.deployment_needed == 'true' && (steps.deploy.outcome == 'failure' || steps.health.outcome == 'failure') && inputs.has-dockge == true
638+ if : steps.backup.outputs.deployment_needed == 'true' && (steps.deploy-existing.outcome == 'failure' || steps.deploy-new .outcome == 'failure' || steps.health.outcome == 'failure') && inputs.has-dockge == true
613639 continue-on-error : true
614640 run : |
615641 echo "🔄 Rolling back Dockge to previous version..."
@@ -623,7 +649,7 @@ jobs:
623649
624650 - name : Rollback to Previous Version
625651 id : rollback
626- if : steps.backup.outputs.deployment_needed == 'true' && (steps.deploy.outcome == 'failure' || steps.health.outcome == 'failure') && (inputs.has-dockge == false || steps.rollback-dockge.outcome == 'success')
652+ if : steps.backup.outputs.deployment_needed == 'true' && (steps.deploy-existing.outcome == 'failure' || steps.deploy-new .outcome == 'failure' || steps.health.outcome == 'failure') && (inputs.has-dockge == false || steps.rollback-dockge.outcome == 'success')
627653 continue-on-error : true
628654 run : |
629655 echo "🔄 **INITIATING ROLLBACK**"
@@ -657,7 +683,7 @@ jobs:
657683 # visibility into what services are running, which is critical for incident response
658684 - name : Verify Rollback Health
659685 id : rollback-health
660- if : steps.backup.outputs.deployment_needed == 'true' && (steps.deploy.outcome == 'failure' || steps.health.outcome == 'failure') && steps.rollback.conclusion != 'skipped'
686+ if : steps.backup.outputs.deployment_needed == 'true' && (steps.deploy-existing.outcome == 'failure' || steps.deploy-new .outcome == 'failure' || steps.health.outcome == 'failure') && steps.rollback.conclusion != 'skipped'
661687 continue-on-error : true
662688 run : |
663689 echo "🔍 Verifying rollback health status"
@@ -763,15 +789,15 @@ jobs:
763789 echo "✅ Repository already at target commit"
764790 echo "📋 Target stacks: $STACK_LIST"
765791 echo "🔄 SHA: ${{ inputs.target-ref }}"
766- elif [ "${{ inputs.force-deploy }}" = "true" ] && [ "${{ steps.deploy.outcome }}" == "success" ] && [ "${{ steps.health.outcome }}" == "success" ]; then
792+ elif [ "${{ inputs.force-deploy }}" = "true" ] && ( [ "${{ steps.deploy-existing .outcome }}" == "success" ] || [ "${{ steps.deploy-existing.outcome }}" == "skipped" ]) && ([ "${{ steps.deploy-new.outcome }}" == "success" ] || [ "${{ steps.deploy-new.outcome }}" == "skipped" ]) && [ "${{ steps.health.outcome }}" == "success" ]; then
767793 echo "🔄 **FORCE DEPLOYMENT SUCCESSFUL**"
768794 echo "✅ All stacks force-deployed and healthy"
769795 echo "📋 Deployed stacks: $STACK_LIST"
770796 echo "🔄 SHA: ${{ inputs.target-ref }}"
771797 if [ "${{ steps.cleanup.outcome }}" == "success" ]; then
772798 echo "🧹 Cleanup completed successfully"
773799 fi
774- elif [ "${{ steps.deploy.outcome }}" == "success" ] && [ "${{ steps.health.outcome }}" == "success" ]; then
800+ elif ( [ "${{ steps.deploy-existing .outcome }}" == "success" ] || [ "${{ steps.deploy-existing.outcome }}" == "skipped" ]) && ([ "${{ steps.deploy-new.outcome }}" == "success" ] || [ "${{ steps.deploy-new.outcome }}" == "skipped" ]) && [ "${{ steps.health.outcome }}" == "success" ]; then
775801 echo "🎉 **DEPLOYMENT SUCCESSFUL**"
776802 echo "✅ All stacks deployed and healthy"
777803 echo "📋 Deployed stacks: $STACK_LIST"
@@ -781,7 +807,8 @@ jobs:
781807 fi
782808 else
783809 echo "💥 **DEPLOYMENT FAILED**"
784- echo "❌ Deploy status: ${{ steps.deploy.outcome }}"
810+ echo "❌ Deploy existing status: ${{ steps.deploy-existing.outcome }}"
811+ echo "❌ Deploy new status: ${{ steps.deploy-new.outcome }}"
785812 echo "❌ Health check status: ${{ steps.health.outcome }}"
786813 if [ "${{ steps.rollback.outcome }}" == "success" ]; then
787814 echo "🔄 Rollback completed successfully"
0 commit comments