|
7 | 7 | "net/url" |
8 | 8 | "os" |
9 | 9 | "os/exec" |
10 | | - "regexp" |
11 | 10 | "strings" |
12 | 11 | "time" |
13 | 12 |
|
@@ -367,6 +366,11 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc { |
367 | 366 | owner = org |
368 | 367 | } |
369 | 368 |
|
| 369 | + bu, err := validateBaseURL(baseURL) |
| 370 | + if err != nil { |
| 371 | + return nil, diag.FromErr(err) |
| 372 | + } |
| 373 | + |
370 | 374 | if appAuth, ok := d.Get("app_auth").([]any); ok && len(appAuth) > 0 && appAuth[0] != nil { |
371 | 375 | appAuthAttr := appAuth[0].(map[string]any) |
372 | 376 |
|
@@ -397,25 +401,16 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc { |
397 | 401 | return nil, wrapErrors([]error{fmt.Errorf("app_auth.pem_file must be set and contain a non-empty value")}) |
398 | 402 | } |
399 | 403 |
|
400 | | - appToken, err := GenerateOAuthTokenFromApp(baseURL, appID, appInstallationID, appPemFile) |
| 404 | + appToken, err := GenerateOAuthTokenFromApp(bu, appID, appInstallationID, appPemFile) |
401 | 405 | if err != nil { |
402 | 406 | return nil, wrapErrors([]error{err}) |
403 | 407 | } |
404 | 408 |
|
405 | 409 | token = appToken |
406 | 410 | } |
407 | 411 |
|
408 | | - isGithubDotCom, err := regexp.MatchString("^"+regexp.QuoteMeta("https://api.github.com"), baseURL) |
409 | | - if err != nil { |
410 | | - return nil, diag.FromErr(err) |
411 | | - } |
412 | | - |
413 | 412 | if token == "" { |
414 | | - ghAuthToken, err := tokenFromGhCli(baseURL, isGithubDotCom) |
415 | | - if err != nil { |
416 | | - return nil, diag.FromErr(fmt.Errorf("gh auth token: %w", err)) |
417 | | - } |
418 | | - token = ghAuthToken |
| 413 | + token = tokenFromGHCLI(bu) |
419 | 414 | } |
420 | 415 |
|
421 | 416 | writeDelay := d.Get("write_delay_ms").(int) |
@@ -488,41 +483,49 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc { |
488 | 483 | } |
489 | 484 | } |
490 | 485 |
|
| 486 | +// validateBaseURL checks that the provided base URL is valid and can be used. |
| 487 | +func validateBaseURL(b string) (*url.URL, error) { |
| 488 | + u, err := url.Parse(b) |
| 489 | + if err != nil { |
| 490 | + return nil, err |
| 491 | + } |
| 492 | + |
| 493 | + if !u.IsAbs() { |
| 494 | + return nil, fmt.Errorf("base_url must be absolute") |
| 495 | + } |
| 496 | + |
| 497 | + hostname := u.Hostname() |
| 498 | + if hostname == DotComHost || GHECDataResidencyHostMatch.MatchString(hostname) { |
| 499 | + if u.Scheme != "https" { |
| 500 | + return nil, fmt.Errorf("base_url for github.com or ghe.com must use the https scheme") |
| 501 | + } |
| 502 | + if len(u.Path) > 1 { |
| 503 | + return nil, fmt.Errorf("base_url for github.com or ghe.com must not contain a path, got %s", u.Path) |
| 504 | + } |
| 505 | + } |
| 506 | + |
| 507 | + return u, err |
| 508 | +} |
| 509 | + |
491 | 510 | // See https://github.com/integrations/terraform-provider-github/issues/1822 |
492 | | -func tokenFromGhCli(baseURL string, isGithubDotCom bool) (string, error) { |
| 511 | +func tokenFromGHCLI(u *url.URL) string { |
493 | 512 | ghCliPath := os.Getenv("GH_PATH") |
494 | 513 | if ghCliPath == "" { |
495 | 514 | ghCliPath = "gh" |
496 | 515 | } |
497 | | - hostname := "" |
498 | | - if isGithubDotCom { |
499 | | - hostname = "github.com" |
500 | | - } else { |
501 | | - parsedURL, err := url.Parse(baseURL) |
502 | | - if err != nil { |
503 | | - return "", fmt.Errorf("parse %s: %w", baseURL, err) |
504 | | - } |
505 | | - hostname = parsedURL.Host |
| 516 | + |
| 517 | + host := u.Host |
| 518 | + if u.Hostname() == DotComHost { |
| 519 | + host = "github.com" |
506 | 520 | } |
507 | | - // GitHub CLI uses different base URLs in ~/.config/gh/hosts.yml, so when |
508 | | - // we're using the standard base path of this provider, it doesn't align |
509 | | - // with the way `gh` CLI stores the credentials. The following doesn't work: |
510 | | - // |
511 | | - // $ gh auth token --hostname api.github.com |
512 | | - // > no oauth token |
513 | | - // |
514 | | - // ... but the following does work correctly |
515 | | - // |
516 | | - // $ gh auth token --hostname github.com |
517 | | - // > gh..<valid token> |
518 | | - hostname = strings.TrimPrefix(hostname, "api.") |
519 | | - out, err := exec.Command(ghCliPath, "auth", "token", "--hostname", hostname).Output() |
| 521 | + |
| 522 | + out, err := exec.Command(ghCliPath, "auth", "token", "--hostname", host).Output() |
520 | 523 | if err != nil { |
521 | 524 | // GH CLI is either not installed or there was no `gh auth login` command issued, |
522 | 525 | // which is fine. don't return the error to keep the flow going |
523 | | - return "", nil //nolint:nilerr |
| 526 | + return "" |
524 | 527 | } |
525 | 528 |
|
526 | 529 | log.Printf("[INFO] Using the token from GitHub CLI") |
527 | | - return strings.TrimSpace(string(out)), nil |
| 530 | + return strings.TrimSpace(string(out)) |
528 | 531 | } |
0 commit comments