Skip to content

fix(ci): make production lambda check robust #2

fix(ci): make production lambda check robust

fix(ci): make production lambda check robust #2

name: Deploy Preprod Hotfix
on:
push:
branches:
- "hotfix/**"
workflow_dispatch:
inputs:
branch:
description: "Branch to deploy (defaults to current ref)"
required: false
type: string
skip_tests:
description: "Skip tests before deployment"
required: false
default: "false"
type: choice
options:
- "true"
- "false"
concurrency:
group: deploy-preprod-hotfix-${{ github.ref_name }}
cancel-in-progress: true
jobs:
pre-deployment-checks:
name: Pre-Deployment Validation
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.skip_tests != 'true' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch || github.ref }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- 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 Unit Tests
run: |
source .venv/bin/activate
python -m pytest coaching/tests/unit/ -v --cov=coaching/src --cov-fail-under=70
shell: bash
env:
PYTHONPATH: coaching:shared:.
deploy-coaching:
name: Deploy to Preprod
runs-on: ubuntu-latest
needs: [pre-deployment-checks]
if: ${{ always() && (needs.pre-deployment-checks.result == 'success' || (github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')) }}
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch || github.ref }}
- 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: preprod
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
smoke-tests:
name: Post-Deployment Smoke Tests
runs-on: ubuntu-latest
needs: [deploy-coaching]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch || github.ref }}
- 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 preprod)
echo "url=$URL" >> $GITHUB_OUTPUT
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
- name: Health Check
run: |
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 "❌ Health check failed with HTTP $HTTP_CODE"
exit 1
fi
echo "✅ Health check passed ($HTTP_CODE)"
- name: CORS Preflight Check
run: |
ORIGIN="https://preprod.purposepath.app"
TARGET="${{ steps.api-url.outputs.url }}/api/v1/ai/execute-async"
CORS_HEADERS=$(curl -s -D - -o /dev/null -X OPTIONS "$TARGET" \
-H "Origin: $ORIGIN" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Authorization,Content-Type,X-Tenant-Id")
ALLOW_ORIGIN=$(echo "$CORS_HEADERS" | tr -d '\r' | awk -F': ' 'tolower($1)=="access-control-allow-origin"{print $2}' | tail -n 1)
ALLOW_CREDENTIALS=$(echo "$CORS_HEADERS" | tr -d '\r' | awk -F': ' 'tolower($1)=="access-control-allow-credentials"{print $2}' | tail -n 1)
if [ "$ALLOW_ORIGIN" != "$ORIGIN" ]; then
echo "❌ Invalid Access-Control-Allow-Origin: '$ALLOW_ORIGIN' (expected '$ORIGIN')"
exit 1
fi
if [ "$ALLOW_CREDENTIALS" != "true" ]; then
echo "❌ Invalid Access-Control-Allow-Credentials: '$ALLOW_CREDENTIALS' (expected 'true')"
exit 1
fi
echo "✅ CORS preflight returned expected headers"