From f84bbb1d8bbae5d9b1b000e231b9f139b23ee0c4 Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Fri, 30 Jan 2026 15:03:56 -0800 Subject: [PATCH] Add --dry-run support for --update to check for updates without installing --- docs/cli-reference.md | 5 +++-- integration/integration_test.go | 23 +++++++++++++++++++++++ internal/update/update.go | 24 +++++++++++++++++++----- main.go | 2 +- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 0562e23..82f5d15 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -456,7 +456,7 @@ Print detailed build information. ### `--update` -Update fetch binary in place. +Update fetch binary in place. Use with `--dry-run` to check for updates without installing. ### `--complete SHELL` @@ -469,10 +469,11 @@ fetch --complete fish > ~/.config/fish/completions/fetch.fish ### `--dry-run` -Print request information without sending. +Print request information without sending. When used with `--update`, checks for the latest version without installing. ```sh fetch --dry-run -m POST -j '{"test": true}' example.com +fetch --update --dry-run ``` ## Environment Variables diff --git a/integration/integration_test.go b/integration/integration_test.go index 351753c..89ad659 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1191,6 +1191,29 @@ func TestMain(t *testing.T) { res = runFetch(t, fetchPath, "--version") assertExitCode(t, 0, res) + // Test dry-run update when already on latest version. + newVersion.Store(&version) + dryRunModTime := getModTime(t, fetchPath) + res = runFetch(t, fetchPath, server.URL, "--update", "--dry-run") + assertExitCode(t, 0, res) + assertBufContains(t, res.stderr, "Already using the latest version") + if !getModTime(t, fetchPath).Equal(dryRunModTime) { + t.Fatal("binary was modified during dry-run update (same version)") + } + + // Test dry-run update when a new version is available. + newVersion.Store(&newStr) + dryRunModTime = getModTime(t, fetchPath) + res = runFetch(t, fetchPath, server.URL, "--update", "--dry-run") + assertExitCode(t, 0, res) + assertBufContains(t, res.stderr, "Update available") + assertBufContains(t, res.stderr, newStr) + assertBufNotContains(t, res.stderr, "Updated fetch:") + assertBufNotContains(t, res.stderr, "Downloading") + if !getModTime(t, fetchPath).Equal(dryRunModTime) { + t.Fatal("binary was modified during dry-run update") + } + // Test the auto-update functionality. res = runFetch(t, fetchPath, "--version", "--auto-update", "0s") assertExitCode(t, 0, res) diff --git a/internal/update/update.go b/internal/update/update.go index abae949..9ae5758 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -21,8 +21,8 @@ import ( // Update checks the API for the latest fetch version and upgrades the current // executable in-place, returning the exit code to use. -func Update(ctx context.Context, p *core.Printer, timeout time.Duration, silent bool) int { - err := update(ctx, p, timeout, silent) +func Update(ctx context.Context, p *core.Printer, timeout time.Duration, silent bool, dryRun bool) int { + err := update(ctx, p, timeout, silent, dryRun) if err == nil { return 0 } @@ -31,7 +31,7 @@ func Update(ctx context.Context, p *core.Printer, timeout time.Duration, silent return 1 } -func update(ctx context.Context, p *core.Printer, timeout time.Duration, silent bool) error { +func update(ctx context.Context, p *core.Printer, timeout time.Duration, silent bool, dryRun bool) error { if timeout > 0 { // Ensure the context is cancelled after the provided timeout. var cancel context.CancelFunc @@ -61,10 +61,10 @@ func update(ctx context.Context, p *core.Printer, timeout time.Duration, silent }() // Perform the update. - return updateInner(ctx, p, silent) + return updateInner(ctx, p, silent, dryRun) } -func updateInner(ctx context.Context, p *core.Printer, silent bool) error { +func updateInner(ctx context.Context, p *core.Printer, silent bool, dryRun bool) error { c := client.NewClient(client.ClientConfig{}) // Get the current executable path and verify that we have write @@ -104,6 +104,20 @@ func updateInner(ctx context.Context, p *core.Printer, silent bool) error { return nil } + if dryRun { + if !silent { + p.WriteString("Update available: ") + p.WriteString(version) + p.WriteString(" -> ") + p.Set(core.Bold) + p.WriteString(latest.TagName) + p.Reset() + p.WriteString("\n") + p.Flush() + } + return nil + } + // Look for the artifact URL for our OS and architecture. artifactURL := getArtifactURL(latest) if artifactURL == "" { diff --git a/main.go b/main.go index e3bfa87..cfadeaa 100644 --- a/main.go +++ b/main.go @@ -96,7 +96,7 @@ func main() { if app.Update { p := handle.Stderr() timeout := getValue(app.Cfg.Timeout) - status := update.Update(ctx, p, timeout, verbosity == core.VSilent) + status := update.Update(ctx, p, timeout, verbosity == core.VSilent, app.DryRun) os.Exit(status) }