Merge branch 'trunk' into eugene/attestation/fetch-oci-bundle

This commit is contained in:
Eugene 2024-08-07 10:21:34 -07:00 committed by GitHub
commit cc0fe091c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 343 additions and 204 deletions

View file

@ -299,7 +299,7 @@ jobs:
rpmsign --addsign dist/*.rpm
- name: Attest release artifacts
if: inputs.environment == 'production'
uses: actions/attest-build-provenance@5e9cb68e95676991667494a6a4e59b8a2f13e1d0 # v1.3.3
uses: actions/attest-build-provenance@210c1913531870065f03ce1f9440dd87bc0938cd # v1.4.0
with:
subject-path: "dist/gh_*"
- name: Run createrepo

View file

@ -286,7 +286,7 @@ func IssueStatus(client *Client, repo ghrepo.Interface, options IssueStatusOptio
}
}
}
}`
}`
variables := map[string]interface{}{
"owner": repo.RepoOwner(),

View file

@ -152,13 +152,13 @@ func StatusCheckRollupGraphQLWithCountByState() string {
contexts {
checkRunCount,
checkRunCountsByState {
state,
count
state,
count
},
statusContextCount,
statusContextCountsByState {
state,
count
state,
count
}
}
}

View file

@ -18,7 +18,7 @@ func NewCmdAttestation(f *cmdutil.Factory) *cobra.Command {
Aliases: []string{"at"},
Long: heredoc.Doc(`
Download and verify artifact attestations.
`),
`),
}
root.AddCommand(download.NewDownloadCmd(f, nil))

View file

@ -32,22 +32,22 @@ func NewTrustedRootCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Com
Long: heredoc.Docf(`
### NOTE: This feature is currently in beta, and subject to change.
Output contents for a trusted_root.jsonl file, likely for offline verification.
Output contents for a trusted_root.jsonl file, likely for offline verification.
When using %[1]sgh attestation verify%[1]s, if your machine is on the internet,
this will happen automatically. But to do offline verification, you need to
supply a trusted root file with %[1]s--custom-trusted-root%[1]s; this command
will help you fetch a %[1]strusted_root.jsonl%[1]s file for that purpose.
When using %[1]sgh attestation verify%[1]s, if your machine is on the internet,
this will happen automatically. But to do offline verification, you need to
supply a trusted root file with %[1]s--custom-trusted-root%[1]s; this command
will help you fetch a %[1]strusted_root.jsonl%[1]s file for that purpose.
You can call this command without any flags to get a trusted root file covering
the Sigstore Public Good Instance as well as GitHub's Sigstore instance.
You can call this command without any flags to get a trusted root file covering
the Sigstore Public Good Instance as well as GitHub's Sigstore instance.
Otherwise you can use %[1]s--tuf-url%[1]s to specify the URL of a custom TUF
repository mirror, and %[1]s--tuf-root%[1]s should be the path to the
%[1]sroot.json%[1]s file that you securely obtained out-of-band.
Otherwise you can use %[1]s--tuf-url%[1]s to specify the URL of a custom TUF
repository mirror, and %[1]s--tuf-root%[1]s should be the path to the
%[1]sroot.json%[1]s file that you securely obtained out-of-band.
If you just want to verify the integrity of your local TUF repository, and don't
want the contents of a trusted_root.jsonl file, use %[1]s--verify-only%[1]s.
If you just want to verify the integrity of your local TUF repository, and don't
want the contents of a trusted_root.jsonl file, use %[1]s--verify-only%[1]s.
`, "`"),
Example: heredoc.Doc(`
# Get a trusted_root.jsonl for both Sigstore Public Good and GitHub's instance

View file

@ -60,7 +60,7 @@ func newSSHCmd(app *App) *cobra.Command {
The %[1]sssh%[1]s command will automatically create a public/private ssh key pair in the
%[1]s~/.ssh%[1]s directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by %[1]s-i%[1]s in %[1]s<ssh-flags>%[1]s
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to %[1]sssh -G%[1]s)

View file

@ -72,7 +72,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
Find a PR that introduced a given commit
$ gh pr list --search "<SHA>" --state merged
`),
`),
Aliases: []string{"ls"},
Args: cmdutil.NoArgsQuoteReminder,
RunE: func(cmd *cobra.Command, args []string) error {

View file

@ -88,7 +88,7 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the %[1]s--admin%[1]s flag.
`, "`"),
`, "`"),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.Finder = shared.NewFinder(f)

View file

@ -101,23 +101,23 @@ func pullRequestStatus(httpClient *http.Client, repo ghrepo.Interface, options r
}
query := fragments + queryPrefix + `
viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) {
totalCount: issueCount
edges {
node {
...prWithReviews
}
}
}
reviewRequested: search(query: $reviewerQuery, type: ISSUE, first: $per_page) {
totalCount: issueCount
edges {
node {
...pr
}
}
}
}
viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) {
totalCount: issueCount
edges {
node {
...prWithReviews
}
}
}
reviewRequested: search(query: $reviewerQuery, type: ISSUE, first: $per_page) {
totalCount: issueCount
edges {
node {
...pr
}
}
}
}
`
currentUsername := options.Username

View file

@ -115,7 +115,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
Use release notes from a file
$ gh release create v1.2.3 -F changelog.md
Don't mark the release as latest
$ gh release create v1.2.3 --latest=false

View file

@ -88,7 +88,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
To create a remote repository non-interactively, supply the repository name and one of %[1]s--public%[1]s, %[1]s--private%[1]s, or %[1]s--internal%[1]s.
Pass %[1]s--clone%[1]s to clone the new repository locally.
If the %[1]sOWNER/%[1]s portion of the %[1]sOWNER/REPO%[1]s name argument is omitted, it
If the %[1]sOWNER/%[1]s portion of the %[1]sOWNER/REPO%[1]s name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with %[1]s--source%[1]s.

View file

@ -79,18 +79,18 @@ func NewCmdRepoCredits(f *cmdutil.Factory, runF func(*CreditsOptions) error) *co
Use: "credits [<repository>]",
Short: "View credits for a repository",
Example: heredoc.Doc(`
# view credits for the current repository
$ gh repo credits
# view credits for the current repository
$ gh repo credits
# view credits for a specific repository
$ gh repo credits cool/repo
# view credits for a specific repository
$ gh repo credits cool/repo
# print a non-animated thank you
$ gh repo credits -s
# print a non-animated thank you
$ gh repo credits -s
# pipe to just print the contributors, one per line
$ gh repo credits | cat
`),
# pipe to just print the contributors, one per line
$ gh repo credits | cat
`),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {

View file

@ -158,25 +158,25 @@ var HelpTopics = []helpTopic{
$ gh pr list --json number,title,author
[
{
"author": {
"login": "monalisa"
},
"number": 123,
"title": "A helpful contribution"
"author": {
"login": "monalisa"
},
"number": 123,
"title": "A helpful contribution"
},
{
"author": {
"login": "codercat"
},
"number": 124,
"title": "Improve the docs"
"author": {
"login": "codercat"
},
"number": 124,
"title": "Improve the docs"
},
{
"author": {
"login": "cli-maintainer"
},
"number": 125,
"title": "An exciting new feature"
"author": {
"login": "cli-maintainer"
},
"number": 125,
"title": "An exciting new feature"
}
]
@ -193,32 +193,31 @@ var HelpTopics = []helpTopic{
| map(.labels = (.labels | map(.name))) # show only the label names
| .[:3] # select the first 3 results'
[
{
"labels": [
"enhancement",
"needs triage"
],
"number": 123,
"title": "A helpful contribution"
},
{
"labels": [
"help wanted",
"docs",
"good first issue"
],
"number": 125,
"title": "Improve the docs"
},
{
"labels": [
"enhancement",
],
"number": 7221,
"title": "An exciting new feature"
}
{
"labels": [
"enhancement",
"needs triage"
],
"number": 123,
"title": "A helpful contribution"
},
{
"labels": [
"help wanted",
"docs",
"good first issue"
],
"number": 125,
"title": "Improve the docs"
},
{
"labels": [
"enhancement",
],
"number": 7221,
"title": "An exciting new feature"
}
]
# using the --template flag with the hyperlink helper
gh issue list --json title,url --template '{{range .}}{{hyperlink .url .title}}{{"\n"}}{{end}}'

View file

@ -51,17 +51,17 @@ func NewCmdDownload(f *cmdutil.Factory, runF func(*DownloadOptions) error) *cobr
`),
Args: cobra.MaximumNArgs(1),
Example: heredoc.Doc(`
# Download all artifacts generated by a workflow run
$ gh run download <run-id>
# Download all artifacts generated by a workflow run
$ gh run download <run-id>
# Download a specific artifact within a run
$ gh run download <run-id> -n <name>
# Download a specific artifact within a run
$ gh run download <run-id> -n <name>
# Download specific artifacts across all runs in a repository
$ gh run download -n <name1> -n <name2>
# Download specific artifacts across all runs in a repository
$ gh run download -n <name1> -n <name2>
# Select artifacts to download interactively
$ gh run download
# Select artifacts to download interactively
$ gh run download
`),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {

View file

@ -58,7 +58,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
Use: "list",
Short: "List recent workflow runs",
Long: heredoc.Docf(`
List recent workflow runs.
List recent workflow runs.
Note that providing the %[1]sworkflow_name%[1]s to the %[1]s-w%[1]s flag will not fetch disabled workflows.
Also pass the %[1]s-a%[1]s flag to fetch disabled workflow runs using the %[1]sworkflow_name%[1]s and the %[1]s-w%[1]s flag.

View file

@ -45,7 +45,7 @@ func NewCmdCommits(f *cmdutil.Factory, runF func(*CommitsOptions) error) *cobra.
GitHub search syntax is documented at:
<https://docs.github.com/search-github/searching-on-github/searching-commits>
`),
`),
Example: heredoc.Doc(`
# search commits matching set of keywords "readme" and "typo"
$ gh search commits readme typo
@ -64,7 +64,7 @@ func NewCmdCommits(f *cmdutil.Factory, runF func(*CommitsOptions) error) *cobra.
# search commits authored before February 1st, 2022
$ gh search commits --author-date="<2022-02-01"
`),
`),
RunE: func(c *cobra.Command, args []string) error {
if len(args) == 0 && c.Flags().NFlag() == 0 {
return cmdutil.FlagErrorf("specify search keywords or flags")

View file

@ -34,7 +34,7 @@ func NewCmdIssues(f *cmdutil.Factory, runF func(*shared.IssuesOptions) error) *c
GitHub search syntax is documented at:
<https://docs.github.com/search-github/searching-on-github/searching-issues-and-pull-requests>
`),
`),
Example: heredoc.Doc(`
# search issues matching set of keywords "readme" and "typo"
$ gh search issues readme typo
@ -56,8 +56,7 @@ func NewCmdIssues(f *cmdutil.Factory, runF func(*shared.IssuesOptions) error) *c
# search issues only from un-archived repositories (default is all repositories)
$ gh search issues --owner github --archived=false
`),
`),
RunE: func(c *cobra.Command, args []string) error {
if len(args) == 0 && c.Flags().NFlag() == 0 {
return cmdutil.FlagErrorf("specify search keywords or flags")

View file

@ -36,7 +36,7 @@ func NewCmdPrs(f *cmdutil.Factory, runF func(*shared.IssuesOptions) error) *cobr
GitHub search syntax is documented at:
<https://docs.github.com/search-github/searching-on-github/searching-issues-and-pull-requests>
`),
`),
Example: heredoc.Doc(`
# search pull requests matching set of keywords "fix" and "bug"
$ gh search prs fix bug
@ -58,7 +58,7 @@ func NewCmdPrs(f *cmdutil.Factory, runF func(*shared.IssuesOptions) error) *cobr
# search pull requests only from un-archived repositories (default is all repositories)
$ gh search prs --owner github --archived=false
`),
`),
RunE: func(c *cobra.Command, args []string) error {
if len(args) == 0 && c.Flags().NFlag() == 0 {
return cmdutil.FlagErrorf("specify search keywords or flags")

View file

@ -46,7 +46,7 @@ func NewCmdRepos(f *cmdutil.Factory, runF func(*ReposOptions) error) *cobra.Comm
GitHub search syntax is documented at:
<https://docs.github.com/search-github/searching-on-github/searching-for-repositories>
`),
`),
Example: heredoc.Doc(`
# search repositories matching set of keywords "cli" and "shell"
$ gh search repos cli shell
@ -68,7 +68,7 @@ func NewCmdRepos(f *cmdutil.Factory, runF func(*ReposOptions) error) *cobra.Comm
# search repositories excluding archived repositories
$ gh search repos --archived=false
`),
`),
RunE: func(c *cobra.Command, args []string) error {
if len(args) == 0 && c.Flags().NFlag() == 0 {
return cmdutil.FlagErrorf("specify search keywords or flags")

View file

@ -105,24 +105,27 @@ func removeRun(opts *DeleteOptions) error {
}
}
var path string
switch secretEntity {
case shared.Organization:
path = fmt.Sprintf("orgs/%s/%s/secrets/%s", orgName, secretApp, opts.SecretName)
case shared.Environment:
path = fmt.Sprintf("repos/%s/environments/%s/secrets/%s", ghrepo.FullName(baseRepo), envName, opts.SecretName)
case shared.User:
path = fmt.Sprintf("user/codespaces/secrets/%s", opts.SecretName)
case shared.Repository:
path = fmt.Sprintf("repos/%s/%s/secrets/%s", ghrepo.FullName(baseRepo), secretApp, opts.SecretName)
}
cfg, err := opts.Config()
if err != nil {
return err
}
host, _ := cfg.Authentication().DefaultHost()
var path string
var host string
switch secretEntity {
case shared.Organization:
path = fmt.Sprintf("orgs/%s/%s/secrets/%s", orgName, secretApp, opts.SecretName)
host, _ = cfg.Authentication().DefaultHost()
case shared.Environment:
path = fmt.Sprintf("repos/%s/environments/%s/secrets/%s", ghrepo.FullName(baseRepo), envName, opts.SecretName)
host = baseRepo.RepoHost()
case shared.User:
path = fmt.Sprintf("user/codespaces/secrets/%s", opts.SecretName)
host, _ = cfg.Authentication().DefaultHost()
case shared.Repository:
path = fmt.Sprintf("repos/%s/%s/secrets/%s", ghrepo.FullName(baseRepo), secretApp, opts.SecretName)
host = baseRepo.RepoHost()
}
err = client.REST(host, "DELETE", path, nil, nil)
if err != nil {

View file

@ -13,6 +13,7 @@ import (
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCmdDelete(t *testing.T) {
@ -121,9 +122,10 @@ func TestNewCmdDelete(t *testing.T) {
func Test_removeRun_repo(t *testing.T) {
tests := []struct {
name string
opts *DeleteOptions
wantPath string
name string
opts *DeleteOptions
host string
httpStubs func(*httpmock.Registry)
}{
{
name: "Actions",
@ -131,7 +133,21 @@ func Test_removeRun_repo(t *testing.T) {
Application: "actions",
SecretName: "cool_secret",
},
wantPath: "repos/owner/repo/actions/secrets/cool_secret",
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/actions/secrets/cool_secret"), "api.github.com"), httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "Actions GHES",
opts: &DeleteOptions{
Application: "actions",
SecretName: "cool_secret",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "api/v3/repos/owner/repo/actions/secrets/cool_secret"), "example.com"), httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "Dependabot",
@ -139,23 +155,38 @@ func Test_removeRun_repo(t *testing.T) {
Application: "dependabot",
SecretName: "cool_dependabot_secret",
},
wantPath: "repos/owner/repo/dependabot/secrets/cool_dependabot_secret",
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/dependabot/secrets/cool_dependabot_secret"), "api.github.com"), httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "Dependabot GHES",
opts: &DeleteOptions{
Application: "dependabot",
SecretName: "cool_dependabot_secret",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "api/v3/repos/owner/repo/dependabot/secrets/cool_dependabot_secret"), "example.com"), httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "defaults to Actions",
opts: &DeleteOptions{
SecretName: "cool_secret",
},
wantPath: "repos/owner/repo/actions/secrets/cool_secret",
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/actions/secrets/cool_secret"), "api.github.com"), httpmock.StatusStringResponse(204, "No Content"))
},
},
}
for _, tt := range tests {
reg := &httpmock.Registry{}
reg.Register(
httpmock.REST("DELETE", tt.wantPath),
httpmock.StatusStringResponse(204, "No Content"))
tt.httpStubs(reg)
defer reg.Verify(t)
ios, _, _, _ := iostreams.Test()
@ -167,44 +198,70 @@ func Test_removeRun_repo(t *testing.T) {
return config.NewBlankConfig(), nil
}
tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
return ghrepo.FromFullName("owner/repo")
return ghrepo.FromFullNameWithHost("owner/repo", tt.host)
}
err := removeRun(tt.opts)
assert.NoError(t, err)
reg.Verify(t)
}
}
func Test_removeRun_env(t *testing.T) {
reg := &httpmock.Registry{}
reg.Register(
httpmock.REST("DELETE", "repos/owner/repo/environments/development/secrets/cool_secret"),
httpmock.StatusStringResponse(204, "No Content"))
ios, _, _, _ := iostreams.Test()
opts := &DeleteOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
tests := []struct {
name string
opts *DeleteOptions
host string
httpStubs func(*httpmock.Registry)
}{
{
name: "delete environment secret",
opts: &DeleteOptions{
SecretName: "cool_secret",
EnvName: "development",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/environments/development/secrets/cool_secret"), "api.github.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
Config: func() (gh.Config, error) {
return config.NewBlankConfig(), nil
{
name: "delete environment secret GHES",
opts: &DeleteOptions{
SecretName: "cool_secret",
EnvName: "development",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "api/v3/repos/owner/repo/environments/development/secrets/cool_secret"), "example.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.FromFullName("owner/repo")
},
SecretName: "cool_secret",
EnvName: "development",
}
err := removeRun(opts)
assert.NoError(t, err)
for _, tt := range tests {
reg := &httpmock.Registry{}
tt.httpStubs(reg)
defer reg.Verify(t)
reg.Verify(t)
ios, _, _, _ := iostreams.Test()
tt.opts.IO = ios
tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
if tt.host != "" {
return ghrepo.FromFullNameWithHost("owner/repo", tt.host)
}
return ghrepo.FromFullName("owner/repo")
}
tt.opts.HttpClient = func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
}
tt.opts.Config = func() (gh.Config, error) {
return config.NewBlankConfig(), nil
}
err := removeRun(tt.opts)
require.NoError(t, err)
}
}
func Test_removeRun_org(t *testing.T) {

View file

@ -91,22 +91,24 @@ func removeRun(opts *DeleteOptions) error {
}
}
var path string
switch variableEntity {
case shared.Organization:
path = fmt.Sprintf("orgs/%s/actions/variables/%s", orgName, opts.VariableName)
case shared.Environment:
path = fmt.Sprintf("repos/%s/environments/%s/variables/%s", ghrepo.FullName(baseRepo), envName, opts.VariableName)
case shared.Repository:
path = fmt.Sprintf("repos/%s/actions/variables/%s", ghrepo.FullName(baseRepo), opts.VariableName)
}
cfg, err := opts.Config()
if err != nil {
return err
}
host, _ := cfg.Authentication().DefaultHost()
var path string
var host string
switch variableEntity {
case shared.Organization:
path = fmt.Sprintf("orgs/%s/actions/variables/%s", orgName, opts.VariableName)
host, _ = cfg.Authentication().DefaultHost()
case shared.Environment:
path = fmt.Sprintf("repos/%s/environments/%s/variables/%s", ghrepo.FullName(baseRepo), envName, opts.VariableName)
host = baseRepo.RepoHost()
case shared.Repository:
path = fmt.Sprintf("repos/%s/actions/variables/%s", ghrepo.FullName(baseRepo), opts.VariableName)
host = baseRepo.RepoHost()
}
err = client.REST(host, "DELETE", path, nil, nil)
if err != nil {

View file

@ -13,6 +13,7 @@ import (
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCmdDelete(t *testing.T) {
@ -87,16 +88,32 @@ func TestNewCmdDelete(t *testing.T) {
func TestRemoveRun(t *testing.T) {
tests := []struct {
name string
opts *DeleteOptions
wantPath string
name string
opts *DeleteOptions
host string
httpStubs func(*httpmock.Registry)
}{
{
name: "repo",
opts: &DeleteOptions{
VariableName: "cool_variable",
},
wantPath: "repos/owner/repo/actions/variables/cool_variable",
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/actions/variables/cool_variable"), "api.github.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "repo GHES",
opts: &DeleteOptions{
VariableName: "cool_variable",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "api/v3/repos/owner/repo/actions/variables/cool_variable"), "example.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "env",
@ -104,7 +121,23 @@ func TestRemoveRun(t *testing.T) {
VariableName: "cool_variable",
EnvName: "development",
},
wantPath: "repos/owner/repo/environments/development/variables/cool_variable",
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/environments/development/variables/cool_variable"), "api.github.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "env GHES",
opts: &DeleteOptions{
VariableName: "cool_variable",
EnvName: "development",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "api/v3/repos/owner/repo/environments/development/variables/cool_variable"), "example.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "org",
@ -112,35 +145,34 @@ func TestRemoveRun(t *testing.T) {
VariableName: "cool_variable",
OrgName: "UmbrellaCorporation",
},
wantPath: "orgs/UmbrellaCorporation/actions/variables/cool_variable",
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "orgs/UmbrellaCorporation/actions/variables/cool_variable"), "api.github.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reg := &httpmock.Registry{}
tt.httpStubs(reg)
defer reg.Verify(t)
reg.Register(httpmock.REST("DELETE", tt.wantPath),
httpmock.StatusStringResponse(204, "No Content"))
ios, _, _, _ := iostreams.Test()
tt.opts.IO = ios
tt.opts.HttpClient = func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
}
tt.opts.Config = func() (gh.Config, error) {
return config.NewBlankConfig(), nil
}
tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
return ghrepo.FromFullName("owner/repo")
return ghrepo.FromFullNameWithHost("owner/repo", tt.host)
}
err := removeRun(tt.opts)
assert.NoError(t, err)
require.NoError(t, err)
})
}
}

View file

@ -92,22 +92,24 @@ func getRun(opts *GetOptions) error {
}
}
var path string
switch variableEntity {
case shared.Organization:
path = fmt.Sprintf("orgs/%s/actions/variables/%s", orgName, opts.VariableName)
case shared.Environment:
path = fmt.Sprintf("repos/%s/environments/%s/variables/%s", ghrepo.FullName(baseRepo), envName, opts.VariableName)
case shared.Repository:
path = fmt.Sprintf("repos/%s/actions/variables/%s", ghrepo.FullName(baseRepo), opts.VariableName)
}
cfg, err := opts.Config()
if err != nil {
return err
}
host, _ := cfg.Authentication().DefaultHost()
var path string
var host string
switch variableEntity {
case shared.Organization:
path = fmt.Sprintf("orgs/%s/actions/variables/%s", orgName, opts.VariableName)
host, _ = cfg.Authentication().DefaultHost()
case shared.Environment:
path = fmt.Sprintf("repos/%s/environments/%s/variables/%s", ghrepo.FullName(baseRepo), envName, opts.VariableName)
host = baseRepo.RepoHost()
case shared.Repository:
path = fmt.Sprintf("repos/%s/actions/variables/%s", ghrepo.FullName(baseRepo), opts.VariableName)
host = baseRepo.RepoHost()
}
var variable shared.Variable
if err = client.REST(host, "GET", path, nil, &variable); err != nil {

View file

@ -110,6 +110,7 @@ func Test_getRun(t *testing.T) {
tests := []struct {
name string
opts *GetOptions
host string
httpStubs func(*httpmock.Registry)
jsonFields []string
wantOut string
@ -120,8 +121,23 @@ func Test_getRun(t *testing.T) {
opts: &GetOptions{
VariableName: "VARIABLE_ONE",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"),
reg.Register(httpmock.WithHost(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"), "api.github.com"),
httpmock.JSONResponse(shared.Variable{
Value: "repo_var",
}))
},
wantOut: "repo_var\n",
},
{
name: "getting GHES repo variable",
opts: &GetOptions{
VariableName: "VARIABLE_ONE",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("GET", "api/v3/repos/owner/repo/actions/variables/VARIABLE_ONE"), "example.com"),
httpmock.JSONResponse(shared.Variable{
Value: "repo_var",
}))
@ -134,6 +150,7 @@ func Test_getRun(t *testing.T) {
OrgName: "TestOrg",
VariableName: "VARIABLE_ONE",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "orgs/TestOrg/actions/variables/VARIABLE_ONE"),
httpmock.JSONResponse(shared.Variable{
@ -148,8 +165,24 @@ func Test_getRun(t *testing.T) {
EnvName: "Development",
VariableName: "VARIABLE_ONE",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "repos/owner/repo/environments/Development/variables/VARIABLE_ONE"),
reg.Register(httpmock.WithHost(httpmock.REST("GET", "repos/owner/repo/environments/Development/variables/VARIABLE_ONE"), "api.github.com"),
httpmock.JSONResponse(shared.Variable{
Value: "env_var",
}))
},
wantOut: "env_var\n",
},
{
name: "getting GHES env variable",
opts: &GetOptions{
EnvName: "Development",
VariableName: "VARIABLE_ONE",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("GET", "api/v3/repos/owner/repo/environments/Development/variables/VARIABLE_ONE"), "example.com"),
httpmock.JSONResponse(shared.Variable{
Value: "env_var",
}))
@ -161,6 +194,7 @@ func Test_getRun(t *testing.T) {
opts: &GetOptions{
VariableName: "VARIABLE_ONE",
},
host: "github.com",
jsonFields: []string{"name", "value", "visibility", "updatedAt", "createdAt", "numSelectedRepos", "selectedReposURL"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"),
@ -193,6 +227,7 @@ func Test_getRun(t *testing.T) {
opts: &GetOptions{
VariableName: "VARIABLE_ONE",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"),
httpmock.StatusStringResponse(404, "not found"),
@ -205,6 +240,7 @@ func Test_getRun(t *testing.T) {
opts: &GetOptions{
VariableName: "VARIABLE_ONE",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"),
httpmock.StatusStringResponse(400, "not found"),
@ -226,7 +262,7 @@ func Test_getRun(t *testing.T) {
tt.opts.IO = ios
tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
return ghrepo.FromFullName("owner/repo")
return ghrepo.FromFullNameWithHost("owner/repo", tt.host)
}
tt.opts.HttpClient = func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil

View file

@ -17,7 +17,7 @@ func NewCmdVariable(f *cmdutil.Factory) *cobra.Command {
Long: heredoc.Docf(`
Variables can be set at the repository, environment or organization level for use in
GitHub Actions or Dependabot. Run %[1]sgh help variable set%[1]s to learn how to get started.
`, "`"),
`, "`"),
}
cmdutil.EnableRepoOverride(cmd, f)

View file

@ -64,7 +64,7 @@ func NewCmdRun(f *cmdutil.Factory, runF func(*RunOptions) error) *cobra.Command
- Interactively
- Via %[1]s-f/--raw-field%[1]s or %[1]s-F/--field%[1]s flags
- As JSON, via standard input
`, "`"),
`, "`"),
Example: heredoc.Doc(`
# Have gh prompt you for what workflow you'd like to run and interactively collect inputs
$ gh workflow run

View file

@ -56,11 +56,11 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
Short: "View the summary of a workflow",
Args: cobra.MaximumNArgs(1),
Example: heredoc.Doc(`
# Interactively select a workflow to view
$ gh workflow view
# Interactively select a workflow to view
$ gh workflow view
# View a specific workflow
$ gh workflow view 0451
# View a specific workflow
$ gh workflow view 0451
`),
RunE: func(cmd *cobra.Command, args []string) error {
// support `-R, --repo` override

View file

@ -123,6 +123,15 @@ func StringResponse(body string) Responder {
}
}
func WithHost(matcher Matcher, host string) Matcher {
return func(req *http.Request) bool {
if !strings.EqualFold(req.Host, host) {
return false
}
return matcher(req)
}
}
func WithHeader(responder Responder, header string, value string) Responder {
return func(req *http.Request) (*http.Response, error) {
resp, _ := responder(req)

View file

@ -46,10 +46,10 @@ var EditorQuestionTemplate = `
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if .ShowAnswer}}
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
{{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
{{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- color "cyan"}}[(e) to launch {{ .EditorCommand }}{{- if .BlankAllowed }}, enter to skip{{ end }}] {{color "reset"}}
{{- end}}`