feat(update): add self-update command#67
Merged
Conversation
Implements `cloudctl update` (closes #53) which queries the GitHub Releases API for the latest version, downloads the matching .tar.gz for the current OS/arch, verifies the SHA256 checksum produced by GoReleaser, extracts the cloudctl binary, and atomically replaces the running executable via github.com/minio/selfupdate. - `--check` flag performs a version check only (no download/install) - `-o json/yaml` produces machine-readable UpdateResult output - All helper functions have injectable *http.Client variants for unit tests - 13 unit tests added in cmd/update_test.go - UpdateStatus/UpdateResult types added to cmd/output - Plain and interactive printers handle the new UpdateResult type Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
There was a problem hiding this comment.
Pull request overview
Adds a new cloudctl update CLI command to self-update the running binary by querying GitHub Releases, selecting the OS/arch artifact, verifying a SHA256 checksum, and applying the update using minio/selfupdate. This fits into cloudctl as a user-facing maintenance command alongside sync, cluster-version, and version.
Changes:
- Introduces
cloudctl updatewith a--checkmode for “check only” workflows. - Adds
UpdateStatus/UpdateResultoutput types and printer support (plain + interactive). - Adds unit tests for GitHub release parsing, checksum parsing, archive extraction, and output formatting.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| go.mod | Adds indirect dependencies used by the self-update implementation. |
| go.sum | Records sums for newly introduced dependencies. |
| cmd/update.go | Implements the update command: GitHub release lookup, asset selection, checksum verification, archive extraction, and selfupdate apply. |
| cmd/update_test.go | Adds unit tests for update helpers and output formatting. |
| cmd/root.go | Registers update in help text and adds the command to the root command. |
| cmd/output/types.go | Adds UpdateStatus and UpdateResult output types. |
| cmd/output/plain_printer.go | Adds plain-text rendering for UpdateResult. |
| cmd/output/interactive_printer.go | Adds interactive/TTY rendering for UpdateResult. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…guard - Introduce updateHTTPClient with ResponseHeaderTimeout: 30s so network calls cannot hang indefinitely on stalled connections - Use updateHTTPClient in all three public wrappers (fetchLatestRelease, downloadChecksum, downloadAndExtract) instead of http.DefaultClient - Add User-Agent: cloudctl/<version> header to GitHub API requests - Guard strings.Fields()[0] in downloadChecksumFrom against empty/ whitespace-only response bodies to prevent an index-out-of-range panic - Add TestDownloadChecksum_EmptyBody unit test for the new guard Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
Replace bare `defer x.Close()` with `defer func() { _ = x.Close() }()`
on all three resp.Body and the gz.Close defers, matching the pattern
already used in cluster-version.go.
Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
- Clone http.DefaultTransport for updateHTTPClient so proxy settings, TLS config, keep-alives, and HTTP/2 support are preserved - Validate decoded checksum is exactly sha256.Size (32) bytes before use - Cap archive download at 256 MiB with io.LimitReader to prevent OOM on malicious or unexpectedly large responses - Reject non-regular tar entries (symlinks, hard links, etc.) matching the "cloudctl" name to prevent writing an invalid binary - Add tests: InvalidLength, NonRegularEntry for the new guards Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
…r branches - Cap downloadChecksumFrom body read at 4 KiB with io.LimitReader to prevent memory abuse from unexpectedly large checksum responses - Use a safe type-assertion for http.DefaultTransport clone; fall back to http.DefaultTransport as-is when it is not a *http.Transport to avoid a panic if DefaultTransport has been replaced - Add default branch to plainPrinter and interactivePrinter UpdateResult status switches so unknown status values produce visible output Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
- Use golang.org/x/mod/semver for version comparison so that current >= latest (pre-release or self-built builds) is reported as up-to-date rather than triggering an unintended downgrade; fall back to string equality when either version is not valid semver - Return a clear, actionable error on Windows (releases use .zip archives which are not yet supported) with a link to the releases page - Add Timeout: 10min to updateHTTPClient as a hard upper bound on total request time alongside ResponseHeaderTimeout for stalled header phase - Add golang.org/x/mod v0.38.0 as a direct dependency Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
…prefix, and direct deps
- Normalize rel.TagName to v-prefix so semver comparison works for tags
without a leading v (same normalization already applied to currentVersion)
- Add 128 MiB LimitReader guard when reading the cloudctl entry from tar
to prevent decompression-bomb OOM (distinct from the 256 MiB compressed cap)
- Remove "downloading checksum:" prefix from inner HTTP status error to
avoid double-wrapping ("downloading checksum: downloading checksum: HTTP 404")
- Run go mod tidy: minio/selfupdate and golang.org/x/mod are now marked
as direct dependencies since they are imported in cmd/update.go
Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
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.
Summary
cloudctl updatecommand (closes [FEAT cloudctl] - Self-update capability #53) that queries the GitHub Releases API, downloads the matching.tar.gzfor the current OS/arch, verifies the SHA256 checksum, and atomically replaces the running binary viagithub.com/minio/selfupdate--checkflag performs a version check only (no download/install); exits 0 withavailablestatus for scriptingUpdateStatus/UpdateResulttypes added tocmd/output; both plain and interactive printers handle the new typeTest plan
make buildcompiles without errorsmake test— all unit tests incmdandcmd/outputpass (13 new tests incmd/update_test.go)./bin/cloudctl update --helprenders correct help text./bin/cloudctl --helpshowsupdatein the command list./bin/cloudctl update --check(requires network) reports current vs latest version./bin/cloudctl update --check -o jsonemits machine-readableUpdateResult