docs(installer): finalize decisions — self-contained, auto-check, full uninstall
- App + Worker now self-contained (zero .NET runtime dep on target)
- Collapse Manage mode into "update check -> Config view" on every
subsequent launch; Repair + Uninstall become buttons in Config
- Uninstall removes {InstallDir} and ~/.todo-app in full (no prompt
to keep data) — matches user's stated intent
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,8 +37,9 @@ works from inside a source checkout on a machine with the .NET SDK installed:
|
||||
`CreateShortcutsStep`, `RegisterServiceStep`) are fine to keep.
|
||||
|
||||
The installer also contains a partial "Settings" window
|
||||
(`Views/SettingsWindow.xaml`, `Views/SettingsViewModel.cs`) — that wiring will
|
||||
be reused for the Manage-mode branch described below.
|
||||
(`Views/SettingsWindow.xaml`, `Views/SettingsViewModel.cs`) — that wiring is
|
||||
reused for the Config view shown on subsequent launches (see Mode detection
|
||||
below).
|
||||
|
||||
## High-Level Design
|
||||
|
||||
@@ -54,7 +55,8 @@ which is never public. The installer only needs to hit `releases/ClaudeDo`.
|
||||
|
||||
**2) An installer rewrite** that replaces the three publish/deploy steps with
|
||||
a single `DownloadAndExtractStep`, detects existing installs via a marker
|
||||
file, and branches into Install / Update / Manage modes.
|
||||
file, and on subsequent launches checks the Gitea API for updates before
|
||||
deciding whether to show the Update flow or jump straight to the Config view.
|
||||
|
||||
## Release Artifacts
|
||||
|
||||
@@ -71,9 +73,10 @@ Decisions:
|
||||
- **One combined app+worker zip** (not two separate). Reasons: one download,
|
||||
one extract, guaranteed version-locked pair.
|
||||
- **Self-contained installer exe** — user does not need .NET installed.
|
||||
- **App + Worker**: framework-dependent (`--self-contained false`), same as
|
||||
today. This assumes the target machine has the .NET 8 Desktop Runtime.
|
||||
*Open decision — see "Decisions to revisit".*
|
||||
- **App + Worker: self-contained** (`--self-contained true`, `-r win-x64`).
|
||||
Zero runtime dependency on the target machine, at the cost of a larger
|
||||
download (~100 MB combined zip). Decision: acceptable trade-off — the
|
||||
installer is one-click, not per-user-problem-to-debug.
|
||||
- **Checksums file** — plain text, one line per asset (`<sha256> <filename>`),
|
||||
verified by installer before extract.
|
||||
|
||||
@@ -98,8 +101,8 @@ File: `.gitea/workflows/release.yml`
|
||||
1. Checkout
|
||||
2. Setup .NET 8 SDK
|
||||
3. Derive version from tag (`${{ gitea.ref_name }}` without the `v` prefix)
|
||||
4. `dotnet publish src/ClaudeDo.App -c Release -r win-x64 --self-contained false /p:Version=$VERSION -o out/app`
|
||||
5. `dotnet publish src/ClaudeDo.Worker -c Release -r win-x64 --self-contained false /p:Version=$VERSION -o out/worker`
|
||||
4. `dotnet publish src/ClaudeDo.App -c Release -r win-x64 --self-contained true /p:Version=$VERSION -o out/app`
|
||||
5. `dotnet publish src/ClaudeDo.Worker -c Release -r win-x64 --self-contained true /p:Version=$VERSION -o out/worker`
|
||||
6. `dotnet publish src/ClaudeDo.Installer -c Release -r win-x64 --self-contained true /p:Version=$VERSION /p:PublishSingleFile=true -o out/installer`
|
||||
7. Zip `out/app` + `out/worker` as `ClaudeDo-<version>-win-x64.zip` with
|
||||
`app/` and `worker/` as top-level dirs
|
||||
@@ -133,17 +136,45 @@ Written at the end of every successful install or update to
|
||||
The installer reads this on startup (from the default install dir, or a
|
||||
path supplied via CLI arg) to decide which mode to run in.
|
||||
|
||||
### Mode detection (`InstallModeDetector`)
|
||||
### Launch flow (`InstallModeDetector`)
|
||||
|
||||
| `install.json` state | Mode | Wizard flow |
|
||||
|-------------------------------------------|---------------------|-----------------------------------------------------|
|
||||
| absent | **Install** | Welcome → Paths → UiSettings → Service → Install |
|
||||
| present, version < installer's version | **Update** | Welcome (shows old→new) → Install (download + swap) |
|
||||
| present, version == installer's version | **Manage** | Welcome → choose: Repair · Edit Config · Uninstall |
|
||||
| present, version > installer's version | **Downgrade warn** | Welcome explains; user can cancel or force Install |
|
||||
On every launch, the installer checks for `install.json` first:
|
||||
|
||||
The installer's own version comes from its assembly (`AssemblyInformational
|
||||
Version`), set by the workflow's `/p:Version=$VERSION`.
|
||||
```
|
||||
install.json absent?
|
||||
-> Install mode: Welcome -> Paths -> UiSettings -> Service -> Install
|
||||
(writes install.json at the end)
|
||||
|
||||
install.json present?
|
||||
-> Query https://git.kuns.dev/api/v1/repos/releases/ClaudeDo/releases/latest
|
||||
(short timeout; if offline, treat as "no update available")
|
||||
|
||||
latest.tag_name > installed.version
|
||||
-> Update mode: Welcome ("Update v0.1.0 -> v0.2.0, Update / Later")
|
||||
If user accepts -> Install steps (download + swap service)
|
||||
If user declines -> fall through to Config view
|
||||
latest.tag_name <= installed.version (or API unreachable)
|
||||
-> Config view: directly open Paths/UiSettings/Service tabs,
|
||||
prefilled from existing ~/.todo-app/*.json.
|
||||
Action buttons: Save · Repair · Uninstall.
|
||||
```
|
||||
|
||||
Key properties:
|
||||
|
||||
- **First run = wizard**, as today — no behavior change for new users.
|
||||
- **Every subsequent run = update check first**, then either offer update or
|
||||
drop straight into Config. No "Manage page" with a menu of actions — the
|
||||
Config view *is* the default, and Repair/Uninstall are buttons on it.
|
||||
- **Offline / API error = not fatal**: if the release endpoint can't be
|
||||
reached, the installer silently skips the update check and opens Config.
|
||||
The user is never blocked from managing an existing install by a network
|
||||
issue.
|
||||
- **Downgrade** (installed version > latest) is treated the same as "no
|
||||
update available" — we don't ever offer a downgrade.
|
||||
|
||||
The installer's own version (shown for reference in Config) comes from its
|
||||
assembly (`AssemblyInformationalVersion`), set by the workflow's
|
||||
`/p:Version=$VERSION`. The *installed* version comes from `install.json`.
|
||||
|
||||
### New step: `DownloadAndExtractStep`
|
||||
|
||||
@@ -178,15 +209,25 @@ naturally non-destructive.
|
||||
- **Conditional:** `RegisterServiceStep` only if service is not currently
|
||||
registered (covers someone who unregistered it manually)
|
||||
|
||||
### Manage mode — branches
|
||||
### Config view — actions
|
||||
|
||||
- **Repair:** re-download + extract (same as Update), re-create shortcuts,
|
||||
re-register service. Leaves config/DB alone.
|
||||
- **Edit Config:** opens Paths / UiSettings / Service pages prefilled from
|
||||
existing `~/.todo-app/*.json`. Save writes the JSON files and offers
|
||||
"Restart service" if worker config changed. No download.
|
||||
- **Uninstall:** stops + unregisters service, removes shortcuts, deletes
|
||||
`InstallDir`. Asks separately whether to delete `~/.todo-app` (config + DB).
|
||||
- **Save** (primary): writes the Paths / UiSettings / Service fields to
|
||||
`~/.todo-app/*.json`. If worker config changed, prompts "Restart service?"
|
||||
and calls `sc stop` / `sc start` if accepted. No download.
|
||||
- **Repair:** re-download + extract (same as Update flow), re-create
|
||||
shortcuts, re-register service. Leaves config/DB alone. Confirmation
|
||||
dialog before starting.
|
||||
- **Uninstall:** confirmation dialog ("This removes ClaudeDo *and* all of
|
||||
your tasks, config, and database. Type UNINSTALL to confirm."). On
|
||||
confirm:
|
||||
1. Stop + unregister service (`sc stop`, `sc delete ClaudeDoWorker`)
|
||||
2. Remove Start Menu / Desktop shortcuts
|
||||
3. Delete `{InstallDir}` (including `install.json`)
|
||||
4. Delete `~/.todo-app` in full (config + DB + logs)
|
||||
5. Exit
|
||||
|
||||
Everything is removed. No "keep my data" option — that was explicitly
|
||||
declined in the design discussion.
|
||||
|
||||
### Files to add
|
||||
|
||||
@@ -245,34 +286,31 @@ an install in a half-deleted state.
|
||||
|
||||
## Decisions to Revisit
|
||||
|
||||
1. **Framework-dependent vs self-contained App/Worker.** Framework-dependent
|
||||
keeps the zip small (~10 MB) but requires the user to install the .NET 8
|
||||
Desktop Runtime separately. Self-contained is ~80 MB per process but
|
||||
zero-dependency. Current plan: framework-dependent, matches today's
|
||||
behavior. If install friction becomes a problem, flip the flag in the
|
||||
workflow and installer messaging.
|
||||
|
||||
2. **Release notes content.** Auto-generated `git log` summary vs manual
|
||||
1. **Release notes content.** Auto-generated `git log` summary vs manual
|
||||
notes in the tag message vs empty. Start empty; revisit when there are
|
||||
enough releases to care.
|
||||
|
||||
3. **Signed installer.** Out of scope for v1. Users will see a SmartScreen
|
||||
2. **Signed installer.** Out of scope for v1. Users will see a SmartScreen
|
||||
warning the first time. Note this in the README.
|
||||
|
||||
4. **Installer distribution page.** A simple `README.md` badge or a pinned
|
||||
3. **Installer distribution page.** A simple `README.md` badge or a pinned
|
||||
"Latest release" link on the Gitea repo home is enough for v1.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- On a fresh Windows VM with no source checkout and no .NET SDK:
|
||||
- On a fresh Windows VM with **no source checkout, no .NET runtime, and no
|
||||
.NET SDK**:
|
||||
1. Download `ClaudeDo.Installer-<ver>.exe`.
|
||||
2. Run it.
|
||||
3. Complete the wizard.
|
||||
4. ClaudeDo App launches, Worker service is running, a task can be created
|
||||
and picked up.
|
||||
- Running the same installer a second time shows Manage mode.
|
||||
- Running the same installer a second time, with no new release published,
|
||||
opens directly in the Config view after a quick update check.
|
||||
- Publishing a new tag, then running the installer on the existing install,
|
||||
performs an update without touching `~/.todo-app/todo.db` or the config
|
||||
JSONs.
|
||||
offers the update; accepting performs it without touching `~/.todo-app/todo.db`
|
||||
or the config JSONs.
|
||||
- Uninstall leaves no trace: `{InstallDir}` gone, `~/.todo-app` gone, service
|
||||
unregistered, shortcuts removed.
|
||||
- The entire release pipeline runs on `git.kuns.dev` with no manual steps
|
||||
beyond `git tag vX.Y.Z && git push --tags`.
|
||||
|
||||
Reference in New Issue
Block a user