Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions cmd/ghawrap/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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)
}
if args[0] == "--" {
args = args[1:]
}

ctx := context.Background()
token, err := runner.Run(ctx)
if err != nil {
fmt.Printf("[ERROR] %s\n", err)
os.Exit(1)
}

os.Setenv("GITHUB_TOKEN", token)
err = runCommand(args, os.Environ())
if err != nil {
fmt.Printf("[ERROR] %s\n", err)
os.Exit(1)
}
}

func runCommand(command []string, envVars []string) error {
bin, err := exec.LookPath(command[0])
if err != nil {
return err
}

return syscall.Exec(bin, command, envVars)
}
2 changes: 1 addition & 1 deletion auth.go → githubapps/auth.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package githubapps

import (
"context"
Expand Down
117 changes: 117 additions & 0 deletions githubapps/credential.go
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 1 addition & 1 deletion store.go → githubapps/store.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package githubapps

import (
"encoding/gob"
Expand Down
86 changes: 15 additions & 71 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down