Skip to content

Commit 5467402

Browse files
Copilotpelikhan
andauthored
fix: use remove+install --pin in upgrade retry path to support prerelease repos
The `gh extension upgrade --force` retry was failing with "unable to retrieve latest version for extension 'aw'" because the gh CLI uses /releases/latest internally, which returns 404 for prerelease-only repos. Change the retry to use `gh extension remove` + `gh extension install --pin VERSION` which calls fetchReleaseFromTag (not fetchLatestRelease) and works with any tag. Also extract `extensionRepo` constant to avoid duplicating "github/gh-aw". Agent-Logs-Url: https://github.com/github/gh-aw/sessions/fac75fb6-53df-4321-bf1a-374951416e41 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
1 parent cab1ac2 commit 5467402

2 files changed

Lines changed: 61 additions & 8 deletions

File tree

actions/setup-cli/install.sh

Lines changed: 25 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cli/update_extension_check.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,38 @@ func upgradeExtensionIfOutdated(verbose bool, includePrereleases bool) (bool, st
146146
}
147147
}
148148

149-
retryCmd := exec.Command("gh", extensionUpgradeArgs()...)
149+
// Retry path: remove + reinstall at the exact target version.
150+
//
151+
// Using "gh extension upgrade --force" again would call fetchLatestRelease
152+
// (/releases/latest) internally, which returns 404 for prerelease-only repos
153+
// and causes "unable to retrieve latest version for extension" errors.
154+
// Using "gh extension install --pin VERSION" instead calls fetchReleaseFromTag,
155+
// which accepts any tag (stable or prerelease).
156+
//
157+
// We must remove the extension first because "gh extension install" checks
158+
// whether the extension is already present via its manifest.yml. With the
159+
// manifest in place the install command takes the "already installed" code
160+
// path and does nothing; removing the extension clears that guard.
161+
//
162+
// Note: the backup file lives inside the extension directory, so if the
163+
// remove step succeeds the backup is also gone; we clear backupPath to
164+
// avoid a misleading restore attempt on subsequent failures.
165+
removeCmd := exec.Command("gh", "extension", "remove", extensionRepo)
166+
removeCmd.Stdout = os.Stderr
167+
removeCmd.Stderr = os.Stderr
168+
if removeErr := removeCmd.Run(); removeErr == nil {
169+
// Extension directory (and the backup inside it) has been deleted.
170+
backupPath = ""
171+
} else {
172+
updateExtensionCheckLog.Printf("Could not remove extension before reinstall (will attempt install anyway): %v", removeErr)
173+
}
174+
175+
retryCmd := exec.Command("gh", "extension", "install", extensionRepo, "--pin", latestVersion)
150176
retryCmd.Stdout = os.Stderr
151177
retryCmd.Stderr = os.Stderr
152178
if retryErr := retryCmd.Run(); retryErr != nil {
153-
// Retry also failed. Restore the backup so the user still has gh-aw.
179+
// Retry also failed. Restore the backup so the user still has gh-aw
180+
// (only possible when the remove step above did not succeed).
154181
if backupPath != "" {
155182
restoreExecutableBackup(installPath, backupPath)
156183
}
@@ -162,12 +189,13 @@ func upgradeExtensionIfOutdated(verbose bool, includePrereleases bool) (bool, st
162189
fmt.Fprintln(os.Stderr, " gh extension upgrade gh-aw")
163190
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("If that does not work, try reinstalling:"))
164191
fmt.Fprintln(os.Stderr, " gh extension remove gh-aw")
165-
fmt.Fprintln(os.Stderr, " gh extension install github/gh-aw")
192+
fmt.Fprintln(os.Stderr, " gh extension install "+extensionRepo)
166193
}
167194
return false, "", fmt.Errorf("failed to upgrade gh-aw extension: %w", retryErr)
168195
}
169196

170-
// Retry succeeded. Clean up the backup.
197+
// Retry succeeded. Clean up the backup if it still exists
198+
// (it will be gone when the remove step above succeeded).
171199
if backupPath != "" {
172200
cleanupExecutableBackup(backupPath)
173201
}
@@ -262,5 +290,8 @@ func isWindowsLockError(output string, err error) bool {
262290
// --force is required so pinned installs (e.g. `gh extension install ... --pin`)
263291
// can be upgraded in-place.
264292
func extensionUpgradeArgs() []string {
265-
return []string{"extension", "upgrade", "github/gh-aw", "--force"}
293+
return []string{"extension", "upgrade", extensionRepo, "--force"}
266294
}
295+
296+
// extensionRepo is the GitHub repo slug used in all gh-extension CLI invocations.
297+
const extensionRepo = "github/gh-aw"

0 commit comments

Comments
 (0)