chore(deps): bump the angular-ecosystem group in /src/ui/web with 18 updates #178
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: CI - Build and Test | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| pull_request: | |
| branches: [ main, develop ] | |
| workflow_dispatch: | |
| env: | |
| DOTNET_VERSION: '10.0.x' | |
| NODE_VERSION: '24.11.1' | |
| jobs: | |
| detect-changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| backend: ${{ steps.changes.outputs.backend }} | |
| frontend: ${{ steps.changes.outputs.frontend }} | |
| docs: ${{ steps.changes.outputs.docs }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect changes | |
| uses: dorny/paths-filter@v3 | |
| id: changes | |
| with: | |
| filters: | | |
| backend: | |
| - 'src/api/**' | |
| - 'src/lib/**' | |
| - 'Menlo.slnx' | |
| - 'Directory.Build.props' | |
| - 'Directory.Packages.props' | |
| - '.github/workflows/ci.yml' | |
| - '.github/workflows/cd-backend.yml' | |
| frontend: | |
| - 'src/ui/**' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - '.github/workflows/ci.yml' | |
| - '.github/workflows/cd-frontend.yml' | |
| docs: | |
| - 'docs/**' | |
| - 'README.md' | |
| backend-tests: | |
| name: Backend - Build and Test | |
| runs-on: ubuntu-latest | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.backend == 'true' | |
| # Required for OIDC authentication with Azure | |
| permissions: | |
| id-token: write | |
| contents: read | |
| services: | |
| postgres: | |
| image: postgres:17 | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: menlo_test | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| # Authenticate to Azure using federated credentials | |
| # Skip for Dependabot PRs which don't have access to secrets | |
| - name: Azure Login | |
| if: github.actor != 'dependabot[bot]' | |
| uses: azure/login@v2 | |
| with: | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| # Create an ephemeral client secret for testing | |
| # This secret is short-lived (1 hour) and automatically deleted after tests | |
| # Skip for Dependabot PRs which don't have access to secrets | |
| - name: Create ephemeral Azure AD client secret | |
| if: github.actor != 'dependabot[bot]' | |
| id: create-secret | |
| uses: azure/cli@v2 | |
| with: | |
| azcliversion: latest | |
| inlineScript: | | |
| DISPLAY_NAME="CI-Ephemeral-${{ github.run_id }}" | |
| # Create a new client secret with 1 hour expiry | |
| SECRET_OUTPUT=$(az ad app credential reset \ | |
| --id ${{ secrets.AZUREAD_APP_CLIENT_ID }} \ | |
| --display-name "$DISPLAY_NAME" \ | |
| --end-date "$(date -u -d '+1 hour' '+%Y-%m-%dT%H:%M:%S+00:00')" \ | |
| --append \ | |
| --query "password" \ | |
| --output tsv) | |
| # Mask the secret to prevent it appearing in logs | |
| echo "::add-mask::$SECRET_OUTPUT" | |
| # List credentials to find the keyId by display name | |
| KEY_ID=$(az ad app credential list \ | |
| --id ${{ secrets.AZUREAD_APP_CLIENT_ID }} \ | |
| --query "[?displayName=='$DISPLAY_NAME'].keyId | [0]" \ | |
| --output tsv) | |
| # Set outputs for use in subsequent steps | |
| echo "client-secret=$SECRET_OUTPUT" >> "$GITHUB_OUTPUT" | |
| echo "key-id=$KEY_ID" >> "$GITHUB_OUTPUT" | |
| echo "✅ Created ephemeral client secret with Key ID: $KEY_ID (expires in 1 hour)" | |
| - name: Restore dependencies | |
| run: dotnet restore | |
| - name: Build solution | |
| run: dotnet build --no-restore --configuration Release | |
| - name: Run unit tests | |
| env: | |
| # Azure AD test configuration | |
| # Note: Tests use mock authentication by default, but these allow | |
| # running integration tests against real Azure AD if needed | |
| AzureAd__Instance: "https://login.microsoftonline.com/" | |
| AzureAd__TenantId: ${{ secrets.AZURE_TENANT_ID }} | |
| AzureAd__ClientId: ${{ secrets.AZUREAD_APP_CLIENT_ID }} | |
| AzureAd__ClientSecret: ${{ steps.create-secret.outputs.client-secret }} | |
| AzureAd__CookieDomain: "localhost" | |
| run: | | |
| dotnet test \ | |
| --no-build \ | |
| --configuration Release \ | |
| --verbosity normal \ | |
| --collect:"XPlat Code Coverage" \ | |
| --results-directory ./coverage \ | |
| --logger trx \ | |
| --logger "console;verbosity=detailed" | |
| # Always delete the ephemeral secret, even if tests fail | |
| # Only runs if the secret was created (skipped for Dependabot PRs) | |
| - name: Delete ephemeral Azure AD client secret | |
| if: always() && github.actor != 'dependabot[bot]' && steps.create-secret.outcome == 'success' && steps.create-secret.outputs.key-id != '' | |
| uses: azure/cli@v2 | |
| with: | |
| azcliversion: latest | |
| inlineScript: | | |
| az ad app credential delete \ | |
| --id ${{ secrets.AZUREAD_APP_CLIENT_ID }} \ | |
| --key-id ${{ steps.create-secret.outputs.key-id }} | |
| echo "✅ Ephemeral client secret deleted successfully" | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v6 | |
| if: always() | |
| with: | |
| name: backend-test-results | |
| path: | | |
| **/*.trx | |
| coverage/**/* | |
| - name: Upload code coverage to Codecov | |
| uses: codecov/codecov-action@v5 | |
| if: always() | |
| with: | |
| directory: ./coverage | |
| flags: backend | |
| name: backend-coverage | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| continue-on-error: true | |
| backend-security: | |
| name: Backend - Security Scan | |
| runs-on: ubuntu-latest | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.backend == 'true' | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Restore dependencies | |
| run: dotnet restore | |
| - name: Run security audit | |
| run: dotnet list package --vulnerable --include-transitive | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v4 | |
| with: | |
| languages: csharp | |
| - name: Autobuild | |
| uses: github/codeql-action/autobuild@v4 | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v4 | |
| frontend-tests: | |
| name: Frontend - Build and Test | |
| runs-on: ubuntu-latest | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.frontend == 'true' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Get pnpm store directory | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Setup Angular cache | |
| uses: actions/cache@v5 | |
| with: | |
| path: src/ui/web/.angular/cache | |
| key: ${{ runner.os }}-angular-cache-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-angular-cache- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Lint | |
| working-directory: src/ui/web | |
| run: pnpm run lint | |
| - name: Build applications | |
| working-directory: src/ui/web | |
| run: pnpm run build:all:prod | |
| - name: Create version metadata | |
| working-directory: src/ui/web | |
| run: | | |
| mkdir -p dist/menlo-app | |
| cat > dist/menlo-app/version.json << EOF | |
| { | |
| "commit": "${{ github.sha }}", | |
| "branch": "${{ github.ref_name }}", | |
| "buildNumber": "${{ github.run_number }}", | |
| "buildId": "${{ github.run_id }}", | |
| "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")", | |
| "actor": "${{ github.actor }}", | |
| "repository": "${{ github.repository }}" | |
| } | |
| EOF | |
| - name: Verify version metadata | |
| working-directory: src/ui/web | |
| run: | | |
| if [ ! -f "dist/menlo-app/version.json" ]; then | |
| echo "❌ version.json not found" | |
| exit 1 | |
| fi | |
| echo "✅ version.json created successfully" | |
| cat dist/menlo-app/version.json | |
| - name: Run tests | |
| working-directory: src/ui/web | |
| run: pnpm run test:all -- --coverage --reporters=default --reporters=junit | |
| env: | |
| NODE_ENV: test | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v6 | |
| if: always() | |
| with: | |
| name: frontend-test-results | |
| path: | | |
| src/ui/web/coverage/**/* | |
| src/ui/web/test-results/**/* | |
| - name: Upload code coverage to Codecov | |
| uses: codecov/codecov-action@v5 | |
| if: always() | |
| with: | |
| directory: ./src/ui/web/coverage | |
| flags: frontend | |
| name: frontend-coverage | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| continue-on-error: true | |
| - name: Build Storybook (App) | |
| working-directory: src/ui/web | |
| run: pnpm run build-storybook | |
| - name: Build Storybook (Lib) | |
| working-directory: src/ui/web | |
| run: pnpm run build-storybook:lib | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: frontend-dist | |
| path: | | |
| src/ui/web/dist/**/* | |
| src/ui/web/storybook-static/**/* | |
| - name: Upload Pages deployment artifact | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: frontend-pages-dist | |
| path: src/ui/web/dist/menlo-app/**/* | |
| frontend-security: | |
| name: Frontend - Security Scan | |
| runs-on: ubuntu-latest | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.frontend == 'true' | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Audit dependencies | |
| run: pnpm audit --audit-level moderate | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v4 | |
| with: | |
| languages: typescript | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v4 | |
| integration-tests: | |
| name: Integration Tests | |
| runs-on: ubuntu-latest | |
| needs: [backend-tests, frontend-tests] | |
| if: | | |
| always() && | |
| (needs.backend-tests.result == 'success' || needs.backend-tests.result == 'skipped') && | |
| (needs.frontend-tests.result == 'success' || needs.frontend-tests.result == 'skipped') | |
| services: | |
| postgres: | |
| image: postgres:17 | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: menlo_test | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Restore .NET dependencies | |
| run: dotnet restore | |
| - name: Install pnpm dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build backend | |
| run: dotnet build --no-restore --configuration Release | |
| - name: Build frontend | |
| working-directory: src/ui/web | |
| run: pnpm run build:prod | |
| - name: Run integration tests | |
| run: | | |
| dotnet test \ | |
| --no-build \ | |
| --configuration Release \ | |
| --filter "Category=Integration" \ | |
| --verbosity normal \ | |
| --logger trx \ | |
| --logger "console;verbosity=detailed" | |
| env: | |
| ConnectionStrings__DefaultConnection: "Host=localhost;Port=5432;Database=menlo_test;Username=postgres;Password=postgres" | |
| - name: Upload integration test results | |
| uses: actions/upload-artifact@v6 | |
| if: always() | |
| with: | |
| name: integration-test-results | |
| path: "**/*.trx" | |
| docs-validation: | |
| name: Documentation Validation | |
| runs-on: ubuntu-latest | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.docs == 'true' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Install markdownlint | |
| run: npm install -g markdownlint-cli2 | |
| - name: Lint documentation | |
| run: markdownlint-cli2 --config .config/.markdownlint-cli2.jsonc docs/**/*.md README.md | |
| - name: Check for broken links | |
| uses: tcort/github-action-markdown-link-check@v1 | |
| with: | |
| use-quiet-mode: 'yes' | |
| use-verbose-mode: 'no' | |
| quality-gate: | |
| name: Quality Gate | |
| runs-on: ubuntu-latest | |
| needs: [backend-tests, frontend-tests, backend-security, frontend-security, integration-tests, docs-validation] | |
| if: always() | |
| steps: | |
| - name: Check test results | |
| run: | | |
| echo "Backend Tests: ${{ needs.backend-tests.result }}" | |
| echo "Frontend Tests: ${{ needs.frontend-tests.result }}" | |
| echo "Backend Security: ${{ needs.backend-security.result }}" | |
| echo "Frontend Security: ${{ needs.frontend-security.result }}" | |
| echo "Integration Tests: ${{ needs.integration-tests.result }}" | |
| echo "Docs Validation: ${{ needs.docs-validation.result }}" | |
| # Check if any required job failed | |
| if [[ "${{ needs.backend-tests.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.frontend-tests.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.backend-security.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.frontend-security.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.integration-tests.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.docs-validation.result }}" == "failure" ]]; then | |
| echo "❌ Quality gate failed - one or more required checks failed" | |
| exit 1 | |
| fi | |
| echo "✅ Quality gate passed - all checks successful" | |
| - name: Update status | |
| run: echo "CI pipeline completed successfully" |