github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsgit/git-remote-keybase/main.go (about) 1 // Copyright 2017 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 // Git remote helper for the Keybase file system. 6 7 package main 8 9 import ( 10 "context" 11 "flag" 12 "fmt" 13 "os" 14 "path/filepath" 15 "strings" 16 17 "github.com/keybase/client/go/kbconst" 18 "github.com/keybase/client/go/kbfs/env" 19 "github.com/keybase/client/go/kbfs/kbfsgit" 20 "github.com/keybase/client/go/kbfs/libfs" 21 "github.com/keybase/client/go/kbfs/libgit" 22 "github.com/keybase/client/go/kbfs/libkbfs" 23 "github.com/keybase/client/go/kbfs/stderrutils" 24 "github.com/keybase/client/go/libkb" 25 ) 26 27 var version = flag.Bool("version", false, "Print version") 28 29 const usageFormatStr = `Usage: 30 git-remote-keybase -version 31 32 To run against remote KBFS servers: 33 git-remote-keybase %s <remote> [keybase://<repo>] 34 35 To run in a local testing environment: 36 git-remote-keybase %s <remote> [keybase://<repo>] 37 38 Defaults: 39 %s 40 ` 41 42 func getUsageString(ctx libkbfs.Context) string { 43 remoteUsageStr := libkbfs.GetRemoteUsageString() 44 localUsageStr := libkbfs.GetLocalUsageString() 45 defaultUsageStr := libkbfs.GetDefaultsUsageString(ctx) 46 return fmt.Sprintf( 47 usageFormatStr, remoteUsageStr, localUsageStr, defaultUsageStr) 48 } 49 50 func getLocalGitDir() (gitDir string) { 51 gitDir = os.Getenv("GIT_DIR") 52 // On Windows, git annoyingly puts normal slashes in the 53 // environment variable. 54 return filepath.FromSlash(gitDir) 55 } 56 57 func start() (startErr *libfs.Error) { 58 kbCtx := env.NewContextWithPerfLog(libkb.GitPerfLogFileName) 59 60 switch kbCtx.GetRunMode() { 61 case kbconst.ProductionRunMode: 62 case kbconst.StagingRunMode: 63 fmt.Fprintf(os.Stderr, "Running in staging mode\n") 64 case kbconst.DevelRunMode: 65 fmt.Fprintf(os.Stderr, "Running in devel mode\n") 66 default: 67 panic(fmt.Sprintf("Unexpected run mode: %s", kbCtx.GetRunMode())) 68 } 69 70 defaultParams, storageRoot, err := libgit.Params(kbCtx, 71 kbCtx.GetDataDir(), nil) 72 if err != nil { 73 return libfs.InitError(err.Error()) 74 } 75 defer func() { 76 rmErr := os.RemoveAll(storageRoot) 77 if rmErr != nil { 78 fmt.Fprintf(os.Stderr, 79 "Error cleaning storage dir %s: %+v\n", storageRoot, rmErr) 80 } 81 }() 82 defaultLogPath := filepath.Join(kbCtx.GetLogDir(), libkb.GitLogFileName) 83 84 // Make sure the service is running before blocking on a connection to it. 85 err = kbCtx.CheckService() 86 if err != nil { 87 startErr = libfs.InitError(err.Error()) 88 return startErr 89 } 90 91 // Duplicate the stderr fd, so that when the logger closes it when 92 // redirecting log messages to a file, we will still be able to 93 // write status updates back to the git process. 94 stderrFile, err := stderrutils.DupStderr() 95 if err != nil { 96 return libfs.InitError(err.Error()) 97 } 98 defer stderrFile.Close() 99 100 defer func() { 101 // Now that the stderr has been duplicated, print all errors 102 // to the duplicate as well as to the new `os.Stderr` down in 103 // `main()`, so that the error shows up both in the log and to 104 // the user. 105 if startErr != nil { 106 fmt.Fprintf(stderrFile, "git-remote-keybase error: (%d) %s\n", 107 startErr.Code, startErr.Message) 108 } 109 }() 110 111 kbfsParams := libkbfs.AddFlagsWithDefaults( 112 flag.CommandLine, defaultParams, defaultLogPath) 113 flag.Parse() 114 115 if *version { 116 fmt.Printf("%s\n", libkbfs.VersionString()) 117 return nil 118 } 119 120 if len(flag.Args()) < 1 { 121 fmt.Print(getUsageString(kbCtx)) 122 return libfs.InitError("no remote repo specified") 123 } 124 125 remote := flag.Arg(0) 126 var repo string 127 lfs := false 128 if len(flag.Args()) > 1 && remote != "lfs" { 129 repo = flag.Arg(1) 130 } else if remote == "lfs" && len(flag.Args()) == 3 { 131 lfs = true 132 remote = flag.Arg(1) 133 repo = flag.Arg(2) 134 } else { 135 // For LFS invocation, on some systems/shells the arguments 136 // actually come together in a single quoted argument for some 137 // reason. 138 s := strings.Fields(remote) 139 if len(s) > 2 { 140 lfs = s[0] == "lfs" 141 remote = s[1] 142 repo = s[2] 143 } 144 } 145 146 if !lfs && len(flag.Args()) > 2 { 147 fmt.Print(getUsageString(kbCtx)) 148 return libfs.InitError("extra arguments specified (flags go before the first argument)") 149 } 150 151 if lfs { 152 // For LFS uploads we should be flushing the journal 153 // constantly, so we don't build up a huge batch of data that 154 // conflict resolution can't handle. (See HOTPOT-1554.) 155 kbfsParams.TLFJournalBackgroundWorkStatus = 156 libkbfs.TLFJournalBackgroundWorkEnabled 157 } 158 159 options := kbfsgit.StartOptions{ 160 KbfsParams: *kbfsParams, 161 Remote: remote, 162 Repo: repo, 163 GitDir: getLocalGitDir(), 164 LFS: lfs, 165 } 166 167 ctx := context.Background() 168 return kbfsgit.Start( 169 ctx, options, kbCtx, defaultLogPath, os.Stdin, os.Stdout, 170 stderrFile) 171 } 172 173 func main() { 174 runMode := os.Getenv("KEYBASE_RUN_MODE") 175 if len(runMode) == 0 { 176 // Default to prod. 177 os.Setenv("KEYBASE_RUN_MODE", "prod") 178 } 179 180 err := start() 181 if err != nil { 182 fmt.Fprintf(os.Stderr, "git-remote-keybase error: (%d) %s\n", 183 err.Code, err.Message) 184 os.Exit(err.Code) 185 } 186 os.Exit(0) 187 }