-
Notifications
You must be signed in to change notification settings - Fork 352
Fix: fallback to git tags when GitHub Releases API returns empty for gh aw upgrade #23147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e125c76
0fc5c1c
b14a739
411fa0b
3571a43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -169,7 +169,7 @@ func getLatestActionRelease(repo, currentVersion string, allowMajor, verbose boo | |
| updateLog.Printf("Using base repository: %s for action: %s", baseRepo, repo) | ||
|
|
||
| // Use gh CLI to get releases | ||
| output, err := workflow.RunGHCombined("Fetching releases...", "api", fmt.Sprintf("/repos/%s/releases", baseRepo), "--jq", ".[].tag_name") | ||
| output, err := runGHReleasesAPIFn(baseRepo) | ||
| if err != nil { | ||
| // Check if this is an authentication error | ||
| outputStr := string(output) | ||
|
|
@@ -191,21 +191,34 @@ func getLatestActionRelease(repo, currentVersion string, allowMajor, verbose boo | |
|
|
||
| releases := strings.Split(strings.TrimSpace(string(output)), "\n") | ||
| if len(releases) == 0 || releases[0] == "" { | ||
| return "", "", errors.New("no releases found") | ||
| // No GitHub Releases found; fall back to tag scanning via git ls-remote. | ||
| // Some repositories publish tags without creating GitHub Releases — this is safe | ||
| // 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) | ||
| } | ||
| return latestRelease, latestSHA, nil | ||
| } | ||
|
|
||
| // Parse current version | ||
| currentVer := parseVersion(currentVersion) | ||
|
|
||
| // Find all valid semantic version releases and sort by semver | ||
| // Find all valid stable semantic version releases (skip prereleases such as v1.0.0-beta.1). | ||
| // Per semver rules, v1.1.0-beta.1 > v1.0.0, so without this filter a prerelease of a | ||
| // higher base version could be incorrectly selected as the upgrade target. | ||
| type releaseWithVersion struct { | ||
| tag string | ||
| version *semanticVersion | ||
| } | ||
| var validReleases []releaseWithVersion | ||
| for _, release := range releases { | ||
| releaseVer := parseVersion(release) | ||
| if releaseVer != nil { | ||
| if releaseVer != nil && releaseVer.pre == "" { | ||
| validReleases = append(validReleases, releaseWithVersion{ | ||
| tag: release, | ||
| version: releaseVer, | ||
|
|
@@ -225,7 +238,7 @@ func getLatestActionRelease(repo, currentVersion string, allowMajor, verbose boo | |
| // If current version is not valid, return the highest semver release | ||
| if currentVer == nil { | ||
| latestRelease := validReleases[0].tag | ||
| sha, err := getActionSHAForTag(baseRepo, latestRelease) | ||
| sha, err := getActionSHAForTagFn(baseRepo, latestRelease) | ||
| if err != nil { | ||
| return "", "", fmt.Errorf("failed to get SHA for %s: %w", latestRelease, err) | ||
| } | ||
|
|
@@ -264,7 +277,7 @@ func getLatestActionRelease(repo, currentVersion string, allowMajor, verbose boo | |
| } | ||
|
|
||
| // Get the SHA for the latest compatible release | ||
| sha, err := getActionSHAForTag(baseRepo, latestCompatible) | ||
| sha, err := getActionSHAForTagFn(baseRepo, latestCompatible) | ||
| if err != nil { | ||
| return "", "", fmt.Errorf("failed to get SHA for %s: %w", latestCompatible, err) | ||
| } | ||
|
|
@@ -319,15 +332,19 @@ func getLatestActionReleaseViaGit(repo, currentVersion string, allowMajor, verbo | |
| // Parse current version | ||
| currentVer := parseVersion(currentVersion) | ||
|
|
||
| // Find all valid semantic version releases and sort by semver | ||
| // Find all valid stable semantic version releases (skip prereleases such as v1.0.0-beta.1). | ||
| // Per semver rules, v1.1.0-beta.1 > v1.0.0, so without this filter a prerelease of a | ||
| // higher base version could be incorrectly selected as the upgrade target. | ||
| // git ls-remote --tags returns every tag, so the prerelease check is especially important | ||
| // for this fallback path. | ||
| type releaseWithVersion struct { | ||
| tag string | ||
| version *semanticVersion | ||
| } | ||
| var validReleases []releaseWithVersion | ||
| for _, release := range releases { | ||
| releaseVer := parseVersion(release) | ||
| if releaseVer != nil { | ||
| if releaseVer != nil && releaseVer.pre == "" { | ||
| validReleases = append(validReleases, releaseWithVersion{ | ||
| tag: release, | ||
| version: releaseVer, | ||
|
|
@@ -429,6 +446,20 @@ var actionRefPattern = regexp.MustCompile(`(uses:\s+)([a-zA-Z0-9][a-zA-Z0-9_-]*/ | |
| // tests to avoid network calls. | ||
| var getLatestActionReleaseFn = getLatestActionRelease | ||
|
|
||
| // 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") | ||
| } | ||
|
Comment on lines
+449
to
+457
|
||
|
|
||
| // getActionSHAForTagFn resolves the commit SHA for a given tag. It can be replaced in | ||
| // tests to avoid network calls. | ||
| var getActionSHAForTagFn = getActionSHAForTag | ||
|
|
||
| // latestReleaseResult caches a resolved version/SHA pair. | ||
| type latestReleaseResult struct { | ||
| version string | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.