blob: a2bb705e1a966acb5cd11777d2393ac5c381c1ae [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"crypto/rand"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"go.fuchsia.dev/jiri"
"go.fuchsia.dev/jiri/analytics_util"
"go.fuchsia.dev/jiri/cmdline"
)
var cmdInit = &cmdline.Command{
Runner: cmdline.RunnerFunc(runInit),
Name: "init",
Short: "Create a new jiri root",
Long: `
The "init" command creates new jiri "root" - basically a [root]/.jiri_root
directory and template files.
Running "init" in existing jiri [root] is safe.
`,
ArgsName: "[directory]",
ArgsLong: `
If you provide a directory, the command is run inside it. If this directory
does not exists, it will be created.
`,
}
var (
cacheFlag string
sharedFlag bool
showAnalyticsDataFlag bool
analyticsOptFlag string
rewriteSsoToHttpsFlag string
ssoCookieFlag string
keepGitHooks string
enableLockfileFlag string
lockfileNameFlag string
prebuiltJSON string
optionalAttrs string
partialFlag bool
partialSkipFlag arrayFlag
offloadPackfilesFlag bool
cipdParanoidFlag string
cipdMaxThreads int
)
const (
optionalAttrsNotSet = "[ATTRIBUTES_NOT_SET]"
)
func init() {
cmdInit.Flags.StringVar(&cacheFlag, "cache", "", "Jiri cache directory.")
cmdInit.Flags.BoolVar(&sharedFlag, "shared", false, "[DEPRECATED] All caches are shared.")
cmdInit.Flags.BoolVar(&showAnalyticsDataFlag, "show-analytics-data", false, "Show analytics data that jiri collect when you opt-in and exits.")
cmdInit.Flags.StringVar(&analyticsOptFlag, "analytics-opt", "", "Opt in/out of analytics collection. Takes true/false")
cmdInit.Flags.StringVar(&rewriteSsoToHttpsFlag, "rewrite-sso-to-https", "", "Rewrites sso fetches, clones, etc to https. Takes true/false.")
cmdInit.Flags.StringVar(&ssoCookieFlag, "sso-cookie-path", "", "Path to master SSO cookie file.")
cmdInit.Flags.StringVar(&keepGitHooks, "keep-git-hooks", "", "Whether to keep current git hooks in '.git/hooks' when doing 'jiri update'. Takes true/false.")
cmdInit.Flags.StringVar(&enableLockfileFlag, "enable-lockfile", "", "Enable lockfile enforcement")
cmdInit.Flags.StringVar(&lockfileNameFlag, "lockfile-name", "", "Set up filename of lockfile")
cmdInit.Flags.StringVar(&prebuiltJSON, "prebuilt-json", "", "Set up filename for prebuilt json file")
// Empty string is not used as default value for optionalAttrs as we
// use empty string to clear existing saved attributes.
cmdInit.Flags.StringVar(&optionalAttrs, "fetch-optional", optionalAttrsNotSet, "Set up attributes of optional projects and packages that should be fetched by jiri.")
cmdInit.Flags.BoolVar(&partialFlag, "partial", false, "Whether to use a partial checkout.")
cmdInit.Flags.Var(&partialSkipFlag, "skip-partial", "Skip using partial checkouts for these remotes.")
cmdInit.Flags.BoolVar(&offloadPackfilesFlag, "offload-packfiles", true, "Whether to use a CDN for packfiles if available.")
cmdInit.Flags.StringVar(&cipdParanoidFlag, "cipd-paranoid-mode", "", "Whether to use paranoid mode in cipd.")
// Default (0) causes CIPD to use as many threads as there are CPUs.
cmdInit.Flags.IntVar(&cipdMaxThreads, "cipd-max-threads", 0, "Number of threads to use for unpacking CIPD packages. If zero, uses all CPUs.")
}
func runInit(env *cmdline.Env, args []string) error {
if len(args) > 1 {
return fmt.Errorf("wrong number of arguments")
}
if showAnalyticsDataFlag {
fmt.Printf("%s\n", analytics_util.CollectedData)
return nil
}
var dir string
var err error
if len(args) == 1 {
dir, err = filepath.Abs(args[0])
if err != nil {
return err
}
if _, err := os.Stat(dir); err != nil {
if !os.IsNotExist(err) {
return err
}
if err := os.Mkdir(dir, 0755); err != nil {
return err
}
}
} else {
dir, err = os.Getwd()
if err != nil {
return err
}
}
d := filepath.Join(dir, jiri.RootMetaDir)
if _, err := os.Stat(d); err != nil {
if !os.IsNotExist(err) {
return err
}
if err := os.Mkdir(d, 0755); err != nil {
return err
}
}
if cacheFlag != "" {
cache, err := filepath.Abs(cacheFlag)
if err != nil {
return err
}
if _, err := os.Stat(cache); os.IsNotExist(err) {
if err := os.MkdirAll(cache, 0755); err != nil {
return err
}
}
}
config := &jiri.Config{}
configPath := filepath.Join(d, jiri.ConfigFile)
if _, err := os.Stat(configPath); err == nil {
config, err = jiri.ConfigFromFile(configPath)
if err != nil {
return err
}
} else if !os.IsNotExist(err) {
return err
}
if cacheFlag != "" {
config.CachePath = cacheFlag
}
if keepGitHooks != "" {
if val, err := strconv.ParseBool(keepGitHooks); err != nil {
return fmt.Errorf("'keep-git-hooks' flag should be true or false")
} else {
config.KeepGitHooks = val
}
}
if rewriteSsoToHttpsFlag != "" {
if val, err := strconv.ParseBool(rewriteSsoToHttpsFlag); err != nil {
return fmt.Errorf("'rewrite-sso-to-https' flag should be true or false")
} else {
config.RewriteSsoToHttps = val
}
}
if optionalAttrs != optionalAttrsNotSet {
config.FetchingAttrs = optionalAttrs
}
if partialFlag {
config.Partial = partialFlag
}
for _, r := range partialSkipFlag {
config.PartialSkip = append(config.PartialSkip, r)
}
if offloadPackfilesFlag {
config.OffloadPackfiles = offloadPackfilesFlag
}
if ssoCookieFlag != "" {
config.SsoCookiePath = ssoCookieFlag
}
if lockfileNameFlag != "" {
config.LockfileName = lockfileNameFlag
}
if prebuiltJSON != "" {
config.PrebuiltJSON = prebuiltJSON
}
if enableLockfileFlag != "" {
if _, err := strconv.ParseBool(enableLockfileFlag); err != nil {
return fmt.Errorf("'enable-lockfile' flag should be true or false")
}
config.LockfileEnabled = enableLockfileFlag
}
if cipdParanoidFlag != "" {
if _, err := strconv.ParseBool(cipdParanoidFlag); err != nil {
return fmt.Errorf("'cipd-paranoid-mode' flag should be true or false")
}
config.CipdParanoidMode = cipdParanoidFlag
}
config.CipdMaxThreads = cipdMaxThreads
if analyticsOptFlag != "" {
if val, err := strconv.ParseBool(analyticsOptFlag); err != nil {
return fmt.Errorf("'analytics-opt' flag should be true or false")
} else {
if val {
config.AnalyticsOptIn = "yes"
config.AnalyticsVersion = analytics_util.Version
bytes := make([]byte, 16)
io.ReadFull(rand.Reader, bytes)
if err != nil {
return err
}
bytes[6] = (bytes[6] & 0x0f) | 0x40
bytes[8] = (bytes[8] & 0x3f) | 0x80
config.AnalyticsUserId = fmt.Sprintf("%x-%x-%x-%x-%x", bytes[0:4], bytes[4:6], bytes[6:8], bytes[8:10], bytes[10:])
} else {
config.AnalyticsOptIn = "no"
config.AnalyticsVersion = ""
config.AnalyticsUserId = ""
}
}
}
if err := config.Write(configPath); err != nil {
return err
}
// TODO(phosek): also create an empty manifest
return nil
}