go.fuchsia.dev/jiri@v0.0.0-20240502161911-b66513b29486/cmd/jiri/init.go (about)

     1  // Copyright 2016 The Fuchsia Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"crypto/rand"
     9  	"fmt"
    10  	"io"
    11  	"os"
    12  	"path/filepath"
    13  	"strconv"
    14  
    15  	"go.fuchsia.dev/jiri"
    16  	"go.fuchsia.dev/jiri/analytics_util"
    17  	"go.fuchsia.dev/jiri/cmdline"
    18  )
    19  
    20  var cmdInit = &cmdline.Command{
    21  	Runner: cmdline.RunnerFunc(runInit),
    22  	Name:   "init",
    23  	Short:  "Create a new jiri root",
    24  	Long: `
    25  The "init" command creates new jiri "root" - basically a [root]/.jiri_root
    26  directory and template files.
    27  
    28  Running "init" in existing jiri [root] is safe.
    29  `,
    30  	ArgsName: "[directory]",
    31  	ArgsLong: `
    32  If you provide a directory, the command is run inside it. If this directory
    33  does not exists, it will be created.
    34  `,
    35  }
    36  
    37  var (
    38  	cacheFlag             string
    39  	dissociateFlag        bool
    40  	sharedFlag            bool
    41  	showAnalyticsDataFlag bool
    42  	analyticsOptFlag      string
    43  	rewriteSsoToHttpsFlag string
    44  	ssoCookieFlag         string
    45  	keepGitHooks          string
    46  	enableLockfileFlag    string
    47  	lockfileNameFlag      string
    48  	prebuiltJSON          string
    49  	enableSubmodules      string
    50  	optionalAttrs         string
    51  	partialFlag           bool
    52  	partialSkipFlag       arrayFlag
    53  	offloadPackfilesFlag  bool
    54  	cipdParanoidFlag      string
    55  	cipdMaxThreads        int
    56  	excludeDirsFlag       arrayFlag
    57  )
    58  
    59  const (
    60  	optionalAttrsNotSet = "[ATTRIBUTES_NOT_SET]"
    61  )
    62  
    63  func init() {
    64  	cmdInit.Flags.StringVar(&cacheFlag, "cache", "", "Jiri cache directory.")
    65  	cmdInit.Flags.BoolVar(&sharedFlag, "shared", false, "[DEPRECATED] All caches are shared.")
    66  	cmdInit.Flags.BoolVar(&dissociateFlag, "dissociate", false, "Dissociate the git cache after a clone or fetch.")
    67  	cmdInit.Flags.BoolVar(&showAnalyticsDataFlag, "show-analytics-data", false, "Show analytics data that jiri collect when you opt-in and exits.")
    68  	cmdInit.Flags.StringVar(&analyticsOptFlag, "analytics-opt", "", "Opt in/out of analytics collection. Takes true/false")
    69  	cmdInit.Flags.StringVar(&rewriteSsoToHttpsFlag, "rewrite-sso-to-https", "", "Rewrites sso fetches, clones, etc to https. Takes true/false.")
    70  	cmdInit.Flags.StringVar(&ssoCookieFlag, "sso-cookie-path", "", "Path to master SSO cookie file.")
    71  	cmdInit.Flags.StringVar(&keepGitHooks, "keep-git-hooks", "", "Whether to keep current git hooks in '.git/hooks' when doing 'jiri update'. Takes true/false.")
    72  	cmdInit.Flags.StringVar(&enableLockfileFlag, "enable-lockfile", "", "Enable lockfile enforcement")
    73  	cmdInit.Flags.StringVar(&lockfileNameFlag, "lockfile-name", "", "Set up filename of lockfile")
    74  	cmdInit.Flags.StringVar(&prebuiltJSON, "prebuilt-json", "", "Set up filename for prebuilt json file")
    75  	cmdInit.Flags.StringVar(&enableSubmodules, "enable-submodules", "", "Enable submodules structure")
    76  	// Empty string is not used as default value for optionalAttrs as we
    77  	// use empty string to clear existing saved attributes.
    78  	cmdInit.Flags.StringVar(&optionalAttrs, "fetch-optional", optionalAttrsNotSet, "Set up attributes of optional projects and packages that should be fetched by jiri.")
    79  	cmdInit.Flags.BoolVar(&partialFlag, "partial", false, "Whether to use a partial checkout.")
    80  	cmdInit.Flags.Var(&partialSkipFlag, "skip-partial", "Skip using partial checkouts for these remotes.")
    81  	cmdInit.Flags.BoolVar(&offloadPackfilesFlag, "offload-packfiles", true, "Whether to use a CDN for packfiles if available.")
    82  	cmdInit.Flags.StringVar(&cipdParanoidFlag, "cipd-paranoid-mode", "", "Whether to use paranoid mode in cipd.")
    83  	// Default (0) causes CIPD to use as many threads as there are CPUs.
    84  	cmdInit.Flags.IntVar(&cipdMaxThreads, "cipd-max-threads", 0, "Number of threads to use for unpacking CIPD packages. If zero, uses all CPUs.")
    85  	cmdInit.Flags.Var(&excludeDirsFlag, "exclude-dirs", "Directories to skip when searching for local projects (Default: out).")
    86  }
    87  
    88  func runInit(env *cmdline.Env, args []string) error {
    89  	if len(args) > 1 {
    90  		return fmt.Errorf("wrong number of arguments")
    91  	}
    92  
    93  	if showAnalyticsDataFlag {
    94  		fmt.Printf("%s\n", analytics_util.CollectedData)
    95  		return nil
    96  	}
    97  
    98  	var dir string
    99  	var err error
   100  	if len(args) == 1 {
   101  		dir, err = filepath.Abs(args[0])
   102  		if err != nil {
   103  			return err
   104  		}
   105  		if _, err := os.Stat(dir); err != nil {
   106  			if !os.IsNotExist(err) {
   107  				return err
   108  			}
   109  			if err := os.Mkdir(dir, 0755); err != nil {
   110  				return err
   111  			}
   112  		}
   113  	} else {
   114  		dir, err = os.Getwd()
   115  		if err != nil {
   116  			return err
   117  		}
   118  	}
   119  
   120  	d := filepath.Join(dir, jiri.RootMetaDir)
   121  	if _, err := os.Stat(d); err != nil {
   122  		if !os.IsNotExist(err) {
   123  			return err
   124  		}
   125  		if err := os.Mkdir(d, 0755); err != nil {
   126  			return err
   127  		}
   128  	}
   129  
   130  	if cacheFlag != "" {
   131  		cache, err := filepath.Abs(cacheFlag)
   132  		if err != nil {
   133  			return err
   134  		}
   135  		if _, err := os.Stat(cache); os.IsNotExist(err) {
   136  			if err := os.MkdirAll(cache, 0755); err != nil {
   137  				return err
   138  			}
   139  		}
   140  	}
   141  
   142  	config := &jiri.Config{}
   143  	configPath := filepath.Join(d, jiri.ConfigFile)
   144  	if _, err := os.Stat(configPath); err == nil {
   145  		config, err = jiri.ConfigFromFile(configPath)
   146  		if err != nil {
   147  			return err
   148  		}
   149  	} else if !os.IsNotExist(err) {
   150  		return err
   151  	}
   152  
   153  	if cacheFlag != "" {
   154  		config.CachePath = cacheFlag
   155  	}
   156  
   157  	if dissociateFlag {
   158  		config.Dissociate = true
   159  	}
   160  
   161  	if keepGitHooks != "" {
   162  		if val, err := strconv.ParseBool(keepGitHooks); err != nil {
   163  			return fmt.Errorf("'keep-git-hooks' flag should be true or false")
   164  		} else {
   165  			config.KeepGitHooks = val
   166  		}
   167  	}
   168  
   169  	if gitConfigSubm, err := jiri.GitGetConfig("jiri.enableSubmodules"); err == nil {
   170  		if _, err := strconv.ParseBool(gitConfigSubm); err != nil {
   171  			return fmt.Errorf("'jiri.enableSubmodules' from git config should be true or false")
   172  		}
   173  		config.EnableSubmodules = gitConfigSubm
   174  	}
   175  
   176  	if enableSubmodules != "" {
   177  		if _, err := strconv.ParseBool(enableSubmodules); err != nil {
   178  			return fmt.Errorf("'enable-submodules' flag should be true or false")
   179  		} else {
   180  			config.EnableSubmodules = enableSubmodules
   181  		}
   182  	}
   183  
   184  	if rewriteSsoToHttpsFlag != "" {
   185  		if val, err := strconv.ParseBool(rewriteSsoToHttpsFlag); err != nil {
   186  			return fmt.Errorf("'rewrite-sso-to-https' flag should be true or false")
   187  		} else {
   188  			config.RewriteSsoToHttps = val
   189  		}
   190  	}
   191  
   192  	if optionalAttrs != optionalAttrsNotSet {
   193  		config.FetchingAttrs = optionalAttrs
   194  	}
   195  
   196  	if partialFlag {
   197  		config.Partial = partialFlag
   198  	}
   199  
   200  	for _, r := range partialSkipFlag {
   201  		config.PartialSkip = append(config.PartialSkip, r)
   202  	}
   203  
   204  	if offloadPackfilesFlag {
   205  		config.OffloadPackfiles = offloadPackfilesFlag
   206  	}
   207  
   208  	if ssoCookieFlag != "" {
   209  		config.SsoCookiePath = ssoCookieFlag
   210  	}
   211  
   212  	if lockfileNameFlag != "" {
   213  		config.LockfileName = lockfileNameFlag
   214  	}
   215  
   216  	if prebuiltJSON != "" {
   217  		config.PrebuiltJSON = prebuiltJSON
   218  	}
   219  
   220  	if enableLockfileFlag != "" {
   221  		if _, err := strconv.ParseBool(enableLockfileFlag); err != nil {
   222  			return fmt.Errorf("'enable-lockfile' flag should be true or false")
   223  		}
   224  		config.LockfileEnabled = enableLockfileFlag
   225  	}
   226  
   227  	if cipdParanoidFlag != "" {
   228  		if _, err := strconv.ParseBool(cipdParanoidFlag); err != nil {
   229  			return fmt.Errorf("'cipd-paranoid-mode' flag should be true or false")
   230  		}
   231  		config.CipdParanoidMode = cipdParanoidFlag
   232  	}
   233  
   234  	config.CipdMaxThreads = cipdMaxThreads
   235  
   236  	if analyticsOptFlag != "" {
   237  		if val, err := strconv.ParseBool(analyticsOptFlag); err != nil {
   238  			return fmt.Errorf("'analytics-opt' flag should be true or false")
   239  		} else {
   240  			if val {
   241  				config.AnalyticsOptIn = "yes"
   242  				config.AnalyticsVersion = analytics_util.Version
   243  
   244  				bytes := make([]byte, 16)
   245  				io.ReadFull(rand.Reader, bytes)
   246  				if err != nil {
   247  					return err
   248  				}
   249  				bytes[6] = (bytes[6] & 0x0f) | 0x40
   250  				bytes[8] = (bytes[8] & 0x3f) | 0x80
   251  
   252  				config.AnalyticsUserId = fmt.Sprintf("%x-%x-%x-%x-%x", bytes[0:4], bytes[4:6], bytes[6:8], bytes[8:10], bytes[10:])
   253  			} else {
   254  				config.AnalyticsOptIn = "no"
   255  				config.AnalyticsVersion = ""
   256  				config.AnalyticsUserId = ""
   257  			}
   258  		}
   259  	}
   260  
   261  	if len(excludeDirsFlag) != 0 {
   262  		config.ExcludeDirs = []string{}
   263  	}
   264  
   265  	for _, r := range excludeDirsFlag {
   266  		config.ExcludeDirs = append(config.ExcludeDirs, r)
   267  	}
   268  
   269  	if err := config.Write(configPath); err != nil {
   270  		return err
   271  	}
   272  	// TODO(phosek): also create an empty manifest
   273  
   274  	return nil
   275  }