Skip to content

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ClickWrap

A small system wrapped around ClickOnce: a server that hosts each app's zipped ClickOnce publish output, a library apps use to check for updates, and a wrapper installer that downloads the zip and runs setup.exe.

ClickOnce stays the actual install and update mechanism. Nothing here replaces it — and uninstall remains ClickOnce's job, in Add/Remove Programs.

src/ClickWrap.Server/         Blazor Server admin page + two API endpoints, files on disk
src/ClickWrap.UpdateClient/   one assembly: version check + hand-off to the installer, no UI
src/ClickWrap.Installer/      WPF exe, one self-contained build per app

From an app's point of view the whole thing is two calls:

var update = await client.CheckForUpdateAsync("race-timer");   // version worked out for you
if (update is not null && userSaidYes)
    InstalledApp.UpdateAndExit("race-timer");                  // updates and closes the app

Docs

Developer guide Setup, and how to ship an app or a new version end to end. Start here.
Architecture How the three pieces fit, and where state lives.
Server Storage layout, API, /admin, configuration.
Update-check library API, return values, version comparison.
Installer install.yaml, existing-install policy, self-update, per-app builds.
ClickOnce behaviour The verified facts the whole design rests on.

Quick start

dotnet build ClickWrap.slnx
dotnet run --project src/ClickWrap.Server

Upload a zipped publish folder at http://localhost:8080/admin, add src/ClickWrap.Installer/apps/{appId}.yaml, then:

pwsh ./build/publish-installers.ps1 -App race-timer

out/race-timer/RaceTimerSetup.exe is the single file you distribute. Running it again is how updates are applied, so there is no separate updater to ship.

The three things most likely to bite you

  • installFolder is permanent. ClickOnce refuses to update an app from a folder other than the one it was installed from. Change it after an app ships and updates break for everyone who has it.
  • Do not start signing manifests for an app that has already shipped. It changes the ClickOnce identity, and every existing user gets a duplicate side-by-side install instead of an update.
  • Set CLICKWRAP_PUBLIC_BASE_URL on the server. Behind a Cloudflare Tunnel the inbound host is not the public one, so without it installers are handed download URLs they cannot reach.
  • Scope Cloudflare Access to the whole host, with a bypass for /api/*. Protecting /admin alone does not work: Blazor routes to it client-side over the SignalR circuit, so Access never sees a request to challenge. See server.md.

Credits

Icons courtesy of Axialis Software.

The icon is used unmodified, as its licence requires: it ships with the 16x16 and 24x24 frames it was generated with, and must not be re-rendered at other sizes for distribution. That is why the installer's icon looks slightly soft at Explorer's 32px and 48px views — it is deliberate, not an oversight.