func (C *CMD) execute() (code int, err error) { c := exec.Command(C.Name, C.Args...) c.Stdout = C.Stdout c.Stderr = C.Stderr c.Env = os.Environ() if C.EchoStdout { c.Stdout = io.MultiWriter(os.Stdout, c.Stdout) } if C.EchoStderr { c.Stderr = io.MultiWriter(os.Stderr, c.Stderr) } if C.WriteStdout != nil { c.Stdout = io.MultiWriter(C.WriteStdout, c.Stdout) } if C.WriteStderr != nil { c.Stderr = io.MultiWriter(C.WriteStderr, c.Stderr) } if C.EchoStdout || C.EchoStderr { cli.Logf("shell> %s", C) } if err := c.Start(); err != nil { cli.Fatalf("Unable to begin command execution; %s", err) } err = c.Wait() if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { return status.ExitStatus(), err } } cli.Fatalf("Command failed, unable to get exit code: %s", C) } return 0, nil }
func (s *Sous) AssembleTargetContext(targetName string) (Target, *Context) { packs := s.Packs p := DetectProjectType(packs) if p == nil { cli.Fatalf("no buildable project detected") } pack := CompiledPack{Pack: p} target, ok := pack.GetTarget(targetName) if !ok { cli.Fatalf("The %s build pack does not support %s", pack, targetName) } if fatal := CheckForProblems(pack.Pack); fatal { cli.Fatal() } context := GetContext(targetName) err := target.Check() if err != nil { cli.Fatalf("unable to %s %s project: %s", targetName, pack, err) } // If the pack specifies a version, check it matches the tagged version packAppVersion := strings.Split(pack.AppVersion(), "+")[0] if packAppVersion != "" { pv := version.Version(packAppVersion) gv := version.Version(context.BuildVersion.MajorMinorPatch) if !pv.Version.LimitedEqual(gv.Version) { cli.Warn("using latest git tagged version %s; your code reports version %s, which is ignored", gv, pv) } } return target, context }
func Contracts(sous *core.Sous, args []string) { contractsFlags.Parse(args) args = contractsFlags.Args() timeout := *timeoutFlag targetName := "app" if len(args) != 0 { targetName = args[0] } core.RequireGit() core.RequireDocker() if *dockerImage != "" { cli.Fatalf("-image flag not yet implemented") } target, context := sous.AssembleTargetContext(targetName) sous.RunTarget(target, context) cli.Logf("=> Running Contracts") cli.Logf(`=> **TIP:** Open another terminal in this directory and type **sous logs -f**`) taskHost := core.DivineTaskHost() port0, err := ports.GetFreePort() if err != nil { cli.Fatalf("Unable to get free port: %s", err) } dr := docker.NewRun(context.DockerTag()) dr.AddEnv("PORT0", strconv.Itoa(port0)) dr.AddEnv("TASK_HOST", taskHost) dr.StdoutFile = context.FilePath("stdout") dr.StderrFile = context.FilePath("stderr") container, err := dr.Background().Start() if err != nil { cli.Fatalf("Unable to start container: %s", err) } cli.AddCleanupTask(func() error { return container.KillIfRunning() }) failed := 0 for _, c := range theContracts { cli.Logf(`===> CHECKING CONTRACT: "%s"`, c.Name) cli.Logf(`===> Description: %s`, c.Desc(dr)) if c.Tips != nil { cli.Logf("===> **TIPS for this contract:**") cli.LogBulletList(" -", c.Tips(dr)) } failed += within(timeout, func() bool { return c.Premise(dr) }) } if failed != 0 { cli.Fatalf("%d contracts failed.", failed) } cli.Success() }
func Stamp(sous *core.Sous, args []string) { target := "app" if len(args) == 0 { cli.Fatalf("sous stamp requires at least one argument (a docker label)") } _, context := sous.AssembleTargetContext(target) if context.BuildNumber() == 0 { cli.Fatalf("no builds yet; sous stamp operates on your last successful build of the app target") } tag := context.DockerTag() run := docker.NewRun(tag) run.AddLabels(parseLabels(args)) run.StdoutFile = "/dev/null" run.StderrFile = "/dev/null" container, err := run.Background().Start() if err != nil { cli.Fatalf("Failed to start container for labeling: %s", err) } if err := container.KillIfRunning(); err != nil { cli.Fatalf("Failed to kill labelling container %s: %s", container, err) } cid := container.CID() if err := docker.Commit(cid, tag); err != nil { cli.Fatalf("Failed to commit labelled container %s: %s", container, err) } cli.Successf("Successfully added labels to %s; remember to push.", tag) }
func main() { if len(os.Args) < 2 { usage() } sousFlags, args := parseFlags(os.Args[2:]) command := os.Args[1] var cfg *config.Config var sous *core.Sous if command != "config" { updateHourly() cfg = config.Load() trapSignals() defer cli.Cleanup() sous = core.NewSous(Version, Revision, OS, Arch, loadCommands(), BuildPacks(cfg), sousFlags, cfg) } else { sous = core.NewSous(Version, Revision, OS, Arch, loadCommands(), nil, sousFlags, nil) } c, ok := sous.Commands[command] if !ok { cli.Fatalf("Command %s not recognised; try `sous help`", command) } // It is the responsibility of the command to exit with an appropriate // error code... c.Func(sous, args) // If it does not, we assume it failed... cli.Fatalf("Command did not complete correctly") }
func (c *CMD) JSON(v interface{}) { o := c.Out() if err := json.Unmarshal([]byte(o), &v); err != nil { cli.Fatalf("Unable to parse JSON from %s as %T: %s", c, v, err) } if v == nil { cli.Fatalf("Unmarshalled nil") } }
func RequireVersion(r *version.R) { if c := cmd.ExitCode("git", "--version"); c != 0 { cli.Fatalf("git required") } v := version.Version(cmd.Table("git", "--version")[0][2]) if !r.IsSatisfiedBy(v) { cli.Fatalf("you have git version %s; want %s", v, r.Original) } }
func (c *container) Image() string { var dc []DockerContainer cmd.JSON(&dc, "docker", "inspect", c.Name()) if len(dc) == 0 { cli.Fatalf("Container %s does not exist.", c) } if len(dc) != 1 { cli.Fatalf("Multiple containers match %s", c) } return dc[0].Image }
func ImageID(image string) string { var i []Image cmd.JSON(&i, "docker", "inspect", image) if len(i) == 0 { cli.Fatalf("image missing after pull: %s", image) } if len(i) != 1 { cli.Fatalf("multiple images match %s; ensure sous is using unique tags", image) } return i[0].ID }
func (ts Targets) Add(target Target) { n := target.Name() if _, ok := ts[n]; ok { cli.Fatalf("target %s already added", n) } _, ok := knownTargets[n] if !ok { cli.Fatalf("target %s is not known", n) } ts[n] = target }
func getLabelsFromImage(imageTag string) map[string]string { var images []*Image cmd.JSON(&images, "docker", "inspect", imageTag) if len(images) == 0 { cli.Fatalf("cannot find image %s", imageTag) } if len(images) > 1 { cli.Fatalf("multiple images named %s, cannot continue", imageTag) } image := images[0] return image.Config.Labels }
func (t *AppTarget) PreDockerBuild(c *core.Context) { if t.artifactPath == "" { cli.Fatalf("Artifact path not set by compile target.") } if !file.Exists(t.artifactPath) { cli.Fatalf("Artifact not at %s", t.artifactPath) } filename := path.Base(t.artifactPath) localArtifact := filename file.TemporaryLink(t.artifactPath, "./"+localArtifact) t.artifactPath = localArtifact }
func Load() *Config { if c == nil { if !file.ReadJSON(&c, "~/.sous/config") { if err := Update(); err != nil { cli.Fatalf("Unable to load config: %s", err) } if !file.ReadJSON(&c, "~/.sous/config") { cli.Fatalf("Unable to read %s", "~/.sous/config") } } } return c }
func ReadJSON(v interface{}, pathFormat string, a ...interface{}) bool { b, exists, path := Read(pathFormat, a...) if !exists { return false } if err := json.Unmarshal(b, &v); err != nil { cli.Fatalf("Unable to parse JSON in %s as %T: %s", path, v, err) } if v == nil { cli.Fatalf("Unmarshalled nil") } return true }
func (t *AppTarget) SetState(fromTarget string, state interface{}) { if fromTarget != "compile" { return } m, ok := state.(map[string]string) if !ok { cli.Fatalf("app target got a %T from compile target, expected map[string]string", state) } artifactPath, ok := m["artifactPath"] if !ok { cli.Fatalf("app target got %+v from compile target; expected key 'artifactPath'", m) } t.artifactPath = artifactPath }
func Config(sous *core.Sous, args []string) { if len(args) == 0 || len(args) > 2 { cli.Fatalf("usage: sous config <key> [<new-value>]") } if len(args) == 1 { if v, ok := config.Properties()[args[0]]; ok { cli.Outf(v) cli.Success() } cli.Fatalf("Key %s not found", args[0]) } config.Set(args[0], args[1]) cli.Logf("Successfully set %s to %s", args[0], args[1]) cli.Success() }
func IndexIsDirty() bool { code := cmd.ExitCode("git", "diff-index", "--quiet", "HEAD") if code > 1 || code < 0 { cli.Fatalf("Unable to determine if git index is dirty; Got exit code %d; want 0-1") } return code == 1 }
func getOriginURL() *url.URL { table := cmd.Table("git", "remote", "-v") if len(table) == 0 { cli.Fatalf("no git remotes set up") } for _, row := range table { if row[0] == "origin" { u, err := url.Parse(row[1]) if err != nil { cli.Fatalf("unable to parse origin (%s) as URL; %s", row[1], err) } return u } } return nil }
func Ls(sous *core.Sous, args []string) { //globalFlag := lsFlags.Bool("g", false, "global: list files in all projects sous has built") //lsFlags.Parse(args) //global := *globalFlag args = lsFlags.Args() if len(args) != 0 { cli.Fatalf("sous ls does not accept any arguments") } _, context := sous.AssembleTargetContext("app") cli.Outf(" ===> Images") images := sous.LsImages(context) if len(images) == 0 { cli.Logf(" no images for this project") } for _, image := range images { cli.Logf(" %s:%s", image.Name, image.Tag) } cli.Outf(" ===> Containers") containers := sous.LsContainers(context) if len(containers) == 0 { cli.Logf(" no containers for this project") } for _, container := range containers { cli.Logf(" %s (%s)", container.Name(), container.CID()) } cli.Success() }
func WriteJSON(data interface{}, pathFormat string, a ...interface{}) { b, err := json.MarshalIndent(data, "", "\t") if err != nil { cli.Fatalf("Unable to marshal %T object to JSON: %s", data, err) } Write(b, pathFormat, a...) }
func Find(glob string) []string { files, err := filepath.Glob(glob) if err != nil { cli.Fatalf("Unable to search for files matching %s; %s", glob, err) } return files }
func Create(path string) *os.File { f, err := os.Create(path) if err != nil { cli.Fatalf("Unable to write to file: %s", err) } return f }
func RequireDaemon() { if c := cmd.ExitCode("docker", "ps"); c != 0 { cli.Logf("`docker ps` exited with code %d", c) if dockermachine.Installed() { vms := dockermachine.RunningVMs() if len(vms) != 0 { cli.Fatalf(`Tip: eval "$(docker-machine env %s)"`, vms[0]) } vms = dockermachine.VMs() switch len(vms) { case 0: cli.Logf("Tip: you should create a machine using docker-machine") case 1: start := "" if cmd.Stdout("docker-machine", "status", vms[0]) != "Running" { start = fmt.Sprintf("docker-machine start %s && ", vms[0]) } cli.Logf(`Tip: %seval "$(docker-machine env %s)"`, start, vms[0]) default: cli.Logf("Tip: start one of your docker machines (%s)", strings.Join(vms, ", ")) } } cli.Fatal() } }
// BuildFile builds the specified docker file in the context of the specified // directory. func BuildFile(dockerfile, dir, tag string) string { if !file.Exists(dockerfile) { cli.Fatalf("File does not exist: %s", dockerfile) } dir = path.Resolve(dir) localDockerfile := ".SousDockerfile" if file.Exists(localDockerfile) { file.Remove(localDockerfile) } file.RemoveOnExit(localDockerfile) // If there is a .gitignore, but no .dockerignore, link it as .dockerignore if file.Exists(".gitignore") { if file.Exists(".dockerignore") { cli.Warn("./.dockerignore found, it is recommended to remove this so Sous can use your .gitignore") } else { file.TemporaryLink(".gitignore", ".dockerignore") // We try to clean this file up early, in preperation for the next build step defer file.Remove(".dockerignore") } } file.TemporaryLink(dockerfile, localDockerfile) // We try to clean the local Dockerfile up early, in preperation for the next build step defer file.Remove(localDockerfile) return dockerCmd("build", "-f", localDockerfile, "-t", tag, dir).Out() }
func Version(s string) *V { v, err := NewVersion(s) if err != nil { cli.Fatalf("unable to parse version string '%s'; %s", s, err) } return v }
func (p *Pack) dockerFrom(nodeVersion, target string) string { if tag, ok := p.Config.AvailableVersions.GetBaseImageTag(nodeVersion, target); ok { return tag } cli.Fatalf("No base image available for NodeJS %s, target: %s", nodeVersion, target) return "" }
func TemporaryLink(path, newPath string) { if Exists(newPath) { cli.Fatalf("Unable to link file to %s, it already exists", newPath) } RemoveOnExit(newPath) Link(path, newPath) }
func (c *CMD) Out() string { out, err := c.OutErr() if err != nil { cli.Fatalf("Error running %s; %s", c, err) } return out }
func Write(data []byte, pathFormat string, a ...interface{}) { p := path.Resolve(pathFormat, a...) dir.EnsureExists(path.BaseDir(p)) err := ioutil.WriteFile(p, data, 0777) if err != nil { cli.Fatalf("unable to write file %s; %s", p, err) } }
func (c *Context) TemporaryLinkResource(name string) { fileContents, ok := resources.Files[name] if !ok { cli.Fatalf("Cannot find resource %s, ensure go generate succeeded", name) } c.SaveFile(fileContents, name) file.TemporaryLink(c.FilePath(name), name) }