Merge branch 'feature/issue-333-fix-docs-sync-summary-quoting' into dev #495
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: Deploy Dev | |
| on: | |
| push: | |
| branches: [dev] | |
| workflow_dispatch: | |
| inputs: | |
| skip_tests: | |
| description: 'Skip tests before deployment' | |
| required: false | |
| default: 'false' | |
| type: choice | |
| options: | |
| - 'true' | |
| - 'false' | |
| # Serialize Pulumi updates: overlapping pushes caused 409 "Another update is currently in progress". | |
| concurrency: | |
| group: deploy-dev-coaching | |
| cancel-in-progress: false | |
| jobs: | |
| pre-deployment-checks: | |
| name: Pre-Deployment Validation | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event.inputs.skip_tests != 'true' }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.14' | |
| # Note: We don't use pip cache since uv manages dependencies | |
| - 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=60 | |
| shell: bash | |
| env: | |
| PYTHONPATH: coaching:shared:. | |
| deploy-coaching: | |
| name: Deploy to Dev | |
| runs-on: ubuntu-latest | |
| needs: [pre-deployment-checks] | |
| if: always() && (needs.pre-deployment-checks.result == 'success' || github.event.inputs.skip_tests == 'true') | |
| 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.14' | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@v5 | |
| with: | |
| version: "latest" | |
| - name: Install Pulumi Python dependencies | |
| working-directory: coaching/pulumi | |
| shell: bash | |
| run: | | |
| python -m venv .venv | |
| source .venv/bin/activate | |
| pip install --upgrade pip | |
| 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 }} | |
| # Cancelled workflows / overlapping runs can leave Pulumi Cloud holding an update lease (409). | |
| - name: Clear stale Pulumi update lock | |
| working-directory: coaching/pulumi | |
| continue-on-error: true | |
| env: | |
| PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} | |
| run: | | |
| curl -fsSL https://get.pulumi.com | sh -s -- --silent | |
| export PATH="$HOME/.pulumi/bin:$PATH" | |
| pulumi cancel --yes --stack dev || true | |
| - name: Deploy Coaching Service | |
| uses: pulumi/actions@v5 | |
| with: | |
| command: up | |
| stack-name: dev | |
| 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: Set up uv (topic seeding) | |
| uses: astral-sh/setup-uv@v5 | |
| with: | |
| version: "latest" | |
| - name: Seed topic registry (DynamoDB + S3 prompts) | |
| shell: bash | |
| run: | | |
| uv venv .venv-seed | |
| source .venv-seed/bin/activate | |
| uv pip install -r coaching/requirements.txt | |
| export PYTHONPATH="coaching:shared:." | |
| export STAGE=dev | |
| export AWS_REGION=us-east-1 | |
| # Must match infrastructure/pulumi S3 bucket (purposepath-coaching-prompts-<account>-<stack>) | |
| export PROMPTS_BUCKET="purposepath-coaching-prompts-380276784420-${STAGE}" | |
| python -m coaching.src.scripts.seed_topics | |
| - name: Get API Gateway URL | |
| id: api-url | |
| working-directory: coaching/pulumi | |
| run: | | |
| URL=$(pulumi stack output customDomainUrl --stack dev) | |
| echo "url=$URL" >> $GITHUB_OUTPUT | |
| env: | |
| PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} | |
| - name: Deployment Summary | |
| run: | | |
| echo "## Deployment Summary - Dev" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ Deployment successful" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Environment:** Dev" >> $GITHUB_STEP_SUMMARY | |
| echo "**Stack:** dev" >> $GITHUB_STEP_SUMMARY | |
| echo "**API URL:** ${{ steps.api-url.outputs.url }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Region:** us-east-1" >> $GITHUB_STEP_SUMMARY | |
| 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 dev) | |
| echo "url=$URL" >> $GITHUB_OUTPUT | |
| env: | |
| PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }} | |
| - name: Health Check | |
| run: | | |
| echo "Testing API health endpoint..." | |
| # Trailing slash avoids 307 redirect from Starlette (smoke expects final 2xx). | |
| HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" ${{ steps.api-url.outputs.url }}/api/v1/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" | |
| exit 1 | |
| fi | |
| - name: CORS Preflight Check | |
| run: | | |
| echo "Testing CORS preflight behavior..." | |
| ORIGIN="https://dev.purposepath.app" | |
| TARGET="${{ steps.api-url.outputs.url }}/api/v1/health/" | |
| CORS_HEADERS=$(curl -s -D - -o /dev/null -X OPTIONS "$TARGET" \ | |
| -H "Origin: $ORIGIN" \ | |
| -H "Access-Control-Request-Method: GET" \ | |
| -H "Access-Control-Request-Headers: Authorization,Content-Type") | |
| 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" | |
| - name: Smoke Test Summary | |
| run: | | |
| echo "## Smoke Tests - Dev" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ Health check passed" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ API is responsive" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ CORS preflight check passed" >> $GITHUB_STEP_SUMMARY |