blob: d110dc1590a29b8eba6c925599104f5f2808216e [file] [log] [blame]
// Copyright 2015 The Go 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 (
"flag"
"fmt"
"os"
"strings"
"golang.org/x/build/buildlet"
"golang.org/x/build/dashboard"
"golang.org/x/build/envutil"
)
func run(args []string) error {
fs := flag.NewFlagSet("run", flag.ContinueOnError)
fs.Usage = func() {
fmt.Fprintln(os.Stderr, "create usage: gomote run [run-opts] <instance> <cmd> [args...]")
fs.PrintDefaults()
os.Exit(1)
}
var sys bool
fs.BoolVar(&sys, "system", false, "run inside the system, and not inside the workdir; this is implicit if cmd starts with '/'")
var debug bool
fs.BoolVar(&debug, "debug", false, "write debug info about the command's execution before it begins")
var env stringSlice
fs.Var(&env, "e", "Environment variable KEY=value. The -e flag may be repeated multiple times to add multiple things to the environment.")
var path string
fs.StringVar(&path, "path", "", "Comma-separated list of ExecOpts.Path elements. The special string 'EMPTY' means to run without any $PATH. The empty string (default) does not modify the $PATH. Otherwise, the following expansions apply: the string '$PATH' expands to the current PATH element(s), the substring '$WORKDIR' expands to the buildlet's temp workdir.")
var dir string
fs.StringVar(&dir, "dir", "", "Directory to run from. Defaults to the directory of the command, or the work directory if -system is true.")
var builderEnv string
fs.StringVar(&builderEnv, "builderenv", "", "Optional alternate builder to act like. Must share the same underlying buildlet host type, or it's an error. For instance, linux-amd64-race or linux-386-387 are compatible with linux-amd64, but openbsd-amd64 and openbsd-386 are different hosts.")
fs.Parse(args)
if fs.NArg() < 2 {
fs.Usage()
}
name, cmd := fs.Arg(0), fs.Arg(1)
var conf dashboard.BuildConfig
bc, conf, err := clientAndConf(name)
if err != nil {
return err
}
if builderEnv != "" {
altConf, ok := dashboard.Builders[builderEnv]
if !ok {
return fmt.Errorf("unknown --builderenv=%q builder value", builderEnv)
}
if altConf.HostType != conf.HostType {
return fmt.Errorf("--builderEnv=%q has host type %q, which is not compatible with the named buildlet's host type %q",
builderEnv, altConf.HostType, conf.HostType)
}
conf = altConf
}
var pathOpt []string
if path == "EMPTY" {
pathOpt = []string{} // non-nil
} else if path != "" {
pathOpt = strings.Split(path, ",")
}
remoteErr, execErr := bc.Exec(cmd, buildlet.ExecOpts{
Dir: dir,
SystemLevel: sys || strings.HasPrefix(cmd, "/"),
Output: os.Stdout,
Args: fs.Args()[2:],
ExtraEnv: envutil.Dedup(conf.GOOS() == "windows", append(conf.Env(), []string(env)...)),
Debug: debug,
Path: pathOpt,
})
if execErr != nil {
return fmt.Errorf("Error trying to execute %s: %v", cmd, execErr)
}
return remoteErr
}
// stringSlice implements flag.Value, specifically for storing environment
// variable key=value pairs.
type stringSlice []string
func (*stringSlice) String() string { return "" } // default value
func (ss *stringSlice) Set(v string) error {
if v != "" {
if !strings.Contains(v, "=") {
return fmt.Errorf("-e argument %q doesn't contains an '=' sign.", v)
}
*ss = append(*ss, v)
}
return nil
}