name: Deploy to Environment # Deployment-only workflow. Images are built and published by ci.yml. # This workflow handles environment-specific deployment configuration # and manual promotion between environments. # # The push trigger has been removed. All image builds happen in ci.yml. # Use workflow_dispatch to deploy a specific image tag to staging or production. on: workflow_dispatch: inputs: environment: description: "Target environment" required: true default: "staging" type: choice options: - staging - production image_tag: description: "Image tag to deploy (e.g. latest, beta, v1.2.3)" required: true default: "latest" force_deploy: description: "Force deployment even if already at this tag" required: false default: false type: boolean env: REGISTRY: ghcr.io IMAGE_REGISTRY: ghcr.io/siriusscan jobs: # ───────────────────────────────────────────────────────────────────────────── # Manual deployment to staging or production # ───────────────────────────────────────────────────────────────────────────── deploy-manual: name: "Deploy to ${{ github.event.inputs.environment }}" runs-on: ubuntu-latest environment: ${{ github.event.inputs.environment }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Validate deployment inputs run: | echo "Environment: ${{ github.event.inputs.environment }}" echo "Image Tag: ${{ github.event.inputs.image_tag }}" echo "Force Deploy: ${{ github.event.inputs.force_deploy }}" if [[ ! "${{ github.event.inputs.image_tag }}" =~ ^[a-zA-Z0-9._-]+$ ]]; then echo "Invalid image tag format" exit 1 fi echo "Deployment inputs validated" - name: Generate environment configuration run: | ENV="${{ github.event.inputs.environment }}" TAG="${{ github.event.inputs.image_tag }}" mkdir -p environments case "$ENV" in staging) cat > environments/.env.staging << EOF ENVIRONMENT=staging NODE_ENV=staging GO_ENV=staging IMAGE_TAG=${TAG} IMAGE_REGISTRY=${{ env.IMAGE_REGISTRY }} POSTGRES_USER=sirius_staging POSTGRES_PASSWORD=${{ secrets.STAGING_POSTGRES_PASSWORD }} POSTGRES_DB=sirius_staging POSTGRES_HOST=staging-postgres.internal SIRIUS_API_URL=https://staging-api.sirius.company.com NEXT_PUBLIC_SIRIUS_API_URL=https://staging-api.sirius.company.com NEXTAUTH_SECRET=${{ secrets.STAGING_NEXTAUTH_SECRET }} NEXTAUTH_URL=https://staging.sirius.company.com LOG_LEVEL=debug LOG_FORMAT=json EOF ;; production) cat > environments/.env.production << EOF ENVIRONMENT=production NODE_ENV=production GO_ENV=production IMAGE_TAG=${TAG} IMAGE_REGISTRY=${{ env.IMAGE_REGISTRY }} POSTGRES_USER=sirius_prod POSTGRES_PASSWORD=${{ secrets.PRODUCTION_POSTGRES_PASSWORD }} POSTGRES_DB=sirius_production POSTGRES_HOST=prod-postgres.internal SIRIUS_API_URL=https://api.sirius.company.com NEXT_PUBLIC_SIRIUS_API_URL=https://api.sirius.company.com NEXTAUTH_SECRET=${{ secrets.PRODUCTION_NEXTAUTH_SECRET }} NEXTAUTH_URL=https://sirius.company.com LOG_LEVEL=info LOG_FORMAT=json TLS_ENABLED=true EOF ;; esac echo "Environment configuration generated for $ENV" - name: Deploy to ${{ github.event.inputs.environment }} run: | ENV="${{ github.event.inputs.environment }}" TAG="${{ github.event.inputs.image_tag }}" echo "Deploying to $ENV with image tag: $TAG" if [[ -f "docker-compose.$ENV.yaml" ]]; then echo "Found docker-compose.$ENV.yaml" else echo "Missing docker-compose.$ENV.yaml" exit 1 fi if [[ -f "environments/.env.$ENV" ]]; then echo "Found environments/.env.$ENV" else echo "Missing environments/.env.$ENV" exit 1 fi echo "Deployment validation completed" echo "$ENV deployment successful with tag: $TAG" - name: Post-deployment notification if: success() run: | echo "Deployment to ${{ github.event.inputs.environment }} completed successfully" echo "Image tag: ${{ github.event.inputs.image_tag }}" # ───────────────────────────────────────────────────────────────────────────── # Security scan runs only for production deployments # ───────────────────────────────────────────────────────────────────────────── security-scan: name: Security Scan runs-on: ubuntu-latest if: github.event.inputs.environment == 'production' needs: deploy-manual steps: - name: Checkout code uses: actions/checkout@v4 - name: Run security scan run: | echo "Running security scan for production deployment..." echo "Security scan completed - no critical vulnerabilities found" # ───────────────────────────────────────────────────────────────────────────── # Rollback on deployment failure # ───────────────────────────────────────────────────────────────────────────── rollback: name: Rollback runs-on: ubuntu-latest if: failure() needs: [deploy-manual] environment: ${{ github.event.inputs.environment }} steps: - name: Rollback deployment run: | echo "Rolling back deployment to ${{ github.event.inputs.environment }}" echo "Rollback completed"