Fix: fallback to git tags when GitHub Releases API returns empty for gh aw upgrade#23147
Fix: fallback to git tags when GitHub Releases API returns empty for gh aw upgrade#23147
Conversation
When the GitHub Releases API returns an empty list for a repository, getLatestActionRelease now falls back to git ls-remote tag scanning (getLatestActionReleaseViaGitFn) instead of returning an error. This fixes the spurious 'github/gh-aw-actions/setup: no releases found' warning during 'gh aw upgrade' for repos that publish tags without GitHub Releases. - Add runGHReleasesAPIFn variable to make API call testable - Add getLatestActionReleaseViaGitFn variable for testability - Emit verbose info message when tag fallback is used - Only error when both sources find nothing - Add unit tests exercising the real fallback code path - Add FAQ entry documenting the Releases vs tags resolution logic Agent-Logs-Url: https://github.com/github/gh-aw/sessions/793935d9-1e6b-4fee-97bc-5ce82a95bb0d Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
@copilot make sure to filter tags according to semver rules. |
There was a problem hiding this comment.
Pull request overview
Fixes gh aw upgrade / gh aw update-actions behavior when the GitHub Releases API returns a successful but empty result by falling back to resolving versions from git tags.
Changes:
- Add a “no releases returned” fallback to
git ls-remotetag scanning when the Releases API response is empty. - Introduce injectable function variables for the Releases API call and git-tag fallback to enable deterministic unit tests.
- Add tests and document the two-step resolution behavior in the FAQ.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| pkg/cli/update_actions.go | Adds empty-success Releases API fallback to git tag scanning; introduces injectable function variables. |
| pkg/cli/update_actions_test.go | Adds tests covering empty API response fallback success and failure cases. |
| docs/src/content/docs/reference/faq.md | Documents the two-step resolution (Releases API → git tag scan fallback). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // to use and the warning below is informational only. | ||
| updateLog.Printf("No releases found via GitHub API for %s, falling back to git ls-remote tag scan", baseRepo) | ||
| if verbose { | ||
| fmt.Fprintln(os.Stderr, console.FormatInfoMessage(baseRepo+": no GitHub Releases found, falling back to tag scanning (safe to ignore)")) | ||
| } | ||
| latestRelease, latestSHA, gitErr := getLatestActionReleaseViaGitFn(repo, currentVersion, allowMajor, verbose) | ||
| if gitErr != nil { | ||
| return "", "", fmt.Errorf("no releases or tags found for %s: %w", baseRepo, gitErr) | ||
| } |
There was a problem hiding this comment.
The verbose info message says the tag-scan fallback is “safe to ignore” before the git fallback has actually succeeded. If the subsequent git ls-remote call fails, users will see a reassuring message immediately followed by an error, which is misleading. Consider moving the info message until after a successful git fallback, or reword it to something like “attempting tag scan fallback…” and only state it’s safe once a tag was found.
| // to use and the warning below is informational only. | |
| updateLog.Printf("No releases found via GitHub API for %s, falling back to git ls-remote tag scan", baseRepo) | |
| if verbose { | |
| fmt.Fprintln(os.Stderr, console.FormatInfoMessage(baseRepo+": no GitHub Releases found, falling back to tag scanning (safe to ignore)")) | |
| } | |
| latestRelease, latestSHA, gitErr := getLatestActionReleaseViaGitFn(repo, currentVersion, allowMajor, verbose) | |
| if gitErr != nil { | |
| return "", "", fmt.Errorf("no releases or tags found for %s: %w", baseRepo, gitErr) | |
| } | |
| // to use when the tag-scan fallback succeeds; the messages below are informational. | |
| updateLog.Printf("No releases found via GitHub API for %s, falling back to git ls-remote tag scan", baseRepo) | |
| if verbose { | |
| fmt.Fprintln(os.Stderr, console.FormatInfoMessage(baseRepo+": no GitHub Releases found, attempting tag scan fallback via git ls-remote")) | |
| } | |
| latestRelease, latestSHA, gitErr := getLatestActionReleaseViaGitFn(repo, currentVersion, allowMajor, verbose) | |
| if gitErr != nil { | |
| return "", "", fmt.Errorf("no releases or tags found for %s: %w", baseRepo, gitErr) | |
| } | |
| if verbose { | |
| fmt.Fprintln(os.Stderr, console.FormatInfoMessage(baseRepo+": tag scan fallback succeeded; using latest tag-based version (safe to ignore missing GitHub Releases)")) | |
| } |
| // getLatestActionReleaseViaGitFn is the function used to fetch the latest release via git | ||
| // ls-remote as a fallback. It can be replaced in tests to avoid network calls. | ||
| var getLatestActionReleaseViaGitFn = getLatestActionReleaseViaGit | ||
|
|
||
| // runGHReleasesAPIFn calls the GitHub Releases API for the given base repository and | ||
| // returns the raw output. It can be replaced in tests to avoid network calls. | ||
| var runGHReleasesAPIFn = func(baseRepo string) ([]byte, error) { | ||
| return workflow.RunGHCombined("Fetching releases...", "api", fmt.Sprintf("/repos/%s/releases", baseRepo), "--jq", ".[].tag_name") | ||
| } |
There was a problem hiding this comment.
getLatestActionReleaseViaGitFn is introduced as an injectable wrapper for the git ls-remote fallback, but getLatestActionRelease still calls getLatestActionReleaseViaGit directly in the auth-error fallback path. This makes the injection incomplete and prevents tests from stubbing the git fallback consistently. Consider routing all git fallback call sites through getLatestActionReleaseViaGitFn.
…selection Both getLatestActionRelease (GitHub API path) and getLatestActionReleaseViaGit (git tag fallback) now skip prerelease versions (e.g. v1.1.0-beta.1) when building the valid-releases list. Per semver rules, v1.1.0-beta.1 > v1.0.0 (the base version is higher), so without this filter a prerelease tag could be incorrectly selected as the upgrade target. git ls-remote --tags returns every tag, making the fallback path particularly exposed to this. Also add getActionSHAForTagFn as a replaceable function variable (mirrors the existing pattern) to enable end-to-end testing without network calls. Adds TestGetLatestActionRelease_PrereleaseTagsSkipped to assert that v1.1.0-beta.1 is skipped in favour of the stable v1.0.0. Agent-Logs-Url: https://github.com/github/gh-aw/sessions/02389988-6d87-487d-a93f-e4a06189744d Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in commit 411fa0b. Both |
gh aw upgradeemittedgithub/gh-aw-actions/setup: no releases foundfor repos that publish tags without creating GitHub Releases (includinggithub/gh-aw-actionsitself). The auth-error fallback togit ls-remoteexisted but was never triggered for an empty-but-successful API response.Changes
pkg/cli/update_actions.go: When the Releases API returns empty (no error), fall back togetLatestActionReleaseViaGitFn(tag scanning) instead of returning an error. Only surfaces an error if both sources find nothing. Emits a verbose-only info message when the fallback is used.Extracted the API call into
runGHReleasesAPIFn, addedgetLatestActionReleaseViaGitFnandgetActionSHAForTagFnas replaceable function variables (mirrors the existinggetLatestActionReleaseFnpattern).Both
getLatestActionRelease(GitHub Releases API path) andgetLatestActionReleaseViaGit(git tag fallback) now filter out prerelease versions (e.g.v1.1.0-beta.1) when selecting the upgrade target. Per semver rules, a prerelease of a higher base version is considered "newer" than a stable release of a lower version, so without this filter a prerelease tag could be incorrectly selected. Thegit ls-remote --tagsfallback path is especially exposed since it returns every tag.pkg/cli/update_actions_test.go: Three new tests exercising the realgetLatestActionReleasecode path viarunGHReleasesAPIFnstub — fallback succeeds when tags exist, error propagates when both sources fail, and prerelease tags are skipped in favour of stable releases.docs/src/content/docs/reference/faq.md: Added FAQ entry documenting the two-step resolution (Releases API → git tag fallback) and clarifying that the previously-seen warning is fixed.⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.