From 44f54c5af10395d7135310d4197eda672496a1ef Mon Sep 17 00:00:00 2001 From: Scott Lougheed Date: Thu, 4 Jun 2026 13:41:16 -0700 Subject: [PATCH 1/2] Add GitHub Enterprise support to the gh CLI provisioner. Use GH_ENTERPRISE_TOKEN for Enterprise Server hosts and GH_TOKEN for github.com and Enterprise Cloud, so users can authenticate to both when they have separate credentials configured. Fixes #137 Co-authored-by: Cursor --- plugins/github/personal_access_token.go | 3 +- plugins/github/personal_access_token_test.go | 1 + plugins/github/provisioner.go | 64 +++++++++++++++++ plugins/github/provisioner_test.go | 76 ++++++++++++++++++++ 4 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 plugins/github/provisioner.go create mode 100644 plugins/github/provisioner_test.go diff --git a/plugins/github/personal_access_token.go b/plugins/github/personal_access_token.go index f8c8772e4..2c888bd1c 100644 --- a/plugins/github/personal_access_token.go +++ b/plugins/github/personal_access_token.go @@ -6,7 +6,6 @@ import ( "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/importer" - "github.com/1Password/shell-plugins/sdk/provision" "github.com/1Password/shell-plugins/sdk/schema" "github.com/1Password/shell-plugins/sdk/schema/credname" "github.com/1Password/shell-plugins/sdk/schema/fieldname" @@ -38,7 +37,7 @@ func PersonalAccessToken() schema.CredentialType { Optional: true, }, }, - DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + DefaultProvisioner: GitHubCLIProvisioner{}, Importer: importer.TryAll( importer.TryEnvVarPair(defaultEnvVarMapping), importer.TryAllEnvVars(fieldname.Token, "GH_TOKEN", "GITHUB_PAT"), diff --git a/plugins/github/personal_access_token_test.go b/plugins/github/personal_access_token_test.go index cdcda9eb2..35c17c1dd 100644 --- a/plugins/github/personal_access_token_test.go +++ b/plugins/github/personal_access_token_test.go @@ -121,6 +121,7 @@ func TestPersonalAccessTokenProvisioner(t *testing.T) { }, ExpectedOutput: sdk.ProvisionOutput{ Environment: map[string]string{ + "GH_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", "GITHUB_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", }, }, diff --git a/plugins/github/provisioner.go b/plugins/github/provisioner.go new file mode 100644 index 000000000..6261af493 --- /dev/null +++ b/plugins/github/provisioner.go @@ -0,0 +1,64 @@ +package github + +import ( + "context" + "strings" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +const ( + githubHost = "github.com" + localhostHost = "github.localhost" + tenancyHost = "ghe.com" +) + +// GitHubCLIProvisioner provisions auth tokens using the environment variables expected by the GitHub CLI. +// GitHub.com and GitHub Enterprise Cloud (*.ghe.com) use GH_TOKEN and GITHUB_TOKEN. +// GitHub Enterprise Server uses GH_ENTERPRISE_TOKEN and GITHUB_ENTERPRISE_TOKEN with GH_HOST. +type GitHubCLIProvisioner struct{} + +func (p GitHubCLIProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + token, ok := in.ItemFields[fieldname.Token] + if !ok { + return + } + + host := strings.ToLower(strings.TrimSpace(in.ItemFields[fieldname.Host])) + + if usesEnterpriseToken(host) { + out.AddEnvVar("GH_ENTERPRISE_TOKEN", token) + out.AddEnvVar("GITHUB_ENTERPRISE_TOKEN", token) + if host != "" { + out.AddEnvVar("GH_HOST", host) + } + return + } + + out.AddEnvVar("GH_TOKEN", token) + out.AddEnvVar("GITHUB_TOKEN", token) + if host != "" && host != githubHost { + out.AddEnvVar("GH_HOST", host) + } +} + +func usesEnterpriseToken(host string) bool { + if host == "" || host == githubHost || host == localhostHost { + return false + } + + return !isTenancyHost(host) +} + +func isTenancyHost(host string) bool { + return strings.HasSuffix(host, "."+tenancyHost) +} + +func (p GitHubCLIProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { + // Environment variables get wiped automatically when the process exits. +} + +func (p GitHubCLIProvisioner) Description() string { + return "Provision GitHub CLI environment variables: GH_TOKEN, GITHUB_TOKEN, GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN, GH_HOST" +} diff --git a/plugins/github/provisioner_test.go b/plugins/github/provisioner_test.go new file mode 100644 index 000000000..1c4b28320 --- /dev/null +++ b/plugins/github/provisioner_test.go @@ -0,0 +1,76 @@ +package github + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/plugintest" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func TestGitHubCLIProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, GitHubCLIProvisioner{}, map[string]plugintest.ProvisionCase{ + "github.com": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Token: "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "GH_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GITHUB_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + }, + }, + }, + "github.com with explicit host": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Token: "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + fieldname.Host: "github.com", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "GH_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GITHUB_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + }, + }, + }, + "GitHub Enterprise Server": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Token: "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + fieldname.Host: "enterprise.github.com", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "GH_ENTERPRISE_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GITHUB_ENTERPRISE_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GH_HOST": "enterprise.github.com", + }, + }, + }, + "GitHub Enterprise Cloud": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Token: "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + fieldname.Host: "company.ghe.com", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "GH_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GITHUB_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GH_HOST": "company.ghe.com", + }, + }, + }, + "github.localhost": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Token: "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + fieldname.Host: "github.localhost", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "GH_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GITHUB_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", + "GH_HOST": "github.localhost", + }, + }, + }, + }) +} From 2493ba28a26008a795b44cdadae3b9f026697d3a Mon Sep 17 00:00:00 2001 From: Scott Lougheed Date: Thu, 4 Jun 2026 14:03:15 -0700 Subject: [PATCH 2/2] fixing linting problem --- plugins/github/provisioner_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/github/provisioner_test.go b/plugins/github/provisioner_test.go index 1c4b28320..b21fe8501 100644 --- a/plugins/github/provisioner_test.go +++ b/plugins/github/provisioner_test.go @@ -55,7 +55,7 @@ func TestGitHubCLIProvisioner(t *testing.T) { Environment: map[string]string{ "GH_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", "GITHUB_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", - "GH_HOST": "company.ghe.com", + "GH_HOST": "company.ghe.com", }, }, }, @@ -68,7 +68,7 @@ func TestGitHubCLIProvisioner(t *testing.T) { Environment: map[string]string{ "GH_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", "GITHUB_TOKEN": "github_pat_OYXGsaLFxgNy9msXs44LFNzg3wh0VsXRGycViVc0iKPOqczc1QKlB3ZVVrm5ESukqKR8nE3jzPBEXAMPLE", - "GH_HOST": "github.localhost", + "GH_HOST": "github.localhost", }, }, },