Skip to content

feat: snipemgr self-upgrade — replace the running binary from the latest GitHub release #45

Description

@jackvaughanjr

Summary

Users currently have no in-tool way to update snipemgr itself. This issue adds a self-upgrade subcommand that fetches the latest GitHub release, downloads the platform-appropriate asset, and replaces the current binary in-place.

Desired behaviour

$ snipemgr self-upgrade
Checking for updates...
Upgrading snipemgr from v1.3.0 to v1.4.0...
✓ snipemgr upgraded to v1.4.0

$ snipemgr self-upgrade   # already current
Checking for updates...
Already up to date (v1.4.0).

Asset naming

Release assets follow the pattern already used by the release workflow:

Platform Asset name
macOS arm64 snipemgr-darwin-arm64
macOS amd64 snipemgr-darwin-amd64
Linux amd64 snipemgr-linux-amd64
Linux arm64 snipemgr-linux-arm64
Windows amd64 snipemgr-windows-amd64.exe

Constructed at runtime as snipemgr-{runtime.GOOS}-{runtime.GOARCH} (+ .exe on Windows).

Implementation notes

New file: cmd/self_upgrade.go

  1. Call installer.FetchLatestRelease(selfupdate.Owner, selfupdate.Repo, token) (existing function, 30-second timeout — acceptable for an explicitly invoked command)
  2. Compare rel.TagName against rootCmd.Version with registry.CompareVersions; exit early if already current
  3. Locate the matching asset in rel.Assets; return a clear error if not found (e.g. unsupported platform)
  4. os.Executable() to get the current binary path
  5. Write new binary to a temp file in the same directory as the current binary (os.CreateTemp(dir, "snipemgr-*.tmp"))
  6. os.Chmod(tmp, 0755)
  7. os.Rename(tmp, exePath) — atomic on Linux/macOS (same filesystem); defer os.Remove(tmp) as a safety net in case rename fails
  8. Update the update-check cache with the new version and current timestamp
  9. No --dry-run support needed — this is an explicit, user-initiated action

Download helper (inline in cmd/self_upgrade.go)

A small downloadToTemp(url, dir, token string) (string, error) that:

  • http.NewRequest + optional Authorization header
  • http.Client{Timeout: 5 * time.Minute} (binary can be several MB)
  • os.CreateTemp(dir, "snipemgr-*.tmp") + io.Copy

This is intentionally not shared with internal/installer to avoid coupling the self-upgrade path to the integration installer's internals.

Edge cases

Case Behaviour
Binary is in a system path (/usr/local/bin) os.Rename will fail with a permission error; print "replacing binary (permission denied? try sudo)"
No matching asset for current platform Clear error: "no asset found for darwin/arm64 (expected snipemgr-darwin-arm64)"
Dev build (version == "dev") Upgrade proceeds normally — CompareVersions("dev", "1.4.0") treats "dev" as 0.0.0 so it will always attempt an upgrade. This is intentional for testing.
Windows os.Rename cannot replace a running .exe; the rename will fail with a clear OS error. No special handling needed for v1 — the error message is self-explanatory.
Network failure during download Temp file is removed by the deferred os.Remove; binary is untouched

Acceptance criteria

  • snipemgr self-upgrade downloads and replaces the binary when a newer version is available
  • Prints "Already up to date" when already on the latest version
  • The replaced binary passes snipemgr --version showing the new version
  • The update-check cache is updated after a successful upgrade (suppresses the nudge from feat: passive update nudge — notify when a newer snipemgr release is available #44 on the next run)
  • Download failure leaves the existing binary completely untouched
  • Permission errors print an actionable message (suggest sudo)
  • No asset for the current platform produces a clear error, not a panic

Related

Depends on #44 for the update-check cache path constant (selfupdate.DefaultCachePath).

Metadata

Metadata

Assignees

No one assigned

    Labels

    tier-2Solid features, moderate complexity

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions