Mastering GitHub Actions: Advanced Conditional Workflows with Dynamic Inputs and Job Orchestration

GitHub Actions workflows become incredibly powerful when you can control their execution based on user inputs and dynamic conditions. This comprehensive guide explores advanced patterns for implementing conditional workflows, from basic input-driven job selection to sophisticated orchestration strategies that adapt to different deployment scenarios.

Understanding workflow_dispatch and Input-Driven Automation

The workflow_dispatch trigger enables manual workflow execution with customizable inputs, providing the foundation for building flexible, user-controlled automation pipelines.

Basic Input Types and Configuration

GitHub Actions supports several input types that enable rich user interaction:

name: Advanced Conditional Deployment

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target deployment environment'
        type: choice
        options:
          - development
          - staging
          - production
        required: true
        default: 'development'
      
      service_name:
        description: 'Service to deploy'
        type: choice
        options:
          - user-service
          - payment-service
          - notification-service
          - all-services
        required: true
      
      deployment_strategy:
        description: 'Deployment strategy'
        type: choice
        options:
          - rolling-update
          - blue-green
          - canary
        required: true
        default: 'rolling-update'
      
      skip_tests:
        description: 'Skip test execution'
        type: boolean
        default: false
      
      custom_tag:
        description: 'Custom Docker tag (optional)'
        type: string
        required: false
      
      rollback_version:
        description: 'Version to rollback to (for rollback deployments)'
        type: string
        required: false

env:
  DEPLOYMENT_ENV: ${{ github.event.inputs.environment }}
  SERVICE_NAME: ${{ github.event.inputs.service_name }}
  STRATEGY: ${{ github.event.inputs.deployment_strategy }}

Advanced Input Validation and Processing

jobs:
  validate-inputs:
    runs-on: ubuntu-latest
    outputs:
      is-valid: ${{ steps.validation.outputs.valid }}
      docker-tag: ${{ steps.tag-generation.outputs.tag }}
      services-to-deploy: ${{ steps.service-parsing.outputs.services }}
    steps:
      - name: Validate Environment Input
        id: validation
        run: |
          if [[ "${{ github.event.inputs.environment }}" == "production" ]]; then
            if [[ "${{ github.actor }}" != "admin-user" ]] && [[ "${{ github.actor }}" != "devops-team" ]]; then
              echo "โŒ Production deployments require admin privileges"
              echo "valid=false" >> $GITHUB_OUTPUT
              exit 1
            fi
          fi
          
          if [[ "${{ github.event.inputs.deployment_strategy }}" == "blue-green" ]] && [[ "${{ github.event.inputs.environment }}" != "production" ]]; then
            echo "โš ๏ธ Blue-green deployment is only available for production"
            echo "valid=false" >> $GITHUB_OUTPUT
            exit 1
          fi
          
          echo "โœ… Input validation passed"
          echo "valid=true" >> $GITHUB_OUTPUT
      
      - name: Generate Docker Tag
        id: tag-generation
        run: |
          if [[ -n "${{ github.event.inputs.custom_tag }}" ]]; then
            TAG="${{ github.event.inputs.custom_tag }}"
          else
            TAG="${{ github.sha }}"
          fi
          echo "tag=${TAG}" >> $GITHUB_OUTPUT
          echo "๐Ÿท๏ธ Using Docker tag: ${TAG}"
      
      - name: Parse Services to Deploy
        id: service-parsing
        run: |
          if [[ "${{ github.event.inputs.service_name }}" == "all-services" ]]; then
            SERVICES="user-service,payment-service,notification-service"
          else
            SERVICES="${{ github.event.inputs.service_name }}"
          fi
          echo "services=${SERVICES}" >> $GITHUB_OUTPUT
          echo "๐ŸŽฏ Services to deploy: ${SERVICES}"

Conditional Job Execution Patterns

Pattern 1: Environment-Based Job Execution

  # Development Environment Jobs
  deploy-to-dev:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'development' && needs.validate-inputs.outputs.is-valid == 'true' }}
    needs: validate-inputs
    environment: development
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Deploy to Development
        run: |
          echo "๐Ÿš€ Deploying ${{ needs.validate-inputs.outputs.services-to-deploy }} to development"
          # Fast deployment without extensive testing
          ./scripts/deploy-dev.sh \
            --services="${{ needs.validate-inputs.outputs.services-to-deploy }}" \
            --tag="${{ needs.validate-inputs.outputs.docker-tag }}"

  # Staging Environment Jobs
  deploy-to-staging:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'staging' && needs.validate-inputs.outputs.is-valid == 'true' }}
    needs: [validate-inputs, run-tests]
    environment: staging
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Deploy to Staging
        run: |
          echo "๐ŸŽญ Deploying ${{ needs.validate-inputs.outputs.services-to-deploy }} to staging"
          ./scripts/deploy-staging.sh \
            --services="${{ needs.validate-inputs.outputs.services-to-deploy }}" \
            --tag="${{ needs.validate-inputs.outputs.docker-tag }}" \
            --strategy="${{ github.event.inputs.deployment_strategy }}"

  # Production Environment Jobs (with approval)
  deploy-to-production:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'production' && needs.validate-inputs.outputs.is-valid == 'true' }}
    needs: [validate-inputs, run-tests, security-scan]
    environment: 
      name: production
      url: https://app.production.com
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Production Deployment
        run: |
          echo "๐Ÿญ Deploying ${{ needs.validate-inputs.outputs.services-to-deploy }} to production"
          ./scripts/deploy-production.sh \
            --services="${{ needs.validate-inputs.outputs.services-to-deploy }}" \
            --tag="${{ needs.validate-inputs.outputs.docker-tag }}" \
            --strategy="${{ github.event.inputs.deployment_strategy }}"

Pattern 2: Service-Specific Job Execution

  # User Service Deployment
  deploy-user-service:
    runs-on: ubuntu-latest
    if: ${{ contains(needs.validate-inputs.outputs.services-to-deploy, 'user-service') }}
    needs: validate-inputs
    steps:
      - name: Deploy User Service
        run: |
          echo "๐Ÿ‘ค Deploying User Service"
          ./scripts/deploy-service.sh \
            --service="user-service" \
            --environment="${{ github.event.inputs.environment }}" \
            --tag="${{ needs.validate-inputs.outputs.docker-tag }}"
          
          # Service-specific health checks
          curl -f "https://user-service.${{ github.event.inputs.environment }}.com/health" || exit 1

  # Payment Service Deployment
  deploy-payment-service:
    runs-on: ubuntu-latest
    if: ${{ contains(needs.validate-inputs.outputs.services-to-deploy, 'payment-service') }}
    needs: [validate-inputs, security-scan]  # Payment service requires security scan
    steps:
      - name: Deploy Payment Service
        run: |
          echo "๐Ÿ’ณ Deploying Payment Service"
          ./scripts/deploy-service.sh \
            --service="payment-service" \
            --environment="${{ github.event.inputs.environment }}" \
            --tag="${{ needs.validate-inputs.outputs.docker-tag }}"
          
          # PCI compliance checks for payment service
          ./scripts/pci-compliance-check.sh

Pattern 3: Strategy-Based Deployment Jobs

  # Rolling Update Deployment
  rolling-deployment:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.deployment_strategy == 'rolling-update' }}
    needs: validate-inputs
    steps:
      - name: Rolling Update Deployment
        run: |
          echo "๐Ÿ”„ Executing rolling update deployment"
          kubectl set image deployment/app \
            app="${{ needs.validate-inputs.outputs.docker-tag }}" \
            --record
          kubectl rollout status deployment/app

  # Blue-Green Deployment
  blue-green-deployment:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.deployment_strategy == 'blue-green' }}
    needs: validate-inputs
    steps:
      - name: Blue-Green Deployment
        run: |
          echo "๐Ÿ”ต๐ŸŸข Executing blue-green deployment"
          # Deploy to green environment
          ./scripts/deploy-green.sh --tag="${{ needs.validate-inputs.outputs.docker-tag }}"
          
          # Health check green environment
          ./scripts/health-check-green.sh
          
          # Switch traffic to green
          ./scripts/switch-to-green.sh
          
          # Cleanup blue environment
          ./scripts/cleanup-blue.sh

  # Canary Deployment
  canary-deployment:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.deployment_strategy == 'canary' }}
    needs: validate-inputs
    steps:
      - name: Canary Deployment
        run: |
          echo "๐Ÿค Executing canary deployment"
          # Deploy canary version (10% traffic)
          ./scripts/deploy-canary.sh \
            --tag="${{ needs.validate-inputs.outputs.docker-tag }}" \
            --traffic-percentage="10"
          
          # Monitor canary metrics
          sleep 300  # 5 minutes monitoring
          ./scripts/check-canary-metrics.sh
          
          # Promote to full deployment if metrics are good
          ./scripts/promote-canary.sh

Advanced Dynamic Job Generation

Matrix Strategy with Input-Based Configuration

  dynamic-service-deployment:
    runs-on: ubuntu-latest
    if: ${{ needs.validate-inputs.outputs.is-valid == 'true' }}
    needs: validate-inputs
    strategy:
      matrix:
        service: ${{ fromJson(needs.parse-services.outputs.service-matrix) }}
        environment: [${{ github.event.inputs.environment }}]
      fail-fast: false
      max-parallel: 3
    steps:
      - name: Deploy Service
        run: |
          echo "๐Ÿš€ Deploying ${{ matrix.service }} to ${{ matrix.environment }}"
          ./scripts/deploy-service.sh \
            --service="${{ matrix.service }}" \
            --environment="${{ matrix.environment }}" \
            --tag="${{ needs.validate-inputs.outputs.docker-tag }}"

  parse-services:
    runs-on: ubuntu-latest
    needs: validate-inputs
    outputs:
      service-matrix: ${{ steps.create-matrix.outputs.matrix }}
    steps:
      - name: Create Service Matrix
        id: create-matrix
        run: |
          SERVICES="${{ needs.validate-inputs.outputs.services-to-deploy }}"
          # Convert comma-separated services to JSON array
          SERVICE_ARRAY=$(echo $SERVICES | jq -R 'split(",")')
          echo "matrix=${SERVICE_ARRAY}" >> $GITHUB_OUTPUT

Conditional Test Execution

  run-tests:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.skip_tests == 'false' && needs.validate-inputs.outputs.is-valid == 'true' }}
    needs: validate-inputs
    strategy:
      matrix:
        test-type: [unit, integration, e2e]
        exclude:
          # Skip e2e tests for development environment
          - test-type: e2e
            environment: ${{ github.event.inputs.environment == 'development' && 'development' || '' }}
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Run Tests
        run: |
          case "${{ matrix.test-type }}" in
            "unit")
              echo "๐Ÿงช Running unit tests"
              npm run test:unit
              ;;
            "integration")
              echo "๐Ÿ”— Running integration tests"
              npm run test:integration
              ;;
            "e2e")
              echo "๐ŸŽญ Running e2e tests"
              npm run test:e2e
              ;;
          esac
      
      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results-${{ matrix.test-type }}
          path: test-results/

Security and Approval Workflows

Environment Protection Rules

  security-scan:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'production' || github.event.inputs.environment == 'staging' }}
    needs: validate-inputs
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Container Security Scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ needs.validate-inputs.outputs.docker-tag }}'
          format: 'sarif'
          output: 'trivy-results.sarif'
      
      - name: Upload Trivy Scan Results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'
      
      - name: SAST Security Scan
        uses: securecodewarrior/github-action-add-sarif@v1
        with:
          sarif-file: security-scan-results.sarif

  compliance-check:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'production' }}
    needs: [validate-inputs, security-scan]
    steps:
      - name: SOC2 Compliance Check
        run: |
          echo "๐Ÿ“‹ Running SOC2 compliance checks"
          ./scripts/soc2-compliance.sh
      
      - name: PCI DSS Validation
        if: ${{ contains(needs.validate-inputs.outputs.services-to-deploy, 'payment-service') }}
        run: |
          echo "๐Ÿ’ณ Running PCI DSS validation"
          ./scripts/pci-dss-check.sh

Manual Approval Integration

  request-approval:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'production' }}
    needs: [validate-inputs, run-tests, security-scan]
    environment:
      name: production-approval
    steps:
      - name: Request Deployment Approval
        uses: trstringer/manual-approval@v1
        with:
          secret: ${{ github.TOKEN }}
          approvers: devops-team,security-team
          minimum-approvals: 2
          issue-title: "Production Deployment Approval"
          issue-body: |
            ## Production Deployment Request
            
            **Services:** ${{ needs.validate-inputs.outputs.services-to-deploy }}
            **Tag:** ${{ needs.validate-inputs.outputs.docker-tag }}
            **Strategy:** ${{ github.event.inputs.deployment_strategy }}
            **Requested by:** ${{ github.actor }}
            
            ### Pre-deployment Checklist
            - [ ] Security scans passed
            - [ ] Tests completed successfully
            - [ ] Rollback plan documented
            - [ ] Monitoring alerts configured
            
            Please review and approve this deployment.

Rollback and Recovery Workflows

Automated Rollback on Failure

  monitor-deployment:
    runs-on: ubuntu-latest
    if: ${{ always() && (needs.deploy-to-production.result == 'success' || needs.deploy-to-staging.result == 'success') }}
    needs: [deploy-to-production, deploy-to-staging]
    steps:
      - name: Monitor Deployment Health
        id: health-check
        run: |
          echo "๐Ÿ” Monitoring deployment health for 5 minutes"
          for i in {1..10}; do
            if curl -f "https://app.${{ github.event.inputs.environment }}.com/health"; then
              echo "โœ… Health check $i passed"
            else
              echo "โŒ Health check $i failed"
              echo "health-failed=true" >> $GITHUB_OUTPUT
              break
            fi
            sleep 30
          done
      
      - name: Trigger Rollback
        if: ${{ steps.health-check.outputs.health-failed == 'true' }}
        uses: actions/github-script@v7
        with:
          script: |
            const { data: workflow } = await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'rollback.yml',
              ref: 'main',
              inputs: {
                environment: '${{ github.event.inputs.environment }}',
                rollback_version: '${{ github.event.inputs.rollback_version }}',
                reason: 'Automated rollback due to health check failure'
              }
            });

  manual-rollback:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.rollback_version != '' }}
    needs: validate-inputs
    steps:
      - name: Execute Rollback
        run: |
          echo "โช Rolling back to version ${{ github.event.inputs.rollback_version }}"
          ./scripts/rollback.sh \
            --version="${{ github.event.inputs.rollback_version }}" \
            --environment="${{ github.event.inputs.environment }}" \
            --services="${{ needs.validate-inputs.outputs.services-to-deploy }}"

Advanced Notification and Reporting

Comprehensive Deployment Notifications

  notify-deployment-status:
    runs-on: ubuntu-latest
    if: always()
    needs: [validate-inputs, deploy-to-production, deploy-to-staging, deploy-to-dev]
    steps:
      - name: Determine Deployment Status
        id: status
        run: |
          if [[ "${{ needs.deploy-to-production.result }}" == "success" ]] || \
             [[ "${{ needs.deploy-to-staging.result }}" == "success" ]] || \
             [[ "${{ needs.deploy-to-dev.result }}" == "success" ]]; then
            echo "status=success" >> $GITHUB_OUTPUT
            echo "color=good" >> $GITHUB_OUTPUT
          else
            echo "status=failure" >> $GITHUB_OUTPUT
            echo "color=danger" >> $GITHUB_OUTPUT
          fi
      
      - name: Slack Notification
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ steps.status.outputs.status }}
          channel: '#deployments'
          text: |
            *Deployment ${{ steps.status.outputs.status }}*
            
            โ€ข **Environment:** ${{ github.event.inputs.environment }}
            โ€ข **Services:** ${{ needs.validate-inputs.outputs.services-to-deploy }}
            โ€ข **Strategy:** ${{ github.event.inputs.deployment_strategy }}
            โ€ข **Tag:** ${{ needs.validate-inputs.outputs.docker-tag }}
            โ€ข **Triggered by:** ${{ github.actor }}
            
            <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Workflow>
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
      
      - name: Teams Notification
        if: ${{ github.event.inputs.environment == 'production' }}
        uses: skitionek/notify-microsoft-teams@master
        with:
          webhook_url: ${{ secrets.TEAMS_WEBHOOK }}
          overwrite: |
            {
              "text": "Production Deployment ${{ steps.status.outputs.status }}",
              "sections": [
                {
                  "activityTitle": "Deployment Summary",
                  "facts": [
                    {"name": "Environment", "value": "${{ github.event.inputs.environment }}"},
                    {"name": "Services", "value": "${{ needs.validate-inputs.outputs.services-to-deploy }}"},
                    {"name": "Status", "value": "${{ steps.status.outputs.status }}"}
                  ]
                }
              ]
            }

Cost Optimization and Resource Management

Dynamic Resource Allocation

  optimize-resources:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'development' }}
    needs: validate-inputs
    steps:
      - name: Scale Down Development Resources
        run: |
          echo "๐Ÿ’ฐ Optimizing development environment resources"
          kubectl scale deployment --replicas=1 --all -n development
          kubectl patch hpa --patch '{"spec":{"maxReplicas":3}}' --all -n development

  weekend-shutdown:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.environment == 'development' && github.event.schedule == '0 18 * * 5' }}
    steps:
      - name: Weekend Shutdown
        run: |
          echo "๐ŸŒ™ Shutting down development environment for weekend"
          kubectl scale deployment --replicas=0 --all -n development

Testing and Validation Strategies

Comprehensive Testing Pipeline

  integration-tests:
    runs-on: ubuntu-latest
    if: ${{ github.event.inputs.skip_tests == 'false' }}
    needs: [validate-inputs]
    services:
      postgres:
        image: postgres:14
        env:
          POSTGRES_PASSWORD: testpass
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Run Integration Tests
        run: |
          export DATABASE_URL="postgresql://postgres:testpass@localhost:5432/testdb"
          export REDIS_URL="redis://localhost:6379"
          
          npm run test:integration
          
      - name: Load Test
        if: ${{ github.event.inputs.environment != 'development' }}
        run: |
          echo "๐Ÿš€ Running load tests"
          artillery quick \
            --count 50 \
            --num 100 \
            https://app.${{ github.event.inputs.environment }}.com

Monitoring and Observability Integration

Deployment Tracking and Metrics

  track-deployment:
    runs-on: ubuntu-latest
    needs: [deploy-to-production, deploy-to-staging, deploy-to-dev]
    if: always()
    steps:
      - name: Record Deployment Metrics
        run: |
          # Send deployment event to DataDog
          curl -X POST "https://api.datadoghq.com/api/v1/events" \
            -H "Content-Type: application/json" \
            -H "DD-API-KEY: ${{ secrets.DATADOG_API_KEY }}" \
            -d '{
              "title": "Deployment: ${{ github.event.inputs.service_name }}",
              "text": "Deployed ${{ needs.validate-inputs.outputs.services-to-deploy }} to ${{ github.event.inputs.environment }}",
              "tags": [
                "environment:${{ github.event.inputs.environment }}",
                "service:${{ github.event.inputs.service_name }}",
                "strategy:${{ github.event.inputs.deployment_strategy }}"
              ]
            }'
      
      - name: Update Grafana Annotations
        run: |
          curl -X POST "https://grafana.company.com/api/annotations" \
            -H "Authorization: Bearer ${{ secrets.GRAFANA_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{
              "text": "Deployment: ${{ github.event.inputs.service_name }}",
              "tags": ["deployment", "${{ github.event.inputs.environment }}"],
              "time": '${{ github.event.timestamp }}'
            }'

Best Practices and Optimization

Workflow Performance Optimization

  cache-dependencies:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      
      - name: Cache Node Modules
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
      
      - name: Cache Docker Layers
        uses: actions/cache@v3
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-

Security Best Practices

  security-hardening:
    runs-on: ubuntu-latest
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: strict
          allowed-endpoints: >
            api.github.com:443
            github.com:443
            objects.githubusercontent.com:443
            registry-1.docker.io:443
      
      - name: Verify Signatures
        run: |
          # Verify container image signatures
          cosign verify --key cosign.pub ${{ needs.validate-inputs.outputs.docker-tag }}

Conclusion

Advanced conditional workflows in GitHub Actions enable sophisticated automation strategies that adapt to different deployment scenarios, user requirements, and organizational policies. By leveraging input-driven job execution, dynamic matrix strategies, and comprehensive validation patterns, you can create robust CI/CD pipelines that provide flexibility without sacrificing security or reliability.

Key Takeaways

  1. Input Validation: Always validate user inputs before executing critical operations
  2. Conditional Logic: Use conditional expressions to control job execution based on inputs and environment state
  3. Security Integration: Implement security scans, compliance checks, and approval workflows for production deployments
  4. Monitoring: Include comprehensive monitoring, notification, and rollback capabilities
  5. Performance: Optimize workflows with caching, parallel execution, and resource management
  6. Documentation: Provide clear input descriptions and maintain workflow documentation

Implementation Strategy

Start Simple: Begin with basic conditional jobs and gradually add complexity Test Thoroughly: Validate workflows in development environments before production use Monitor Performance: Track workflow execution times and optimize bottlenecks Maintain Security: Regular security reviews and updates to approval processes Document Patterns: Create reusable templates and clear documentation for team adoption

By implementing these advanced patterns, you can create GitHub Actions workflows that provide the flexibility and control needed for modern DevOps practices while maintaining the reliability and security required for production environments.

Additional Resources