github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }