Disable telemetry for GHES
This commit is contained in:
parent
18dc5e77f0
commit
3ed389d664
23 changed files with 920 additions and 656 deletions
|
|
@ -223,7 +223,7 @@ func NewCmdApi(f *cmdutil.Factory, runF func(*ApiOptions) error) *cobra.Command
|
|||
},
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(c *cobra.Command, args []string) {
|
||||
opts.BaseRepo = cmdutil.OverrideBaseRepoFunc(f, "")
|
||||
opts.BaseRepo = cmdutil.OverrideBaseRepoFunc(f.BaseRepo, "")
|
||||
},
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
opts.RequestPath = args[0]
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
//go:build integration
|
||||
|
||||
package verify
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/telemetry"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/api"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/artifact/oci"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/io"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/test"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
|
||||
"github.com/cli/cli/v2/pkg/cmd/factory"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
o "github.com/cli/cli/v2/pkg/option"
|
||||
"github.com/cli/go-gh/v2/pkg/auth"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -26,12 +28,15 @@ func TestVerifyIntegration(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
hc, err := factory.HttpClientFunc(
|
||||
&config.AuthConfig{},
|
||||
ios,
|
||||
"test",
|
||||
"",
|
||||
&telemetry.NoOpService{},
|
||||
)()
|
||||
require.NoError(t, err)
|
||||
|
||||
host, _ := auth.DefaultHost()
|
||||
|
||||
|
|
@ -143,12 +148,15 @@ func TestVerifyIntegrationCustomIssuer(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
hc, err := factory.HttpClientFunc(
|
||||
&config.AuthConfig{},
|
||||
ios,
|
||||
"test",
|
||||
"",
|
||||
&telemetry.NoOpService{},
|
||||
)()
|
||||
require.NoError(t, err)
|
||||
|
||||
host, _ := auth.DefaultHost()
|
||||
|
||||
|
|
@ -217,12 +225,16 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg := config.NewBlankConfig()
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
hc, err := factory.HttpClientFunc(
|
||||
cfg.Authentication(),
|
||||
ios,
|
||||
"test",
|
||||
"",
|
||||
&telemetry.NoOpService{},
|
||||
)()
|
||||
require.NoError(t, err)
|
||||
|
||||
host, _ := auth.DefaultHost()
|
||||
|
||||
|
|
@ -310,22 +322,28 @@ func TestVerifyIntegrationReusableWorkflowSignerWorkflow(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg := config.NewBlankConfig()
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
hc, err := factory.HttpClientFunc(
|
||||
cfg.Authentication(),
|
||||
ios,
|
||||
"test",
|
||||
"",
|
||||
&telemetry.NoOpService{},
|
||||
)()
|
||||
require.NoError(t, err)
|
||||
|
||||
host, _ := auth.DefaultHost()
|
||||
|
||||
sigstoreVerifier, err := verification.NewLiveSigstoreVerifier(sigstoreConfig)
|
||||
require.NoError(t, err)
|
||||
baseOpts := Options{
|
||||
APIClient: api.NewLiveClient(hc, host, logger),
|
||||
ArtifactPath: artifactPath,
|
||||
BundlePath: bundlePath,
|
||||
Config: cmdFactory.Config,
|
||||
APIClient: api.NewLiveClient(hc, host, logger),
|
||||
ArtifactPath: artifactPath,
|
||||
BundlePath: bundlePath,
|
||||
Config: func() (gh.Config, error) {
|
||||
return cfg, nil
|
||||
},
|
||||
DigestAlgorithm: "sha256",
|
||||
Logger: logger,
|
||||
OCIClient: oci.NewLiveClient(),
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func NewCmdLogin(f *cmdutil.Factory, runF func(*LoginOptions) error) *cobra.Comm
|
|||
opts.Hostname, _ = ghauth.DefaultHost()
|
||||
}
|
||||
|
||||
opts.MainExecutable = f.Executable()
|
||||
opts.MainExecutable = f.ExecutablePath
|
||||
if runF != nil {
|
||||
return runF(opts)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ func NewCmdRefresh(f *cmdutil.Factory, runF func(*RefreshOptions) error) *cobra.
|
|||
return cmdutil.FlagErrorf("--hostname required when not running interactively")
|
||||
}
|
||||
|
||||
opts.MainExecutable = f.Executable()
|
||||
opts.MainExecutable = f.ExecutablePath
|
||||
if runF != nil {
|
||||
return runF(opts)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func NewCmdSetupGit(f *cmdutil.Factory, runF func(*SetupGitOptions) error) *cobr
|
|||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.CredentialsHelperConfig = &gitcredentials.HelperConfig{
|
||||
SelfExecutablePath: f.Executable(),
|
||||
SelfExecutablePath: f.ExecutablePath,
|
||||
GitClient: f.GitClient,
|
||||
}
|
||||
if opts.Hostname == "" && opts.Force {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,14 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type ghExecutable struct {
|
||||
executablePath string
|
||||
}
|
||||
|
||||
func (e *ghExecutable) Executable() string {
|
||||
return e.executablePath
|
||||
}
|
||||
|
||||
func NewCmdCodespace(f *cmdutil.Factory) *cobra.Command {
|
||||
root := &cobra.Command{
|
||||
Use: "codespace",
|
||||
|
|
@ -17,7 +25,7 @@ func NewCmdCodespace(f *cmdutil.Factory) *cobra.Command {
|
|||
|
||||
app := NewApp(
|
||||
f.IOStreams,
|
||||
f,
|
||||
&ghExecutable{executablePath: f.ExecutablePath},
|
||||
codespacesAPI.New(f),
|
||||
f.Browser,
|
||||
f.Remotes,
|
||||
|
|
|
|||
|
|
@ -4,46 +4,46 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/cli/cli/v2/api"
|
||||
ghContext "github.com/cli/cli/v2/context"
|
||||
"github.com/cli/cli/v2/git"
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/gh/ghtelemetry"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/prompter"
|
||||
"github.com/cli/cli/v2/pkg/cmd/extension"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
xcolor "github.com/cli/go-gh/v2/pkg/x/color"
|
||||
)
|
||||
|
||||
var ssoHeader string
|
||||
var ssoURLRE = regexp.MustCompile(`\burl=([^;]+)`)
|
||||
|
||||
func New(appVersion string, invokingAgent string) *cmdutil.Factory {
|
||||
func New(appVersion string, invokingAgent string, cfg gh.Config, ios *iostreams.IOStreams, executablePath string, telemetryDisabler ghtelemetry.Disabler) *cmdutil.Factory {
|
||||
f := &cmdutil.Factory{
|
||||
AppVersion: appVersion,
|
||||
InvokingAgent: invokingAgent,
|
||||
Config: configFunc(), // No factory dependencies
|
||||
ExecutableName: "gh",
|
||||
AppVersion: appVersion,
|
||||
InvokingAgent: invokingAgent,
|
||||
Cfg: cfg,
|
||||
Config: func() (gh.Config, error) {
|
||||
return cfg, nil
|
||||
}, // No factory dependencies
|
||||
ExecutablePath: executablePath,
|
||||
}
|
||||
|
||||
f.IOStreams = ioStreams(f) // Depends on Config
|
||||
f.HttpClient = httpClientFunc(f, appVersion, invokingAgent) // Depends on Config, IOStreams, appVersion, and invokingAgent
|
||||
f.PlainHttpClient = plainHttpClientFunc(f, appVersion, invokingAgent) // Depends on IOStreams, appVersion, and invokingAgent
|
||||
f.GitClient = newGitClient(f) // Depends on IOStreams, and Executable
|
||||
f.Remotes = remotesFunc(f) // Depends on Config, and GitClient
|
||||
f.BaseRepo = BaseRepoFunc(f) // Depends on Remotes
|
||||
f.Prompter = newPrompter(f) // Depends on Config and IOStreams
|
||||
f.Browser = newBrowser(f) // Depends on Config, and IOStreams
|
||||
f.ExtensionManager = extensionManager(f) // Depends on Config, HttpClient, and IOStreams
|
||||
f.Branch = branchFunc(f) // Depends on GitClient
|
||||
f.IOStreams = ios
|
||||
f.HttpClient = HttpClientFunc(cfg.Authentication(), ios, appVersion, invokingAgent, telemetryDisabler)
|
||||
f.PlainHttpClient = plainHttpClientFunc(ios, appVersion, invokingAgent, telemetryDisabler)
|
||||
f.GitClient = newGitClient(f) // Depends on IOStreams, and Executable
|
||||
f.Remotes = remotesFunc(f) // Depends on Config, and GitClient
|
||||
f.BaseRepo = BaseRepoFunc(f.Remotes)
|
||||
f.Prompter = newPrompter(f) // Depends on Config and IOStreams
|
||||
f.Browser = newBrowser(f) // Depends on Config, and IOStreams
|
||||
f.ExtensionManager = extensionManager(f) // Depends on Config, HttpClient, and IOStreams
|
||||
f.Branch = branchFunc(f) // Depends on GitClient
|
||||
|
||||
return f
|
||||
}
|
||||
|
|
@ -73,9 +73,9 @@ func New(appVersion string, invokingAgent string) *cmdutil.Factory {
|
|||
// origin https://github.com/cli/cli-fork.git (push)
|
||||
//
|
||||
// With this resolution function, the upstream will always be chosen (assuming we have authenticated with github.com).
|
||||
func BaseRepoFunc(f *cmdutil.Factory) func() (ghrepo.Interface, error) {
|
||||
func BaseRepoFunc(remotesFunc func() (ghContext.Remotes, error)) func() (ghrepo.Interface, error) {
|
||||
return func() (ghrepo.Interface, error) {
|
||||
remotes, err := f.Remotes()
|
||||
remotes, err := remotesFunc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -187,19 +187,15 @@ func remotesFunc(f *cmdutil.Factory) func() (ghContext.Remotes, error) {
|
|||
return rr.Resolver()
|
||||
}
|
||||
|
||||
func httpClientFunc(f *cmdutil.Factory, appVersion string, invokingAgent string) func() (*http.Client, error) {
|
||||
func HttpClientFunc(authCfg gh.AuthConfig, ios *iostreams.IOStreams, appVersion string, invokingAgent string, telemetryDisabler ghtelemetry.Disabler) func() (*http.Client, error) {
|
||||
return func() (*http.Client, error) {
|
||||
io := f.IOStreams
|
||||
cfg, err := f.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := api.HTTPClientOptions{
|
||||
Config: cfg.Authentication(),
|
||||
Log: io.ErrOut,
|
||||
LogColorize: io.ColorEnabled(),
|
||||
AppVersion: appVersion,
|
||||
InvokingAgent: invokingAgent,
|
||||
Config: authCfg,
|
||||
Log: ios.ErrOut,
|
||||
LogColorize: ios.ColorEnabled(),
|
||||
AppVersion: appVersion,
|
||||
InvokingAgent: invokingAgent,
|
||||
TelemetryDisabler: telemetryDisabler,
|
||||
}
|
||||
client, err := api.NewHTTPClient(opts)
|
||||
if err != nil {
|
||||
|
|
@ -210,16 +206,16 @@ func httpClientFunc(f *cmdutil.Factory, appVersion string, invokingAgent string)
|
|||
}
|
||||
}
|
||||
|
||||
func plainHttpClientFunc(f *cmdutil.Factory, appVersion string, invokingAgent string) func() (*http.Client, error) {
|
||||
func plainHttpClientFunc(ios *iostreams.IOStreams, appVersion string, invokingAgent string, telemetryDisabler ghtelemetry.Disabler) func() (*http.Client, error) {
|
||||
return func() (*http.Client, error) {
|
||||
io := f.IOStreams
|
||||
opts := api.HTTPClientOptions{
|
||||
Log: io.ErrOut,
|
||||
LogColorize: io.ColorEnabled(),
|
||||
Log: ios.ErrOut,
|
||||
LogColorize: ios.ColorEnabled(),
|
||||
AppVersion: appVersion,
|
||||
InvokingAgent: invokingAgent,
|
||||
// This is required to prevent automatic setting of auth and other headers.
|
||||
SkipDefaultHeaders: true,
|
||||
TelemetryDisabler: telemetryDisabler,
|
||||
}
|
||||
client, err := api.NewHTTPClient(opts)
|
||||
if err != nil {
|
||||
|
|
@ -231,9 +227,8 @@ func plainHttpClientFunc(f *cmdutil.Factory, appVersion string, invokingAgent st
|
|||
|
||||
func newGitClient(f *cmdutil.Factory) *git.Client {
|
||||
io := f.IOStreams
|
||||
ghPath := f.Executable()
|
||||
client := &git.Client{
|
||||
GhPath: ghPath,
|
||||
GhPath: f.ExecutablePath,
|
||||
Stderr: io.ErrOut,
|
||||
Stdin: io.In,
|
||||
Stdout: io.Out,
|
||||
|
|
@ -252,18 +247,6 @@ func newPrompter(f *cmdutil.Factory) prompter.Prompter {
|
|||
return prompter.New(editor, io)
|
||||
}
|
||||
|
||||
func configFunc() func() (gh.Config, error) {
|
||||
var cachedConfig gh.Config
|
||||
var configError error
|
||||
return func() (gh.Config, error) {
|
||||
if cachedConfig != nil || configError != nil {
|
||||
return cachedConfig, configError
|
||||
}
|
||||
cachedConfig, configError = config.NewConfig()
|
||||
return cachedConfig, configError
|
||||
}
|
||||
}
|
||||
|
||||
func branchFunc(f *cmdutil.Factory) func() (string, error) {
|
||||
return func() (string, error) {
|
||||
currentBranch, err := f.GitClient.CurrentBranch(context.Background())
|
||||
|
|
@ -293,72 +276,6 @@ func extensionManager(f *cmdutil.Factory) *extension.Manager {
|
|||
return em
|
||||
}
|
||||
|
||||
func ioStreams(f *cmdutil.Factory) *iostreams.IOStreams {
|
||||
io := iostreams.System()
|
||||
cfg, err := f.Config()
|
||||
if err != nil {
|
||||
return io
|
||||
}
|
||||
|
||||
if _, ghPromptDisabled := os.LookupEnv("GH_PROMPT_DISABLED"); ghPromptDisabled {
|
||||
io.SetNeverPrompt(true)
|
||||
} else if prompt := cfg.Prompt(""); prompt.Value == "disabled" {
|
||||
io.SetNeverPrompt(true)
|
||||
}
|
||||
|
||||
falseyValues := []string{"false", "0", "no", ""}
|
||||
|
||||
accessiblePrompterValue, accessiblePrompterIsSet := os.LookupEnv("GH_ACCESSIBLE_PROMPTER")
|
||||
if accessiblePrompterIsSet {
|
||||
if !slices.Contains(falseyValues, accessiblePrompterValue) {
|
||||
io.SetAccessiblePrompterEnabled(true)
|
||||
}
|
||||
} else if prompt := cfg.AccessiblePrompter(""); prompt.Value == "enabled" {
|
||||
io.SetAccessiblePrompterEnabled(true)
|
||||
}
|
||||
|
||||
experimentalPrompterValue, experimentalPrompterIsSet := os.LookupEnv("GH_EXPERIMENTAL_PROMPTER")
|
||||
if experimentalPrompterIsSet {
|
||||
if !slices.Contains(falseyValues, experimentalPrompterValue) {
|
||||
io.SetExperimentalPrompterEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
ghSpinnerDisabledValue, ghSpinnerDisabledIsSet := os.LookupEnv("GH_SPINNER_DISABLED")
|
||||
if ghSpinnerDisabledIsSet {
|
||||
if !slices.Contains(falseyValues, ghSpinnerDisabledValue) {
|
||||
io.SetSpinnerDisabled(true)
|
||||
}
|
||||
} else if spinnerDisabled := cfg.Spinner(""); spinnerDisabled.Value == "disabled" {
|
||||
io.SetSpinnerDisabled(true)
|
||||
}
|
||||
|
||||
// Pager precedence
|
||||
// 1. GH_PAGER
|
||||
// 2. pager from config
|
||||
// 3. PAGER
|
||||
if ghPager, ghPagerExists := os.LookupEnv("GH_PAGER"); ghPagerExists {
|
||||
io.SetPager(ghPager)
|
||||
} else if pager := cfg.Pager(""); pager.Value != "" {
|
||||
io.SetPager(pager.Value)
|
||||
}
|
||||
|
||||
if ghColorLabels, ghColorLabelsExists := os.LookupEnv("GH_COLOR_LABELS"); ghColorLabelsExists {
|
||||
switch ghColorLabels {
|
||||
case "", "0", "false", "no":
|
||||
io.SetColorLabels(false)
|
||||
default:
|
||||
io.SetColorLabels(true)
|
||||
}
|
||||
} else if prompt := cfg.ColorLabels(""); prompt.Value == "enabled" {
|
||||
io.SetColorLabels(true)
|
||||
}
|
||||
|
||||
io.SetAccessibleColorsEnabled(xcolor.IsAccessibleColorsEnabled())
|
||||
|
||||
return io
|
||||
}
|
||||
|
||||
// SSOURL returns the URL of a SAML SSO challenge received by the server for clients that use ExtractHeader
|
||||
// to extract the value of the "X-GitHub-SSO" response header.
|
||||
func SSOURL() string {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
ghmock "github.com/cli/cli/v2/internal/gh/mock"
|
||||
"github.com/cli/cli/v2/internal/telemetry"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/httpmock"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
|
|
@ -66,7 +67,6 @@ func Test_BaseRepo(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1", "")
|
||||
rr := &remoteResolver{
|
||||
readRemotes: func() (git.RemoteSet, error) {
|
||||
return tt.remotes, nil
|
||||
|
|
@ -90,8 +90,10 @@ func Test_BaseRepo(t *testing.T) {
|
|||
return cfg, nil
|
||||
},
|
||||
}
|
||||
f.Remotes = rr.Resolver()
|
||||
f.BaseRepo = BaseRepoFunc(f)
|
||||
remotes := rr.Resolver()
|
||||
f := &cmdutil.Factory{
|
||||
BaseRepo: BaseRepoFunc(remotes),
|
||||
}
|
||||
repo, err := f.BaseRepo()
|
||||
if tt.wantsErr {
|
||||
assert.Error(t, err)
|
||||
|
|
@ -204,7 +206,7 @@ func Test_SmartBaseRepo(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1", "")
|
||||
f := &cmdutil.Factory{}
|
||||
rr := &remoteResolver{
|
||||
readRemotes: func() (git.RemoteSet, error) {
|
||||
return tt.remotes, nil
|
||||
|
|
@ -297,7 +299,6 @@ func Test_OverrideBaseRepo(t *testing.T) {
|
|||
if tt.envOverride != "" {
|
||||
t.Setenv("GH_REPO", tt.envOverride)
|
||||
}
|
||||
f := New("1", "")
|
||||
rr := &remoteResolver{
|
||||
readRemotes: func() (git.RemoteSet, error) {
|
||||
return tt.remotes, nil
|
||||
|
|
@ -306,8 +307,10 @@ func Test_OverrideBaseRepo(t *testing.T) {
|
|||
return tt.config, nil
|
||||
},
|
||||
}
|
||||
f.Remotes = rr.Resolver()
|
||||
f.BaseRepo = cmdutil.OverrideBaseRepoFunc(f, tt.argOverride)
|
||||
remotes := rr.Resolver()
|
||||
f := &cmdutil.Factory{
|
||||
BaseRepo: cmdutil.OverrideBaseRepoFunc(BaseRepoFunc(remotes), tt.argOverride),
|
||||
}
|
||||
repo, err := f.BaseRepo()
|
||||
if tt.wantsErr {
|
||||
assert.Error(t, err)
|
||||
|
|
@ -321,341 +324,6 @@ func Test_OverrideBaseRepo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_ioStreams_pager(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
config gh.Config
|
||||
wantPager string
|
||||
}{
|
||||
{
|
||||
name: "GH_PAGER and PAGER set",
|
||||
env: map[string]string{
|
||||
"GH_PAGER": "GH_PAGER",
|
||||
"PAGER": "PAGER",
|
||||
},
|
||||
wantPager: "GH_PAGER",
|
||||
},
|
||||
{
|
||||
name: "GH_PAGER and config pager set",
|
||||
env: map[string]string{
|
||||
"GH_PAGER": "GH_PAGER",
|
||||
},
|
||||
config: pagerConfig(),
|
||||
wantPager: "GH_PAGER",
|
||||
},
|
||||
{
|
||||
name: "config pager and PAGER set",
|
||||
env: map[string]string{
|
||||
"PAGER": "PAGER",
|
||||
},
|
||||
config: pagerConfig(),
|
||||
wantPager: "CONFIG_PAGER",
|
||||
},
|
||||
{
|
||||
name: "only PAGER set",
|
||||
env: map[string]string{
|
||||
"PAGER": "PAGER",
|
||||
},
|
||||
wantPager: "PAGER",
|
||||
},
|
||||
{
|
||||
name: "GH_PAGER set to blank string",
|
||||
env: map[string]string{
|
||||
"GH_PAGER": "",
|
||||
"PAGER": "PAGER",
|
||||
},
|
||||
wantPager: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.env != nil {
|
||||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
} else {
|
||||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
io := ioStreams(f)
|
||||
assert.Equal(t, tt.wantPager, io.GetPager())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ioStreams_prompt(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config gh.Config
|
||||
promptDisabled bool
|
||||
env map[string]string
|
||||
}{
|
||||
{
|
||||
name: "default config",
|
||||
promptDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "config with prompt disabled",
|
||||
config: disablePromptConfig(),
|
||||
promptDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "prompt disabled via GH_PROMPT_DISABLED env var",
|
||||
env: map[string]string{"GH_PROMPT_DISABLED": "1"},
|
||||
promptDisabled: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.env != nil {
|
||||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
} else {
|
||||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
io := ioStreams(f)
|
||||
assert.Equal(t, tt.promptDisabled, io.GetNeverPrompt())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ioStreams_spinnerDisabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config gh.Config
|
||||
spinnerDisabled bool
|
||||
env map[string]string
|
||||
}{
|
||||
{
|
||||
name: "default config",
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "config with spinner disabled",
|
||||
config: disableSpinnersConfig(),
|
||||
spinnerDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "config with spinner enabled",
|
||||
config: enableSpinnersConfig(),
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "spinner disabled via GH_SPINNER_DISABLED env var = 0",
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "0"},
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "spinner disabled via GH_SPINNER_DISABLED env var = false",
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "false"},
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "spinner disabled via GH_SPINNER_DISABLED env var = no",
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "no"},
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "spinner enabled via GH_SPINNER_DISABLED env var = 1",
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "1"},
|
||||
spinnerDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "spinner enabled via GH_SPINNER_DISABLED env var = true",
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "true"},
|
||||
spinnerDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "config enabled but env disabled, respects env",
|
||||
config: enableSpinnersConfig(),
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "true"},
|
||||
spinnerDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "config disabled but env enabled, respects env",
|
||||
config: disableSpinnersConfig(),
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "false"},
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
} else {
|
||||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
io := ioStreams(f)
|
||||
assert.Equal(t, tt.spinnerDisabled, io.GetSpinnerDisabled())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ioStreams_accessiblePrompterEnabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config gh.Config
|
||||
accessiblePrompterEnabled bool
|
||||
env map[string]string
|
||||
}{
|
||||
{
|
||||
name: "default config",
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "config with accessible prompter enabled",
|
||||
config: enableAccessiblePrompterConfig(),
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "config with accessible prompter disabled",
|
||||
config: disableAccessiblePrompterConfig(),
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "accessible prompter enabled via GH_ACCESSIBLE_PROMPTER env var = 1",
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "1"},
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "accessible prompter enabled via GH_ACCESSIBLE_PROMPTER env var = true",
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "true"},
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "accessible prompter disabled via GH_ACCESSIBLE_PROMPTER env var = 0",
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "0"},
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "config disabled but env enabled, respects env",
|
||||
config: disableAccessiblePrompterConfig(),
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "true"},
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "config enabled but env disabled, respects env",
|
||||
config: enableAccessiblePrompterConfig(),
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "false"},
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
} else {
|
||||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
io := ioStreams(f)
|
||||
assert.Equal(t, tt.accessiblePrompterEnabled, io.AccessiblePrompterEnabled())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ioStreams_colorLabels(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config gh.Config
|
||||
colorLabelsEnabled bool
|
||||
env map[string]string
|
||||
}{
|
||||
{
|
||||
name: "default config",
|
||||
colorLabelsEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "config with colorLabels enabled",
|
||||
config: enableColorLabelsConfig(),
|
||||
colorLabelsEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "config with colorLabels disabled",
|
||||
config: disableColorLabelsConfig(),
|
||||
colorLabelsEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "colorLabels enabled via `1` in GH_COLOR_LABELS env var",
|
||||
env: map[string]string{"GH_COLOR_LABELS": "1"},
|
||||
colorLabelsEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "colorLabels enabled via `true` in GH_COLOR_LABELS env var",
|
||||
env: map[string]string{"GH_COLOR_LABELS": "true"},
|
||||
colorLabelsEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "colorLabels enabled via `yes` in GH_COLOR_LABELS env var",
|
||||
env: map[string]string{"GH_COLOR_LABELS": "yes"},
|
||||
colorLabelsEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "colorLabels disable via empty string in GH_COLOR_LABELS env var",
|
||||
env: map[string]string{"GH_COLOR_LABELS": ""},
|
||||
colorLabelsEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "colorLabels disabled via `0` in GH_COLOR_LABELS env var",
|
||||
env: map[string]string{"GH_COLOR_LABELS": "0"},
|
||||
colorLabelsEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "colorLabels disabled via `false` in GH_COLOR_LABELS env var",
|
||||
env: map[string]string{"GH_COLOR_LABELS": "false"},
|
||||
colorLabelsEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "colorLabels disabled via `no` in GH_COLOR_LABELS env var",
|
||||
env: map[string]string{"GH_COLOR_LABELS": "no"},
|
||||
colorLabelsEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.env != nil {
|
||||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
} else {
|
||||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
io := ioStreams(f)
|
||||
assert.Equal(t, tt.colorLabelsEnabled, io.ColorLabels())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSOURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -683,13 +351,9 @@ func TestSSOURL(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
}
|
||||
cfg := config.NewBlankConfig()
|
||||
ios, _, _, stderr := iostreams.Test()
|
||||
f.IOStreams = ios
|
||||
client, err := httpClientFunc(f, "v1.2.3", "")()
|
||||
client, err := HttpClientFunc(cfg.Authentication(), ios, "v1.2.3", "", &telemetry.NoOpService{})()
|
||||
require.NoError(t, err)
|
||||
req, err := http.NewRequest("GET", ts.URL, nil)
|
||||
if tt.sso != "" {
|
||||
|
|
@ -718,13 +382,8 @@ func TestPlainHttpClient(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
}
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
f.IOStreams = ios
|
||||
client, err := plainHttpClientFunc(f, "v1.2.3", "")()
|
||||
client, err := plainHttpClientFunc(ios, "v1.2.3", "", &telemetry.NoOpService{})()
|
||||
require.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest("GET", ts.URL, nil)
|
||||
|
|
@ -759,7 +418,7 @@ func TestNewGitClient(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1", "")
|
||||
f := &cmdutil.Factory{}
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
|
|
@ -767,7 +426,7 @@ func TestNewGitClient(t *testing.T) {
|
|||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
f.ExecutableName = tt.executable
|
||||
f.ExecutablePath = tt.executable
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
f.IOStreams = ios
|
||||
c := newGitClient(f)
|
||||
|
|
@ -784,35 +443,3 @@ func defaultConfig() *ghmock.ConfigMock {
|
|||
cfg.Set("nonsense.com", "oauth_token", "BLAH")
|
||||
return cfg
|
||||
}
|
||||
|
||||
func pagerConfig() gh.Config {
|
||||
return config.NewFromString("pager: CONFIG_PAGER")
|
||||
}
|
||||
|
||||
func disablePromptConfig() gh.Config {
|
||||
return config.NewFromString("prompt: disabled")
|
||||
}
|
||||
|
||||
func enableAccessiblePrompterConfig() gh.Config {
|
||||
return config.NewFromString("accessible_prompter: enabled")
|
||||
}
|
||||
|
||||
func disableAccessiblePrompterConfig() gh.Config {
|
||||
return config.NewFromString("accessible_prompter: disabled")
|
||||
}
|
||||
|
||||
func disableSpinnersConfig() gh.Config {
|
||||
return config.NewFromString("spinner: disabled")
|
||||
}
|
||||
|
||||
func enableSpinnersConfig() gh.Config {
|
||||
return config.NewFromString("spinner: enabled")
|
||||
}
|
||||
|
||||
func disableColorLabelsConfig() gh.Config {
|
||||
return config.NewFromString("color_labels: disabled")
|
||||
}
|
||||
|
||||
func enableColorLabelsConfig() gh.Config {
|
||||
return config.NewFromString("color_labels: enabled")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,22 +2,27 @@ package shared
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/pkg/cmd/factory"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/cli/cli/v2/pkg/search"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSearcher(t *testing.T) {
|
||||
f := factory.New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
f := &cmdutil.Factory{
|
||||
Config: func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
},
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{}, nil
|
||||
},
|
||||
}
|
||||
_, err := Searcher(f)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ import (
|
|||
)
|
||||
|
||||
type PreviewOptions struct {
|
||||
IO *iostreams.IOStreams
|
||||
HttpClient func() (*http.Client, error)
|
||||
Prompter prompter.Prompter
|
||||
Executable func() string
|
||||
RenderFile func(string, string) string
|
||||
IO *iostreams.IOStreams
|
||||
HttpClient func() (*http.Client, error)
|
||||
Prompter prompter.Prompter
|
||||
ExecutablePath string
|
||||
RenderFile func(string, string) string
|
||||
|
||||
RepoArg string
|
||||
SkillName string
|
||||
|
|
@ -38,10 +38,10 @@ type PreviewOptions struct {
|
|||
// NewCmdPreview creates the "skills preview" command.
|
||||
func NewCmdPreview(f *cmdutil.Factory, runF func(*PreviewOptions) error) *cobra.Command {
|
||||
opts := &PreviewOptions{
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Prompter: f.Prompter,
|
||||
Executable: f.Executable,
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Prompter: f.Prompter,
|
||||
ExecutablePath: f.ExecutablePath,
|
||||
}
|
||||
opts.RenderFile = func(filePath, content string) string {
|
||||
return renderMarkdownPreview(opts.IO, filePath, content)
|
||||
|
|
|
|||
|
|
@ -47,12 +47,12 @@ var SkillSearchFields = []string{
|
|||
}
|
||||
|
||||
type SearchOptions struct {
|
||||
IO *iostreams.IOStreams
|
||||
HttpClient func() (*http.Client, error)
|
||||
Config func() (gh.Config, error)
|
||||
Prompter prompter.Prompter
|
||||
Executable string // path to the current gh binary for install subprocess
|
||||
Exporter cmdutil.Exporter
|
||||
IO *iostreams.IOStreams
|
||||
HttpClient func() (*http.Client, error)
|
||||
Config func() (gh.Config, error)
|
||||
Prompter prompter.Prompter
|
||||
ExecutablePath string // path to the current gh binary for install subprocess
|
||||
Exporter cmdutil.Exporter
|
||||
|
||||
// User inputs
|
||||
Query string
|
||||
|
|
@ -64,11 +64,11 @@ type SearchOptions struct {
|
|||
// NewCmdSearch creates the "skills search" command.
|
||||
func NewCmdSearch(f *cmdutil.Factory, runF func(*SearchOptions) error) *cobra.Command {
|
||||
opts := &SearchOptions{
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Config: f.Config,
|
||||
Prompter: f.Prompter,
|
||||
Executable: f.Executable(),
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Config: f.Config,
|
||||
Prompter: f.Prompter,
|
||||
ExecutablePath: f.ExecutablePath,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
@ -585,7 +585,7 @@ func promptInstall(opts *SearchOptions, skills []skillResult) error {
|
|||
}
|
||||
|
||||
//nolint:gosec // arguments are from user-selected search results, not arbitrary input
|
||||
cmd := exec.Command(opts.Executable, "skills", "install", s.Repo, installArg,
|
||||
cmd := exec.Command(opts.ExecutablePath, "skills", "install", s.Repo, installArg,
|
||||
"--agent", host.ID, "--scope", scope)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = opts.IO.Out
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ package cmdutil
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/cli/v2/context"
|
||||
"github.com/cli/cli/v2/git"
|
||||
|
|
@ -18,7 +15,7 @@ import (
|
|||
|
||||
type Factory struct {
|
||||
AppVersion string
|
||||
ExecutableName string
|
||||
ExecutablePath string
|
||||
InvokingAgent string
|
||||
|
||||
Browser browser.Browser
|
||||
|
|
@ -27,8 +24,11 @@ type Factory struct {
|
|||
IOStreams *iostreams.IOStreams
|
||||
Prompter prompter.Prompter
|
||||
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Branch func() (string, error)
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Branch func() (string, error)
|
||||
Cfg gh.Config
|
||||
// TODO: Config should be removed in favour of cfg being passed to the right place,
|
||||
// but this is going to be very invasive and shouldn't be done as part of a feature change.
|
||||
Config func() (gh.Config, error)
|
||||
HttpClient func() (*http.Client, error)
|
||||
// PlainHttpClient is a special HTTP client that does not automatically set
|
||||
|
|
@ -37,69 +37,3 @@ type Factory struct {
|
|||
PlainHttpClient func() (*http.Client, error)
|
||||
Remotes func() (context.Remotes, error)
|
||||
}
|
||||
|
||||
// Executable is the path to the currently invoked binary
|
||||
func (f *Factory) Executable() string {
|
||||
ghPath := os.Getenv("GH_PATH")
|
||||
if ghPath != "" {
|
||||
return ghPath
|
||||
}
|
||||
if !strings.ContainsRune(f.ExecutableName, os.PathSeparator) {
|
||||
f.ExecutableName = executable(f.ExecutableName)
|
||||
}
|
||||
return f.ExecutableName
|
||||
}
|
||||
|
||||
// Finds the location of the executable for the current process as it's found in PATH, respecting symlinks.
|
||||
// If the process couldn't determine its location, return fallbackName. If the executable wasn't found in
|
||||
// PATH, return the absolute location to the program.
|
||||
//
|
||||
// The idea is that the result of this function is callable in the future and refers to the same
|
||||
// installation of gh, even across upgrades. This is needed primarily for Homebrew, which installs software
|
||||
// under a location such as `/usr/local/Cellar/gh/1.13.1/bin/gh` and symlinks it from `/usr/local/bin/gh`.
|
||||
// When the version is upgraded, Homebrew will often delete older versions, but keep the symlink. Because of
|
||||
// this, we want to refer to the `gh` binary as `/usr/local/bin/gh` and not as its internal Homebrew
|
||||
// location.
|
||||
//
|
||||
// None of this would be needed if we could just refer to GitHub CLI as `gh`, i.e. without using an absolute
|
||||
// path. However, for some reason Homebrew does not include `/usr/local/bin` in PATH when it invokes git
|
||||
// commands to update its taps. If `gh` (no path) is being used as git credential helper, as set up by `gh
|
||||
// auth login`, running `brew update` will print out authentication errors as git is unable to locate
|
||||
// Homebrew-installed `gh`.
|
||||
func executable(fallbackName string) string {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return fallbackName
|
||||
}
|
||||
|
||||
base := filepath.Base(exe)
|
||||
path := os.Getenv("PATH")
|
||||
for _, dir := range filepath.SplitList(path) {
|
||||
p, err := filepath.Abs(filepath.Join(dir, base))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
f, err := os.Lstat(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p == exe {
|
||||
return p
|
||||
} else if f.Mode()&os.ModeSymlink != 0 {
|
||||
realP, err := filepath.EvalSymlinks(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
realExe, err := filepath.EvalSymlinks(exe)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if realP == realExe {
|
||||
return p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exe
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
package cmdutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_executable(t *testing.T) {
|
||||
testExe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testExeName := filepath.Base(testExe)
|
||||
|
||||
// Create 3 extra PATH entries that each contain an executable with the same name as the running test
|
||||
// process. The first is a symlink, but to an unrelated executable, the second is a symlink to our test
|
||||
// process and thus represents the result we want, and the third one is an unrelated executable.
|
||||
dir := t.TempDir()
|
||||
bin1 := filepath.Join(dir, "bin1")
|
||||
bin1Exe := filepath.Join(bin1, testExeName)
|
||||
bin2 := filepath.Join(dir, "bin2")
|
||||
bin2Exe := filepath.Join(bin2, testExeName)
|
||||
bin3 := filepath.Join(dir, "bin3")
|
||||
bin3Exe := filepath.Join(bin3, testExeName)
|
||||
|
||||
if err := os.MkdirAll(bin1, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(bin2, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(bin3, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f, err := os.OpenFile(bin3Exe, os.O_CREATE, 0755); err == nil {
|
||||
f.Close()
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(testExe, bin2Exe); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(bin3Exe, bin1Exe); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oldPath := os.Getenv("PATH")
|
||||
t.Setenv("PATH", strings.Join([]string{bin1, bin2, bin3, oldPath}, string(os.PathListSeparator)))
|
||||
|
||||
if got := executable(""); got != bin2Exe {
|
||||
t.Errorf("executable() = %q, want %q", got, bin2Exe)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_executable_relative(t *testing.T) {
|
||||
testExe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testExeName := filepath.Base(testExe)
|
||||
|
||||
// Create 3 extra PATH entries that each contain an executable with the same name as the running test
|
||||
// process. The first is a relative symlink, but to an unrelated executable, the second is a relative
|
||||
// symlink to our test process and thus represents the result we want, and the third one is an unrelated
|
||||
// executable.
|
||||
dir := t.TempDir()
|
||||
bin1 := filepath.Join(dir, "bin1")
|
||||
bin1Exe := filepath.Join(bin1, testExeName)
|
||||
bin2 := filepath.Join(dir, "bin2")
|
||||
bin2Exe := filepath.Join(bin2, testExeName)
|
||||
bin3 := filepath.Join(dir, "bin3")
|
||||
bin3Exe := filepath.Join(bin3, testExeName)
|
||||
|
||||
if err := os.MkdirAll(bin1, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(bin2, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(bin3, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if f, err := os.OpenFile(bin3Exe, os.O_CREATE, 0755); err == nil {
|
||||
f.Close()
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bin2Rel, err := filepath.Rel(bin2, testExe)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(bin2Rel, bin2Exe); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bin1Rel, err := filepath.Rel(bin1, bin3Exe)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(bin1Rel, bin1Exe); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oldPath := os.Getenv("PATH")
|
||||
t.Setenv("PATH", strings.Join([]string{bin1, bin2, bin3, oldPath}, string(os.PathListSeparator)))
|
||||
|
||||
if got := executable(""); got != bin2Exe {
|
||||
t.Errorf("executable() = %q, want %q", got, bin2Exe)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Executable_override(t *testing.T) {
|
||||
override := strings.Join([]string{"C:", "cygwin64", "home", "gh.exe"}, string(os.PathSeparator))
|
||||
t.Setenv("GH_PATH", override)
|
||||
f := Factory{}
|
||||
if got := f.Executable(); got != override {
|
||||
t.Errorf("executable() = %q, want %q", got, override)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,12 +52,12 @@ func EnableRepoOverride(cmd *cobra.Command, f *Factory) {
|
|||
return err
|
||||
}
|
||||
repoOverride, _ := cmd.Flags().GetString("repo")
|
||||
f.BaseRepo = OverrideBaseRepoFunc(f, repoOverride)
|
||||
f.BaseRepo = OverrideBaseRepoFunc(f.BaseRepo, repoOverride)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func OverrideBaseRepoFunc(f *Factory, override string) func() (ghrepo.Interface, error) {
|
||||
func OverrideBaseRepoFunc(baseRepoFunc func() (ghrepo.Interface, error), override string) func() (ghrepo.Interface, error) {
|
||||
if override == "" {
|
||||
override = os.Getenv("GH_REPO")
|
||||
}
|
||||
|
|
@ -66,5 +66,5 @@ func OverrideBaseRepoFunc(f *Factory, override string) func() (ghrepo.Interface,
|
|||
return ghrepo.FromFullName(override)
|
||||
}
|
||||
}
|
||||
return f.BaseRepo
|
||||
return baseRepoFunc
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue