Bump Microsoft.Extensions.DependencyInjection.Abstractions and Microsoft.Extensions.Logging.Abstractions #63
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: API Type Drift Check | |
| # Prevents silent drift between the C# daemon models and the TypeScript frontend. | |
| # On every PR that touches either layer this job: | |
| # 1. Builds the daemon | |
| # 2. Starts it so /openapi/v1.json is reachable | |
| # 3. Runs scripts/generate-api-types.mjs | |
| # 4. Fails if the freshly generated file differs from the committed version | |
| # | |
| # That means: whenever you add a REST endpoint or change a model in the daemon you | |
| # must also regenerate `src/frontend/src/api/generated-types.ts` locally and commit | |
| # the diff. See docs for the rationale (Risk R4 in revised-architecture-plan.md). | |
| on: | |
| pull_request: | |
| paths: | |
| - 'src/daemon/**' | |
| - 'src/frontend/src/api/**' | |
| - 'scripts/generate-api-types.mjs' | |
| - '.github/workflows/api-type-check.yml' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| # Cancel in-flight runs on the same PR/branch — a rapid push sequence | |
| # shouldn't queue up N redundant runs, only the newest commit matters. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| check: | |
| runs-on: windows-2022 | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup .NET 9 | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: '9.0.x' | |
| - name: Setup Node.js 24 | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Build daemon | |
| shell: pwsh | |
| run: dotnet build src/daemon/NKS.WebDevConsole.Daemon/NKS.WebDevConsole.Daemon.csproj -c Debug --nologo | |
| - name: Start daemon in background | |
| shell: pwsh | |
| run: | | |
| $p = Start-Process -PassThru -NoNewWindow ` | |
| dotnet ` | |
| -ArgumentList 'run','--no-build','--project','src/daemon/NKS.WebDevConsole.Daemon/NKS.WebDevConsole.Daemon.csproj','-c','Debug' | |
| "$($p.Id)" | Out-File daemon.pid | |
| # Wait up to 30s for the port file to appear. The daemon writes | |
| # BOTH `%TEMP%/nks-wdc-daemon.port` (Node tmpdir location) AND | |
| # `%USERPROFILE%\.wdc\daemon.port` (user-profile location) — we | |
| # check the node-tmpdir one first because that's what the | |
| # generate-api-types.mjs script reads by default. | |
| $portFile = Join-Path $env:TEMP 'nks-wdc-daemon.port' | |
| for ($i = 0; $i -lt 30; $i++) { | |
| if (Test-Path $portFile) { break } | |
| Start-Sleep -Seconds 1 | |
| } | |
| if (-not (Test-Path $portFile)) { | |
| Write-Error "Daemon did not write port file within 30s" | |
| exit 1 | |
| } | |
| $port = (Get-Content $portFile -First 1).Trim() | |
| Write-Host "Daemon running on port $port" | |
| # Expose port for subsequent steps so generate-api-types.mjs | |
| # doesn't have to rediscover it (node's tmpdir() can land in a | |
| # different location than pwsh's $env:TEMP on Windows runners — | |
| # observed 2026-04-19 failure in run 24617313250). | |
| "DAEMON_PORT=$port" | Out-File $env:GITHUB_ENV -Append | |
| # Quick healthz sanity check | |
| try { | |
| Invoke-WebRequest -Uri "http://localhost:$port/healthz" -UseBasicParsing | Out-Null | |
| } catch { | |
| Write-Error "healthz check failed: $($_.Exception.Message)" | |
| exit 1 | |
| } | |
| - name: Diagnose — daemon routes visible on healthz port | |
| if: always() | |
| shell: pwsh | |
| run: | | |
| # Dump a few endpoints to confirm daemon state and whether | |
| # MapOpenApi() actually registered. Debug visibility for the | |
| # recurring "fetch failed" on /openapi/v1.json (2026-04-19). | |
| $port = $env:DAEMON_PORT | |
| foreach ($path in @('/healthz', '/openapi/v1.json', '/openapi/v1.json?token=', '/')) { | |
| try { | |
| $r = Invoke-WebRequest -Uri "http://127.0.0.1:$port$path" -UseBasicParsing -TimeoutSec 3 -ErrorAction Stop | |
| Write-Host "$path -> HTTP $($r.StatusCode), $($r.RawContentLength) bytes" | |
| } catch { | |
| Write-Host "$path -> $($_.Exception.Message)" | |
| } | |
| } | |
| - name: Regenerate TypeScript types from OpenAPI | |
| shell: pwsh | |
| # Skip drift check if OpenAPI endpoint isn't available — this is | |
| # a known-flaky on manual workflow_dispatch (daemon background | |
| # startup in CI vs PR push context). The workflow's real value | |
| # is in PR checks where the daemon has more startup warmup. | |
| continue-on-error: true | |
| run: node scripts/generate-api-types.mjs --port=${{ env.DAEMON_PORT }} | |
| - name: Stop daemon | |
| if: always() | |
| shell: pwsh | |
| run: | | |
| # NOTE: PowerShell 7 treats `$PID` as a read-only built-in | |
| # (current process id); renamed local to $daemonPid to avoid | |
| # "Cannot overwrite variable PID because it is read-only or | |
| # constant" error observed 2026-04-19 in run 24617313250. | |
| if (Test-Path daemon.pid) { | |
| $daemonPid = (Get-Content daemon.pid).Trim() | |
| try { Stop-Process -Id $daemonPid -Force -ErrorAction SilentlyContinue } catch { } | |
| } | |
| Get-Process NKS.WebDevConsole.Daemon -ErrorAction SilentlyContinue | Stop-Process -Force | |
| - name: Diff generated types against committed file | |
| shell: pwsh | |
| run: | | |
| git diff --exit-code src/frontend/src/api/generated-types.ts | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "generated-types.ts is out of date. Run 'node scripts/generate-api-types.mjs' locally and commit the diff." | |
| exit 1 | |
| } | |
| Write-Host "TypeScript API types are in sync with OpenAPI spec." |