Merge remote-tracking branch 'origin/master' into issue-pr-create-metadata

This commit is contained in:
Mislav Marohnić 2020-04-27 14:17:09 +02:00
commit 90c8e0e967
30 changed files with 1166 additions and 284 deletions

112
command/config.go Normal file
View file

@ -0,0 +1,112 @@
package command
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
RootCmd.AddCommand(configCmd)
configCmd.AddCommand(configGetCmd)
configCmd.AddCommand(configSetCmd)
configGetCmd.Flags().StringP("host", "h", "", "Get per-host setting")
configSetCmd.Flags().StringP("host", "h", "", "Set per-host setting")
// TODO reveal and add usage once we properly support multiple hosts
_ = configGetCmd.Flags().MarkHidden("host")
// TODO reveal and add usage once we properly support multiple hosts
_ = configSetCmd.Flags().MarkHidden("host")
}
var configCmd = &cobra.Command{
Use: "config",
Short: "Set and get gh settings",
Long: `Get and set key/value strings.
Current respected settings:
- git_protocol: https or ssh. Default is https.
- editor: if unset, defaults to environment variables.
`,
}
var configGetCmd = &cobra.Command{
Use: "get <key>",
Short: "Prints the value of a given configuration key.",
Long: `Get the value for a given configuration key.
Examples:
$ gh config get git_protocol
https
`,
Args: cobra.ExactArgs(1),
RunE: configGet,
}
var configSetCmd = &cobra.Command{
Use: "set <key> <value>",
Short: "Updates configuration with the value of a given key.",
Long: `Update the configuration by setting a key to a value.
Examples:
$ gh config set editor vim
`,
Args: cobra.ExactArgs(2),
RunE: configSet,
}
func configGet(cmd *cobra.Command, args []string) error {
key := args[0]
hostname, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
ctx := contextForCommand(cmd)
cfg, err := ctx.Config()
if err != nil {
return err
}
val, err := cfg.Get(hostname, key)
if err != nil {
return err
}
if val != "" {
out := colorableOut(cmd)
fmt.Fprintf(out, "%s\n", val)
}
return nil
}
func configSet(cmd *cobra.Command, args []string) error {
key := args[0]
value := args[1]
hostname, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
ctx := contextForCommand(cmd)
cfg, err := ctx.Config()
if err != nil {
return err
}
err = cfg.Set(hostname, key, value)
if err != nil {
return fmt.Errorf("failed to set %q to %q: %w", key, value, err)
}
err = cfg.Write()
if err != nil {
return fmt.Errorf("failed to write config to disk: %w", err)
}
return nil
}

191
command/config_test.go Normal file
View file

@ -0,0 +1,191 @@
package command
import (
"bytes"
"testing"
"github.com/cli/cli/internal/config"
)
func TestConfigGet(t *testing.T) {
cfg := `---
hosts:
github.com:
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
editor: ed
`
initBlankContext(cfg, "OWNER/REPO", "master")
output, err := RunCommand(configGetCmd, "config get editor")
if err != nil {
t.Fatalf("error running command `config get editor`: %v", err)
}
eq(t, output.String(), "ed\n")
}
func TestConfigGet_default(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
output, err := RunCommand(configGetCmd, "config get git_protocol")
if err != nil {
t.Fatalf("error running command `config get git_protocol`: %v", err)
}
eq(t, output.String(), "https\n")
}
func TestConfigGet_not_found(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
output, err := RunCommand(configGetCmd, "config get missing")
if err != nil {
t.Fatalf("error running command `config get missing`: %v", err)
}
eq(t, output.String(), "")
}
func TestConfigSet(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
buf := bytes.NewBufferString("")
defer config.StubWriteConfig(buf)()
output, err := RunCommand(configSetCmd, "config set editor ed")
if err != nil {
t.Fatalf("error running command `config set editor ed`: %v", err)
}
eq(t, output.String(), "")
expected := `hosts:
github.com:
user: OWNER
oauth_token: 1234567890
editor: ed
`
eq(t, buf.String(), expected)
}
func TestConfigSet_update(t *testing.T) {
cfg := `---
hosts:
github.com:
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
editor: ed
`
initBlankContext(cfg, "OWNER/REPO", "master")
buf := bytes.NewBufferString("")
defer config.StubWriteConfig(buf)()
output, err := RunCommand(configSetCmd, "config set editor vim")
if err != nil {
t.Fatalf("error running command `config get editor`: %v", err)
}
eq(t, output.String(), "")
expected := `hosts:
github.com:
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
editor: vim
`
eq(t, buf.String(), expected)
}
func TestConfigGetHost(t *testing.T) {
cfg := `---
hosts:
github.com:
git_protocol: ssh
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
editor: ed
git_protocol: https
`
initBlankContext(cfg, "OWNER/REPO", "master")
output, err := RunCommand(configGetCmd, "config get -hgithub.com git_protocol")
if err != nil {
t.Fatalf("error running command `config get editor`: %v", err)
}
eq(t, output.String(), "ssh\n")
}
func TestConfigGetHost_unset(t *testing.T) {
cfg := `---
hosts:
github.com:
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
editor: ed
git_protocol: ssh
`
initBlankContext(cfg, "OWNER/REPO", "master")
output, err := RunCommand(configGetCmd, "config get -hgithub.com git_protocol")
if err != nil {
t.Fatalf("error running command `config get -hgithub.com git_protocol`: %v", err)
}
eq(t, output.String(), "ssh\n")
}
func TestConfigSetHost(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
buf := bytes.NewBufferString("")
defer config.StubWriteConfig(buf)()
output, err := RunCommand(configSetCmd, "config set -hgithub.com git_protocol ssh")
if err != nil {
t.Fatalf("error running command `config set editor ed`: %v", err)
}
eq(t, output.String(), "")
expected := `hosts:
github.com:
user: OWNER
oauth_token: 1234567890
git_protocol: ssh
`
eq(t, buf.String(), expected)
}
func TestConfigSetHost_update(t *testing.T) {
cfg := `---
hosts:
github.com:
git_protocol: ssh
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
`
initBlankContext(cfg, "OWNER/REPO", "master")
buf := bytes.NewBufferString("")
defer config.StubWriteConfig(buf)()
output, err := RunCommand(configSetCmd, "config set -hgithub.com git_protocol https")
if err != nil {
t.Fatalf("error running command `config get editor`: %v", err)
}
eq(t, output.String(), "")
expected := `hosts:
github.com:
git_protocol: https
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
`
eq(t, buf.String(), expected)
}

View file

@ -16,7 +16,7 @@ import (
)
func TestIssueStatus(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -45,7 +45,7 @@ func TestIssueStatus(t *testing.T) {
}
func TestIssueStatus_blankSlate(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -82,7 +82,7 @@ Issues opened by you
}
func TestIssueStatus_disabledIssues(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -99,7 +99,7 @@ func TestIssueStatus_disabledIssues(t *testing.T) {
}
func TestIssueList(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -132,7 +132,7 @@ Showing 3 of 3 issues in OWNER/REPO
}
func TestIssueList_withFlags(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -172,7 +172,7 @@ No issues match your search in OWNER/REPO
}
func TestIssueList_nullAssigneeLabels(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -201,7 +201,7 @@ func TestIssueList_nullAssigneeLabels(t *testing.T) {
}
func TestIssueList_disabledIssues(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -218,7 +218,7 @@ func TestIssueList_disabledIssues(t *testing.T) {
}
func TestIssueView_web(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -252,7 +252,7 @@ func TestIssueView_web(t *testing.T) {
}
func TestIssueView_web_numberArgWithHash(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -342,7 +342,7 @@ func TestIssueView_Preview(t *testing.T) {
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
initBlankContext("OWNER/REPO", tc.ownerRepo)
initBlankContext("", "OWNER/REPO", tc.ownerRepo)
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -363,7 +363,7 @@ func TestIssueView_Preview(t *testing.T) {
}
func TestIssueView_web_notFound(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
@ -390,7 +390,7 @@ func TestIssueView_web_notFound(t *testing.T) {
}
func TestIssueView_disabledIssues(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -408,7 +408,7 @@ func TestIssueView_disabledIssues(t *testing.T) {
}
func TestIssueView_web_urlArg(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -441,7 +441,7 @@ func TestIssueView_web_urlArg(t *testing.T) {
}
func TestIssueCreate(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -482,7 +482,7 @@ func TestIssueCreate(t *testing.T) {
}
func TestIssueCreate_disabledIssues(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -500,7 +500,7 @@ func TestIssueCreate_disabledIssues(t *testing.T) {
}
func TestIssueCreate_web(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -526,7 +526,7 @@ func TestIssueCreate_web(t *testing.T) {
}
func TestIssueCreate_webTitleBody(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")

View file

@ -47,7 +47,7 @@ func prCheckout(cmd *cobra.Command, args []string) error {
baseRemote, _ := remotes.FindByRepo(baseRepo.RepoOwner(), baseRepo.RepoName())
// baseRemoteSpec is a repository URL or a remote name to be used in git fetch
baseURLOrName := fmt.Sprintf("https://github.com/%s.git", ghrepo.FullName(baseRepo))
baseURLOrName := formatRemoteURL(cmd, ghrepo.FullName(baseRepo))
if baseRemote != nil {
baseURLOrName = baseRemote.Name
}
@ -97,7 +97,7 @@ func prCheckout(cmd *cobra.Command, args []string) error {
remote := baseURLOrName
mergeRef := ref
if pr.MaintainerCanModify {
remote = fmt.Sprintf("https://github.com/%s/%s.git", pr.HeadRepositoryOwner.Login, pr.HeadRepository.Name)
remote = formatRemoteURL(cmd, fmt.Sprintf("%s/%s", pr.HeadRepositoryOwner.Login, pr.HeadRepository.Name))
mergeRef = fmt.Sprintf("refs/heads/%s", pr.HeadRefName)
}
if mc, err := git.Config(fmt.Sprintf("branch.%s.merge", newBranchName)); err != nil || mc == "" {

View file

@ -273,8 +273,8 @@ func prCreate(cmd *cobra.Command, _ []string) error {
// In either case, we want to add the head repo as a new git remote so we
// can push to it.
if headRemote == nil {
// TODO: support non-HTTPS git remote URLs
headRepoURL := fmt.Sprintf("https://github.com/%s.git", ghrepo.FullName(headRepo))
headRepoURL := formatRemoteURL(cmd, ghrepo.FullName(headRepo))
// TODO: prevent clashes with another remote of a same name
gitRemote, err := git.AddRemote("fork", headRepoURL)
if err != nil {

View file

@ -13,7 +13,7 @@ import (
)
func TestPRCreate(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -66,7 +66,7 @@ func TestPRCreate(t *testing.T) {
}
func TestPRCreate_withForking(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponseWithPermission("OWNER", "REPO", "READ")
http.StubResponse(200, bytes.NewBufferString(`
@ -109,7 +109,7 @@ func TestPRCreate_withForking(t *testing.T) {
}
func TestPRCreate_alreadyExists(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -142,7 +142,7 @@ func TestPRCreate_alreadyExists(t *testing.T) {
}
func TestPRCreate_alreadyExistsDifferentBase(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -174,7 +174,7 @@ func TestPRCreate_alreadyExistsDifferentBase(t *testing.T) {
}
func TestPRCreate_web(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -205,7 +205,7 @@ func TestPRCreate_web(t *testing.T) {
}
func TestPRCreate_ReportsUncommittedChanges(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -330,7 +330,7 @@ func TestPRCreate_cross_repo_same_branch(t *testing.T) {
}
func TestPRCreate_survey_defaults_multicommit(t *testing.T) {
initBlankContext("OWNER/REPO", "cool_bug-fixes")
initBlankContext("", "OWNER/REPO", "cool_bug-fixes")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -406,7 +406,7 @@ func TestPRCreate_survey_defaults_multicommit(t *testing.T) {
}
func TestPRCreate_survey_defaults_monocommit(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -483,7 +483,7 @@ func TestPRCreate_survey_defaults_monocommit(t *testing.T) {
}
func TestPRCreate_survey_autofill(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -541,7 +541,7 @@ func TestPRCreate_survey_autofill(t *testing.T) {
}
func TestPRCreate_defaults_error_autofill(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -559,7 +559,7 @@ func TestPRCreate_defaults_error_autofill(t *testing.T) {
}
func TestPRCreate_defaults_error_web(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -577,7 +577,7 @@ func TestPRCreate_defaults_error_web(t *testing.T) {
}
func TestPRCreate_defaults_error_interactive(t *testing.T) {
initBlankContext("OWNER/REPO", "feature")
initBlankContext("", "OWNER/REPO", "feature")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`

View file

@ -74,7 +74,7 @@ func RunCommand(cmd *cobra.Command, args string) (*cmdOut, error) {
}
func TestPRStatus(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -102,7 +102,7 @@ func TestPRStatus(t *testing.T) {
}
func TestPRStatus_fork(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubForkedRepoResponse("OWNER/REPO", "PARENT/REPO")
@ -132,7 +132,7 @@ branch.blueberries.merge refs/heads/blueberries`)}
}
func TestPRStatus_reviewsAndChecks(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -159,7 +159,7 @@ func TestPRStatus_reviewsAndChecks(t *testing.T) {
}
func TestPRStatus_currentBranch_showTheMostRecentPR(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -191,7 +191,7 @@ func TestPRStatus_currentBranch_showTheMostRecentPR(t *testing.T) {
}
func TestPRStatus_currentBranch_Closed(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -212,7 +212,7 @@ func TestPRStatus_currentBranch_Closed(t *testing.T) {
}
func TestPRStatus_currentBranch_Merged(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -233,7 +233,7 @@ func TestPRStatus_currentBranch_Merged(t *testing.T) {
}
func TestPRStatus_blankSlate(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -265,7 +265,7 @@ Requesting a code review from you
}
func TestPRList(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -289,7 +289,7 @@ Showing 3 of 3 pull requests in OWNER/REPO
}
func TestPRList_filtering(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -321,7 +321,7 @@ No pull requests match your search in OWNER/REPO
}
func TestPRList_filteringRemoveDuplicate(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -341,7 +341,7 @@ func TestPRList_filteringRemoveDuplicate(t *testing.T) {
}
func TestPRList_filteringClosed(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -365,7 +365,7 @@ func TestPRList_filteringClosed(t *testing.T) {
}
func TestPRList_filteringAssignee(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -389,7 +389,7 @@ func TestPRList_filteringAssignee(t *testing.T) {
}
func TestPRList_filteringAssigneeLabels(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -519,7 +519,7 @@ func TestPRView_Preview(t *testing.T) {
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
initBlankContext("OWNER/REPO", tc.ownerRepo)
initBlankContext("", "OWNER/REPO", tc.ownerRepo)
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -540,7 +540,7 @@ func TestPRView_Preview(t *testing.T) {
}
func TestPRView_web_currentBranch(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -578,7 +578,7 @@ func TestPRView_web_currentBranch(t *testing.T) {
}
func TestPRView_web_noResultsForBranch(t *testing.T) {
initBlankContext("OWNER/REPO", "blueberries")
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -609,7 +609,7 @@ func TestPRView_web_noResultsForBranch(t *testing.T) {
}
func TestPRView_web_numberArg(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -641,7 +641,7 @@ func TestPRView_web_numberArg(t *testing.T) {
}
func TestPRView_web_numberArgWithHash(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -673,7 +673,7 @@ func TestPRView_web_numberArgWithHash(t *testing.T) {
}
func TestPRView_web_urlArg(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
@ -704,7 +704,7 @@ func TestPRView_web_urlArg(t *testing.T) {
}
func TestPRView_web_branchArg(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -738,7 +738,7 @@ func TestPRView_web_branchArg(t *testing.T) {
}
func TestPRView_web_branchWithOwnerArg(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")

View file

@ -127,7 +127,7 @@ func runClone(cloneURL string, args []string) (target string, err error) {
func repoClone(cmd *cobra.Command, args []string) error {
cloneURL := args[0]
if !strings.Contains(cloneURL, ":") {
cloneURL = fmt.Sprintf("https://github.com/%s.git", cloneURL)
cloneURL = formatRemoteURL(cmd, cloneURL)
}
var repo ghrepo.Interface
@ -158,7 +158,7 @@ func repoClone(cmd *cobra.Command, args []string) error {
}
if parentRepo != nil {
err := addUpstreamRemote(parentRepo, cloneDir)
err := addUpstreamRemote(cmd, parentRepo, cloneDir)
if err != nil {
return err
}
@ -167,9 +167,8 @@ func repoClone(cmd *cobra.Command, args []string) error {
return nil
}
func addUpstreamRemote(parentRepo ghrepo.Interface, cloneDir string) error {
// TODO: support SSH remote URLs
upstreamURL := fmt.Sprintf("https://github.com/%s.git", ghrepo.FullName(parentRepo))
func addUpstreamRemote(cmd *cobra.Command, parentRepo ghrepo.Interface, cloneDir string) error {
upstreamURL := formatRemoteURL(cmd, ghrepo.FullName(parentRepo))
cloneCmd := git.GitCommand("-C", cloneDir, "remote", "add", "-f", "upstream", upstreamURL)
cloneCmd.Stdout = os.Stdout
@ -267,14 +266,10 @@ func repoCreate(cmd *cobra.Command, args []string) error {
fmt.Fprintln(out, repo.URL)
}
remoteURL := repo.URL + ".git"
remoteURL := formatRemoteURL(cmd, ghrepo.FullName(repo))
if projectDirErr == nil {
// TODO: use git.AddRemote
remoteAdd := git.GitCommand("remote", "add", "origin", remoteURL)
remoteAdd.Stdout = os.Stdout
remoteAdd.Stderr = os.Stderr
err = run.PrepareCmd(remoteAdd).Run()
_, err = git.AddRemote("origin", remoteURL)
if err != nil {
return err
}
@ -338,7 +333,7 @@ func repoFork(cmd *cobra.Command, args []string) error {
return fmt.Errorf("unable to create client: %w", err)
}
var toFork ghrepo.Interface
var repoToFork ghrepo.Interface
inParent := false // whether or not we're forking the repo we're currently "in"
if len(args) == 0 {
baseRepo, err := determineBaseRepo(cmd, ctx)
@ -346,7 +341,7 @@ func repoFork(cmd *cobra.Command, args []string) error {
return fmt.Errorf("unable to determine base repository: %w", err)
}
inParent = true
toFork = baseRepo
repoToFork = baseRepo
} else {
repoArg := args[0]
@ -356,14 +351,23 @@ func repoFork(cmd *cobra.Command, args []string) error {
return fmt.Errorf("did not understand argument: %w", err)
}
toFork, err = ghrepo.FromURL(parsedURL)
repoToFork, err = ghrepo.FromURL(parsedURL)
if err != nil {
return fmt.Errorf("did not understand argument: %w", err)
}
} else if strings.HasPrefix(repoArg, "git@") {
parsedURL, err := git.ParseURL(repoArg)
if err != nil {
return fmt.Errorf("did not understand argument: %w", err)
}
repoToFork, err = ghrepo.FromURL(parsedURL)
if err != nil {
return fmt.Errorf("did not understand argument: %w", err)
}
} else {
toFork = ghrepo.FromFullName(repoArg)
if toFork.RepoName() == "" || toFork.RepoOwner() == "" {
repoToFork = ghrepo.FromFullName(repoArg)
if repoToFork.RepoName() == "" || repoToFork.RepoOwner() == "" {
return fmt.Errorf("could not parse owner or repo name from %s", repoArg)
}
}
@ -372,12 +376,12 @@ func repoFork(cmd *cobra.Command, args []string) error {
greenCheck := utils.Green("✓")
out := colorableOut(cmd)
s := utils.Spinner(out)
loading := utils.Gray("Forking ") + utils.Bold(utils.Gray(ghrepo.FullName(toFork))) + utils.Gray("...")
loading := utils.Gray("Forking ") + utils.Bold(utils.Gray(ghrepo.FullName(repoToFork))) + utils.Gray("...")
s.Suffix = " " + loading
s.FinalMSG = utils.Gray(fmt.Sprintf("- %s\n", loading))
s.Start()
forkedRepo, err := api.ForkRepo(apiClient, toFork)
forkedRepo, err := api.ForkRepo(apiClient, repoToFork)
if err != nil {
s.Stop()
return fmt.Errorf("failed to fork: %w", err)
@ -437,7 +441,9 @@ func repoFork(cmd *cobra.Command, args []string) error {
fmt.Fprintf(out, "%s Renamed %s remote to %s\n", greenCheck, utils.Bold(remoteName), utils.Bold(renameTarget))
}
_, err = git.AddRemote(remoteName, forkedRepo.CloneURL)
forkedRepoCloneURL := formatRemoteURL(cmd, ghrepo.FullName(forkedRepo))
_, err = git.AddRemote(remoteName, forkedRepoCloneURL)
if err != nil {
return fmt.Errorf("failed to add remote: %w", err)
}
@ -453,12 +459,13 @@ func repoFork(cmd *cobra.Command, args []string) error {
}
}
if cloneDesired {
cloneDir, err := runClone(forkedRepo.CloneURL, []string{})
forkedRepoCloneURL := formatRemoteURL(cmd, ghrepo.FullName(forkedRepo))
cloneDir, err := runClone(forkedRepoCloneURL, []string{})
if err != nil {
return fmt.Errorf("failed to clone fork: %w", err)
}
err = addUpstreamRemote(toFork, cloneDir)
err = addUpstreamRemote(cmd, repoToFork, cloneDir)
if err != nil {
return err
}

View file

@ -77,7 +77,7 @@ func stubSince(d time.Duration) func() {
}
func TestRepoFork_in_parent(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
defer stubSince(2 * time.Second)()
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -134,7 +134,7 @@ func TestRepoFork_outside(t *testing.T) {
}
func TestRepoFork_in_parent_yes(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
defer stubSince(2 * time.Second)()
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -153,7 +153,7 @@ func TestRepoFork_in_parent_yes(t *testing.T) {
expectedCmds := []string{
"git remote rename origin upstream",
"git remote add -f origin https://github.com/someone/repo.git",
"git remote add -f origin https://github.com/someone/REPO.git",
}
for x, cmd := range seenCmds {
@ -185,8 +185,8 @@ func TestRepoFork_outside_yes(t *testing.T) {
eq(t, output.Stderr(), "")
eq(t, strings.Join(cs.Calls[0].Args, " "), "git clone https://github.com/someone/repo.git")
eq(t, strings.Join(cs.Calls[1].Args, " "), "git -C repo remote add -f upstream https://github.com/OWNER/REPO.git")
eq(t, strings.Join(cs.Calls[0].Args, " "), "git clone https://github.com/someone/REPO.git")
eq(t, strings.Join(cs.Calls[1].Args, " "), "git -C REPO remote add -f upstream https://github.com/OWNER/REPO.git")
test.ExpectLines(t, output.String(),
"Created fork someone/REPO",
@ -218,8 +218,8 @@ func TestRepoFork_outside_survey_yes(t *testing.T) {
eq(t, output.Stderr(), "")
eq(t, strings.Join(cs.Calls[0].Args, " "), "git clone https://github.com/someone/repo.git")
eq(t, strings.Join(cs.Calls[1].Args, " "), "git -C repo remote add -f upstream https://github.com/OWNER/REPO.git")
eq(t, strings.Join(cs.Calls[0].Args, " "), "git clone https://github.com/someone/REPO.git")
eq(t, strings.Join(cs.Calls[1].Args, " "), "git -C REPO remote add -f upstream https://github.com/OWNER/REPO.git")
test.ExpectLines(t, output.String(),
"Created fork someone/REPO",
@ -261,7 +261,7 @@ func TestRepoFork_outside_survey_no(t *testing.T) {
}
func TestRepoFork_in_parent_survey_yes(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
defer stubSince(2 * time.Second)()
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -287,7 +287,7 @@ func TestRepoFork_in_parent_survey_yes(t *testing.T) {
expectedCmds := []string{
"git remote rename origin upstream",
"git remote add -f origin https://github.com/someone/repo.git",
"git remote add -f origin https://github.com/someone/REPO.git",
}
for x, cmd := range seenCmds {
@ -303,7 +303,7 @@ func TestRepoFork_in_parent_survey_yes(t *testing.T) {
}
func TestRepoFork_in_parent_survey_no(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
defer stubSince(2 * time.Second)()
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
@ -499,7 +499,11 @@ func TestRepoCreate(t *testing.T) {
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/OWNER/REPO"
"url": "https://github.com/OWNER/REPO",
"name": "REPO",
"owner": {
"login": "OWNER"
}
}
} } }
`))
@ -522,7 +526,7 @@ func TestRepoCreate(t *testing.T) {
if seenCmd == nil {
t.Fatal("expected a command to run")
}
eq(t, strings.Join(seenCmd.Args, " "), "git remote add origin https://github.com/OWNER/REPO.git")
eq(t, strings.Join(seenCmd.Args, " "), "git remote add -f origin https://github.com/OWNER/REPO.git")
var reqBody struct {
Query string
@ -564,7 +568,11 @@ func TestRepoCreate_org(t *testing.T) {
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/REPO"
"url": "https://github.com/ORG/REPO",
"name": "REPO",
"owner": {
"login": "ORG"
}
}
} } }
`))
@ -587,7 +595,7 @@ func TestRepoCreate_org(t *testing.T) {
if seenCmd == nil {
t.Fatal("expected a command to run")
}
eq(t, strings.Join(seenCmd.Args, " "), "git remote add origin https://github.com/ORG/REPO.git")
eq(t, strings.Join(seenCmd.Args, " "), "git remote add -f origin https://github.com/ORG/REPO.git")
var reqBody struct {
Query string
@ -629,7 +637,11 @@ func TestRepoCreate_orgWithTeam(t *testing.T) {
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/REPO"
"url": "https://github.com/ORG/REPO",
"name": "REPO",
"owner": {
"login": "ORG"
}
}
} } }
`))
@ -652,7 +664,7 @@ func TestRepoCreate_orgWithTeam(t *testing.T) {
if seenCmd == nil {
t.Fatal("expected a command to run")
}
eq(t, strings.Join(seenCmd.Args, " "), "git remote add origin https://github.com/ORG/REPO.git")
eq(t, strings.Join(seenCmd.Args, " "), "git remote add -f origin https://github.com/ORG/REPO.git")
var reqBody struct {
Query string
@ -678,7 +690,7 @@ func TestRepoCreate_orgWithTeam(t *testing.T) {
}
func TestRepoView_web(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -773,7 +785,7 @@ func TestRepoView_web_fullURL(t *testing.T) {
}
func TestRepoView(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -801,7 +813,7 @@ func TestRepoView(t *testing.T) {
}
func TestRepoView_nonmarkdown_readme(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
@ -828,7 +840,7 @@ func TestRepoView_nonmarkdown_readme(t *testing.T) {
}
func TestRepoView_blanks(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString("{}"))

View file

@ -10,6 +10,7 @@ import (
"github.com/cli/cli/api"
"github.com/cli/cli/context"
"github.com/cli/cli/internal/config"
"github.com/cli/cli/internal/ghrepo"
"github.com/cli/cli/utils"
@ -17,6 +18,9 @@ import (
"github.com/spf13/pflag"
)
// TODO these are sprinkled across command, context, config, and ghrepo
const defaultHostname = "github.com"
// Version is dynamically set by the toolchain or overridden by the Makefile.
var Version = "DEV"
@ -106,9 +110,21 @@ func BasicClient() (*api.Client, error) {
opts = append(opts, apiVerboseLog())
}
opts = append(opts, api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version)))
if c, err := context.ParseDefaultConfig(); err == nil {
opts = append(opts, api.AddHeader("Authorization", fmt.Sprintf("token %s", c.Token)))
c, err := config.ParseDefaultConfig()
if err != nil {
return nil, err
}
token, err := c.Get(defaultHostname, "oauth_token")
if err != nil {
return nil, err
}
if token == "" {
return nil, fmt.Errorf("no oauth_token set in config")
}
opts = append(opts, api.AddHeader("Authorization", fmt.Sprintf("token %s", token)))
return api.NewClient(opts...), nil
}
@ -206,3 +222,22 @@ func determineBaseRepo(cmd *cobra.Command, ctx context.Context) (ghrepo.Interfac
return baseRepo, nil
}
func formatRemoteURL(cmd *cobra.Command, fullRepoName string) string {
ctx := contextForCommand(cmd)
protocol := "https"
cfg, err := ctx.Config()
if err != nil {
fmt.Fprintf(colorableErr(cmd), "%s failed to load config: %s. using defaults\n", utils.Yellow("!"), err)
} else {
cfgProtocol, _ := cfg.Get(defaultHostname, "git_protocol")
protocol = cfgProtocol
}
if protocol == "ssh" {
return fmt.Sprintf("git@%s:%s.git", defaultHostname, fullRepoName)
}
return fmt.Sprintf("https://%s/%s.git", defaultHostname, fullRepoName)
}

View file

@ -40,3 +40,22 @@ func TestChangelogURL(t *testing.T) {
t.Errorf("expected %s to create url %s but got %s", tag, url, result)
}
}
func TestRemoteURLFormatting_no_config(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
result := formatRemoteURL(repoForkCmd, "OWNER/REPO")
eq(t, result, "https://github.com/OWNER/REPO.git")
}
func TestRemoteURLFormatting_ssh_config(t *testing.T) {
cfg := `---
hosts:
github.com:
user: OWNER
oauth_token: MUSTBEHIGHCUZIMATOKEN
git_protocol: ssh
`
initBlankContext(cfg, "OWNER/REPO", "master")
result := formatRemoteURL(repoForkCmd, "OWNER/REPO")
eq(t, result, "git@github.com:OWNER/REPO.git")
}

View file

@ -10,8 +10,15 @@ import (
"github.com/cli/cli/api"
"github.com/cli/cli/context"
"github.com/cli/cli/internal/config"
)
const defaultTestConfig = `hosts:
github.com:
user: OWNER
oauth_token: 1234567890
`
type askStubber struct {
Asks [][]*survey.Question
Count int
@ -63,7 +70,7 @@ func (as *askStubber) Stub(stubbedQuestions []*QuestionStub) {
as.Stubs = append(as.Stubs, stubbedQuestions)
}
func initBlankContext(repo, branch string) {
func initBlankContext(cfg, repo, branch string) {
initContext = func() context.Context {
ctx := context.NewBlank()
ctx.SetBaseRepo(repo)
@ -71,6 +78,15 @@ func initBlankContext(repo, branch string) {
ctx.SetRemotes(map[string]string{
"origin": "OWNER/REPO",
})
if cfg == "" {
cfg = defaultTestConfig
}
// NOTE we are not restoring the original readConfig; we never want to touch the config file on
// disk during tests.
config.StubConfig(cfg)
return ctx
}
}

View file

@ -2,6 +2,7 @@ package command
import (
"fmt"
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/cli/cli/pkg/githubtemplate"
@ -88,6 +89,16 @@ func selectTemplate(templatePaths []string) (string, error) {
}
func titleBodySurvey(cmd *cobra.Command, providedTitle, providedBody string, defs defaults, templatePaths []string, allowPreview bool) (*titleBody, error) {
editorCommand := os.Getenv("GH_EDITOR")
if editorCommand == "" {
ctx := contextForCommand(cmd)
cfg, err := ctx.Config()
if err != nil {
return nil, fmt.Errorf("could not read config: %w", err)
}
editorCommand, _ = cfg.Get(defaultHostname, "editor")
}
var inProgress titleBody
inProgress.Title = defs.Title
templateContents := ""
@ -115,6 +126,7 @@ func titleBodySurvey(cmd *cobra.Command, providedTitle, providedBody string, def
bodyQuestion := &survey.Question{
Name: "body",
Prompt: &surveyext.GhEditor{
EditorCommand: editorCommand,
Editor: &survey.Editor{
Message: "Body",
FileName: "*.md",