build(deps-dev): bump @nuxt/test-utils from 3.23.0 to 4.0.3 in /src/Gatherstead.Web #104
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/CD | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| # Serialize deploys per branch so two pushes to main can't race the same App Service / database. | |
| concurrency: | |
| group: ci-cd-${{ github.ref }} | |
| cancel-in-progress: false | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| build-backend: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore dependencies | |
| run: dotnet restore --locked-mode | |
| - name: Build | |
| run: dotnet build --no-restore --configuration Release | |
| - name: Test | |
| run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --settings coverlet.runsettings --results-directory coverage | |
| - name: Generate coverage report | |
| run: | | |
| dotnet tool install --global dotnet-reportgenerator-globaltool | |
| reportgenerator \ | |
| -reports:"coverage/**/coverage.cobertura.xml" \ | |
| -targetdir:"coverage/report" \ | |
| -reporttypes:"MarkdownSummaryGithub;Cobertura" | |
| - name: Write coverage to job summary | |
| run: cat coverage/report/SummaryGithub.md >> $GITHUB_STEP_SUMMARY | |
| - name: Upload coverage report | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-report | |
| path: coverage/report/ | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: coverage/report/Cobertura.xml | |
| fail_ci_if_error: false | |
| build-frontend: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: src/Gatherstead.Web | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v6 | |
| with: | |
| version: '10.33.0' | |
| - name: Setup Node | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: '24' | |
| cache: pnpm | |
| cache-dependency-path: src/Gatherstead.Web/pnpm-lock.yaml | |
| - name: Install (frozen lockfile) | |
| run: pnpm install --frozen-lockfile | |
| - name: Lint | |
| run: pnpm run lint | |
| - name: Test | |
| run: pnpm run test:run | |
| - name: Build | |
| run: pnpm build | |
| check-openapi-freshness: | |
| needs: [build-backend] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore dependencies | |
| run: dotnet restore --locked-mode | |
| - name: Build API (Release) | |
| run: dotnet build src/Gatherstead.Api/Gatherstead.Api.csproj --no-restore --configuration Release | |
| - name: Restore dotnet tools | |
| run: dotnet tool restore | |
| - name: Generate fresh OpenAPI spec | |
| run: | | |
| dotnet swagger tofile \ | |
| --output /tmp/openapi-fresh.json \ | |
| src/Gatherstead.Api/bin/Release/net10.0/Gatherstead.Api.dll v1 | |
| - name: Fail if spec is stale (run scripts/generate-openapi.sh to update) | |
| run: | | |
| if [ ! -f src/Gatherstead.Api/openapi.json ]; then | |
| echo "::warning::openapi.json is not yet committed. Run scripts/generate-openapi.sh once to generate and commit it." | |
| else | |
| diff src/Gatherstead.Api/openapi.json /tmp/openapi-fresh.json | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Deploy jobs — only on push to main, and only after build + test are green. | |
| # Auth is GitHub OIDC → the id-gat-ci user-assigned managed identity (no secrets). | |
| # Required repo secrets: AZURE_CLIENT_ID (ciIdentityClientId output), AZURE_TENANT_ID, | |
| # AZURE_SUBSCRIPTION_ID, DEMO_APPINSIGHTS_CONNECTION_STRING. | |
| # (The demo SWA token is fetched at runtime via the CI identity — no stored token.) | |
| # Required repo variables (from Bicep outputs): AZURE_RESOURCE_GROUP, API_APP_NAME, | |
| # WEB_APP_NAME, SQL_SERVER_NAME, SQL_DATABASE_NAME, DEMO_SWA_NAME. | |
| # (KEYVAULT_CMK_ID is no longer used by CI — it's an input to the manual admin | |
| # Always Encrypted setup; see docs/DEPLOYMENT.md.) | |
| # Optional repo variables (public site URLs baked into the web/demo builds; unset = link/CTA hidden): | |
| # PROJECT_GITHUB_URL, DOCS_URL, LIVE_URL (demo's "Go live" CTA), DEMO_URL (prod's "Try the Demo" CTA), | |
| # CONTACT_EMAIL (mailto on the /contact page; unset = neutral fallback, no mailto link). | |
| # --------------------------------------------------------------------------- | |
| deploy-migrations: | |
| name: Deploy DB migrations | |
| needs: [build-backend, build-frontend] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Azure login (OIDC) | |
| uses: azure/login@v3 | |
| with: | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| - name: Restore dependencies | |
| run: dotnet restore --locked-mode | |
| - name: Build migration bundle | |
| run: | | |
| # dotnet-ef is pinned in .config/dotnet-tools.json alongside the EF Core packages. | |
| dotnet tool restore | |
| # Built offline (no DB connection). Unlike an --idempotent script, the bundle reads | |
| # __EFMigrationsHistory at run time and sends ONLY pending migrations, so SQL from | |
| # already-applied migrations is never re-parsed against a schema it no longer matches. | |
| dotnet ef migrations bundle --project src/Gatherstead.Data --configuration Release --force -o efbundle | |
| chmod +x efbundle | |
| - name: Open SQL firewall for runner | |
| run: | | |
| RUNNER_IP=$(curl -fsSL https://api.ipify.org) | |
| az sql server firewall-rule create \ | |
| --resource-group "${{ vars.AZURE_RESOURCE_GROUP }}" \ | |
| --server "${{ vars.SQL_SERVER_NAME }}" \ | |
| --name "gh-actions-${{ github.run_id }}" \ | |
| --start-ip-address "$RUNNER_IP" --end-ip-address "$RUNNER_IP" | |
| - name: Apply migrations | |
| run: | | |
| # Active Directory Default → DefaultAzureCredential picks up the azure/login (Azure CLI) session. | |
| # Serverless DB may be auto-paused; the first connect wakes it, so allow a generous timeout. | |
| # --verbose lists which migrations were pending, which is the record of what prod actually ran. | |
| ./efbundle --verbose \ | |
| --connection "Server=tcp:${{ vars.SQL_SERVER_NAME }}.database.windows.net,1433;Database=${{ vars.SQL_DATABASE_NAME }};Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;Connection Timeout=60;" | |
| - name: Remove SQL firewall rule | |
| if: always() | |
| run: | | |
| az sql server firewall-rule delete \ | |
| --resource-group "${{ vars.AZURE_RESOURCE_GROUP }}" \ | |
| --server "${{ vars.SQL_SERVER_NAME }}" \ | |
| --name "gh-actions-${{ github.run_id }}" || true | |
| # NOTE: Always Encrypted setup (CMK/CEK, column encryption, temporal retention) is intentionally | |
| # NOT a CI job. Wrapping the CEK requires Key Vault Crypto access (which the CI identity does not | |
| # have) and the setup is driven by the Gatherstead.Data.Setup tool rather than a raw script. It is | |
| # run manually as a SQL admin; see docs/DEPLOYMENT.md. | |
| deploy-api: | |
| name: Deploy API | |
| needs: [build-backend, build-frontend, deploy-migrations] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore dependencies | |
| run: dotnet restore --locked-mode | |
| - name: Publish API | |
| run: dotnet publish src/Gatherstead.Api/Gatherstead.Api.csproj --no-restore -c Release -o publish/api | |
| - name: Package | |
| run: cd publish/api && zip -r ../../api.zip . | |
| - name: Azure login (OIDC) | |
| uses: azure/login@v3 | |
| with: | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| - name: Deploy to App Service | |
| run: | | |
| az webapp deploy \ | |
| --resource-group "${{ vars.AZURE_RESOURCE_GROUP }}" \ | |
| --name "${{ vars.API_APP_NAME }}" \ | |
| --src-path api.zip --type zip | |
| deploy-web: | |
| name: Deploy Web | |
| needs: [deploy-api] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: src/Gatherstead.Web | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| version: '10.33.0' | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: '24' | |
| cache: pnpm | |
| cache-dependency-path: src/Gatherstead.Web/pnpm-lock.yaml | |
| - name: Install (frozen lockfile) | |
| run: pnpm install --frozen-lockfile | |
| - name: Build (SSR) | |
| env: | |
| NUXT_PUBLIC_GITHUB_URL: ${{ vars.PROJECT_GITHUB_URL }} | |
| NUXT_PUBLIC_DOCS_URL: ${{ vars.DOCS_URL }} | |
| NUXT_PUBLIC_DEMO_URL: ${{ vars.DEMO_URL }} | |
| NUXT_PUBLIC_CONTACT_EMAIL: ${{ vars.CONTACT_EMAIL }} | |
| run: pnpm build | |
| - name: Package | |
| # Keep the .output directory in the archive — the App Service start command is | |
| # `node .output/server/index.mjs` (see infrastructure/modules/appservice.bicep). | |
| run: zip -r web.zip .output | |
| - name: Azure login (OIDC) | |
| uses: azure/login@v3 | |
| with: | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| - name: Deploy to App Service | |
| run: | | |
| az webapp deploy \ | |
| --resource-group "${{ vars.AZURE_RESOURCE_GROUP }}" \ | |
| --name "${{ vars.WEB_APP_NAME }}" \ | |
| --src-path web.zip --type zip | |
| deploy-demo: | |
| name: Deploy demo site | |
| needs: [deploy-api] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| version: '11.5.0' | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: '.node-version' | |
| cache: 'pnpm' | |
| cache-dependency-path: src/Gatherstead.Web/pnpm-lock.yaml | |
| - name: Install dependencies | |
| working-directory: src/Gatherstead.Web | |
| run: pnpm install --frozen-lockfile | |
| - name: Generate static demo site | |
| working-directory: src/Gatherstead.Web | |
| env: | |
| NUXT_PUBLIC_DEMO_MODE: 'true' | |
| NUXT_PUBLIC_LIVE_URL: ${{ vars.LIVE_URL }} | |
| NUXT_PUBLIC_GITHUB_URL: ${{ vars.PROJECT_GITHUB_URL }} | |
| NUXT_PUBLIC_DOCS_URL: ${{ vars.DOCS_URL }} | |
| NUXT_PUBLIC_CONTACT_EMAIL: ${{ vars.CONTACT_EMAIL }} | |
| # Demo App Insights (appi-gat-demo-*) — ingestion-only; safe to bake into the static build. | |
| NUXT_PUBLIC_APP_INSIGHTS_CONNECTION_STRING: ${{ secrets.DEMO_APPINSIGHTS_CONNECTION_STRING }} | |
| run: pnpm generate | |
| - name: Azure login (OIDC) | |
| uses: azure/login@v3 | |
| with: | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| - name: Fetch SWA deployment token | |
| id: swa | |
| run: | | |
| TOKEN=$(az staticwebapp secrets list \ | |
| --name "${{ vars.DEMO_SWA_NAME }}" \ | |
| --resource-group "${{ vars.AZURE_RESOURCE_GROUP }}" \ | |
| --query "properties.apiKey" -o tsv) | |
| echo "::add-mask::$TOKEN" | |
| echo "token=$TOKEN" >> "$GITHUB_OUTPUT" | |
| - name: Deploy to Azure Static Web Apps | |
| uses: Azure/static-web-apps-deploy@v1 | |
| with: | |
| azure_static_web_apps_api_token: ${{ steps.swa.outputs.token }} | |
| action: upload | |
| app_location: src/Gatherstead.Web/.output/public | |
| skip_app_build: true |