Skip to content

Auto-update pipeline: in-process self-updater + workflow maintains latest.txt#8

Merged
matisseduffield merged 1 commit into
masterfrom
claude/auto-update-pipeline
May 30, 2026
Merged

Auto-update pipeline: in-process self-updater + workflow maintains latest.txt#8
matisseduffield merged 1 commit into
masterfrom
claude/auto-update-pipeline

Conversation

@matisseduffield

Copy link
Copy Markdown
Owner

Summary

Makes auto-update actually work for this fork. The existing flow checked rex706/SAM's latest.txt (so it would never see this fork's releases) and required an external rex706/Updater binary. Replaced with an in-process self-updater + a workflow step that maintains latest.txt automatically.

How the new flow works

  1. On launch (with CheckForUpdates setting enabled), SAM fetches https://raw.githubusercontent.com/matisseduffield/SAM/master/latest.txt. New 2-line format:
    1.5.9331.26727
    https://github.com/matisseduffield/SAM/releases/download/v1.5.9331.26727/SAM.exe
    
  2. If the version is newer than what's running, user gets a prompt that explicitly says "Your accounts (info.dat) and settings (samsettings.ini) will be preserved."
  3. On Yes: SelfUpdater.DownloadAndApplyAsync:
    • Downloads to SAM.exe.new next to the running EXE
    • Validates >=4 MB (matches the Costura embedding threshold the workflow enforces — protects against half-downloaded files)
    • Writes a one-shot %TEMP%\sam_update_*.cmd that polls the current PID, atomically moves the new EXE over the old when it exits, relaunches SAM, deletes itself
    • Calls Application.Current.Shutdown()
  4. New SAM.exe launches in the same folder, finds the same info.dat / samsettings.ini / .sambackup files, picks up the DPAPI-sealed key from samsettings.ini or falls back to the legacy PRIVATE_KEY constant (which is identical between builds), and just works.

Maintaining latest.txt

release.yml gains a final step that runs after the GitHub Release is created:

- name: Update latest.txt on master
  continue-on-error: true
  shell: pwsh
  run: |
    ...
    Set-Content -Path latest.txt -Value "$version`n$downloadUrl`n" -NoNewline
    git commit -m "Bump latest.txt to $version [skip ci]" && git push

Wrapped in continue-on-error so a transient git push race can't fail an otherwise-successful release. [skip ci] keeps it from re-triggering the workflow.

What happens for older installs

Anyone running a previously-installed SAM (pre-this-PR) is still pointed at rex706/SAM's latest.txt — they have to download this PR's release once manually. From the next release after that, auto-update takes over.

Files

Status Path
new Core/SelfUpdater.cs
modified Core/UpdateHelper.cs — 2-line format, latestDownloadUrl, delegates to SelfUpdater
modified Views/AccountsWindow.xaml.cs — fork URLs + removes legacy "rebuild yourself" warning
modified latest.txt — new format, points at this fork
modified .github/workflows/release.yml — auto-commit latest.txt on release
modified SAM.csproj — register SelfUpdater.cs

Test plan

  • Tag a release (e.g. v1.5.9331.26727) → workflow builds, creates Release, then commits latest.txt bump to master
  • Drop the built SAM.exe into a fresh folder, launch — no update prompt (current version matches latest.txt)
  • Manually edit local latest.txt to an older version → relaunch → no prompt
  • Bump latest.txt to a newer version → relaunch → update prompt appears with the "info.dat preserved" message
  • Click Yes → SAM.exe.new appears briefly, SAM exits, %TEMP%\sam_update_*.cmd swaps it in, SAM relaunches on the new version
  • info.dat, samsettings.ini, any .sambackup files untouched after update
  • If the download URL in latest.txt is broken: error dialog points to the releases page

Generated by Claude Code

Replaces the rex706/Updater external-binary update path with an
in-process self-updater so newer releases land on running installs
without manual intervention.

- Core/SelfUpdater.cs: downloads the release SAM.exe alongside the
  current binary as SAM.exe.new, validates it is >=4 MB (matching the
  Costura embedding threshold the release workflow already enforces),
  writes a one-shot cmd script to %TEMP% that waits for the current
  PID to exit, atomically moves the new EXE over the old, relaunches
  SAM.exe and deletes itself. info.dat / samsettings.ini / .sambackup
  are untouched because only SAM.exe is replaced.
- Core/UpdateHelper: latest.txt is now read as 2 lines (version +
  direct download URL). CheckForUpdate also exposes latestDownloadUrl
  so StartUpdate doesn't have to re-fetch. StartUpdate now defers to
  SelfUpdater and falls back to opening the releases page if the
  download URL is missing or the self-update throws. Update-available
  prompt now explicitly says info.dat and samsettings.ini are
  preserved.
- AccountsWindow: updateCheckUrl / repositoryUrl now point at
  matisseduffield/SAM (was rex706/SAM, which would never serve this
  fork's releases). UpdateResponse.Update path no longer detours
  through the legacy "PRIVATE_KEY rebuild yourself" warning — the
  self-updater preserves data regardless of whether the user has a
  DPAPI-sealed key or the legacy build-time constant, since both old
  and new builds resolve eKey identically.
- latest.txt: rewritten in the new 2-line format pointing at this
  fork's release URL pattern. The placeholder version (26727) gets
  overwritten by the workflow on the next tag push.
- .github/workflows/release.yml: after creating the GitHub Release,
  checks out master, rewrites latest.txt with the freshly-tagged
  version + matching SAM.exe asset URL, and pushes back with
  "[skip ci]" to avoid re-triggering. Wrapped in continue-on-error so
  a transient git failure can't fail an otherwise-successful release.
@matisseduffield matisseduffield marked this pull request as ready for review May 30, 2026 07:44
@matisseduffield matisseduffield merged commit d7496ca into master May 30, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4854029845

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return;
}

await UpdateHelper.StartUpdate(updateCheckUrl, releasesUrl);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the legacy-key update guard

When the update prompt is accepted by a user whose samsettings.ini does not contain a DPAPI-sealed key, EncryptionKey.Get() falls back to the legacy build key, and this path now replaces the executable directly. The removed guard was the only thing preventing legacy-key users from auto-updating across releases where the embedded key changes (the file even notes keys change before releases), so those users can restart into a build that cannot decrypt their existing info.dat; keep the manual/migration path for EncryptionKey.IsUsingLegacyKey before calling the updater.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants