github.com/keybase/client/go@v0.0.0-20240520164431-4f512a4c85a3/client/cmd_git_lfs_config.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "os/exec" 9 "runtime" 10 "strings" 11 12 "github.com/keybase/cli" 13 "github.com/keybase/client/go/libcmdline" 14 "github.com/keybase/client/go/libkb" 15 ) 16 17 const ( 18 gitURLPrefix = "keybase://" 19 ) 20 21 type CmdGitLFSConfig struct { 22 libkb.Contextified 23 path string 24 repo string 25 } 26 27 func newCmdGitLFSConfig(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command { 28 return cli.Command{ 29 Name: "lfs-config", 30 Usage: "Configures a keybase git checkout to use LFS", 31 Description: "Git LFS (Large File Storage) is a git extension that keeps pointers to\n large files within your git repository, but the files themselves are stored\n externally. KBFS supports being the external storage for LFS, and running\n this command in a checkout will configure it to use KBFS for LFS.\n To install Git LFS, see https://git-lfs.github.com.", 32 Action: func(c *cli.Context) { 33 cmd := NewCmdGitLFSConfigRunner(g) 34 cl.ChooseCommand(cmd, "lfs-config", c) 35 }, 36 Flags: []cli.Flag{ 37 cli.StringFlag{ 38 Name: "path", 39 Usage: "Location of local git checkout (default: current working dir)", 40 }, 41 cli.StringFlag{ 42 Name: "repo", 43 Usage: "Keybase repo URL (default: first keybase remote URL in checkout)", 44 }, 45 }, 46 } 47 } 48 49 func NewCmdGitLFSConfigRunner(g *libkb.GlobalContext) *CmdGitLFSConfig { 50 return &CmdGitLFSConfig{Contextified: libkb.NewContextified(g)} 51 } 52 53 func (c *CmdGitLFSConfig) ParseArgv(ctx *cli.Context) error { 54 c.path = ctx.String("path") 55 c.repo = ctx.String("repo") 56 if c.repo != "" && !strings.HasPrefix(c.repo, gitURLPrefix) { 57 return fmt.Errorf("%s is not a valid Keybase repo", c.repo) 58 } 59 return nil 60 } 61 62 func (c *CmdGitLFSConfig) gitExec(command ...string) (string, error) { 63 path := []string{} 64 if c.path != "" { 65 path = []string{"-C", c.path} 66 } 67 cmd := exec.Command("git", 68 append(path, command...)...) 69 output, err := cmd.CombinedOutput() 70 if err != nil { 71 return "", err 72 } 73 return string(output), nil 74 } 75 76 func (c *CmdGitLFSConfig) getRepo() (string, error) { 77 if c.repo != "" { 78 return c.repo, nil 79 } 80 81 // Use the first keybase:// link by default. 82 output, err := c.gitExec("remote", "-v") 83 if err != nil { 84 return "", err 85 } 86 reader := bytes.NewBufferString(output) 87 for { 88 line, err := reader.ReadString('\n') 89 if err == io.EOF { 90 return "", errors.New("No keybase remote found") 91 } else if err != nil { 92 return "", err 93 } 94 95 s := strings.Fields(line) 96 if len(s) < 2 { 97 continue 98 } 99 if strings.HasPrefix(s[1], gitURLPrefix) { 100 return s[1], nil 101 } 102 } 103 } 104 105 func (c *CmdGitLFSConfig) Run() error { 106 dui := c.G().UI.GetDumbOutputUI() 107 108 // Find the repo URL. 109 repo, err := c.getRepo() 110 if err != nil { 111 return err 112 } 113 114 // Add the necessary lfs config. 115 _, err = c.gitExec( 116 "config", "--add", "lfs.standalonetransferagent", "keybase-lfs") 117 if err != nil { 118 return err 119 } 120 _, err = c.gitExec( 121 "config", "--add", "lfs.customtransfer.keybase-lfs.path", 122 "git-remote-keybase") 123 if err != nil { 124 return err 125 } 126 // Note that the "origin" here as a remote name doesn't really 127 // matter, since git-remote-keybase doesn't use it for anything 128 // when in LFS mode. It's only there because it's expected in the 129 // argument list. 130 quoteArgs := "" 131 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { 132 // Windows and macOS require quotes around the args, but linux 133 // does not. 134 quoteArgs = "\"" 135 } 136 _, err = c.gitExec( 137 "config", "--add", "lfs.customtransfer.keybase-lfs.args", 138 fmt.Sprintf("%slfs origin %s%s", quoteArgs, repo, quoteArgs)) 139 if err != nil { 140 return err 141 } 142 143 repoString := "This repo" 144 if c.path != "" { 145 repoString = "The repo at " + c.path 146 } 147 dui.Printf("Success! %s is now configured to use the following Keybase\nrepository for LFS:\n", repoString) 148 dui.Printf("\t%s\n\n", repo) 149 dui.Printf("Assuming you have installed Git LFS (see https://git-lfs.github.com) you can now\nconfigure git to store certain files directly in Keybase. For example:\n") 150 dui.Printf("\tgit lfs install\n") 151 dui.Printf("\tgit lfs track \"*.zip\"\n") 152 dui.Printf("\tgit add .gitattributes\n\n") 153 dui.Printf("Note that new checkouts of this repository will see a \"missing protocol\" error\nuntil you have configured it with this `keybase git lfs-config` command. After\ndoing so, you can sync the LFS files with this command:\n") 154 dui.Printf("\tgit checkout -f HEAD\n") 155 return nil 156 } 157 158 func (c *CmdGitLFSConfig) GetUsage() libkb.Usage { 159 return libkb.Usage{ 160 Config: true, 161 API: true, 162 KbKeyring: true, 163 } 164 }