test(phase-22b): cover optional-lastName edit in settings panel #168
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 | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: Build (${{ matrix.os }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '20' | |
| cache: npm | |
| cache-dependency-path: | | |
| backend/package-lock.json | |
| frontend/package-lock.json | |
| - name: Backend — install & typecheck | |
| working-directory: backend | |
| run: | | |
| npm ci | |
| npx tsc --noEmit | |
| - name: Frontend — install & typecheck | |
| working-directory: frontend | |
| run: | | |
| npm ci | |
| npx tsc --noEmit | |
| secret-scan: | |
| name: Built-bundle secret scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '20' | |
| cache: npm | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Frontend — install & build | |
| working-directory: frontend | |
| run: | | |
| npm ci | |
| npm run build | |
| # Fails the build if the Supabase service-role JWT (or its literal | |
| # "role":"service_role" marker) ever lands in a shipped client bundle. | |
| # The backend holds service-role; frontend never should. A bundler | |
| # misconfig (e.g. an accidental `import` from a backend-only file, or | |
| # a mis-named VITE_* env) would leak it silently without this gate. | |
| - name: Scan dist/ for service-role markers | |
| working-directory: frontend | |
| run: | | |
| set -euo pipefail | |
| if grep -r -E '"role"\s*:\s*"service_role"|SUPABASE_SERVICE_ROLE_KEY' dist/; then | |
| echo "::error::Frontend bundle contains a service-role marker. Refuse to ship." | |
| exit 1 | |
| fi | |
| echo "No service-role markers in frontend/dist." | |
| content-lint: | |
| name: Content lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '20' | |
| cache: npm | |
| cache-dependency-path: frontend/package-lock.json | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Frontend — install & content-lint | |
| working-directory: frontend | |
| run: | | |
| npm ci | |
| npm run lint:content | |
| solutions-pass: | |
| name: Verify golden solutions | |
| needs: content-lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '20' | |
| cache: npm | |
| cache-dependency-path: frontend/package-lock.json | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Frontend — install & verify solutions | |
| working-directory: frontend | |
| run: | | |
| npm ci | |
| npm run verify:solutions | |
| test: | |
| name: Test (${{ matrix.os }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '20' | |
| cache: npm | |
| cache-dependency-path: | | |
| backend/package-lock.json | |
| frontend/package-lock.json | |
| - name: Backend — install & test | |
| working-directory: backend | |
| run: | | |
| npm ci | |
| npm test | |
| - name: Frontend — install & test | |
| working-directory: frontend | |
| run: | | |
| npm ci | |
| npm test | |
| powershell: | |
| name: PowerShell parse + lint | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Parse all .ps1 files | |
| shell: pwsh | |
| run: | | |
| $failed = $false | |
| Get-ChildItem -Recurse -File -Include *.ps1 | ForEach-Object { | |
| $errors = $null | |
| [System.Management.Automation.Language.Parser]::ParseFile( | |
| $_.FullName, [ref]$null, [ref]$errors | |
| ) | Out-Null | |
| if ($errors -and $errors.Count -gt 0) { | |
| Write-Host "Parse errors in $($_.FullName):" -ForegroundColor Red | |
| $errors | ForEach-Object { Write-Host " $_" } | |
| $script:failed = $true | |
| } | |
| } | |
| if ($failed) { exit 1 } | |
| - name: PSScriptAnalyzer (errors only) | |
| shell: pwsh | |
| run: | | |
| Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser -SkipPublisherCheck | |
| $results = Invoke-ScriptAnalyzer -Path . -Recurse -Severity Error | |
| if ($results) { | |
| $results | Format-Table -AutoSize | |
| exit 1 | |
| } | |
| Write-Host "No PSScriptAnalyzer errors." | |
| shellcheck: | |
| name: Shellcheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Install shellcheck | |
| run: sudo apt-get update && sudo apt-get install -y shellcheck | |
| - name: Run shellcheck | |
| run: | | |
| find . -type f -name "*.sh" \ | |
| -not -path "*/node_modules/*" \ | |
| -not -path "./temp/*" \ | |
| -print0 | xargs -0 --no-run-if-empty shellcheck --severity=warning | |
| cloud-init-drift: | |
| # I-4 (bucket 7): cloud-init.yaml is consumed by Azure *once* per VM | |
| # provision — after first boot, the live VM runs copies of those scripts | |
| # from /usr/local/bin/ and /etc/systemd/system/ that never re-sync from | |
| # the repo. So a PR editing cloud-init.yaml does nothing to production | |
| # unless someone SSHes in and applies the diff manually, or the VM is | |
| # re-provisioned. Surface that as an explicit warning so the next | |
| # reviewer doesn't assume the deploy pipeline will propagate it. | |
| # | |
| # Also shellcheck the embedded refresh-env script — cloud-init.yaml | |
| # ships shell-in-YAML that the main shellcheck job's `*.sh` glob misses. | |
| name: Cloud-init drift warning | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Warn on cloud-init.yaml edits | |
| run: | | |
| set -euo pipefail | |
| base="${{ github.event.pull_request.base.sha }}" | |
| if git diff --name-only "$base"...HEAD | grep -qE '^infra/azure/cloud-init\.yaml$'; then | |
| echo "::warning file=infra/azure/cloud-init.yaml::cloud-init.yaml changed in this PR. The live VM still runs scripts baked in at first-boot — diff against /usr/local/bin/refresh-env on the VM and apply any needed changes by SSH, or plan a re-provision." | |
| else | |
| echo "cloud-init.yaml unchanged in this PR." | |
| fi | |
| - name: Install shellcheck | |
| run: sudo apt-get update && sudo apt-get install -y shellcheck | |
| - name: Shellcheck embedded refresh-env script | |
| run: | | |
| set -euo pipefail | |
| mkdir -p /tmp/cloud-init-extract | |
| awk ' | |
| /^ - path: \/usr\/local\/bin\/refresh-env$/ { capture = 1; next } | |
| capture && emitting && /^ - path:/ { exit } | |
| capture && /^ content: \|/ { emitting = 1; next } | |
| emitting { | |
| # Strip the 6-space YAML indent from the heredoc body. | |
| sub(/^ /, "") | |
| } | |
| ' infra/azure/cloud-init.yaml > /tmp/cloud-init-extract/refresh-env | |
| if [ ! -s /tmp/cloud-init-extract/refresh-env ]; then | |
| echo "::error::Failed to extract refresh-env from cloud-init.yaml — check the awk delimiter in ci.yml" | |
| exit 1 | |
| fi | |
| shellcheck --severity=warning /tmp/cloud-init-extract/refresh-env | |
| line-endings: | |
| name: Line-endings (Windows checkout) | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: .sh files must be LF | |
| shell: pwsh | |
| run: | | |
| $failed = @() | |
| Get-ChildItem -Recurse -File -Include *.sh | | |
| Where-Object { $_.FullName -notmatch "node_modules" } | | |
| ForEach-Object { | |
| $bytes = [System.IO.File]::ReadAllBytes($_.FullName) | |
| if (($bytes | Where-Object { $_ -eq 13 }).Count -gt 0) { | |
| $failed += $_.FullName | |
| } | |
| } | |
| if ($failed) { | |
| Write-Host ".sh files with CR bytes (expected LF only):" -ForegroundColor Red | |
| $failed | ForEach-Object { Write-Host " $_" } | |
| exit 1 | |
| } | |
| Write-Host "All .sh files are LF." | |
| - name: .ps1 files must be CRLF | |
| shell: pwsh | |
| run: | | |
| $failed = @() | |
| Get-ChildItem -Recurse -File -Include *.ps1 | | |
| Where-Object { $_.FullName -notmatch "node_modules" } | | |
| ForEach-Object { | |
| $bytes = [System.IO.File]::ReadAllBytes($_.FullName) | |
| $cr = ($bytes | Where-Object { $_ -eq 13 }).Count | |
| $lf = ($bytes | Where-Object { $_ -eq 10 }).Count | |
| if ($lf -gt 0 -and $cr -ne $lf) { | |
| $failed += $_.FullName | |
| } | |
| } | |
| if ($failed) { | |
| Write-Host ".ps1 files missing CRLF line endings:" -ForegroundColor Red | |
| $failed | ForEach-Object { Write-Host " $_" } | |
| exit 1 | |
| } | |
| Write-Host "All .ps1 files are CRLF." |