Skip to content

feat(upgrade): add tiger upgrade self-update command#163

Merged
aprimakina merged 3 commits into
mainfrom
feat/upgrade-command
Jun 12, 2026
Merged

feat(upgrade): add tiger upgrade self-update command#163
aprimakina merged 3 commits into
mainfrom
feat/upgrade-command

Conversation

@aprimakina

@aprimakina aprimakina commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

What

Adds a tiger upgrade self-update command and reworks the update notifier to check on every invocation, reconciled so the notifier points users at the path that works for their install method.

  • tiger upgrade: replaces the running binary with the latest release: check latest.txt → download the platform archive → verify SHA-256 → extract → atomic in-place swap.

  • Breaking config change: version_check_intervalversion_check (bool, default true). A load-time migration preserves intent for existing configs (interval == 0 stays disabled); explicit version_check / TIGER_VERSION_CHECK wins. config set version_check_interval now errors.

@aprimakina aprimakina self-assigned this Jun 9, 2026
@aprimakina aprimakina force-pushed the feat/upgrade-command branch 2 times, most recently from 64b1f3d to 6ebfc38 Compare June 9, 2026 12:03
Port Ghost's upgrade command to Tiger CLI. `tiger upgrade` (alias
`update`) downloads the latest release archive for the current platform
from releases_url, verifies its SHA-256 checksum, extracts the `tiger`
binary, and atomically replaces the running binary in place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aprimakina aprimakina force-pushed the feat/upgrade-command branch from 6ebfc38 to 56afd5c Compare June 9, 2026 12:08
@aprimakina aprimakina marked this pull request as ready for review June 9, 2026 12:12

@Askir Askir left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me!! Since you build a --version flag and also allow --force we could have an integration test I think, where you'd actually run this agains the live CDN in github actions? Not sure if worth it, but I guess breaking the upgrade command would be quite ugly since it impacts everyone so maybe it is.

Comment on lines +89 to +96
// Kick off a background check for a newer release so the network
// fetch overlaps with the command's actual work; the result is
// printed in PersistentPostRunE. Gated to interactive, non-CI
// terminals. `version --check` runs its own synchronous check.
isVersionCheckCmd := cmd.Name() == "version" && cmd.Flag("check") != nil && cmd.Flag("check").Changed
if cfg.VersionCheck && !skipUpdateCheck && !isVersionCheckCmd &&
!util.IsCI() && util.IsTerminal(cmd.ErrOrStderr()) {
versionCheckCh = make(chan *version.CheckResult, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think these checks also pass during the tiger upgrade command which is probably a bit counter intuitive since it'll post that there is a new version available when upgrading? Or is there a gate somewhere that I am missing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed ✅

if err := cmd.Flags().MarkHidden("version"); err != nil {
panic(err)
}
cmd.Flags().BoolVar(&force, "force", false, "reinstall even if the current version already matches, or the binary was installed via a package manager")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the "replace despite package manager used" truly intended? Do the package managers not trip over it if you change the binaries out underneath them? 😅 I guess its very niche probably a non-issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

By default, it is refused, with the right package-manager command shown instead. --force is a hidden escape hatch (it warns before proceeding), mainly for when the tap/repo lags behind a release or the install-method detection guesses wrong.

And the package managers don't trip over it: the next brew upgrade / apt install just overwrites our binary with its own copy, so the drift self-heals.

The whole mechanism is inspired by ghost's upgrade command, which gates package-manager installs the same way.

}

if runtime.GOOS == "windows" {
cleanupStaleOldBinaries(currentBinaryPath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is only called here right? So we always keep the old one until the next upgrade? I guess that's not necessarily bad as a backup anyway. Just good to know.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right, and it's forced rather than chosen: this branch is Windows-only, and Windows locks a running executable, so the process can't delete its own .exe, only rename it. The earliest opportunity to remove the .old. file is a future invocation, which is what this call does. On Linux/macOS there's no leftover at all; the rename overwrites in place.

aprimakina and others added 2 commits June 12, 2026 13:29
… test

Address PR review feedback:

- Skip the background version check when running `tiger upgrade` (and its
  `update` alias). The command does its own synchronous check, and the
  post-run hook would otherwise print "a new release is available" right
  after a successful upgrade, since the running process still reports the
  old version.

- Add TestUpgradeLiveCDNIntegration: builds a dev binary, upgrades it in
  place via `tiger upgrade --version <latest> --force` against the live
  release CDN (real download, checksum verification, binary replacement),
  then verifies the replaced binary runs and reports the new version.
  Gated behind TIGER_UPGRADE_INTEGRATION so plain `go test ./...` stays
  offline; enabled in the CI test workflow so it runs on every PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`tiger upgrade --version <older>` previously printed "Upgrading tiger
0.20.5 → v0.20.4", which misdescribes the operation. Pick the verb by
comparing the current and target versions, falling back to "Upgrading"
when the current version is unparsable (e.g. dev builds).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@aprimakina aprimakina merged commit dea83c8 into main Jun 12, 2026
2 checks passed
@aprimakina aprimakina deleted the feat/upgrade-command branch June 12, 2026 12:24
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