Merge branch 'trunk' into gh-attestation-download-windows-bug

This commit is contained in:
Meredith Lancaster 2024-12-10 08:25:31 -07:00
commit fb7f2bfea2
14 changed files with 283 additions and 47 deletions

View file

@ -111,6 +111,25 @@ func TestVerifyIntegration(t *testing.T) {
require.Error(t, err)
require.ErrorContains(t, err, "verifying with issuer \"sigstore.dev\"")
})
t.Run("with bundle from OCI registry", func(t *testing.T) {
opts := Options{
APIClient: api.NewLiveClient(hc, host, logger),
ArtifactPath: "oci://ghcr.io/github/artifact-attestations-helm-charts/policy-controller:v0.10.0-github9",
UseBundleFromRegistry: true,
DigestAlgorithm: "sha256",
Logger: logger,
OCIClient: oci.NewLiveClient(),
OIDCIssuer: verification.GitHubOIDCIssuer,
Owner: "github",
PredicateType: verification.SLSAPredicateV1,
SANRegex: "^https://github.com/github/",
SigstoreVerifier: verification.NewLiveSigstoreVerifier(sigstoreConfig),
}
err := runVerify(&opts)
require.NoError(t, err)
})
}
func TestVerifyIntegrationCustomIssuer(t *testing.T) {

View file

@ -59,10 +59,18 @@ func NewCmdBrowse(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Co
}
cmd := &cobra.Command{
Long: "Open the GitHub repository in the web browser.",
Short: "Open the repository in the browser",
Use: "browse [<number> | <path> | <commit-SHA>]",
Args: cobra.MaximumNArgs(1),
Short: "Open repositories, issues, pull requests, and more in the browser",
Long: heredoc.Doc(`
Transition from the terminal to the web browser to view and interact with:
- Issues
- Pull requests
- Repository content
- Repository home page
- Repository settings
`),
Use: "browse [<number> | <path> | <commit-SHA>]",
Args: cobra.MaximumNArgs(1),
Example: heredoc.Doc(`
$ gh browse
#=> Open the home page of the current repository

View file

@ -44,6 +44,13 @@ func NewCmdDevelop(f *cmdutil.Factory, runF func(*DevelopOptions) error) *cobra.
cmd := &cobra.Command{
Use: "develop {<number> | <url>}",
Short: "Manage linked branches for an issue",
Long: heredoc.Docf(`
Manage linked branches for an issue.
When using the %[1]s--base%[1]s flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
%[1]sgh pr create%[1]s.
`, "`"),
Example: heredoc.Doc(`
# List branches for issue 123
$ gh issue develop --list 123
@ -171,6 +178,14 @@ func developRunCreate(opts *DevelopOptions, apiClient *api.Client, issueRepo ghr
return err
}
// Remember which branch to target when creating a PR.
if opts.BaseBranch != "" {
err = opts.GitClient.SetBranchConfig(ctx.Background(), branchName, git.MergeBaseConfig, opts.BaseBranch)
if err != nil {
return err
}
}
fmt.Fprintf(opts.IO.Out, "%s/%s/tree/%s\n", branchRepo.RepoHost(), ghrepo.FullName(branchRepo), branchName)
return checkoutBranch(opts, branchRepo, branchName)

View file

@ -399,6 +399,7 @@ func TestDevelopRun(t *testing.T) {
},
runStubs: func(cs *run.CommandStubber) {
cs.Register(`git fetch origin \+refs/heads/my-branch:refs/remotes/origin/my-branch`, 0, "")
cs.Register(`git config branch\.my-branch\.gh-merge-base main`, 0, "")
},
expectedOut: "github.com/OWNER/REPO/tree/my-branch\n",
},

View file

@ -77,6 +77,10 @@ func NewCmdChecks(f *cmdutil.Factory, runF func(*ChecksOptions) error) *cobra.Co
RunE: func(cmd *cobra.Command, args []string) error {
opts.Finder = shared.NewFinder(f)
if opts.Exporter != nil && opts.Watch {
return cmdutil.FlagErrorf("cannot use `--watch` with `--json` flag")
}
if repoOverride, _ := cmd.Flags().GetString("repo"); repoOverride != "" && len(args) == 0 {
return cmdutil.FlagErrorf("argument required when using the `--repo` flag")
}

View file

@ -78,6 +78,11 @@ func TestNewCmdChecks(t *testing.T) {
cli: "--fail-fast",
wantsError: "cannot use `--fail-fast` flag without `--watch` flag",
},
{
name: "watch with json flag",
cli: "--watch --json workflow",
wantsError: "cannot use `--watch` with `--json` flag",
},
{
name: "required flag",
cli: "--required",
@ -171,7 +176,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
Some checks were not successful
0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
X sad tests 1m26s sweet link
cool tests 1m26s sweet link
@ -191,7 +196,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
Some checks were cancelled
1 cancelled, 0 failing, 2 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
- sad tests 1m26s sweet link
@ -211,7 +216,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
Some checks are still pending
1 cancelled, 0 failing, 2 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
rad tests 1m26s sweet link
@ -232,7 +237,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
awesome tests 1m26s sweet link
cool tests 1m26s sweet link
@ -253,7 +258,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Docf(`
%[1]s[?1049hAll checks were successful
0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
awesome tests 1m26s sweet link
cool tests 1m26s sweet link
@ -281,17 +286,17 @@ func Test_checksRun(t *testing.T) {
},
wantOut: heredoc.Docf(`
%[1]s[?1049h%[1]s[0;0H%[1]s[JRefreshing checks status every 0 seconds. Press Ctrl+C to quit.
Some checks were not successful
0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
X sad tests 1m26s sweet link
cool tests 1m26s sweet link
* slow tests 1m26s sweet link
%[1]s[?1049lSome checks were not successful
0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
X sad tests 1m26s sweet link
cool tests 1m26s sweet link
@ -311,7 +316,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
Some checks were not successful
0 cancelled, 1 failing, 2 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
X a status sweet link
cool tests 1m26s sweet link
@ -397,7 +402,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 1 successful, 2 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
- rad tests 1m26s sweet link
@ -429,7 +434,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 1 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
`),
@ -484,7 +489,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
awesome tests awesome description 1m26s sweet link
cool tests cool description 1m26s sweet link
@ -515,7 +520,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 2 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
tests/cool tests (pull_request) cool description 1m26s sweet link
tests/cool tests (push) cool description 1m26s sweet link
@ -535,7 +540,7 @@ func Test_checksRun(t *testing.T) {
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 1 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
tests/cool tests cool description 1m26s sweet link
`),

View file

@ -119,6 +119,10 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
alongside %[1]s--fill%[1]s, the values specified by %[1]s--title%[1]s and/or %[1]s--body%[1]s will
take precedence and overwrite any autofilled content.
The base branch for the created PR can be specified using the %[1]s--base%[1]s flag. If not provided,
the value of %[1]sgh-merge-base%[1]s git branch config will be used. If not configured, the repository's
default branch will be used.
Link an issue to the pull request by referencing the issue in the body of the pull
request. If the body text mentions %[1]sFixes #123%[1]s or %[1]sCloses #123%[1]s, the referenced issue
will automatically get closed when the pull request gets merged.
@ -513,11 +517,10 @@ func initDefaultTitleBody(ctx CreateContext, state *shared.IssueMetadataState, u
return nil
}
func determineTrackingBranch(gitClient *git.Client, remotes ghContext.Remotes, headBranch string) *git.TrackingRef {
func determineTrackingBranch(gitClient *git.Client, remotes ghContext.Remotes, headBranchConfig *git.BranchConfig) *git.TrackingRef {
refsForLookup := []string{"HEAD"}
var trackingRefs []git.TrackingRef
headBranchConfig := gitClient.ReadBranchConfig(context.Background(), headBranch)
if headBranchConfig.RemoteName != "" {
tr := git.TrackingRef{
RemoteName: headBranchConfig.RemoteName,
@ -530,7 +533,7 @@ func determineTrackingBranch(gitClient *git.Client, remotes ghContext.Remotes, h
for _, remote := range remotes {
tr := git.TrackingRef{
RemoteName: remote.Name,
BranchName: headBranch,
BranchName: headBranchConfig.LocalName,
}
trackingRefs = append(trackingRefs, tr)
refsForLookup = append(refsForLookup, tr.String())
@ -640,9 +643,10 @@ func NewCreateContext(opts *CreateOptions) (*CreateContext, error) {
var headRepo ghrepo.Interface
var headRemote *ghContext.Remote
headBranchConfig := gitClient.ReadBranchConfig(context.Background(), headBranch)
if isPushEnabled {
// determine whether the head branch is already pushed to a remote
if pushedTo := determineTrackingBranch(gitClient, remotes, headBranch); pushedTo != nil {
if pushedTo := determineTrackingBranch(gitClient, remotes, &headBranchConfig); pushedTo != nil {
isPushEnabled = false
if r, err := remotes.FindByName(pushedTo.RemoteName); err == nil {
headRepo = r
@ -715,6 +719,9 @@ func NewCreateContext(opts *CreateOptions) (*CreateContext, error) {
}
baseBranch := opts.BaseBranch
if baseBranch == "" {
baseBranch = headBranchConfig.MergeBase
}
if baseBranch == "" {
baseBranch = baseRepo.DefaultBranchRef.Name
}

View file

@ -1,6 +1,7 @@
package create
import (
ctx "context"
"encoding/json"
"errors"
"fmt"
@ -261,6 +262,15 @@ func TestNewCmdCreate(t *testing.T) {
cli: "--editor",
wantsErr: true,
},
{
name: "fill and base",
cli: "--fill --base trunk",
wantsOpts: CreateOptions{
Autofill: true,
BaseBranch: "trunk",
MaintainerCanModify: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -323,17 +333,18 @@ func TestNewCmdCreate(t *testing.T) {
func Test_createRun(t *testing.T) {
tests := []struct {
name string
setup func(*CreateOptions, *testing.T) func()
cmdStubs func(*run.CommandStubber)
promptStubs func(*prompter.PrompterMock)
httpStubs func(*httpmock.Registry, *testing.T)
expectedOutputs []string
expectedOut string
expectedErrOut string
expectedBrowse string
wantErr string
tty bool
name string
setup func(*CreateOptions, *testing.T) func()
cmdStubs func(*run.CommandStubber)
promptStubs func(*prompter.PrompterMock)
httpStubs func(*httpmock.Registry, *testing.T)
expectedOutputs []string
expectedOut string
expectedErrOut string
expectedBrowse string
wantErr string
tty bool
customBranchConfig bool
}{
{
name: "nontty web",
@ -626,7 +637,6 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
},
@ -690,7 +700,6 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
},
@ -737,7 +746,6 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
},
@ -787,7 +795,6 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register("git remote rename origin upstream", 0, "")
cs.Register(`git remote add origin https://github.com/monalisa/REPO.git`, 0, "")
@ -846,7 +853,6 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp \^branch\\\.feature\\\.`, 1, "") // determineTrackingBranch
cs.Register("git show-ref --verify", 0, heredoc.Doc(`
deadbeef HEAD
deadb00f refs/remotes/upstream/feature
@ -878,6 +884,7 @@ func Test_createRun(t *testing.T) {
assert.Equal(t, "my-feat2", input["headRefName"].(string))
}))
},
customBranchConfig: true,
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp \^branch\\\.feature\\\.`, 0, heredoc.Doc(`
branch.feature.remote origin
@ -1066,7 +1073,6 @@ func Test_createRun(t *testing.T) {
httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
@ -1099,7 +1105,6 @@ func Test_createRun(t *testing.T) {
mockRetrieveProjects(t, reg)
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
@ -1464,6 +1469,65 @@ func Test_createRun(t *testing.T) {
},
expectedOut: "https://github.com/OWNER/REPO/pull/12\n",
},
{
name: "gh-merge-base",
tty: true,
setup: func(opts *CreateOptions, t *testing.T) func() {
opts.TitleProvided = true
opts.BodyProvided = true
opts.Title = "my title"
opts.Body = "my body"
opts.Branch = func() (string, error) {
return "task1", nil
}
opts.Remotes = func() (context.Remotes, error) {
return context.Remotes{
{
Remote: &git.Remote{
Name: "upstream",
Resolved: "base",
},
Repo: ghrepo.New("OWNER", "REPO"),
},
{
Remote: &git.Remote{
Name: "origin",
},
Repo: ghrepo.New("monalisa", "REPO"),
},
}, nil
}
return func() {}
},
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
reg.Register(
httpmock.GraphQL(`mutation PullRequestCreate\b`),
httpmock.GraphQLMutation(`
{ "data": { "createPullRequest": { "pullRequest": {
"URL": "https://github.com/OWNER/REPO/pull/12"
} } } }
`, func(input map[string]interface{}) {
assert.Equal(t, "REPOID", input["repositoryId"].(string))
assert.Equal(t, "my title", input["title"].(string))
assert.Equal(t, "my body", input["body"].(string))
assert.Equal(t, "feature/feat2", input["baseRefName"].(string))
assert.Equal(t, "monalisa:task1", input["headRefName"].(string))
}))
},
customBranchConfig: true,
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp \^branch\\\.task1\\\.\(remote\|merge\|gh-merge-base\)\$`, 0, heredoc.Doc(`
branch.task1.remote origin
branch.task1.merge refs/heads/task1
branch.task1.gh-merge-base feature/feat2`)) // ReadBranchConfig
cs.Register(`git show-ref --verify`, 0, heredoc.Doc(`
deadbeef HEAD
deadb00f refs/remotes/upstream/feature/feat2
deadbeef refs/remotes/origin/task1`)) // determineTrackingBranch
},
expectedOut: "https://github.com/OWNER/REPO/pull/12\n",
expectedErrOut: "\nCreating pull request for monalisa:task1 into feature/feat2 in OWNER/REPO\n\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -1485,6 +1549,9 @@ func Test_createRun(t *testing.T) {
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git status --porcelain`, 0, "")
if !tt.customBranchConfig {
cs.Register(`git config --get-regexp \^branch\\\..+\\\.\(remote\|merge\|gh-merge-base\)\$`, 0, "")
}
if tt.cmdStubs != nil {
tt.cmdStubs(cs)
@ -1651,7 +1718,8 @@ func Test_determineTrackingBranch(t *testing.T) {
GhPath: "some/path/gh",
GitPath: "some/path/git",
}
ref := determineTrackingBranch(gitClient, tt.remotes, "feature")
headBranchConfig := gitClient.ReadBranchConfig(ctx.Background(), "feature")
ref := determineTrackingBranch(gitClient, tt.remotes, &headBranchConfig)
tt.assert(ref, t)
})
}