Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package application | |
| 6 | |
| 7 import ( | |
| 8 "flag" | |
| 9 "io/ioutil" | |
| 10 "os" | |
| 11 "path/filepath" | |
| 12 "time" | |
| 13 | |
| 14 "github.com/luci/luci-go/vpython" | |
| 15 "github.com/luci/luci-go/vpython/api/env" | |
| 16 "github.com/luci/luci-go/vpython/filesystem" | |
| 17 "github.com/luci/luci-go/vpython/python" | |
| 18 "github.com/luci/luci-go/vpython/spec" | |
| 19 "github.com/luci/luci-go/vpython/venv" | |
| 20 | |
| 21 "github.com/luci/luci-go/common/cli" | |
| 22 "github.com/luci/luci-go/common/errors" | |
| 23 "github.com/luci/luci-go/common/logging" | |
| 24 "github.com/luci/luci-go/common/logging/gologger" | |
| 25 "github.com/luci/luci-go/common/system/environ" | |
| 26 | |
| 27 "github.com/maruel/subcommands" | |
| 28 "github.com/mitchellh/go-homedir" | |
| 29 "golang.org/x/net/context" | |
| 30 ) | |
| 31 | |
| 32 // An A is an application's default configuration. | |
|
iannucci
2017/02/23 02:24:22
why not Config (e.g. application.Config) or Defaul
dnj
2017/03/11 18:07:40
Made "Config".
| |
| 33 type A struct { | |
| 34 // PackageLoader is the package loader to use. | |
|
iannucci
2017/02/23 02:24:22
no documented default values?
dnj
2017/03/11 18:07:40
Nope, no default values at all!
The counterpart o
| |
| 35 PackageLoader venv.PackageLoader | |
| 36 | |
| 37 // VENVPackage is the VirtualEnv package to use for bootstrap generation . | |
| 38 VENVPackage env.Spec_Package | |
| 39 | |
| 40 // PruneThreshold, if > 0, is the maximum age of a VirtualEnv before it | |
| 41 // becomes candidate for pruning. If <= 0, no pruning will be performed. | |
| 42 // | |
| 43 // See venv.Config's PruneThreshold. | |
| 44 PruneThreshold time.Duration | |
| 45 // PruneLimit, if > 0, is the maximum number of VirtualEnv that should b e | |
| 46 // pruned passively. If <= 0, no limit will be applied. | |
| 47 // | |
| 48 // See venv.Config's PruneLimit. | |
| 49 PruneLimit int | |
|
iannucci
2017/02/23 02:24:22
same comments as venv.Config about defaults and na
dnj
2017/03/11 18:07:40
Done.
| |
| 50 | |
| 51 // Opts is the set of configured options. | |
| 52 Opts vpython.Options | |
| 53 } | |
| 54 | |
| 55 func (a *A) mainDev(c context.Context) error { | |
| 56 app := cli.Application{ | |
| 57 Name: "vpython", | |
| 58 Title: "VirtualEnv Python Bootstrap (Development Mode)", | |
| 59 Context: func(context.Context) context.Context { | |
| 60 // Discard the entry Context and use the one passed to u s. | |
|
iannucci
2017/02/23 02:24:22
why? testing?
maybe docstring this func?
edit: o
dnj
2017/03/11 18:07:40
OR... giving subcommands actual Context awareness
| |
| 61 c := c | |
| 62 | |
| 63 // Install our A instance into the Context. | |
| 64 c = withApplication(c, a) | |
| 65 | |
| 66 // Drop down to Info level debugging. | |
| 67 if logging.GetLevel(c) > logging.Info { | |
| 68 c = logging.SetLevel(c, logging.Info) | |
| 69 } | |
| 70 return c | |
| 71 }, | |
| 72 Commands: []*subcommands.Command{ | |
| 73 subcommands.CmdHelp, | |
| 74 subcommandInstall, | |
| 75 }, | |
| 76 } | |
| 77 | |
| 78 return python.Error(subcommands.Run(&app, a.Opts.Args)) | |
| 79 } | |
| 80 | |
| 81 func (a *A) mainImpl(c context.Context, args []string) error { | |
| 82 logConfig := logging.Config{ | |
| 83 Level: logging.Warning, | |
| 84 } | |
| 85 | |
| 86 hdir, err := homedir.Dir() | |
| 87 if err != nil { | |
| 88 return errors.Annotate(err).Reason("failed to get user home dire ctory").Err() | |
| 89 } | |
| 90 | |
| 91 a.Opts = vpython.Options{ | |
| 92 EnvConfig: venv.Config{ | |
| 93 BaseDir: filepath.Join(hdir, ".vpython"), | |
| 94 MaxHashLen: 6, | |
| 95 Package: a.VENVPackage, | |
| 96 PruneThreshold: a.PruneThreshold, | |
| 97 PruneLimit: a.PruneLimit, | |
| 98 Loader: a.PackageLoader, | |
| 99 }, | |
| 100 WaitForEnv: true, | |
| 101 Environ: environ.System(), | |
| 102 } | |
| 103 var specPath string | |
| 104 var devMode bool | |
| 105 | |
| 106 fs := flag.NewFlagSet("", flag.ExitOnError) | |
| 107 fs.BoolVar(&devMode, "dev", devMode, | |
| 108 "Enter development / subcommand mode (use 'help' for more option s).") | |
| 109 fs.StringVar(&a.Opts.EnvConfig.Python, "python", a.Opts.EnvConfig.Python , | |
| 110 "Path to system Python interpreter to use. Default is found on P ATH.") | |
| 111 fs.StringVar(&a.Opts.WorkDir, "workdir", a.Opts.WorkDir, | |
| 112 "Working directory to run the Python interpreter in. Default is current working directory.") | |
| 113 fs.StringVar(&a.Opts.EnvConfig.BaseDir, "root", a.Opts.EnvConfig.BaseDir , | |
| 114 "Path to virtual enviornment root directory. Default is the work ing directory. "+ | |
| 115 "If explicitly set to empty string, a temporary director y will be used and cleaned up "+ | |
| 116 "on completion.") | |
| 117 fs.StringVar(&specPath, "spec", specPath, | |
| 118 "Path to enviornment specification file to load. Default probes for one.") | |
| 119 logConfig.AddFlags(fs) | |
| 120 | |
| 121 if err := fs.Parse(args); err != nil { | |
| 122 if err == flag.ErrHelp { | |
| 123 return nil | |
| 124 } | |
| 125 return errors.Annotate(err).Reason("failed to parse flags").Err( ) | |
| 126 } | |
| 127 a.Opts.Args = fs.Args() | |
| 128 | |
| 129 c = logConfig.Set(c) | |
| 130 | |
| 131 // If an spec path was manually specified, load and use it. | |
| 132 if specPath != "" { | |
| 133 var err error | |
| 134 if a.Opts.EnvConfig.Spec, err = spec.Load(specPath); err != nil { | |
| 135 return errors.Annotate(err).Reason("failed to load envir onment specification file (-env) from: %(path)s"). | |
| 136 D("path", specPath). | |
| 137 Err() | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 // If an empty BaseDir was specified, use a temporary directory and clea n it | |
| 142 // up on completion. | |
| 143 if a.Opts.EnvConfig.BaseDir == "" { | |
| 144 tdir, err := ioutil.TempDir("", "vpython") | |
| 145 if err != nil { | |
| 146 return errors.Annotate(err).Reason("failed to create tem porary directory").Err() | |
| 147 } | |
| 148 defer func() { | |
| 149 logging.Debugf(c, "Removing temporary directory: %s", td ir) | |
| 150 if terr := filesystem.RemoveAll(tdir); terr != nil { | |
| 151 logging.WithError(terr).Warningf(c, "Failed to c lean up temporary directory; leaking: %s", tdir) | |
| 152 } | |
| 153 }() | |
| 154 a.Opts.EnvConfig.BaseDir = tdir | |
| 155 } | |
| 156 | |
| 157 // Development mode (subcommands). | |
| 158 if devMode { | |
| 159 return a.mainDev(c) | |
| 160 } | |
| 161 | |
| 162 if err := vpython.Run(c, a.Opts); err != nil { | |
| 163 return errors.Annotate(err).Err() | |
| 164 } | |
| 165 return nil | |
| 166 } | |
| 167 | |
| 168 // Main is the main application entry point. | |
| 169 func (a *A) Main(c context.Context) int { | |
| 170 c = gologger.StdConfig.Use(c) | |
| 171 c = logging.SetLevel(c, logging.Warning) | |
| 172 | |
| 173 return run(c, func(c context.Context) error { | |
| 174 return a.mainImpl(c, os.Args[1:]) | |
| 175 }) | |
| 176 } | |
| OLD | NEW |