Record agentic invocations in User-Agent header
Detect which AI coding agent is invoking gh by checking well-known environment variables and include the agent name in the User-Agent header sent to GitHub APIs. Supported agents: Codex, Gemini CLI, Copilot CLI, OpenCode, Claude Code, and Amp. Generic AI_AGENT env var is also supported with validation to prevent header injection. Fixes github/cli#1111 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
8723e3bb52
commit
c51769c977
10 changed files with 311 additions and 41 deletions
|
|
@ -15,7 +15,6 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
accept = "Accept"
|
||||
apiVersion = "X-GitHub-Api-Version"
|
||||
apiVersionValue = "2022-11-28"
|
||||
authorization = "Authorization"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ type tokenGetter interface {
|
|||
|
||||
type HTTPClientOptions struct {
|
||||
AppVersion string
|
||||
InvokingAgent string
|
||||
CacheTTL time.Duration
|
||||
Config tokenGetter
|
||||
EnableCache bool
|
||||
|
|
@ -48,8 +49,13 @@ func NewHTTPClient(opts HTTPClientOptions) (*http.Client, error) {
|
|||
clientOpts.LogVerboseHTTP = opts.LogVerboseHTTP
|
||||
}
|
||||
|
||||
ua := fmt.Sprintf("GitHub CLI %s", opts.AppVersion)
|
||||
if opts.InvokingAgent != "" {
|
||||
ua = fmt.Sprintf("%s Agent/%s", ua, opts.InvokingAgent)
|
||||
}
|
||||
|
||||
headers := map[string]string{
|
||||
userAgent: fmt.Sprintf("GitHub CLI %s", opts.AppVersion),
|
||||
userAgent: ua,
|
||||
apiVersion: apiVersionValue,
|
||||
}
|
||||
clientOpts.Headers = headers
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ func TestNewHTTPClient(t *testing.T) {
|
|||
type args struct {
|
||||
config tokenGetter
|
||||
appVersion string
|
||||
invokingAgent string
|
||||
logVerboseHTTP bool
|
||||
skipDefaultHeaders bool
|
||||
}
|
||||
|
|
@ -155,6 +156,18 @@ func TestNewHTTPClient(t *testing.T) {
|
|||
* Request took <duration>
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "includes invoking agent in user-agent header",
|
||||
args: args{
|
||||
appVersion: "v1.2.3",
|
||||
invokingAgent: "copilot-cli",
|
||||
},
|
||||
host: "github.com",
|
||||
wantHeader: map[string][]string{
|
||||
"user-agent": {"GitHub CLI v1.2.3 Agent/copilot-cli"},
|
||||
},
|
||||
wantStderr: "",
|
||||
},
|
||||
}
|
||||
|
||||
var gotReq *http.Request
|
||||
|
|
@ -169,6 +182,7 @@ func TestNewHTTPClient(t *testing.T) {
|
|||
ios, _, _, stderr := iostreams.Test()
|
||||
client, err := NewHTTPClient(HTTPClientOptions{
|
||||
AppVersion: tt.args.appVersion,
|
||||
InvokingAgent: tt.args.invokingAgent,
|
||||
Config: tt.args.config,
|
||||
Log: ios.ErrOut,
|
||||
LogVerboseHTTP: tt.args.logVerboseHTTP,
|
||||
|
|
|
|||
99
internal/agents/detect.go
Normal file
99
internal/agents/detect.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package agents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// AgentName is a validated agent identifier safe for use in HTTP headers.
|
||||
type AgentName string
|
||||
|
||||
const (
|
||||
agentAmp AgentName = "amp"
|
||||
agentClaudeCode AgentName = "claude-code"
|
||||
agentCodex AgentName = "codex"
|
||||
agentCopilotCLI AgentName = "copilot-cli"
|
||||
agentGeminiCLI AgentName = "gemini-cli"
|
||||
agentOpencode AgentName = "opencode"
|
||||
)
|
||||
|
||||
var validAgentName = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
|
||||
|
||||
// parseAgentName validates and returns an AgentName from a raw string.
|
||||
// Only alphanumeric characters, hyphens, and underscores are allowed.
|
||||
func parseAgentName(s string) (AgentName, error) {
|
||||
if !validAgentName.MatchString(s) {
|
||||
return "", fmt.Errorf("invalid agent name %q: must match [a-zA-Z0-9_-]+", s)
|
||||
}
|
||||
return AgentName(s), nil
|
||||
}
|
||||
|
||||
// Detect returns the name of the AI coding agent driving the CLI,
|
||||
// or an empty AgentName if none is detected.
|
||||
func Detect() AgentName {
|
||||
return detectWith(os.LookupEnv)
|
||||
}
|
||||
|
||||
func detectWith(lookup func(string) (string, bool)) AgentName {
|
||||
isSet := func(key string) bool {
|
||||
v, ok := lookup(key)
|
||||
return ok && v != ""
|
||||
}
|
||||
|
||||
valueOf := func(key string) string {
|
||||
v, _ := lookup(key)
|
||||
return v
|
||||
}
|
||||
|
||||
// Generic agent identifiers — checked first because they are the most specific signal.
|
||||
if v, ok := lookup("AI_AGENT"); ok && v != "" {
|
||||
if name, err := parseAgentName(v); err == nil {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
// Tool-specific variables.
|
||||
|
||||
// Check AGENT=amp before the more generic CLAUDECODE=1 since Amp sets both.
|
||||
if valueOf("AGENT") == "amp" {
|
||||
return agentAmp
|
||||
}
|
||||
|
||||
// OpenAI Codex CLI — https://github.com/openai/codex
|
||||
// CODEX_SANDBOX: https://github.com/openai/codex/blob/95e1d5993985019ce0ce0d10689caf1375f95120/codex-rs/core/src/spawn.rs#L25
|
||||
// CODEX_THREAD_ID: https://github.com/openai/codex/blob/95e1d5993985019ce0ce0d10689caf1375f95120/codex-rs/core/src/exec_env.rs#L8
|
||||
// CODEX_CI: https://github.com/openai/codex/blob/95e1d5993985019ce0ce0d10689caf1375f95120/codex-rs/core/src/unified_exec/process_manager.rs#L64
|
||||
if isSet("CODEX_SANDBOX") || isSet("CODEX_CI") || isSet("CODEX_THREAD_ID") {
|
||||
return agentCodex
|
||||
}
|
||||
|
||||
// Google Gemini CLI — https://github.com/google-gemini/gemini-cli
|
||||
// GEMINI_CLI: https://github.com/google-gemini/gemini-cli/blob/46fd7b4864111032a1c7dfa1821b2000fc7531da/docs/tools/shell.md#L96-L97
|
||||
if isSet("GEMINI_CLI") {
|
||||
return agentGeminiCLI
|
||||
}
|
||||
|
||||
// GitHub Copilot CLI
|
||||
// No first-party docs
|
||||
if isSet("COPILOT_CLI") {
|
||||
return agentCopilotCLI
|
||||
}
|
||||
|
||||
// OpenCode — https://github.com/anomalyco/opencode
|
||||
// OPENCODE: https://github.com/anomalyco/opencode/blob/fde201c286a83ff32dda9b41d61d734a4449fe70/packages/opencode/src/index.ts#L78-L80
|
||||
if isSet("OPENCODE") {
|
||||
return agentOpencode
|
||||
}
|
||||
|
||||
// Anthropic Claude Code — https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview
|
||||
// CLAUDECODE: https://code.claude.com/docs/en/env-vars (CLAUDECODE section)
|
||||
// Checked last because other agents (e.g. Amp) set CLAUDECODE=1 alongside their own vars.
|
||||
if isSet("CLAUDECODE") {
|
||||
// There is a CLAUDE_CODE_ENTRYPOINT env var that is set to `cli` or `desktop` etc, but it's not documented
|
||||
// so we don't want to rely on it too heavily. We'll just return a generic claude-code agent name.
|
||||
return agentClaudeCode
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
149
internal/agents/detect_test.go
Normal file
149
internal/agents/detect_test.go
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package agents
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func lookup(vars map[string]string) func(string) (string, bool) {
|
||||
return func(key string) (string, bool) {
|
||||
v, ok := vars[key]
|
||||
return v, ok
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAgentName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want AgentName
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "valid lowercase", input: "my-agent", want: "my-agent"},
|
||||
{name: "valid with underscore", input: "my_agent_v2", want: "my_agent_v2"},
|
||||
{name: "valid uppercase", input: "MyAgent", want: "MyAgent"},
|
||||
{name: "valid numbers", input: "agent123", want: "agent123"},
|
||||
{name: "spaces rejected", input: "my agent", wantErr: true},
|
||||
{name: "newline rejected", input: "my\nagent", wantErr: true},
|
||||
{name: "carriage return rejected", input: "my\ragent", wantErr: true},
|
||||
{name: "null byte rejected", input: "my\x00agent", wantErr: true},
|
||||
{name: "dot rejected", input: "my.agent", wantErr: true},
|
||||
{name: "slash rejected", input: "my/agent", wantErr: true},
|
||||
{name: "empty rejected", input: "", wantErr: true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseAgentName(tt.input)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectWith(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
wantAgent AgentName
|
||||
}{
|
||||
{
|
||||
name: "clean environment",
|
||||
env: map[string]string{},
|
||||
wantAgent: "",
|
||||
},
|
||||
{
|
||||
name: "empty var is not detected",
|
||||
env: map[string]string{"GEMINI_CLI": ""},
|
||||
wantAgent: "",
|
||||
},
|
||||
{
|
||||
name: "AGENT=amp detected as amp",
|
||||
env: map[string]string{"AGENT": "amp"},
|
||||
wantAgent: "amp",
|
||||
},
|
||||
{
|
||||
name: "AGENT with non-amp value is ignored",
|
||||
env: map[string]string{"AGENT": "other"},
|
||||
wantAgent: "",
|
||||
},
|
||||
{
|
||||
name: "AI_AGENT returns value as agent name",
|
||||
env: map[string]string{"AI_AGENT": "some-agent"},
|
||||
wantAgent: "some-agent",
|
||||
},
|
||||
{
|
||||
name: "AI_AGENT with invalid characters is ignored",
|
||||
env: map[string]string{"AI_AGENT": "bad\nagent"},
|
||||
wantAgent: "",
|
||||
},
|
||||
{
|
||||
name: "AI_AGENT with spaces is ignored",
|
||||
env: map[string]string{"AI_AGENT": "bad agent"},
|
||||
wantAgent: "",
|
||||
},
|
||||
{
|
||||
name: "AI_AGENT takes priority over AGENT",
|
||||
env: map[string]string{"AGENT": "amp", "AI_AGENT": "other"},
|
||||
wantAgent: "other",
|
||||
},
|
||||
{
|
||||
name: "CODEX_SANDBOX",
|
||||
env: map[string]string{"CODEX_SANDBOX": "seatbelt"},
|
||||
wantAgent: "codex",
|
||||
},
|
||||
{
|
||||
name: "CODEX_CI",
|
||||
env: map[string]string{"CODEX_CI": "1"},
|
||||
wantAgent: "codex",
|
||||
},
|
||||
{
|
||||
name: "CODEX_THREAD_ID",
|
||||
env: map[string]string{"CODEX_THREAD_ID": "abc"},
|
||||
wantAgent: "codex",
|
||||
},
|
||||
{
|
||||
name: "GEMINI_CLI",
|
||||
env: map[string]string{"GEMINI_CLI": "1"},
|
||||
wantAgent: "gemini-cli",
|
||||
},
|
||||
{
|
||||
name: "COPILOT_CLI",
|
||||
env: map[string]string{"COPILOT_CLI": "1"},
|
||||
wantAgent: "copilot-cli",
|
||||
},
|
||||
{
|
||||
name: "OPENCODE",
|
||||
env: map[string]string{"OPENCODE": "1"},
|
||||
wantAgent: "opencode",
|
||||
},
|
||||
{
|
||||
name: "CLAUDECODE",
|
||||
env: map[string]string{"CLAUDECODE": "1"},
|
||||
wantAgent: "claude-code",
|
||||
},
|
||||
{
|
||||
name: "AGENT=amp takes priority over CLAUDECODE",
|
||||
env: map[string]string{"AGENT": "amp", "CLAUDECODE": "1"},
|
||||
wantAgent: "amp",
|
||||
},
|
||||
{
|
||||
name: "invalid AI_AGENT falls through to tool-specific detection",
|
||||
env: map[string]string{"AI_AGENT": "bad agent", "GEMINI_CLI": "1"},
|
||||
wantAgent: "gemini-cli",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := detectWith(lookup(tt.env))
|
||||
assert.Equal(t, tt.wantAgent, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
surveyCore "github.com/AlecAivazis/survey/v2/core"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/agents"
|
||||
"github.com/cli/cli/v2/internal/build"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/config/migration"
|
||||
|
|
@ -44,7 +45,7 @@ func Main() exitCode {
|
|||
buildVersion := build.Version
|
||||
hasDebug, _ := utils.IsDebugEnabled()
|
||||
|
||||
cmdFactory := factory.New(buildVersion)
|
||||
cmdFactory := factory.New(buildVersion, string(agents.Detect()))
|
||||
stderr := cmdFactory.IOStreams.ErrOut
|
||||
|
||||
ctx := context.Background()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ func TestVerifyIntegration(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test")
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
|
|
@ -143,7 +143,7 @@ func TestVerifyIntegrationCustomIssuer(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test")
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
|
|
@ -217,7 +217,7 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test")
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
|
|
@ -310,7 +310,7 @@ func TestVerifyIntegrationReusableWorkflowSignerWorkflow(t *testing.T) {
|
|||
TUFMetadataDir: o.Some(t.TempDir()),
|
||||
}
|
||||
|
||||
cmdFactory := factory.New("test")
|
||||
cmdFactory := factory.New("test", "")
|
||||
|
||||
hc, err := cmdFactory.HttpClient()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -26,23 +26,23 @@ import (
|
|||
var ssoHeader string
|
||||
var ssoURLRE = regexp.MustCompile(`\burl=([^;]+)`)
|
||||
|
||||
func New(appVersion string) *cmdutil.Factory {
|
||||
func New(appVersion string, invokingAgent string) *cmdutil.Factory {
|
||||
f := &cmdutil.Factory{
|
||||
AppVersion: appVersion,
|
||||
Config: configFunc(), // No factory dependencies
|
||||
ExecutableName: "gh",
|
||||
}
|
||||
|
||||
f.IOStreams = ioStreams(f) // Depends on Config
|
||||
f.HttpClient = httpClientFunc(f, appVersion) // Depends on Config, IOStreams, and appVersion
|
||||
f.PlainHttpClient = plainHttpClientFunc(f, appVersion) // Depends on IOStreams, and appVersion
|
||||
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 = 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
|
||||
|
||||
return f
|
||||
}
|
||||
|
|
@ -186,7 +186,7 @@ func remotesFunc(f *cmdutil.Factory) func() (ghContext.Remotes, error) {
|
|||
return rr.Resolver()
|
||||
}
|
||||
|
||||
func httpClientFunc(f *cmdutil.Factory, appVersion string) func() (*http.Client, error) {
|
||||
func httpClientFunc(f *cmdutil.Factory, appVersion string, invokingAgent string) func() (*http.Client, error) {
|
||||
return func() (*http.Client, error) {
|
||||
io := f.IOStreams
|
||||
cfg, err := f.Config()
|
||||
|
|
@ -194,10 +194,11 @@ func httpClientFunc(f *cmdutil.Factory, appVersion string) func() (*http.Client,
|
|||
return nil, err
|
||||
}
|
||||
opts := api.HTTPClientOptions{
|
||||
Config: cfg.Authentication(),
|
||||
Log: io.ErrOut,
|
||||
LogColorize: io.ColorEnabled(),
|
||||
AppVersion: appVersion,
|
||||
Config: cfg.Authentication(),
|
||||
Log: io.ErrOut,
|
||||
LogColorize: io.ColorEnabled(),
|
||||
AppVersion: appVersion,
|
||||
InvokingAgent: invokingAgent,
|
||||
}
|
||||
client, err := api.NewHTTPClient(opts)
|
||||
if err != nil {
|
||||
|
|
@ -208,13 +209,14 @@ func httpClientFunc(f *cmdutil.Factory, appVersion string) func() (*http.Client,
|
|||
}
|
||||
}
|
||||
|
||||
func plainHttpClientFunc(f *cmdutil.Factory, appVersion string) func() (*http.Client, error) {
|
||||
func plainHttpClientFunc(f *cmdutil.Factory, appVersion string, invokingAgent string) func() (*http.Client, error) {
|
||||
return func() (*http.Client, error) {
|
||||
io := f.IOStreams
|
||||
opts := api.HTTPClientOptions{
|
||||
Log: io.ErrOut,
|
||||
LogColorize: io.ColorEnabled(),
|
||||
AppVersion: appVersion,
|
||||
Log: io.ErrOut,
|
||||
LogColorize: io.ColorEnabled(),
|
||||
AppVersion: appVersion,
|
||||
InvokingAgent: invokingAgent,
|
||||
// This is required to prevent automatic setting of auth and other headers.
|
||||
SkipDefaultHeaders: true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ func Test_BaseRepo(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
rr := &remoteResolver{
|
||||
readRemotes: func() (git.RemoteSet, error) {
|
||||
return tt.remotes, nil
|
||||
|
|
@ -204,7 +204,7 @@ func Test_SmartBaseRepo(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
rr := &remoteResolver{
|
||||
readRemotes: func() (git.RemoteSet, error) {
|
||||
return tt.remotes, nil
|
||||
|
|
@ -297,7 +297,7 @@ func Test_OverrideBaseRepo(t *testing.T) {
|
|||
if tt.envOverride != "" {
|
||||
t.Setenv("GH_REPO", tt.envOverride)
|
||||
}
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
rr := &remoteResolver{
|
||||
readRemotes: func() (git.RemoteSet, error) {
|
||||
return tt.remotes, nil
|
||||
|
|
@ -375,7 +375,7 @@ func Test_ioStreams_pager(t *testing.T) {
|
|||
t.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
|
|
@ -418,7 +418,7 @@ func Test_ioStreams_prompt(t *testing.T) {
|
|||
t.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
|
|
@ -496,7 +496,7 @@ func Test_ioStreams_spinnerDisabled(t *testing.T) {
|
|||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
|
|
@ -564,7 +564,7 @@ func Test_ioStreams_accessiblePrompterEnabled(t *testing.T) {
|
|||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
|
|
@ -642,7 +642,7 @@ func Test_ioStreams_colorLabels(t *testing.T) {
|
|||
t.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
|
|
@ -683,13 +683,13 @@ func TestSSOURL(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
}
|
||||
ios, _, _, stderr := iostreams.Test()
|
||||
f.IOStreams = ios
|
||||
client, err := httpClientFunc(f, "v1.2.3")()
|
||||
client, err := httpClientFunc(f, "v1.2.3", "")()
|
||||
require.NoError(t, err)
|
||||
req, err := http.NewRequest("GET", ts.URL, nil)
|
||||
if tt.sso != "" {
|
||||
|
|
@ -718,13 +718,13 @@ func TestPlainHttpClient(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
f := New("1")
|
||||
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(f, "v1.2.3", "")()
|
||||
require.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest("GET", ts.URL, nil)
|
||||
|
|
@ -759,7 +759,7 @@ func TestNewGitClient(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := New("1")
|
||||
f := New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import (
|
|||
)
|
||||
|
||||
func TestSearcher(t *testing.T) {
|
||||
f := factory.New("1")
|
||||
f := factory.New("1", "")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue