From 4135ac2764ffbbcfeffe31330937a2a8563cae54 Mon Sep 17 00:00:00 2001 From: mackee Date: Fri, 24 Jan 2020 14:11:02 +0900 Subject: [PATCH 1/3] move core logic to sub package --- auth.go => githubapps/auth.go | 2 +- githubapps/credential.go | 117 ++++++++++++++++++++++++++++++++ store.go => githubapps/store.go | 2 +- main.go | 86 ++++------------------- 4 files changed, 134 insertions(+), 73 deletions(-) rename auth.go => githubapps/auth.go (99%) create mode 100644 githubapps/credential.go rename store.go => githubapps/store.go (98%) diff --git a/auth.go b/githubapps/auth.go similarity index 99% rename from auth.go rename to githubapps/auth.go index 701ecb0..efac074 100644 --- a/auth.go +++ b/githubapps/auth.go @@ -1,4 +1,4 @@ -package main +package githubapps import ( "context" diff --git a/githubapps/credential.go b/githubapps/credential.go new file mode 100644 index 0000000..c7734fc --- /dev/null +++ b/githubapps/credential.go @@ -0,0 +1,117 @@ +package githubapps + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + "path/filepath" +) + +const ( + defaultAPIBaseURL = "api.github.com" + defaultCacheFilename = "git-credential-github-apps-token-cache" +) + +var ( + ErrShowHelp = errors.New("receive show help args") +) + +// Runner provides retrieving token +type Runner struct { + hostname string + privateKey string + appID int64 + args []string + options []AutherOption +} + +// Hostname returns hostname from arguments +func (r *Runner) Hostname() string { + return r.hostname +} + +// Args returns command options not contains parsed. +func (r *Runner) Args() []string { + return r.args +} + +// Run returns credentials +func (r *Runner) Run(ctx context.Context) (string, error) { + return readCredential(ctx, r.privateKey, r.appID, r.options) +} + +// ParseArgs is retrieve github apps credentials with command line options. +func ParseArgs() (*Runner, error) { + var ( + privateKey string + appID int64 + installationID int64 + login string + hostname string + apibase string + cachefile string + showHelp bool + ) + cachedir, err := os.UserCacheDir() + if err != nil { + return nil, fmt.Errorf("fail to detect cache dir: %s", err) + } + + flag.StringVar(&privateKey, "privatekey", "private_key.pem", "private key of GitHub Apps") + flag.Int64Var(&appID, "appid", 0, "App ID of GitHub Apps") + flag.Int64Var(&installationID, "installationid", 0, "Installation ID of organization or user on GitHub Apps") + flag.StringVar(&login, "login", "", "login name of organization or user. if not set -installationid, search from Installation ID by use value.") + flag.StringVar(&hostname, "hostname", "github.com", "hostname as using for an accessing in git") + flag.StringVar(&apibase, "apibase", "api.github.com", "API hostname as using for a fetching GitHub APIs") + flag.StringVar( + &cachefile, "cachefile", filepath.Join(cachedir, defaultCacheFilename), + "filename as save cached token", + ) + flag.BoolVar(&showHelp, "h", false, "show this help") + + flag.Parse() + + if showHelp { + flag.PrintDefaults() + return nil, ErrShowHelp + } + + if privateKey == "" || appID == 0 { + return nil, fmt.Errorf("must be set -privatekey and -appid") + } + if installationID == 0 && login == "" { + return nil, fmt.Errorf("must be set -installationid or -login") + } + + ctx := context.Background() + + var options []AutherOption + if defaultAPIBaseURL != apibase { + options = append(options, WithBaseURL(apibase)) + } + if installationID == 0 { + options = append(options, WithLogin(ctx, login)) + } else { + options = append(options, WithInstallationID(installationID)) + } + if cachefile != "" { + s, err := NewFileStore(cachefile) + if err != nil { + return nil, fmt.Errorf("%s", err) + } + options = append(options, WithStore(s)) + } + + return &Runner{hostname: hostname, options: options, privateKey: privateKey, appID: appID, args: flag.Args()}, nil +} + +func readCredential(ctx context.Context, privateKey string, appID int64, options []AutherOption) (string, error) { + auther, err := NewAutherFromFile(privateKey, appID, options...) + if err != nil { + return "", err + } + + return auther.FetchToken(ctx) +} diff --git a/store.go b/githubapps/store.go similarity index 98% rename from store.go rename to githubapps/store.go index c11578b..32d012c 100644 --- a/store.go +++ b/githubapps/store.go @@ -1,4 +1,4 @@ -package main +package githubapps import ( "encoding/gob" diff --git a/main.go b/main.go index b3415c3..9c3bd08 100644 --- a/main.go +++ b/main.go @@ -4,112 +4,56 @@ import ( "bufio" "bytes" "context" - "flag" "fmt" "io" "os" - "path/filepath" "strings" -) -const ( - defaultAPIBaseURL = "api.github.com" - defaultCacheFilename = "git-credential-github-apps-token-cache" + "github.com/mackee/git-credential-github-apps/githubapps" ) func main() { - var ( - privateKey string - appID int64 - installationID int64 - login string - hostname string - apibase string - cachefile string - ) - cachedir, err := os.UserCacheDir() + runner, err := githubapps.ParseArgs() if err != nil { - fmt.Printf("[ERROR] fail to detect cache dir: %s\n", err) + if err == githubapps.ErrShowHelp { + os.Exit(0) + } + fmt.Printf("[ERROR] %s\n", err) os.Exit(1) } - flag.StringVar(&privateKey, "privatekey", "private_key.pem", "private key of GitHub Apps") - flag.Int64Var(&appID, "appid", 0, "App ID of GitHub Apps") - flag.Int64Var(&installationID, "installationid", 0, "Installation ID of organization or user on GitHub Apps") - flag.StringVar(&login, "login", "", "login name of organization or user. if not set -installationid, search from Installation ID by use value.") - flag.StringVar(&hostname, "hostname", "github.com", "hostname as using for an accessing in git") - flag.StringVar(&apibase, "apibase", "api.github.com", "API hostname as using for a fetching GitHub APIs") - flag.StringVar( - &cachefile, "cachefile", filepath.Join(cachedir, defaultCacheFilename), - "filename as save cached token", - ) - - flag.Parse() - - if privateKey == "" || appID == 0 { - fmt.Println("[ERROR] must be set -privatekey and -appid") + args := runner.Args() + if len(args) != 1 || args[0] != "get" { + fmt.Printf("[ERROR] unexpected args\n") os.Exit(1) } - if installationID == 0 && login == "" { - fmt.Println("[ERROR] must be set -installationid or -login") - os.Exit(1) - } - - if flag.NArg() != 1 || flag.Arg(0) != "get" { - os.Exit(0) - } - - ctx := context.Background() - - var options []AutherOption - if defaultAPIBaseURL != apibase { - options = append(options, WithBaseURL(apibase)) - } - if installationID == 0 { - options = append(options, WithLogin(ctx, login)) - } else { - options = append(options, WithInstallationID(installationID)) - } - if cachefile != "" { - s, err := NewFileStore(cachefile) - if err != nil { - fmt.Printf("[ERROR] %s\n", err) - os.Exit(1) - } - options = append(options, WithStore(s)) - } input := &credentialInput{} if _, err := input.ReadFrom(os.Stdin); err != nil { fmt.Printf("[ERROR] %s\n", err) os.Exit(1) } - if input.host != hostname || !strings.HasPrefix(input.protocol, "http") { + if input.host != runner.Hostname() || !strings.HasPrefix(input.protocol, "http") { fmt.Print(input) os.Exit(0) } - if err := printCredential(ctx, privateKey, appID, options); err != nil { + token, err := runner.Run(context.Background()) + if err != nil { fmt.Printf("[ERROR] %s\n", err) os.Exit(1) } + printCredential(token) + os.Exit(0) } -func printCredential(ctx context.Context, privateKey string, appID int64, options []AutherOption) error { - auther, err := NewAutherFromFile(privateKey, appID, options...) - if err != nil { - return err - } - token, err := auther.FetchToken(ctx) - +func printCredential(token string) { fmt.Println("protocol=https") fmt.Println("host=github.com") fmt.Println("username=x-access-token") fmt.Printf("password=%s\n", token) - - return nil } type credentialInput struct { From 81c69ccf5225fb3b7cca768e580583eb971ca810 Mon Sep 17 00:00:00 2001 From: mackee Date: Fri, 24 Jan 2020 14:12:28 +0900 Subject: [PATCH 2/3] add new command: ghawrap - improve GITHUB_TOKEN when exec command --- cmd/ghawrap/main.go | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 cmd/ghawrap/main.go diff --git a/cmd/ghawrap/main.go b/cmd/ghawrap/main.go new file mode 100644 index 0000000..fc63ee0 --- /dev/null +++ b/cmd/ghawrap/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/exec" + "syscall" + + "github.com/mackee/git-credential-github-apps/githubapps" +) + +func main() { + runner, err := githubapps.ParseArgs() + if err != nil { + if err == githubapps.ErrShowHelp { + os.Exit(0) + } + fmt.Printf("[ERROR] %s\n", err) + os.Exit(1) + } + + args := runner.Args() + if len(args) == 0 || (len(args) == 1 && args[0] == "--") { + fmt.Println("[ERROR] not provides command from args. eg. ghawrap -- yourcli options...") + os.Exit(1) + } + name := args[0] + cmdArgs := args[1:] + if name == "--" { + name = args[1] + cmdArgs = args[2:] + } + + ctx := context.Background() + token, err := runner.Run(ctx) + if err != nil { + fmt.Printf("[ERROR] %s\n", err) + os.Exit(1) + } + + err = runCommand(name, cmdArgs, []string{"GITHUB_TOKEN=" + token}) + if err != nil { + fmt.Printf("[ERROR] %s\n", err) + os.Exit(1) + } +} + +func runCommand(name string, args, envVars []string) error { + bin, err := exec.LookPath(name) + if err != nil { + return err + } + + return syscall.Exec(bin, args, envVars) +} From 363973d3c58fdee7505b1c78a4e47f42cea19e14 Mon Sep 17 00:00:00 2001 From: mackee Date: Fri, 24 Jan 2020 14:41:16 +0900 Subject: [PATCH 3/3] fix miss syscall.Exec --- cmd/ghawrap/main.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cmd/ghawrap/main.go b/cmd/ghawrap/main.go index fc63ee0..e8cade0 100644 --- a/cmd/ghawrap/main.go +++ b/cmd/ghawrap/main.go @@ -25,11 +25,8 @@ func main() { fmt.Println("[ERROR] not provides command from args. eg. ghawrap -- yourcli options...") os.Exit(1) } - name := args[0] - cmdArgs := args[1:] - if name == "--" { - name = args[1] - cmdArgs = args[2:] + if args[0] == "--" { + args = args[1:] } ctx := context.Background() @@ -39,18 +36,19 @@ func main() { os.Exit(1) } - err = runCommand(name, cmdArgs, []string{"GITHUB_TOKEN=" + token}) + os.Setenv("GITHUB_TOKEN", token) + err = runCommand(args, os.Environ()) if err != nil { fmt.Printf("[ERROR] %s\n", err) os.Exit(1) } } -func runCommand(name string, args, envVars []string) error { - bin, err := exec.LookPath(name) +func runCommand(command []string, envVars []string) error { + bin, err := exec.LookPath(command[0]) if err != nil { return err } - return syscall.Exec(bin, args, envVars) + return syscall.Exec(bin, command, envVars) }