fix(ci): simplify environment field - always use environment name #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Reusable Deployment Workflow | ||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| environment: | ||
| description: 'Target environment (dev, staging, prod)' | ||
| required: true | ||
| type: string | ||
| stack-name: | ||
| description: 'Pulumi stack name' | ||
| required: true | ||
| type: string | ||
| coverage-threshold: | ||
| description: 'Minimum test coverage percentage' | ||
| required: false | ||
| type: number | ||
| default: 70 | ||
| test-scope: | ||
| description: 'Test scope (unit or all)' | ||
| required: false | ||
| type: string | ||
| default: 'unit' | ||
| skip-tests: | ||
| description: 'Skip pre-deployment tests' | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
| deploy-infrastructure: | ||
| description: 'Deploy infrastructure stack first' | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
| create-release: | ||
| description: 'Create GitHub release' | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
| require-approval: | ||
| description: 'Require manual approval (GitHub environment)' | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
| secrets: | ||
| AWS_ACCESS_KEY_ID: | ||
| required: true | ||
| AWS_SECRET_ACCESS_KEY: | ||
| required: true | ||
| PULUMI_ACCESS_TOKEN: | ||
| required: true | ||
| GITHUB_TOKEN: | ||
| required: false | ||
| jobs: | ||
| pre-deployment-checks: | ||
| name: Pre-Deployment Validation | ||
| runs-on: ubuntu-latest | ||
| if: ${{ !inputs.skip-tests }} | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
| cache: 'pip' | ||
| - name: Set up uv | ||
| uses: astral-sh/setup-uv@v5 | ||
| with: | ||
| version: "latest" | ||
| enable-cache: true | ||
| - name: Create virtual environment | ||
| run: uv venv .venv | ||
| - name: Install dependencies | ||
| run: | | ||
| source .venv/bin/activate | ||
| uv pip install -r coaching/requirements.txt | ||
| uv pip install -r coaching/requirements-dev.txt | ||
| shell: bash | ||
| - name: Run Ruff Linting | ||
| run: | | ||
| source .venv/bin/activate | ||
| python -m ruff check . --exclude=".venv,venv,__pycache__,.pytest_cache" | ||
| shell: bash | ||
| - name: Run MyPy Type Checking | ||
| run: | | ||
| source .venv/bin/activate | ||
| python -m mypy coaching/src/ shared/ --config-file=pyproject.toml | ||
| shell: bash | ||
| - name: Run Tests | ||
| run: | | ||
| source .venv/bin/activate | ||
| if [ "${{ inputs.test-scope }}" == "all" ]; then | ||
| python -m pytest coaching/tests/ -v --cov=coaching/src --cov-fail-under=${{ inputs.coverage-threshold }} | ||
| else | ||
| python -m pytest coaching/tests/unit/ -v --cov=coaching/src --cov-fail-under=${{ inputs.coverage-threshold }} | ||
| fi | ||
| shell: bash | ||
| env: | ||
| PYTHONPATH: coaching:shared:. | ||
| deploy-infrastructure: | ||
| name: Deploy Infrastructure | ||
| runs-on: ubuntu-latest | ||
| needs: [pre-deployment-checks] | ||
| if: | | ||
| always() && | ||
| inputs.deploy-infrastructure && | ||
| (needs.pre-deployment-checks.result == 'success' || inputs.skip-tests) | ||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Install Pulumi Python dependencies | ||
| working-directory: infrastructure/pulumi | ||
| run: pip install -r requirements.txt | ||
| - name: Configure AWS credentials | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| aws-region: us-east-1 | ||
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| - name: Deploy Infrastructure | ||
| uses: pulumi/actions@v5 | ||
| with: | ||
| command: up | ||
| stack-name: ${{ inputs.stack-name }} | ||
| work-dir: infrastructure/pulumi | ||
| env: | ||
| PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} | ||
| AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| AWS_REGION: us-east-1 | ||
| deploy-coaching: | ||
| name: Deploy to ${{ inputs.environment }} | ||
| runs-on: ubuntu-latest | ||
| needs: [pre-deployment-checks, deploy-infrastructure] | ||
| if: | | ||
| always() && | ||
| (needs.pre-deployment-checks.result == 'success' || inputs.skip-tests) && | ||
| (needs.deploy-infrastructure.result == 'success' || needs.deploy-infrastructure.result == 'skipped') | ||
| permissions: | ||
| id-token: write | ||
| contents: write | ||
| environment: ${{ inputs.environment }} | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Install Pulumi Python dependencies | ||
| working-directory: coaching/pulumi | ||
| run: pip install -r requirements.txt | ||
| - name: Configure AWS credentials | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| aws-region: us-east-1 | ||
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| - name: Deploy Coaching Service | ||
| uses: pulumi/actions@v5 | ||
| with: | ||
| command: up | ||
| stack-name: ${{ inputs.stack-name }} | ||
| work-dir: coaching/pulumi | ||
| env: | ||
| PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} | ||
| AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| AWS_REGION: us-east-1 | ||
| - name: Get API Gateway URL | ||
| id: api-url | ||
| working-directory: coaching/pulumi | ||
| run: | | ||
| URL=$(pulumi stack output customDomainUrl --stack ${{ inputs.stack-name }}) | ||
| echo "url=$URL" >> $GITHUB_OUTPUT | ||
| env: | ||
| PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} | ||
| - name: Create GitHub Release | ||
| if: ${{ inputs.create-release }} | ||
| uses: actions/create-release@v1 | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| with: | ||
| tag_name: v${{ github.run_number }} | ||
| release_name: Release v${{ github.run_number }} | ||
| body: | | ||
| Production deployment of PurposePath Coaching API | ||
| **Deployment Details:** | ||
| - Environment: ${{ inputs.environment }} | ||
| - Stack: ${{ inputs.stack-name }} | ||
| - API URL: ${{ steps.api-url.outputs.url }} | ||
| - Deployed at: ${{ github.event.head_commit.timestamp }} | ||
| - Commit: ${{ github.sha }} | ||
| draft: false | ||
| prerelease: false | ||
| continue-on-error: true | ||
| - name: Deployment Summary | ||
| run: | | ||
| echo "## 🚀 Deployment Summary - ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "✅ Deployment successful" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Environment:** ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Stack:** ${{ inputs.stack-name }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "**API URL:** ${{ steps.api-url.outputs.url }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Region:** us-east-1" >> $GITHUB_STEP_SUMMARY | ||
| if [ "${{ inputs.create-release }}" == "true" ]; then | ||
| echo "**Release:** v${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY | ||
| fi | ||
| echo "**Deployed at:** $(date -u)" >> $GITHUB_STEP_SUMMARY | ||
| smoke-tests: | ||
| name: Post-Deployment Smoke Tests | ||
| runs-on: ubuntu-latest | ||
| needs: [deploy-coaching] | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Install Pulumi CLI | ||
| uses: pulumi/actions@v5 | ||
| with: | ||
| pulumi-version: 'latest' | ||
| - name: Configure AWS credentials | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| aws-region: us-east-1 | ||
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| - name: Get API Gateway URL | ||
| id: api-url | ||
| working-directory: coaching/pulumi | ||
| run: | | ||
| URL=$(pulumi stack output customDomainUrl --stack ${{ inputs.stack-name }}) | ||
| echo "url=$URL" >> $GITHUB_OUTPUT | ||
| env: | ||
| PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} | ||
| - name: Health Check | ||
| run: | | ||
| echo "Testing API health endpoint..." | ||
| HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" ${{ steps.api-url.outputs.url }}/health || echo "000") | ||
| if [ "$HTTP_CODE" == "200" ] || [ "$HTTP_CODE" == "404" ]; then | ||
| echo "✅ API is responding (HTTP $HTTP_CODE)" | ||
| else | ||
| echo "⚠️ API returned HTTP $HTTP_CODE" | ||
| if [ "${{ inputs.environment }}" == "prod" ]; then | ||
| echo "Production deployment verification failed!" | ||
| exit 1 | ||
| fi | ||
| fi | ||
| - name: Smoke Test Summary | ||
| run: | | ||
| echo "## Smoke Tests - ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "✅ Health check passed" >> $GITHUB_STEP_SUMMARY | ||
| echo "✅ API is responsive" >> $GITHUB_STEP_SUMMARY | ||
| if [ "${{ inputs.environment }}" == "prod" ]; then | ||
| echo "✅ Production deployment verified" >> $GITHUB_STEP_SUMMARY | ||
| fi | ||
| notify-deployment: | ||
| name: Notify Team | ||
| runs-on: ubuntu-latest | ||
| needs: [deploy-coaching, smoke-tests] | ||
| if: always() && inputs.create-release | ||
| steps: | ||
| - name: Deployment Status | ||
| run: | | ||
| if [ "${{ needs.deploy-coaching.result }}" == "success" ] && [ "${{ needs.smoke-tests.result }}" == "success" ]; then | ||
| echo "✅ ${{ inputs.environment }} deployment completed successfully" | ||
| else | ||
| echo "❌ ${{ inputs.environment }} deployment encountered issues" | ||
| exit 1 | ||
| fi | ||