Skip to content

Building

Abishek Narasimhan edited this page Sep 18, 2026 · 2 revisions

Building

dotnet build RavensPort.slnx -m:1

-m:1 (no parallel MSBuild) avoids an intermittent WPF markup-compile race on a freshly cleaned obj/ that produces spurious CS2001/MC1000 errors. clean-build.bat retries once for the same reason.

Tests

dotnet test tests/RavensPort.Core.Tests/RavensPort.Core.Tests.csproj

877 tests, covering the OAuth and storage layers, the full HTTP method × credential placement matrix against a real upstream, multi-credential routes (several headers, header + body together, the same credential in two slots at once, and routes attaching nothing), static API keys (forwarded in every permitted placement, mixed with an OAuth token on one request, and a key with a line break refused before it reaches the wire), credential testing against a real endpoint that checks what it was sent, the refusal of query-string credentials and of a proxy key sent in a URL, the wipe of the pre-2.0 store, and end-to-end funnel behaviour — including that two funnels over one upstream stay isolated, run in parallel, and never cross-deliver a response.

Coverage

./coverage.ps1 -SkipSystemTests -Open

Runs the unit suite with coverage collection, merges the result, and opens an HTML report under TestResults/coverage-report/. Around 84% of lines in RavensPort.Core.

The figure worth quoting is the union of both suites, because they reach the same assembly from opposite ends — RavensPort.Core.Tests through an InMemoryVault, RavensPort.SystemTests through a real 1Password account. Drop -SkipSystemTests to get it, with the system suite's environment set up first:

$env:OP_SERVICE_ACCOUNT_TOKEN  = '<token for a throwaway account>'
$env:RAVENSPORT_SYSTEM_TEST_ACK = 'i-understand-this-erases-the-ravensport-vault'
./coverage.ps1

Read tests/RavensPort.SystemTests/README.md before doing that — the system suite erases every RavensPort item in the vault the token reaches. Without both variables the script skips it and says so, rather than reporting half a figure as the whole one.

Only RavensPort.Core is measured. RavensPort.App is WPF that no test project references, so including it would add a fixed block of zeroes that hides real movement in Core; the reasoning is written out in coverlet.runsettings, which both runs share so the two halves can legitimately be merged.

In CI, .github/workflows/coverage.yml does the same thing across two workflows: it runs the unit suite itself, waits for system-approval.yml to finish on the same commit, merges both, and publishes the percentage as a job summary and to SonarCloud. The summary always says which halves went into the number — a pull request from a fork gets no vault credentials, so there the system half is honestly absent rather than quietly counted as zero.

Analysis needs a SONAR_TOKEN repository secret; without it the coverage still runs and publishes, only the upload is skipped. SONAR_ORGANIZATION is a repository variable, defaulting to the organisation implied by the project key.

Publishing a standalone exe

dotnet publish src/RavensPort.App/RavensPort.App.csproj -p:PublishProfile=win-x64-selfcontained -c Release

Produces a self-contained RavensPort.exe (~180 MB, runtime bundled) under src/RavensPort.App/bin/Release/net10.0-windows/publish/win-x64/. See THIRD-PARTY-NOTICES.md before redistributing — it bundles components whose licenses require their notices travel along.

Building the Store variant

dotnet build RavensPort.slnx -p:StoreBuild=true -m:1

Defines STORE_BUILD, which drops Proton Pass and mTLS — see BuildProfile and docs/STORE-MSIX.md. It must be a command-line property, not a publish-profile one: profile properties do not cross a ProjectReference, so setting it there would build RavensPort.App with the features removed and RavensPort.Core with them still in. The packaging scripts check the built product name and refuse the wrong build in either direction.

Every other command on this page builds the full app, unchanged.

Project layout

src/RavensPort.Core/            OAuth flows, password-manager storage, YARP proxy config, MCP funnel,
                                API to MCP bridges, activity log — no WPF dependency, just the engine
src/RavensPort.App/             WPF tray app: hosts Kestrel + YARP in-process, tray icon, UI
templates/api-mcp/              manifest starting points, and the format contract for writing one
tools/RavensPort.ManifestCheck/ command-line manifest validator — the same rules the app applies
tests/RavensPort.Core.Tests/    xunit tests for Core — InMemoryVault, no side effects
tests/RavensPort.SystemTests/   the approval suite: one real 1Password vault, end to end

RavensPort.App owns the process. It starts the Kestrel/YARP host on a thread-pool task rather than the WPF dispatcher thread — avoiding a sync-over-async deadlock — then initializes the tray icon. The proxy and the UI share one DI container.

Releases

Pushing a version tag (v*) runs the test suite and, only on success, builds and publishes a release with a provenance attestation. Nothing is released off a failing build.

The installer is the only asset. The bare self-contained exe used to ship beside it, but running it installed nothing — no Start menu entry, no way back in after the tray menu's Exit — which is what Microsoft Store certification rejected. Building from source still produces that exe if you want it.


Clone this wiki locally