package main import ( "errors" "fmt" "net/http" "net/url" "os" "os/exec" "github.com/urfave/cli/v2" "go.jolheiser.com/beaver" ) var ( Version = "develop" output string author string email string gpg string display bool ssh bool insecure bool debug bool ) func main() { app := cli.NewApp() app.Name = "git-import" app.Usage = "Import from vanity git URLs" app.Version = Version app.Flags = []cli.Flag{ &cli.BoolFlag{ Name: "display", Aliases: []string{"d"}, Usage: "Display URL instead of cloning", Destination: &display, }, &cli.BoolFlag{ Name: "ssh", Aliases: []string{"s"}, Usage: "Use SSH if available", Destination: &ssh, }, &cli.StringFlag{ Name: "output", Aliases: []string{"o"}, Usage: "Path to output (default: git-import name)", Destination: &output, }, &cli.StringFlag{ Name: "author", Aliases: []string{"a"}, Usage: "Signature author", Destination: &author, }, &cli.StringFlag{ Name: "email", Aliases: []string{"e"}, Usage: "Signature email", Destination: &email, }, &cli.StringFlag{ Name: "gpg", Aliases: []string{"g"}, Usage: "GPG key to sign with", Destination: &gpg, }, &cli.BoolFlag{ Name: "insecure", Usage: "Use HTTP instead of HTTPS by default", Destination: &insecure, }, &cli.BoolFlag{ Name: "debug", Usage: "Enable debug logging", Destination: &debug, }, } app.Action = doImport if err := app.Run(os.Args); err != nil { beaver.Error(err) } } func doImport(ctx *cli.Context) error { if ctx.NArg() < 1 { return errors.New("must specify an import URL") } if debug { beaver.Console.Level = beaver.DEBUG } importURL := ctx.Args().First() u, err := url.Parse(importURL) if err != nil { return err } if u.Scheme == "" { u.Scheme = "https" if insecure { u.Scheme = "http" } } u.RawQuery = "git-import=1" beaver.Debugf("Getting git-import from %s", u.String()) res, err := http.Get(u.String()) if err != nil { return fmt.Errorf("could not request URL `%s`: %v", u.String(), err) } defer res.Body.Close() gitImport, err := parseMetaGitImport(res.Body) if err != nil { return fmt.Errorf("could not parse: %v", err) } beaver.Debug(gitImport) if display { if ssh { fmt.Println(gitImport.SSH) return nil } fmt.Println(gitImport.HTTP) return nil } return doClone(gitImport) } func doClone(gitImport GitImport) error { if output == "" { output = gitImport.Name } cmd := exec.Command("git", "clone", gitImport.HTTP, output) if ssh { if gitImport.SSH == "" { return errors.New("SSH was not provided by git-import") } if os.Getenv("GIT_SSH_COMMAND") == "" { return errors.New("no environment variable found for GIT_SSH_COMMAND") } cmd = exec.Command("git", "clone", gitImport.SSH, output) } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("could not clone: %v", err) } if ssh { if err := os.Chdir(output); err != nil { return fmt.Errorf("could not change to `%s` directory. Git config will not store SSH command", output) } if err := gitConfig("core.sshCommand", os.Getenv("GIT_SSH_COMMAND")); err != nil { beaver.Errorf("could not configure SSH: %v", err) } } if author != "" { if err := gitConfig("user.name", author); err != nil { beaver.Errorf("could not configure author: %v", err) } } if email != "" { if err := gitConfig("user.email", email); err != nil { beaver.Errorf("could not configure email: %v", err) } } if gpg != "" { if err := gitConfig("user.signingkey", gpg); err != nil { beaver.Errorf("could not configure GPG key: %v", err) } if err := gitConfig("commit.gpgsign", "true"); err != nil { beaver.Errorf("could not configure GPG signing: %v", err) } } return nil } func gitConfig(key, value string) error { cmd := exec.Command("git", "config", "--local", key, value) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }