github.com/abhinav/git-pr@v0.6.1-0.20171029234004-54218d68c11b/cli/config.go (about) 1 package cli 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 9 "github.com/abhinav/git-pr/gateway" 10 "github.com/abhinav/git-pr/git" 11 "github.com/abhinav/git-pr/github" 12 "github.com/abhinav/git-pr/repo" 13 14 gh "github.com/google/go-github/github" 15 "github.com/zalando/go-keyring" 16 "golang.org/x/oauth2" 17 ) 18 19 const _keyringServiceName = "git-fu" 20 21 // Config is the common configuration for all programs in this package. 22 type Config interface { 23 Git() gateway.Git 24 Repo() *repo.Repo 25 GitHub() gateway.GitHub 26 CurrentGitHubUser() string 27 } 28 29 // ConfigBuilder builds a configuration lazily. 30 type ConfigBuilder func() (Config, error) 31 32 type globalConfig struct { 33 RepoName string `short:"r" long:"repo" value-name:"OWNER/REPO" description:"Name of the GitHub repository in the format 'owner/repo'. Defaults to the repository for the current directory."` 34 GitHubUser string `short:"u" long:"user" value-name:"USERNAME" env:"GITHUB_USER" description:"GitHub username."` 35 GitHubToken string `short:"t" long:"token" env:"GITHUB_TOKEN" value-name:"TOKEN" description:"GitHub token used to make requests."` 36 37 token string 38 repo *repo.Repo 39 git gateway.Git 40 github gateway.GitHub 41 } 42 43 var _ Config = (*globalConfig)(nil) 44 45 func (g *globalConfig) buildGit() (gateway.Git, error) { 46 if g.git != nil { 47 return g.git, nil 48 } 49 50 var err error 51 g.git, err = git.NewGateway("") 52 return g.git, err 53 } 54 55 func (g *globalConfig) Token() (string, error) { 56 switch { 57 case g.token != "": 58 return g.token, nil 59 case g.GitHubToken != "": 60 g.token = g.GitHubToken 61 return g.GitHubToken, nil 62 } 63 64 var err error 65 g.token, err = keyring.Get(_keyringServiceName, g.GitHubUser) 66 switch err { 67 case nil: 68 return g.token, nil 69 case keyring.ErrNotFound: 70 return g.askForToken() 71 default: 72 return "", fmt.Errorf("failed to retrieve GitHub token from keyring: %v", err) 73 } 74 } 75 76 func (g *globalConfig) askForToken() (string, error) { 77 fmt.Println("GitHub token not found. " + 78 "Please generate one at https://github.com/settings/tokens") 79 fmt.Printf("GitHub token for %v: ", g.GitHubUser) 80 if _, err := fmt.Scanln(&g.token); err != nil { 81 return "", err 82 } 83 84 g.token = strings.TrimSpace(g.token) 85 if g.token == "" { 86 return "", fmt.Errorf("GitHub token cannot be blank") 87 } 88 89 // TODO: verify token validity before storing 90 91 if err := keyring.Set(_keyringServiceName, g.GitHubUser, g.token); err != nil { 92 return "", fmt.Errorf("failed to store GitHub token in keyring: %v", err) 93 } 94 95 return g.token, nil 96 } 97 98 // globalConfig.Build is a ConfigBuilder 99 func (g *globalConfig) Build() (_ Config, err error) { 100 if g.GitHubUser == "" { 101 return nil, errors.New( 102 "please provide a GitHub username using the -u/--user flag " + 103 "or by setting the GITHUB_USER environment variable") 104 } 105 106 git, err := g.buildGit() 107 if err != nil { 108 return nil, err 109 } 110 111 if g.RepoName != "" { 112 g.repo, err = repo.Parse(g.RepoName) 113 } else { 114 g.repo, err = repo.Guess(git) 115 } 116 if err != nil { 117 return nil, err 118 } 119 120 token, err := g.Token() 121 if err != nil { 122 return nil, err 123 } 124 125 tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) 126 httpClient := oauth2.NewClient(context.Background(), tokenSource) 127 g.github = github.NewGatewayForRepository(gh.NewClient(httpClient), g.repo) 128 return g, nil 129 } 130 131 func (g *globalConfig) Repo() *repo.Repo { 132 return g.repo 133 } 134 135 func (g *globalConfig) CurrentGitHubUser() string { 136 return g.GitHubUser 137 } 138 139 func (g *globalConfig) GitHub() gateway.GitHub { 140 return g.github 141 } 142 143 func (g *globalConfig) Git() gateway.Git { 144 return g.git 145 }