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  }