feat(version): zcli upgrade self-updater + version checks#259
Open
l-hellmann wants to merge 11 commits into
Open
feat(version): zcli upgrade self-updater + version checks#259l-hellmann wants to merge 11 commits into
l-hellmann wants to merge 11 commits into
Conversation
- Trim apiDto.go to only the fields actually consumed (TagName, PublishedAt, Asset.Name, Asset.BrowserDownloadUrl). - Guard the package-level fetch cache with sync.Once. - Propagate real fetch errors instead of returning a "v0.0.0" sentinel that caused a false-positive "update available" warning whenever the version API was unreachable. - Compare versions via golang.org/x/mod/semver. Non-semver current values (notably the "local" default for dev builds) no longer trigger the warning. - Add tests for the new comparison helper.
…l tag
- 24h on-disk cache for the version API response, stored next to
cli.data. fetch() now reads cache first, falls back to network, and
serves a stale cache if the network is unreachable.
- Install-method detection: Detect() reports nix/npm/brew/manual based
on the running binary's path, with a build-time channel override via
ldflags (-X .../version.channel=<name>) for packagers whose install
paths collide with manual installs (AUR, winget MSI).
- Drop the devel build tag. Non-semver version values ("local") cause
IsVersionCheckMismatch and PrintVersionCheck to short-circuit at
runtime, replacing the no-op stubs in version_devel.go.
…e hint - MismatchWarning() reads the on-disk cache only — the synchronous check on every command no longer blocks on the network. The cache is populated by a fire-and-forget RefreshCacheIfStale goroutine spawned alongside the check, so the next invocation has fresh data. - One-line warning replaces the two-line template, and the upgrade hint is now channel-aware: npm/brew/nix users see their package manager's command, install.sh users see the github releases URL. - writeCacheEntry now writes via a tmp file + rename so a process exit during the background refresh can't leave a half-written cache. - Collapse IsVersionCheckMismatch + GetVersionCheckMismatch into a single MismatchWarning(). Drop the embedded message.txt template.
Adds an interactive self-update command for binaries that weren't installed through a package manager. zcli upgrade # interactive: prompt, download, verify, swap zcli upgrade --yes # skip the confirmation zcli upgrade --check # exit 0 = up to date, 1 = behind, 2 = error zcli upgrade --version vX.Y.Z # install a specific tag (incl. downgrade) Behavior: - Refuses package-managed installs (npm/Nix/brew) with the channel- specific upgrade command. - Downloads the release binary and checksums.txt from GitHub, verifies sha256 against the manifest, and atomically swaps via the minio/selfupdate library (handles the Windows rename-on-exit dance). - Surfaces permission errors with a re-run-as-sudo hint instead of a generic failure. Side cleanup: - Unify release asset naming into a single assetName() helper. Fixes a pre-existing bug where GetLatestUrl produced "zcli-windows-amd64" or "zcli-linux-386" instead of "zcli-win-x64.exe" / "zcli-linux-i386". - Manual-install upgrade hint in the passive warning now says "Run: zcli upgrade" now that the command exists.
- `zcli upgrade --check` no longer refuses package-managed installs. Splits PlanUpgrade (always succeeds) from RequireSelfUpdatable (the channel guard), which the cmd only calls after --check has had its chance to report status. - Make `releasesURL` and the `selfupdate.Apply` call swappable package-level vars so tests can drive the full download + verify path against an httptest server without replacing the test binary. - Add end-to-end Upgrade tests: happy path (verifies the manifest -> asset -> checksum wiring), missing asset, manifest 500, binary 404, and the permission-denied -> sudo hint. - Wrap the actual download + verify + swap call in uxHelpers.ProcessCheckWithSpinner so the interactive flow shows a running spinner with friendly success/failure messages. - go.mod: minio/selfupdate promoted to a direct dep (now referenced by upgrade.go and the new tests).
The self-updater (zcli upgrade) fetches the release asset literally named checksums.txt to verify downloaded binaries. goreleaser defaults to zcli_<version>_checksums.txt, so pin the name_template to match. Replaces the manual checksums job that targeted the pre-goreleaser release pipeline.
main's lint config is stricter than the branch was developed against: - rename the cached fetch error var fetchErr -> errFetch (errname) - list InstallManual explicitly in the String/Hint switches (exhaustive) - collapse the applyUpdate lambda to a direct selfupdate.Apply reference and drop the now-unused io import (gocritic unlambda)
Add an InstallDeb method and stamp version.channel via ldflags for every goreleaser-built artifact: raw=manual (self-updatable), npm=npm, deb=deb. This requires splitting the previously-shared raw/npm builds since they ship the same code under different channels. Path detection in src/version is now only a fallback for builds that aren't stamped (plain go build, brew, nix). A dpkg-installed zcli now refuses self-update and points at the .deb instead.
Add errorsx.ExitError, recognized by RunRootCmd's error handler, so a command can signal a specific process exit code by returning instead of calling os.Exit. upgrade --check now returns ExitError(1)/ExitError(2) rather than os.Exit, preserving the documented 0/1/2 contract while staying testable in-process (os.Exit would kill the test runner).
Resolve the version API endpoint at call time, honoring ZEROPS_VERSION_API_URL so tests (and mirrors) can point it elsewhere without rebuilding. Drop the package-level sync.Once/latestResponse memoization: fetch() now relies on the on-disk cache for within-invocation dedup, which removes the global mutable state and the test-only reset hook it required.
The devel tag no longer changed any build (version_devel.go was removed earlier), it only gated the integration tests out of the normal suite. Drop it entirely: remove //go:build devel from the harness and integration tests, the -tags devel flags from the Makefile and CI, and the related doc mentions. Add in-process integration tests for `zcli upgrade` covering --check (0/1/2), explicit --version, and the already-up-to-date path, driven through the harness with the version API stubbed via ZEROPS_VERSION_API_URL. Un-tagging exposed pre-existing lint debt in the test files (these were never linted before): drop the unused ctx param from the harness Run (clearing the nil-context hits), use exec.CommandContext, and exclude musttag for _test.go (tests marshal internal persisted structs that rely on default JSON keys).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What Type of Change is this?
Description (required)
Adds
zcli upgrade, a self-updater for the CLI, plus version-check UX.zcli upgradedownloads the target release binary, verifies its sha256 against the releasechecksums.txt, and atomically swaps the running binary.zcli upgrade --checkprints current vs latest and exits0(up to date) /1(behind) /2(error).checksums.txt(the name the updater expects) and stampsversion.channelper artifact.develbuild tag so integration tests run in the normal suite; adds in-process integration tests forzcli upgrade.Demo
zcli upgrade(self-update on a manual install):Package-manager install refuses self-update:
Related issues & labels (optional)