1- // Package token resolves a GitHub Personal Access Token from multiple sources.
1+ // Package token resolves a Personal Access Token from multiple sources.
22//
33// Priority order:
4- // 1. Explicit flag value (--token / --github-token )
5- // 2. .devlake.env file (GITHUB_PAT =...)
6- // 3. Environment variables ($GITHUB_TOKEN / $GH_TOKEN )
4+ // 1. Explicit flag value (--token)
5+ // 2. .devlake.env file (plugin-specific key, e.g. GITLAB_TOKEN =...)
6+ // 3. Plugin-specific environment variable (e.g. $GITLAB_TOKEN )
77// 4. Interactive masked prompt (terminal)
88package token
99
@@ -25,9 +25,10 @@ type ResolveResult struct {
2525}
2626
2727// Resolve attempts to find a PAT using the priority chain.
28+ // plugin determines which env vars and env file keys to check (e.g. "github", "gitlab", "azure-devops").
2829// scopeHint is displayed in the interactive prompt to guide the user on required scopes.
2930// Returns an error only if no token can be obtained.
30- func Resolve (flagValue , envFilePath , scopeHint string ) (* ResolveResult , error ) {
31+ func Resolve (plugin , flagValue , envFilePath , scopeHint string ) (* ResolveResult , error ) {
3132 // 1. Explicit flag
3233 if flagValue != "" {
3334 return & ResolveResult {Token : flagValue , Source : "flag" }, nil
@@ -38,38 +39,78 @@ func Resolve(flagValue, envFilePath, scopeHint string) (*ResolveResult, error) {
3839 envFilePath = ".devlake.env"
3940 }
4041 if vals , err := envfile .Load (envFilePath ); err == nil {
41- for _ , key := range [] string { "GITHUB_PAT" , "GITHUB_TOKEN" , "GH_TOKEN" } {
42+ for _ , key := range pluginEnvFileKeys ( plugin ) {
4243 if v , ok := vals [key ]; ok && v != "" {
4344 return & ResolveResult {Token : v , Source : "envfile" , EnvFilePath : envFilePath }, nil
4445 }
4546 }
4647 }
4748
4849 // 3. Environment variables
49- for _ , key := range [] string { "GITHUB_TOKEN" , "GH_TOKEN" } {
50+ for _ , key := range pluginEnvVarKeys ( plugin ) {
5051 if v := os .Getenv (key ); v != "" {
5152 return & ResolveResult {Token : v , Source : "environment" }, nil
5253 }
5354 }
5455
5556 // 4. Interactive masked prompt
57+ displayName := pluginDisplayName (plugin )
5658 if ! term .IsTerminal (int (syscall .Stdin )) {
57- return nil , fmt .Errorf ("no GitHub PAT found and stdin is not a terminal.\n " +
58- "Provide a token via --token, .devlake.env file, or $GITHUB_TOKEN" )
59+ envVarExample := pluginEnvVarKeys (plugin )[0 ]
60+ return nil , fmt .Errorf ("no %s PAT found and stdin is not a terminal.\n " +
61+ "Provide a token via --token, .devlake.env file, or $%s" , displayName , envVarExample )
5962 }
6063
61- tok , err := promptMasked (scopeHint )
64+ tok , err := promptMasked (displayName , scopeHint )
6265 if err != nil {
6366 return nil , err
6467 }
6568 return & ResolveResult {Token : tok , Source : "prompt" }, nil
6669}
6770
68- func promptMasked (scopeHint string ) (string , error ) {
71+ // pluginEnvFileKeys returns the ordered .devlake.env key names to check for the given plugin.
72+ func pluginEnvFileKeys (plugin string ) []string {
73+ switch plugin {
74+ case "gitlab" :
75+ return []string {"GITLAB_TOKEN" }
76+ case "azure-devops" :
77+ return []string {"AZURE_DEVOPS_PAT" }
78+ default : // "github", "gh-copilot", or unknown
79+ return []string {"GITHUB_PAT" , "GITHUB_TOKEN" , "GH_TOKEN" }
80+ }
81+ }
82+
83+ // pluginEnvVarKeys returns the ordered environment variable names to check for the given plugin.
84+ func pluginEnvVarKeys (plugin string ) []string {
85+ switch plugin {
86+ case "gitlab" :
87+ return []string {"GITLAB_TOKEN" }
88+ case "azure-devops" :
89+ return []string {"AZURE_DEVOPS_PAT" }
90+ default : // "github", "gh-copilot", or unknown
91+ return []string {"GITHUB_TOKEN" , "GH_TOKEN" }
92+ }
93+ }
94+
95+ // pluginDisplayName returns a human-readable label for prompt messages.
96+ func pluginDisplayName (plugin string ) string {
97+ switch plugin {
98+ case "gh-copilot" :
99+ return "GitHub Copilot"
100+ case "gitlab" :
101+ return "GitLab"
102+ case "azure-devops" :
103+ return "Azure DevOps"
104+ default :
105+ return "GitHub"
106+ }
107+ }
108+
109+ func promptMasked (displayName , scopeHint string ) (string , error ) {
69110 if scopeHint != "" {
70111 fmt .Fprintf (os .Stderr , "Required PAT scopes: %s\n " , scopeHint )
71112 }
72- fmt .Fprint (os .Stderr , "GitHub Personal Access Token: " )
113+ fmt .Fprintf (os .Stderr , "%s Personal Access Token: " , displayName )
73114 raw , err := term .ReadPassword (int (syscall .Stdin ))
74115 fmt .Fprintln (os .Stderr ) // newline after masked input
75116 if err != nil {
0 commit comments