Building & Running¶
Prerequisites¶
- .NET SDK 10 (
dotnet --version→10.x). SDK 8/9 may also be installed; that's fine — the desktop head targetsnet8.0, but the solution (including the browser head) builds with SDK 10. - Workloads (only what you actually build):
- Desktop only → none needed.
- Browser (WASM) head →
dotnet workload install wasm-tools(see gotcha below). - Mobile heads →
android/ios.
- Check installed workloads with
dotnet workload list.
Build & run¶
# Desktop (primary dev target)
dotnet build "src\Collectary.UI.Desktop\Collectary.UI.Desktop.csproj"
.\src\Collectary.UI.Desktop\bin\Debug\net8.0\Collectary.UI.Desktop.exe
# Browser (WASM) — needs the wasm-tools workload (see below)
dotnet run --project src\Collectary.UI.Browser
# then open the printed http://localhost:5235 / https://localhost:7169
# Tests
.\build.ps1 --target Test # all tests (default)
.\build.ps1 --target Coverage # coverage gate (>=95%)
.\build.ps1 --target Mutate # mutation testing
There are also NUKE targets that wrap the day-to-day run/deploy flow and validate cloud credentials up front:
.\build.ps1 --target CheckCredentials # fail fast if any cloud credential env var is missing
.\build.ps1 --target SetCredentials # persist credentials for the current user (see below)
.\build.ps1 --target RunDesktop # build + run the Windows desktop head
.\build.ps1 --target DeployAndroid # build + install the Android head onto a connected device
.\build.ps1 --target BuildApk # publish a signed Android APK
RunDesktop, DeployAndroid, and BuildApk all depend on CheckCredentials, so they refuse to
run with placeholder cloud credentials rather than producing a build that silently can't sign in.
The build project (build/_build.csproj) is in Collectary.slnx so you can edit it with full
IntelliSense, but it's flagged not-to-build for the solution's configurations — a normal
dotnet build Collectary.slnx (and the Compile/Test/Coverage targets) won't compile the
orchestrator. You still run it through .\build.ps1.
Cloud credentials¶
OneDrive and Google Drive sign-in need real OAuth identifiers. They are not committed (the source ships placeholders), and they are read from environment variables:
| Variable | Used by |
|---|---|
COLLECTARY_ONEDRIVE_CLIENT_ID |
OneDrive (all platforms) |
COLLECTARY_ANDROID_SIGNATURE_HASH |
OneDrive redirect on Android |
COLLECTARY_GOOGLE_CLIENT_ID |
Google Drive (Windows desktop) |
COLLECTARY_GOOGLE_CLIENT_SECRET |
Google Drive (Windows desktop) |
CheckCredentials reports each one as ok or MISSING and throws if any is absent. It looks at the
process, user, and machine scopes, so a value persisted by SetCredentials is found right away — and
it copies what it finds into the running build so the targets that depend on it (RunDesktop,
DeployAndroid) and the processes they launch inherit it, no shell restart required.
SetCredentials is interactive. Run it in a terminal and it prompts for each credential in turn:
.\build.ps1 --target SetCredentials
- Input is masked (shown as
*) so the value never appears on screen or in shell history. - If you enter nothing — e.g. a paste didn't land — it shows an error and re-asks the same key before moving on, so you can't accidentally store a blank.
- Each value is persisted as a per-user environment variable (Windows
HKCU\Environment) — permanent across reboots, scoped to your account, never machine-wide.
The NUKE targets pick the values up immediately (see CheckCredentials above). Other
already-running processes — a separate IDE, or the desktop .exe launched by hand — only see them
after you restart them, because Windows hands each process its environment at launch.
Set them in the build environment, not just a run configuration
These must be visible to the build process. A Rider run configuration's environment variables are injected only into that run — they are invisible to the build and are never delivered to an Android device. Set them as system/user environment variables (or in Rider's build environment) so both the desktop run and the Android build can see them.
Android reads these at build time, desktop at runtime
The desktop head reads the variables from the developer's machine at runtime. The Android app
runs on the phone, which has no access to your PC's environment, so the Android head needs the
values baked in at build time (client id + the BrowserTabActivity signature hash in
AndroidManifest.xml). See Sync architecture for the Android sign-in
setup.
EF Core migrations run automatically on desktop startup. To add one:
dotnet ef migrations add <Name> --project src\Collectary.Infrastructure
Gotchas¶
wasm-tools is required to build the browser head¶
Without it you get a runtime System.DllNotFoundException: libSkiaSharp (SQLite's e_sqlite3
fails the same way). wasm-tools runs the native-relink step that compiles SkiaSharp/SQLite native
code into the WASM bundle. It's a build-time workload on the dev/CI machine only — end
users need just a browser; the published output is static files with the native bits baked in.
dotnet workload install wasm-tools # elevated terminal
The browser head is client-side WASM — there is no server¶
The whole app compiles to WebAssembly and runs inside the browser tab, sandboxed. dotnet run just
serves static files. Consequences:
- ❌ No native filesystem → the desktop's
FileSystemImageStoreand on-disk SQLite.dbdon't work. - ✅ Browser APIs only (fetch, IndexedDB).
When OperatingSystem.IsBrowser(), App.BuildContainer() swaps in a browser infrastructure module
(EF Core InMemory provider + an in-memory image store + a null logger, using EnsureCreated()
instead of migrations). Browser data is in-memory and resets on page refresh — a stopgap until
a real backend exists. Desktop is unchanged (SQLite + filesystem).
LAN access to the browser head¶
The default launch profile binds localhost only. Use the Browser (LAN) profile to bind all
interfaces, browse to the PC's real IP over http (not https), set the network profile to
Private, and open the firewall port. The full step-by-step lives in SETUP.md at the repo
root.
Documentation site (this site)¶
The docs are built with Material for MkDocs:
pip install -r requirements.txt
mkdocs serve # live preview at http://127.0.0.1:8000
mkdocs build # builds the static site into ./docs
Markdown source lives in docs-src/; mkdocs build outputs into docs/ (configured via
site_dir in mkdocs.yml). On master, once the quality job (build + tests + format) passes, the
docs.yml GitHub Action rebuilds the site, publishes the
WASM app into docs/app/, and commits docs/ back to the branch.
One-time GitHub setup
For the published site to go live, set the repository's
Settings → Pages → Source to Deploy from a branch, branch master, folder /docs.