chore(deps): update dependency pytest to v9 [security] #1325
Workflow file for this run
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: Phoenix VC Deployment Pipeline | |
| on: | |
| push: | |
| branches: [ "main", "release/*", "staging/*" ] | |
| pull_request: | |
| branches: [ "main", "release/*", "staging/*" ] | |
| workflow_dispatch: | |
| inputs: | |
| notifyOnFailure: | |
| description: 'Send Teams notification if deployment fails' | |
| type: boolean | |
| default: true | |
| deployToProd: | |
| description: 'Also deploy to production after staging succeeds' | |
| type: boolean | |
| default: false | |
| env: | |
| LOCATION_CODE: 'euw' # Default to Europe West | |
| permissions: | |
| contents: write | |
| pages: write | |
| id-token: write | |
| actions: read | |
| concurrency: | |
| group: ${{ github.workflow }}-staging | |
| cancel-in-progress: false | |
| jobs: | |
| log_debug_information: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Show Workflow Inputs | |
| run: | | |
| echo "===========================" | |
| echo "✅ Logging Deployment Variables" | |
| echo "===========================" | |
| echo "GitHub Event Name: ${{ github.event_name }}" | |
| echo "Ref: ${{ github.ref }}" | |
| echo "Base Ref: ${{ github.event.pull_request.base.ref || 'N/A' }}" | |
| echo "Location Code: ${LOCATION_CODE}" | |
| echo "Resource Group Name: [env]-${LOCATION_CODE}-rg-phoenixvc-website" | |
| echo "Static Web App Name: [env]-${LOCATION_CODE}-swa-phoenixvc-website" | |
| echo "Log repository: ${{ github.repository }}" | |
| echo "Azure Subscription ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}" | |
| echo "Prod deployment token: ${{ secrets.PROD_DEPLOYMENT_TOKEN }}" | |
| if [[ -n "${{ secrets.GITHUB_TOKEN }}" ]]; then | |
| echo "✅ GITHUB_TOKEN is set." | |
| else | |
| echo "⚠️ GITHUB_TOKEN is missing!" | |
| fi | |
| if [[ "${{ secrets.AZURE_CREDENTIALS }}" ]]; then | |
| echo "✅ AZURE_CREDENTIALS is set." | |
| else | |
| echo "⚠️ AZURE_CREDENTIALS is missing!" | |
| fi | |
| if [[ -n "${{ secrets.AZURE_SUBSCRIPTION_ID }}" ]]; then | |
| echo "✅ AZURE_SUBSCRIPTION_ID is set." | |
| else | |
| echo "⚠️ AZURE_SUBSCRIPTION_ID is missing!" | |
| fi | |
| if [[ -n "${{ secrets.PROD_DEPLOYMENT_TOKEN }}" ]]; then | |
| echo "✅ PROD_DEPLOYMENT_TOKEN is set." | |
| else | |
| echo "⚠️ PROD_DEPLOYMENT_TOKEN is missing!" | |
| fi | |
| echo "===========================" | |
| check_relevant_changes: | |
| runs-on: ubuntu-latest | |
| needs: [log_debug_information] | |
| outputs: | |
| resources_changed: ${{ steps.check_changes.outputs.resources_changed }} | |
| change_summary: ${{ steps.get_changes.outputs.summary }} | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for Relevant Changes | |
| id: check_changes | |
| run: | | |
| # Get the list of changed files | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}) | |
| else | |
| CHANGED_FILES=$(git diff --name-only HEAD^ HEAD) | |
| fi | |
| # Check if any relevant files changed (excluding .github/workflows/) | |
| RESOURCES_CHANGED=false | |
| while IFS= read -r file; do | |
| if [[ ! "$file" =~ ^\.github/workflows/ ]]; then | |
| RESOURCES_CHANGED=true | |
| break | |
| fi | |
| done <<< "$CHANGED_FILES" | |
| echo "resources_changed=${RESOURCES_CHANGED}" >> $GITHUB_OUTPUT | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| - name: Get Change Summary | |
| id: get_changes | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_BODY: ${{ github.event.pull_request.body }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| if [[ "$EVENT_NAME" == "pull_request" ]]; then | |
| # For PR events, capture the title and body while replacing newlines with spaces | |
| TITLE=$(echo "$PR_TITLE" | tr '\n' ' ') | |
| BODY=$(echo "$PR_BODY" | tr '\n' ' ') | |
| SUMMARY="${TITLE} - ${BODY}" | |
| else | |
| # For push events, use commit messages | |
| SUMMARY=$(git log -1 --pretty=format:"%s%n%b") | |
| fi | |
| # Sanitize and truncate summary (remove any remaining carriage returns/newlines) | |
| SUMMARY=$(echo "$SUMMARY" | tr -d '\r\n' | cut -c1-500) | |
| echo "summary=${SUMMARY}" >> $GITHUB_OUTPUT | |
| # ─────────────────────────────────────────────── | |
| # ✅ BUILD APPS WITH Vite | |
| # ─────────────────────────────────────────────── | |
| build_apps: | |
| runs-on: ubuntu-latest | |
| needs: [log_debug_information, check_relevant_changes] | |
| if: > | |
| (github.event_name == 'workflow_dispatch' || needs.check_relevant_changes.outputs.resources_changed == 'true') | |
| outputs: | |
| web_artifact_id: ${{ steps.upload_web.outputs.artifact-id }} | |
| web_artifact_name: web-dist-${{ steps.set_timestamp.outputs.timestamp }} | |
| design_system_artifact_id: ${{ steps.upload_design.outputs.artifact-id }} | |
| build_timestamp: ${{ steps.set_timestamp.outputs.timestamp }} | |
| web_app_version: ${{ steps.build_web.outputs.version }} | |
| web_app_author: ${{ steps.build_web.outputs.author }} | |
| web_app_repository: ${{ format('https://github.com/{0}', github.repository) }} | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Enable pnpm | |
| run: | | |
| corepack enable | |
| corepack prepare pnpm@10.30.3 --activate | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Set Build Timestamp | |
| id: set_timestamp | |
| run: echo "timestamp=$(date +'%Y%m%d_%H%M%S')" >> $GITHUB_OUTPUT | |
| - name: Build Web App | |
| id: build_web | |
| uses: ./.github/actions/build-vite | |
| with: | |
| app-name: 'web' | |
| environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} | |
| - name: Build Design System | |
| id: build_design | |
| uses: ./.github/actions/build-vite | |
| with: | |
| app-name: 'design-system' | |
| environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} | |
| - name: Upload Web App Artifact | |
| id: upload_web | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: web-dist-${{ steps.set_timestamp.outputs.timestamp }} | |
| path: apps/web/dist | |
| retention-days: 5 | |
| if-no-files-found: error | |
| - name: Upload Design System Artifact | |
| id: upload_design | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: design-system-dist-${{ steps.set_timestamp.outputs.timestamp }} | |
| path: apps/design-system/dist | |
| retention-days: 5 | |
| if-no-files-found: error | |
| # ─────────────────────────────────────────────── | |
| # ✅ SECURITY SCAN | |
| # ─────────────────────────────────────────────── | |
| # security_scan: | |
| # runs-on: ubuntu-latest | |
| # needs: [build_apps] | |
| # if: github.ref == 'refs/heads/main' || github.event_name == 'pull_request' | |
| # steps: | |
| # - name: Checkout code | |
| # uses: actions/checkout@v4 | |
| # - name: Initialize CodeQL | |
| # uses: github/codeql-action/init@v2 | |
| # with: | |
| # languages: javascript, typescript | |
| # - name: Run CodeQL Analysis | |
| # uses: github/codeql-action/analyze@v2 | |
| # with: | |
| # category: "/language:javascript" | |
| # ─────────────────────────────────────────────── | |
| # ✅ STAGING RESOURCE CREATION (Run deploy.sh) | |
| # ─────────────────────────────────────────────── | |
| deploy_resources_staging: | |
| runs-on: ubuntu-latest | |
| needs: [log_debug_information, check_relevant_changes, build_apps] | |
| if: > | |
| (github.event_name == 'workflow_dispatch' || needs.check_relevant_changes.outputs.resources_changed == 'true') | |
| outputs: | |
| staticSiteUrl: ${{ steps.deploy_staging_resources.outputs.staticSiteUrl }} | |
| teamsLogicAppUrl: ${{ steps.deploy_staging_resources.outputs.teamsLogicAppUrl }} | |
| githubLogicAppUrl: ${{ steps.deploy_staging_resources.outputs.githubLogicAppUrl }} | |
| deployment_id: ${{ steps.get_deployment_info.outputs.deployment_id }} | |
| run_id: ${{ github.run_id }} | |
| steps: | |
| - name: Checkout Repository for Staging Resources | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # Add a delay to ensure artifacts are available via the GitHub API | |
| - name: Wait for artifacts to be available | |
| run: | | |
| echo "Waiting for artifacts to be processed and available via GitHub API..." | |
| sleep 30 | |
| echo "Continuing with deployment..." | |
| - name: Deploy Staging Resources | |
| id: deploy_staging_resources | |
| uses: ./.github/actions/deploy-azure-resources | |
| with: | |
| environment: staging | |
| azure_subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} | |
| location_code: ${{ env.LOCATION_CODE }} | |
| repository: ${{ github.repository }} | |
| token: ${{ github.token }} | |
| - name: Get Deployment Information | |
| id: get_deployment_info | |
| run: | | |
| DEPLOYMENT_ID="${{ github.run_id }}_${{ github.sha }}" | |
| # Generate a unique artifact ID based on timestamp and hash | |
| ARTIFACT_ID="phoenixvc_$(date +%Y%m%d)_${{ github.sha }}_${{ github.run_number }}" | |
| echo "deployment_id=${DEPLOYMENT_ID}" >> $GITHUB_OUTPUT | |
| echo "artifact_id=${ARTIFACT_ID}" >> $GITHUB_OUTPUT | |
| # ─────────────────────────────────────────────── | |
| # ✅ STAGING SITE DEPLOYMENT VIA SWA CLI | |
| # ─────────────────────────────────────────────── | |
| swa_deploy_staging: | |
| runs-on: ubuntu-latest | |
| needs: [build_apps, deploy_resources_staging, check_relevant_changes] | |
| continue-on-error: true | |
| timeout-minutes: 15 | |
| environment: | |
| name: staging | |
| url: ${{ steps.swa_deploy.outputs.deployment_url }} | |
| outputs: | |
| staging_url: ${{ steps.swa_deploy.outputs.deployment_url }} | |
| deployment_id: ${{ steps.swa_deploy.outputs.deployment_id }} | |
| if: > | |
| (github.event_name == 'workflow_dispatch' || needs.check_relevant_changes.outputs.resources_changed == 'true') | |
| steps: | |
| - name: Checkout Repository for Staging Resources | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # Remove any steps that try to use a stored token from secrets | |
| # Instead, let the swa-deploy action retrieve the token directly from Azure | |
| - name: Use SWA Deploy Action for Staging | |
| id: swa_deploy | |
| uses: ./.github/actions/swa-deploy | |
| with: | |
| artifact-name: web-dist-${{ needs.build_apps.outputs.build_timestamp }} | |
| run-id: ${{ github.run_id }} | |
| app-name: staging-${{ env.LOCATION_CODE }}-swa-phoenixvc-website | |
| resource-group: staging-${{ env.LOCATION_CODE }}-rg-phoenixvc-website | |
| env: staging | |
| azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} | |
| # Don't pass any deployment_token here - let the action retrieve it | |
| github_token: ${{ github.token }} | |
| # ─────────────────────────────────────────────── | |
| # NOTIFY DEPLOYMENT VIA TEAMS (Combined Notification) | |
| # ─────────────────────────────────────────────── | |
| notify_deployment: | |
| runs-on: ubuntu-latest | |
| # Notify on success, or on failure if notifyOnFailure is enabled | |
| # For production, always notify regardless of outcome. | |
| needs: [build_apps, deploy_resources_staging, swa_deploy_staging, check_relevant_changes] | |
| if: always() | |
| steps: | |
| - name: Log Required Variables | |
| # Only validate when staging actually ran | |
| if: needs.check_relevant_changes.outputs.resources_changed == 'true' || github.event_name == 'workflow_dispatch' | |
| run: | | |
| echo "Logging required variables for Teams Notification..." | |
| echo "Deployment ID: ${{ needs.swa_deploy_staging.outputs.deployment_id }}" | |
| echo "Artifact Name: ${{ needs.build_apps.outputs.web_artifact_name }}" | |
| echo "Run ID: ${{ github.run_id }}" | |
| if [[ -z "${{ needs.swa_deploy_staging.outputs.deployment_id }}" || -z "${{ needs.build_apps.outputs.web_artifact_name }}" || -z "${{ github.run_id }}" ]]; then | |
| echo "❌ Missing required values for approval URL!" | |
| exit 1 | |
| fi | |
| - name: Skip notification - no changes | |
| if: needs.check_relevant_changes.outputs.resources_changed == 'false' && github.event_name != 'workflow_dispatch' | |
| run: | | |
| echo "⏭️ Skipping notification - no relevant changes detected (only workflow files changed)" | |
| - name: Checkout Repository for Notification | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Log Deployment Output Values | |
| run: | | |
| if [[ "${{ needs.check_relevant_changes.outputs.resources_changed }}" == "false" && "${{ github.event_name }}" != "workflow_dispatch" ]]; then | |
| echo "Staging deployment was skipped - no relevant changes detected" | |
| else | |
| echo "Static Site URL: ${{ needs.deploy_resources_staging.outputs.staticSiteUrl }}" | |
| echo "Teams Logic App URL: ${{ needs.deploy_resources_staging.outputs.teamsLogicAppUrl }}" | |
| echo "GitHub Logic App URL: ${{ needs.deploy_resources_staging.outputs.githubLogicAppUrl }}" | |
| fi | |
| - name: Notify Staging via Teams | |
| if: (needs.check_relevant_changes.outputs.resources_changed == 'true' || github.event_name == 'workflow_dispatch') && (needs.swa_deploy_staging.result == 'success' || inputs.notifyOnFailure == true) | |
| uses: ./.github/actions/teams-notification | |
| with: | |
| webhook_url: "https://phoenixvc568.webhook.office.com/webhookb2/17b0291f-b70b-4bdd-9266-f212ae03a2bd@7edf4423-ccb3-4275-bc80-64dae3ef0148/IncomingWebhook/3174ffcca3804d34ae923ba2e131f82e/a41dbcc0-67a6-4a23-bdec-b780dcebefa9/V2MOPWeKY7ywC8-iGptxHlSIZpwHVxw2FLAOAMU2N0hmM1" | |
| title: "Staging Deployment Complete" | |
| message: "The deployment to the STAGING environment has completed successfully. Please review the deployment and take any necessary actions." | |
| environment: "staging" | |
| deployment_url: ${{ needs.swa_deploy_staging.outputs.staging_url }} | |
| logic_app_url: ${{ needs.deploy_resources_staging.outputs.teamsLogicAppUrl }} | |
| github_logic_app_url: ${{ needs.deploy_resources_staging.outputs.githubLogicAppUrl }} | |
| branch: ${{ github.ref }} | |
| location_code: ${{ env.LOCATION_CODE }} | |
| resource_group: staging-${{ env.LOCATION_CODE }}-rg-phoenixvc-website | |
| color: "0076D7" | |
| approval_url: ${{ needs.deploy_resources_staging.outputs.githubLogicAppUrl }} | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| teams_token: ${{ secrets.TEAMS_TOKEN }} | |
| change_summary: ${{ needs.check_relevant_changes.outputs.change_summary }} | |
| deployment_id: ${{ needs.swa_deploy_staging.outputs.deployment_id }} | |
| artifact_id: ${{ needs.build_apps.outputs.web_artifact_name }} | |
| run_id: ${{ github.run_id }} | |
| version: ${{ needs.build_apps.outputs.web_app_version }} | |
| author: ${{ needs.build_apps.outputs.web_app_author }} | |
| repository: ${{ needs.build_apps.outputs.web_app_repository }} | |
| pr_description: ${{ github.event.pull_request.body || 'N/A' }} | |
| # ─────────────────────────────────────────────── | |
| # ✅ PRODUCTION DEPLOYMENT (auto on main push, or manual with checkbox) | |
| # ─────────────────────────────────────────────── | |
| deploy_production: | |
| needs: [build_apps, swa_deploy_staging, check_relevant_changes] | |
| # Trigger production when: | |
| # 1. Auto: push to main + staging success + relevant changes, OR | |
| # 2. Manual: workflow_dispatch + deployToProd checked + staging success | |
| if: > | |
| needs.swa_deploy_staging.result == 'success' && | |
| ( | |
| (github.ref == 'refs/heads/main' && github.event_name == 'push' && needs.check_relevant_changes.outputs.resources_changed == 'true') || | |
| (github.event_name == 'workflow_dispatch' && inputs.deployToProd == true) | |
| ) | |
| uses: ./.github/workflows/production_deployment.yml | |
| with: | |
| artifactSource: 'specific' | |
| artifactId: ${{ needs.build_apps.outputs.web_artifact_name }} | |
| runID: ${{ github.run_id }} | |
| includeDnsUpdate: false | |
| secrets: inherit |