Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

ejahnGithub
Copy link
Contributor

per https://github.com/cli/cli/pull/11451/files

This pull request improves the error message and test coverage of the FetchRefSHA in the release package. The main changes include enhanced error handling for malformed or incomplete responses and the addition of comprehensive unit tests to verify various scenarios.

@Copilot Copilot AI review requested due to automatic review settings August 8, 2025 21:33
@ejahnGithub ejahnGithub requested a review from a team as a code owner August 8, 2025 21:33
@cliAutomation cliAutomation added the external pull request originating outside of the CLI core team label Aug 8, 2025
@cliAutomation
Copy link
Collaborator

Hi! Thanks for the pull request. Please ensure that this change is linked to an issue by mentioning an issue number in the description of the pull request. If this pull request would close the issue, please put the word 'Fixes' before the issue number somewhere in the pull request body. If this is a tiny change like fixing a typo, feel free to ignore this message.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request adds comprehensive unit tests for the FetchRefSHA function in the release package and improves error handling for malformed or incomplete API responses. The changes ensure better test coverage and more informative error messages when parsing Git ref responses fails.

  • Added comprehensive unit tests covering various tag formats (semver, prerelease, partial versions) and error scenarios
  • Enhanced error handling with more descriptive error messages for JSON parsing failures
  • Added validation for empty SHA values in successful API responses

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pkg/cmd/release/shared/fetch_test.go New test file with comprehensive test cases for FetchRefSHA function covering success and error scenarios
pkg/cmd/release/shared/fetch.go Improved error handling with better error messages and validation for empty SHA responses

var ref struct {
Object struct {
SHA string `json:"sha"`
SHA string `json:"sha,omitempty"`
Copy link

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The omitempty tag is unnecessary here since the struct is only used for unmarshaling JSON responses. The omitempty tag affects marshaling behavior, not unmarshaling, so it doesn't provide any benefit in this context.

Suggested change
SHA string `json:"sha,omitempty"`
SHA string `json:"sha"`

Copilot uses AI. Check for mistakes.

@BagToad
Copy link
Member

BagToad commented Aug 12, 2025

Hey @ejahnGithub! πŸ‘‹

I've been working on this behind the scenes discussing with @babakks because I wasn't fully understanding the comments on the other PR.

Now I think we understand the situation, have some corrections to make from comments on that PR, and we have a suggestion to improve this πŸŽ‰

Please let me know what you think πŸ™

Corrections and clarifications

@babakks mentioned:

This change also affects other release subcmds.

But we looked at it together and found that was a mistake - this only affects release verify πŸ‘

image

@ejahnGithub, you mentioned:

When fetching the ref SHA of a tag, a partial semver match returns a 200 response with an empty body, which causes the JSON decode to fail.
For example, if the tag is v1.0.0, only refs/tags/v1.0.0 will work β€” v1 or v1.0 will return 200 with empty body.

But we weren't able to see this API behavior. What we saw instead is that the /git/refs/tags/<tagname> endpoint currently in use returns a 200 response with an array of partial matches - this would still cause the JSON unmarshalling to error of course.

To show it better, here is an example:

Partial lookup

❯ gh -X GET api repos/cli/cli/git/refs/tags/v2.75             
[
  {
    "ref": "refs/tags/v2.75.0",
    "node_id": "MDM6UmVmMjEyNjEzMDQ5OnJlZnMvdGFncy92Mi43NS4w",
    "url": "https://api.github.com/repos/cli/cli/git/refs/tags/v2.75.0",
    "object": {
      "sha": "0e27e840846bbdbda8308c419cedf95914556511",
      "type": "commit",
      "url": "https://api.github.com/repos/cli/cli/git/commits/0e27e840846bbdbda8308c419cedf95914556511"
    }
  },
  {
    "ref": "refs/tags/v2.75.1",
    "node_id": "MDM6UmVmMjEyNjEzMDQ5OnJlZnMvdGFncy92Mi43NS4x",
    "url": "https://api.github.com/repos/cli/cli/git/refs/tags/v2.75.1",
    "object": {
      "sha": "dbff7c5655863cf1c1bf443fe8e41195e0074282",
      "type": "commit",
      "url": "https://api.github.com/repos/cli/cli/git/commits/dbff7c5655863cf1c1bf443fe8e41195e0074282"
    }
  }
]

Exact lookup

❯ gh -X GET api repos/cli/cli/git/refs/tags/v2.75.0
{
  "ref": "refs/tags/v2.75.0",
  "node_id": "MDM6UmVmMjEyNjEzMDQ5OnJlZnMvdGFncy92Mi43NS4w",
  "url": "https://api.github.com/repos/cli/cli/git/refs/tags/v2.75.0",
  "object": {
    "sha": "0e27e840846bbdbda8308c419cedf95914556511",
    "type": "commit",
    "url": "https://api.github.com/repos/cli/cli/git/commits/0e27e840846bbdbda8308c419cedf95914556511"
  }
}

Suggestions

We found a different endpoint, /git/ref/tags/<tagname>, which we think might work better here because it returns no partial matches. It either returns a 404 or a exact tagname match.

Partial lookup

❯ gh -X GET api repos/cli/cli/git/ref/tags/v2.75 
{
  "message": "Not Found",
  "documentation_url": "https://docs.github.com/rest/git/refs#get-a-reference",
  "status": "404"
}
gh: Not Found (HTTP 404)

Exact lookup

❯ gh -X GET api repos/cli/cli/git/ref/tags/v2.75.0
{
  "ref": "refs/tags/v2.75.0",
  "node_id": "MDM6UmVmMjEyNjEzMDQ5OnJlZnMvdGFncy92Mi43NS4w",
  "url": "https://api.github.com/repos/cli/cli/git/refs/tags/v2.75.0",
  "object": {
    "sha": "0e27e840846bbdbda8308c419cedf95914556511",
    "type": "commit",
    "url": "https://api.github.com/repos/cli/cli/git/commits/0e27e840846bbdbda8308c419cedf95914556511"
  }
}

If we use this endpoint, we would be able to avoid hiding the JSON unmarshalling error, while still allowing the case of a partial lookup to return a nice release not found error instead of failing to unmarshal πŸ”₯ It makes the output of the API more predictable.

diff --git a/pkg/cmd/release/shared/fetch.go b/pkg/cmd/release/shared/fetch.go
index f25c17807..4fd3bcbcd 100644
--- a/pkg/cmd/release/shared/fetch.go
+++ b/pkg/cmd/release/shared/fetch.go
@@ -136,7 +136,7 @@ type fetchResult struct {
 }
 
 func FetchRefSHA(ctx context.Context, httpClient *http.Client, repo ghrepo.Interface, tagName string) (string, error) {
-	path := fmt.Sprintf("repos/%s/%s/git/refs/tags/%s", repo.RepoOwner(), repo.RepoName(), tagName)
+	path := fmt.Sprintf("repos/%s/%s/git/ref/tags/%s", repo.RepoOwner(), repo.RepoName(), tagName)
 	req, err := http.NewRequestWithContext(ctx, "GET", ghinstance.RESTPrefix(repo.RepoHost())+path, nil)
 	if err != nil {
 		return "", err
@@ -163,9 +163,9 @@ func FetchRefSHA(ctx context.Context, httpClient *http.Client, repo ghrepo.Inter
 			SHA string `json:"sha"`
 		} `json:"object"`
 	}
+
 	if err := json.NewDecoder(resp.Body).Decode(&ref); err != nil {
-		// release not found
-		return "", ErrReleaseNotFound
+		return "", err
 	}
 
 	return ref.Object.SHA, nil

Note

This change as-is causes test failures that are mocking the /git/refs/tags/<tagname> endpoint.

Copy link
Member

@babakks babakks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, @ejahnGithub! πŸ™ I really like the new implementation and it resolves the concerns we had.

I just had to request for a couple of changes, all regarding the newly added tests which are no longer relevant since the API endpoint we're using now doesn't perform any partial matching.

Comment on lines 15 to 22
tests := []struct {
name string
tagName string
responseStatus int
responseBody string
expectedSHA string
errorMessage string
}{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the unit test here! πŸ™

However, I think the current test cases are no longer needed/relevant. The endpoint we're now calling (i.e. repos/onwer/repo/git/ref/tags/[tag]) does not perform any partial matching. The tag is also not required to be in semver format.

So, I can imagine we only need these test cases:

  • a match (200)
  • a non-match (404)
  • a 500
  • a malformed JSON with 200
    • This never happens, but it should be there for covering all code paths.

Comment on lines 95 to 96
require.Error(t, err)
assert.EqualError(t, err, tt.errorMessage)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two points:

  • Only the assert.EqualError is enough (it also checks the error is not nil).
  • We need to assert sha is an empty string.
Suggested change
require.Error(t, err)
assert.EqualError(t, err, tt.errorMessage)
require.EqualError(t, err, tt.errorMessage)
assert.Empty(t, sha)


path := "repos/owner/repo/git/ref/tags/" + tt.tagName
if tt.responseStatus == 404 {
fakeHTTP.Register(httpmock.REST("GET", path), httpmock.StatusStringResponse(404, "Not Found"))
Copy link
Member

@babakks babakks Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The response body is actually like this:

{
  "message": "Not Found",
  "documentation_url": "https://docs.github.com/rest/git/refs#get-a-reference",
  "status": "404"
}

You can use httpmock.JSONErrorResponse helper function to generate a valid API error value.

@ejahnGithub ejahnGithub requested a review from babakks August 13, 2025 18:47
Copy link
Member

@babakks babakks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks. πŸ™

@ejahnGithub ejahnGithub enabled auto-merge August 14, 2025 18:53
Copy link
Member

@BagToad BagToad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good from cli team side πŸ˜ƒ

This is good to merge whenever package security can review it, I think, @ejahnGithub

Copy link
Contributor

@bdehamer bdehamer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ejahnGithub ejahnGithub merged commit 38a00b8 into trunk Aug 14, 2025
10 of 11 checks passed
@ejahnGithub ejahnGithub deleted the eugene/test-for-release-FetchRefSHA branch August 14, 2025 19:31
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Aug 22, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [cli/cli](https://github.com/cli/cli) | minor | `v2.76.2` -> `v2.78.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>cli/cli (cli/cli)</summary>

### [`v2.78.0`](https://github.com/cli/cli/releases/tag/v2.78.0): GitHub CLI 2.78.0

[Compare Source](cli/cli@v2.77.0...v2.78.0)

#### ℹ️ Note

This release was cut primarily to resolve a Linux package distribution issue. We recommend reviewing [the v2.77.0 release notes](https://github.com/cli/cli/releases/tag/v2.77.0) for the complete set of latest features and fixes.

#### What's Changed

##### ✨ Features

- Add `--force` flag to `gh run cancel` by [@&#8203;ankddev](https://github.com/ankddev) in [#&#8203;11513](cli/cli#11513)

##### πŸ› Fixes

- Fix failing to release Linux packages (affected v2.77.0). See [v2.77.0](https://github.com/cli/cli/releases/tag/v2.77.0) for more information.

**Full Changelog**: <cli/cli@v2.77.0...v2.78.0>

### [`v2.77.0`](https://github.com/cli/cli/releases/tag/v2.77.0): GitHub CLI 2.77.0

[Compare Source](cli/cli@v2.76.2...v2.77.0)

#### ⚠️ Incomplete Release

The v2.77.0 release experienced a failure publishing to our official Linux repos. This is resolved in [v2.78.0](https://github.com/cli/cli/releases/tag/v2.78.0), so we recommend using that release instead.

#### What's Changed

##### ✨ Features

- Report that v1 classic projects are detected on GHES 3.16.x or older by [@&#8203;andyfeller](https://github.com/andyfeller) in [#&#8203;11491](cli/cli#11491)
- Display v2 projects in `gh issue view` by [@&#8203;andyfeller](https://github.com/andyfeller) in [#&#8203;11496](cli/cli#11496)
- View v2 projects in `gh pr view` output by [@&#8203;andyfeller](https://github.com/andyfeller) in [#&#8203;11497](cli/cli#11497)
- Ensure users can see v2 projects when viewing issues and MRs, avoid v1 projects on GHES 3.17 and newer by [@&#8203;andyfeller](https://github.com/andyfeller) in [#&#8203;11514](cli/cli#11514)

##### πŸ› Fixes

- fix error for ErrReleaseNotFound when fetching ref by [@&#8203;ejahnGithub](https://github.com/ejahnGithub) in [#&#8203;11451](cli/cli#11451)
- add test for FetchRefSHA by [@&#8203;ejahnGithub](https://github.com/ejahnGithub) in [#&#8203;11481](cli/cli#11481)
- Fix `gh repo delete --yes` safety issue when no repository argument provided by [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;11536](cli/cli#11536)

##### πŸ“š Docs & Chores

- Improve spam detection evals by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;11419](cli/cli#11419)
- Fix `help wanted` label regexp in CI automation by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;11423](cli/cli#11423)
- Update spam detection to comment on and close issue by [@&#8203;andyfeller](https://github.com/andyfeller) in [#&#8203;11435](cli/cli#11435)
- Adding a note to `gh search` docs to explain the usage of `--` to exclude certain results by [@&#8203;Sukhpreet-s](https://github.com/Sukhpreet-s) in [#&#8203;11162](cli/cli#11162)
- Update issue triage guidelines and label usage by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;11454](cli/cli#11454)
- Reorganize installation docs by [@&#8203;andyfeller](https://github.com/andyfeller) in [#&#8203;11473](cli/cli#11473)
- Update govulncheck workflow to scan source code by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;11482](cli/cli#11482)
- Hidden trusted root flag for release verify by [@&#8203;ejahnGithub](https://github.com/ejahnGithub) in [#&#8203;11511](cli/cli#11511)

##### :dependabot: Dependencies

- Regenerate third-party licenses on trunk pushes by [@&#8203;andyfeller](https://github.com/andyfeller) in [#&#8203;11370](cli/cli#11370)
- Update third-party license versions by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;11557](cli/cli#11557)
- Bump Go to 1.24.6 by [@&#8203;github-actions](https://github.com/github-actions)\[bot] in [#&#8203;11467](cli/cli#11467)
- chore(deps): bump github.com/spf13/pflag from 1.0.6 to 1.0.7 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;11319](cli/cli#11319)
- chore(deps): bump actions/download-artifact from 4 to 5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;11458](cli/cli#11458)
- chore(deps): bump actions/checkout from 4 to 5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;11490](cli/cli#11490)
- chore(deps): bump github.com/yuin/goldmark from 1.7.12 to 1.7.13 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;11368](cli/cli#11368)
- Bump google.golang.org/grpc & other required dependencies by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;11510](cli/cli#11510)
- chore(deps): bump google.golang.org/grpc from 1.73.0 to 1.74.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;11367](cli/cli#11367)
- chore(deps): bump github.com/cli/go-gh/v2 from 2.12.1 to 2.12.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;11537](cli/cli#11537)
- chore(deps): bump github.com/go-viper/mapstructure/v2 from 2.3.0 to 2.4.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;11556](cli/cli#11556)

#### New Contributors

- [@&#8203;Sukhpreet-s](https://github.com/Sukhpreet-s) made their first contribution in [#&#8203;11162](cli/cli#11162)
- [@&#8203;Copilot](https://github.com/Copilot) made their first contribution in [#&#8203;11536](cli/cli#11536)

**Full Changelog**: <cli/cli@v2.76.2...v2.77.0>

</details>

---

### Configuration

πŸ“… **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

β™» **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

πŸ”• **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi4xIiwidXBkYXRlZEluVmVyIjoiNDEuODIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90Il19-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external pull request originating outside of the CLI core team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants