Skip to content

Commit 116d581

Browse files
authored
Merge pull request cli#2388 from cli/gh-token
Add support for GH_TOKEN and GH_ENTERPRISE_TOKEN
2 parents ef52376 + 2eb40f8 commit 116d581

File tree

5 files changed

+187
-56
lines changed

5 files changed

+187
-56
lines changed

cmd/gh/main.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,12 @@ func main() {
136136

137137
cs := cmdFactory.IOStreams.ColorScheme()
138138

139-
authCheckEnabled := os.Getenv("GITHUB_TOKEN") == "" &&
140-
os.Getenv("GITHUB_ENTERPRISE_TOKEN") == "" &&
141-
cmd != nil && cmdutil.IsAuthCheckEnabled(cmd)
142-
if authCheckEnabled {
143-
if !cmdutil.CheckAuth(cfg) {
144-
fmt.Fprintln(stderr, cs.Bold("Welcome to GitHub CLI!"))
145-
fmt.Fprintln(stderr)
146-
fmt.Fprintln(stderr, "To authenticate, please run `gh auth login`.")
147-
fmt.Fprintln(stderr, "You can also set the GITHUB_TOKEN environment variable, if preferred.")
148-
os.Exit(4)
149-
}
139+
if cmd != nil && cmdutil.IsAuthCheckEnabled(cmd) && !cmdutil.CheckAuth(cfg) {
140+
fmt.Fprintln(stderr, cs.Bold("Welcome to GitHub CLI!"))
141+
fmt.Fprintln(stderr)
142+
fmt.Fprintln(stderr, "To authenticate, please run `gh auth login`.")
143+
fmt.Fprintln(stderr, "You can also set the one of the auth token environment variables, if preferred.")
144+
os.Exit(4)
150145
}
151146

152147
rootCmd.SetArgs(expandedArgs)
@@ -248,7 +243,7 @@ func basicClient(currentVersion string) (*api.Client, error) {
248243
}
249244
opts = append(opts, api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", currentVersion)))
250245

251-
token := os.Getenv("GITHUB_TOKEN")
246+
token, _ := config.AuthTokenFromEnv(ghinstance.Default())
252247
if token == "" {
253248
if c, err := config.ParseDefaultConfig(); err == nil {
254249
token, _ = c.Get(ghinstance.Default(), "oauth_token")

internal/config/from_env.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
)
99

1010
const (
11+
GH_TOKEN = "GH_TOKEN"
1112
GITHUB_TOKEN = "GITHUB_TOKEN"
13+
GH_ENTERPRISE_TOKEN = "GH_ENTERPRISE_TOKEN"
1214
GITHUB_ENTERPRISE_TOKEN = "GITHUB_ENTERPRISE_TOKEN"
1315
)
1416

@@ -28,7 +30,8 @@ func (c *envConfig) Hosts() ([]string, error) {
2830
hasDefault = true
2931
}
3032
}
31-
if (err != nil || !hasDefault) && os.Getenv(GITHUB_TOKEN) != "" {
33+
token, _ := AuthTokenFromEnv(ghinstance.Default())
34+
if (err != nil || !hasDefault) && token != "" {
3235
hosts = append([]string{ghinstance.Default()}, hosts...)
3336
return hosts, nil
3437
}
@@ -42,13 +45,8 @@ func (c *envConfig) Get(hostname, key string) (string, error) {
4245

4346
func (c *envConfig) GetWithSource(hostname, key string) (string, string, error) {
4447
if hostname != "" && key == "oauth_token" {
45-
envName := GITHUB_TOKEN
46-
if ghinstance.IsEnterprise(hostname) {
47-
envName = GITHUB_ENTERPRISE_TOKEN
48-
}
49-
50-
if value := os.Getenv(envName); value != "" {
51-
return value, envName, nil
48+
if token, env := AuthTokenFromEnv(hostname); token != "" {
49+
return token, env, nil
5250
}
5351
}
5452

@@ -57,15 +55,26 @@ func (c *envConfig) GetWithSource(hostname, key string) (string, string, error)
5755

5856
func (c *envConfig) CheckWriteable(hostname, key string) error {
5957
if hostname != "" && key == "oauth_token" {
60-
envName := GITHUB_TOKEN
61-
if ghinstance.IsEnterprise(hostname) {
62-
envName = GITHUB_ENTERPRISE_TOKEN
58+
if token, env := AuthTokenFromEnv(hostname); token != "" {
59+
return fmt.Errorf("read-only token in %s cannot be modified", env)
6360
}
61+
}
62+
63+
return c.Config.CheckWriteable(hostname, key)
64+
}
6465

65-
if os.Getenv(envName) != "" {
66-
return fmt.Errorf("read-only token in %s cannot be modified", envName)
66+
func AuthTokenFromEnv(hostname string) (string, string) {
67+
if ghinstance.IsEnterprise(hostname) {
68+
if token := os.Getenv(GH_ENTERPRISE_TOKEN); token != "" {
69+
return token, GH_ENTERPRISE_TOKEN
6770
}
71+
72+
return os.Getenv(GITHUB_ENTERPRISE_TOKEN), GITHUB_ENTERPRISE_TOKEN
6873
}
6974

70-
return c.Config.CheckWriteable(hostname, key)
75+
if token := os.Getenv(GH_TOKEN); token != "" {
76+
return token, GH_TOKEN
77+
}
78+
79+
return os.Getenv(GITHUB_TOKEN), GITHUB_TOKEN
7180
}

internal/config/from_env_test.go

Lines changed: 150 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ import (
1111
func TestInheritEnv(t *testing.T) {
1212
orig_GITHUB_TOKEN := os.Getenv("GITHUB_TOKEN")
1313
orig_GITHUB_ENTERPRISE_TOKEN := os.Getenv("GITHUB_ENTERPRISE_TOKEN")
14+
orig_GH_TOKEN := os.Getenv("GH_TOKEN")
15+
orig_GH_ENTERPRISE_TOKEN := os.Getenv("GH_ENTERPRISE_TOKEN")
1416
t.Cleanup(func() {
1517
os.Setenv("GITHUB_TOKEN", orig_GITHUB_TOKEN)
1618
os.Setenv("GITHUB_ENTERPRISE_TOKEN", orig_GITHUB_ENTERPRISE_TOKEN)
19+
os.Setenv("GH_TOKEN", orig_GH_TOKEN)
20+
os.Setenv("GH_ENTERPRISE_TOKEN", orig_GH_ENTERPRISE_TOKEN)
1721
})
1822

1923
type wants struct {
@@ -28,15 +32,15 @@ func TestInheritEnv(t *testing.T) {
2832
baseConfig string
2933
GITHUB_TOKEN string
3034
GITHUB_ENTERPRISE_TOKEN string
35+
GH_TOKEN string
36+
GH_ENTERPRISE_TOKEN string
3137
hostname string
3238
wants wants
3339
}{
3440
{
35-
name: "blank",
36-
baseConfig: ``,
37-
GITHUB_TOKEN: "",
38-
GITHUB_ENTERPRISE_TOKEN: "",
39-
hostname: "github.com",
41+
name: "blank",
42+
baseConfig: ``,
43+
hostname: "github.com",
4044
wants: wants{
4145
hosts: []string(nil),
4246
token: "",
@@ -45,11 +49,10 @@ func TestInheritEnv(t *testing.T) {
4549
},
4650
},
4751
{
48-
name: "GITHUB_TOKEN over blank config",
49-
baseConfig: ``,
50-
GITHUB_TOKEN: "OTOKEN",
51-
GITHUB_ENTERPRISE_TOKEN: "",
52-
hostname: "github.com",
52+
name: "GITHUB_TOKEN over blank config",
53+
baseConfig: ``,
54+
GITHUB_TOKEN: "OTOKEN",
55+
hostname: "github.com",
5356
wants: wants{
5457
hosts: []string{"github.com"},
5558
token: "OTOKEN",
@@ -58,11 +61,34 @@ func TestInheritEnv(t *testing.T) {
5861
},
5962
},
6063
{
61-
name: "GITHUB_TOKEN not applicable to GHE",
62-
baseConfig: ``,
63-
GITHUB_TOKEN: "OTOKEN",
64-
GITHUB_ENTERPRISE_TOKEN: "",
65-
hostname: "example.org",
64+
name: "GH_TOKEN over blank config",
65+
baseConfig: ``,
66+
GH_TOKEN: "OTOKEN",
67+
hostname: "github.com",
68+
wants: wants{
69+
hosts: []string{"github.com"},
70+
token: "OTOKEN",
71+
source: "GH_TOKEN",
72+
writeable: false,
73+
},
74+
},
75+
{
76+
name: "GITHUB_TOKEN not applicable to GHE",
77+
baseConfig: ``,
78+
GITHUB_TOKEN: "OTOKEN",
79+
hostname: "example.org",
80+
wants: wants{
81+
hosts: []string{"github.com"},
82+
token: "",
83+
source: "~/.config/gh/config.yml",
84+
writeable: true,
85+
},
86+
},
87+
{
88+
name: "GH_TOKEN not applicable to GHE",
89+
baseConfig: ``,
90+
GH_TOKEN: "OTOKEN",
91+
hostname: "example.org",
6692
wants: wants{
6793
hosts: []string{"github.com"},
6894
token: "",
@@ -73,7 +99,6 @@ func TestInheritEnv(t *testing.T) {
7399
{
74100
name: "GITHUB_ENTERPRISE_TOKEN over blank config",
75101
baseConfig: ``,
76-
GITHUB_TOKEN: "",
77102
GITHUB_ENTERPRISE_TOKEN: "ENTOKEN",
78103
hostname: "example.org",
79104
wants: wants{
@@ -83,16 +108,26 @@ func TestInheritEnv(t *testing.T) {
83108
writeable: false,
84109
},
85110
},
111+
{
112+
name: "GH_ENTERPRISE_TOKEN over blank config",
113+
baseConfig: ``,
114+
GH_ENTERPRISE_TOKEN: "ENTOKEN",
115+
hostname: "example.org",
116+
wants: wants{
117+
hosts: []string(nil),
118+
token: "ENTOKEN",
119+
source: "GH_ENTERPRISE_TOKEN",
120+
writeable: false,
121+
},
122+
},
86123
{
87124
name: "token from file",
88125
baseConfig: heredoc.Doc(`
89126
hosts:
90127
github.com:
91128
oauth_token: OTOKEN
92129
`),
93-
GITHUB_TOKEN: "",
94-
GITHUB_ENTERPRISE_TOKEN: "",
95-
hostname: "github.com",
130+
hostname: "github.com",
96131
wants: wants{
97132
hosts: []string{"github.com"},
98133
token: "OTOKEN",
@@ -107,38 +142,128 @@ func TestInheritEnv(t *testing.T) {
107142
github.com:
108143
oauth_token: OTOKEN
109144
`),
110-
GITHUB_TOKEN: "ENVTOKEN",
111-
GITHUB_ENTERPRISE_TOKEN: "",
112-
hostname: "github.com",
145+
GITHUB_TOKEN: "ENVTOKEN",
146+
hostname: "github.com",
113147
wants: wants{
114148
hosts: []string{"github.com"},
115149
token: "ENVTOKEN",
116150
source: "GITHUB_TOKEN",
117151
writeable: false,
118152
},
119153
},
154+
{
155+
name: "GH_TOKEN shadows token from file",
156+
baseConfig: heredoc.Doc(`
157+
hosts:
158+
github.com:
159+
oauth_token: OTOKEN
160+
`),
161+
GH_TOKEN: "ENVTOKEN",
162+
hostname: "github.com",
163+
wants: wants{
164+
hosts: []string{"github.com"},
165+
token: "ENVTOKEN",
166+
source: "GH_TOKEN",
167+
writeable: false,
168+
},
169+
},
170+
{
171+
name: "GITHUB_ENTERPRISE_TOKEN shadows token from file",
172+
baseConfig: heredoc.Doc(`
173+
hosts:
174+
example.org:
175+
oauth_token: OTOKEN
176+
`),
177+
GITHUB_ENTERPRISE_TOKEN: "ENVTOKEN",
178+
hostname: "example.org",
179+
wants: wants{
180+
hosts: []string{"example.org"},
181+
token: "ENVTOKEN",
182+
source: "GITHUB_ENTERPRISE_TOKEN",
183+
writeable: false,
184+
},
185+
},
186+
{
187+
name: "GH_ENTERPRISE_TOKEN shadows token from file",
188+
baseConfig: heredoc.Doc(`
189+
hosts:
190+
example.org:
191+
oauth_token: OTOKEN
192+
`),
193+
GH_ENTERPRISE_TOKEN: "ENVTOKEN",
194+
hostname: "example.org",
195+
wants: wants{
196+
hosts: []string{"example.org"},
197+
token: "ENVTOKEN",
198+
source: "GH_ENTERPRISE_TOKEN",
199+
writeable: false,
200+
},
201+
},
202+
{
203+
name: "GH_TOKEN shadows token from GITHUB_TOKEN",
204+
baseConfig: ``,
205+
GH_TOKEN: "GHTOKEN",
206+
GITHUB_TOKEN: "GITHUBTOKEN",
207+
hostname: "github.com",
208+
wants: wants{
209+
hosts: []string{"github.com"},
210+
token: "GHTOKEN",
211+
source: "GH_TOKEN",
212+
writeable: false,
213+
},
214+
},
215+
{
216+
name: "GH_ENTERPRISE_TOKEN shadows token from GITHUB_ENTERPRISE_TOKEN",
217+
baseConfig: ``,
218+
GH_ENTERPRISE_TOKEN: "GHTOKEN",
219+
GITHUB_ENTERPRISE_TOKEN: "GITHUBTOKEN",
220+
hostname: "example.org",
221+
wants: wants{
222+
hosts: []string(nil),
223+
token: "GHTOKEN",
224+
source: "GH_ENTERPRISE_TOKEN",
225+
writeable: false,
226+
},
227+
},
120228
{
121229
name: "GITHUB_TOKEN adds host entry",
122230
baseConfig: heredoc.Doc(`
123231
hosts:
124232
example.org:
125233
oauth_token: OTOKEN
126234
`),
127-
GITHUB_TOKEN: "ENVTOKEN",
128-
GITHUB_ENTERPRISE_TOKEN: "",
129-
hostname: "github.com",
235+
GITHUB_TOKEN: "ENVTOKEN",
236+
hostname: "github.com",
130237
wants: wants{
131238
hosts: []string{"github.com", "example.org"},
132239
token: "ENVTOKEN",
133240
source: "GITHUB_TOKEN",
134241
writeable: false,
135242
},
136243
},
244+
{
245+
name: "GH_TOKEN adds host entry",
246+
baseConfig: heredoc.Doc(`
247+
hosts:
248+
example.org:
249+
oauth_token: OTOKEN
250+
`),
251+
GH_TOKEN: "ENVTOKEN",
252+
hostname: "github.com",
253+
wants: wants{
254+
hosts: []string{"github.com", "example.org"},
255+
token: "ENVTOKEN",
256+
source: "GH_TOKEN",
257+
writeable: false,
258+
},
259+
},
137260
}
138261
for _, tt := range tests {
139262
t.Run(tt.name, func(t *testing.T) {
140263
os.Setenv("GITHUB_TOKEN", tt.GITHUB_TOKEN)
141264
os.Setenv("GITHUB_ENTERPRISE_TOKEN", tt.GITHUB_ENTERPRISE_TOKEN)
265+
os.Setenv("GH_TOKEN", tt.GH_TOKEN)
266+
os.Setenv("GH_ENTERPRISE_TOKEN", tt.GH_ENTERPRISE_TOKEN)
142267

143268
baseCfg := NewFromString(tt.baseConfig)
144269
cfg := InheritEnv(baseCfg)

pkg/cmd/api/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ original query accepts an '$endCursor: String' variable and that it fetches the
119119
`),
120120
Annotations: map[string]string{
121121
"help:environment": heredoc.Doc(`
122-
GITHUB_TOKEN: an authentication token for github.com API requests.
122+
GH_TOKEN, GITHUB_TOKEN (in order of precedence): an authentication token for github.com API requests.
123123
124-
GITHUB_ENTERPRISE_TOKEN: an authentication token for API requests to GitHub Enterprise.
124+
GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN (in order of precedence): an authentication token for API requests to GitHub Enterprise.
125125
126126
GH_HOST: make the request to a GitHub host other than github.com.
127127
`),

pkg/cmd/root/help_topic.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ var HelpTopics = map[string]map[string]string{
99
"environment": {
1010
"short": "Environment variables that can be used with gh",
1111
"long": heredoc.Doc(`
12-
GITHUB_TOKEN: an authentication token for github.com API requests. Setting this avoids
13-
being prompted to authenticate and takes precedence over previously stored credentials.
12+
GH_TOKEN, GITHUB_TOKEN (in order of precedence): an authentication token for github.com
13+
API requests. Setting this avoids being prompted to authenticate and takes precedence over
14+
previously stored credentials.
1415
15-
GITHUB_ENTERPRISE_TOKEN: an authentication token for API requests to GitHub Enterprise.
16+
GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN (in order of precedence): an authentication
17+
token for API requests to GitHub Enterprise.
1618
1719
GH_REPO: specify the GitHub repository in the "[HOST/]OWNER/REPO" format for commands
1820
that otherwise operate on a local repository.

0 commit comments

Comments
 (0)