Merge "v.io/jiri: remove support for old style profiles"
diff --git a/profiles/commandline/driver.go b/profiles/commandline/driver.go
index 94b8380..6fa89ef 100644
--- a/profiles/commandline/driver.go
+++ b/profiles/commandline/driver.go
@@ -126,6 +126,7 @@
 	specificVersionsFlag bool
 	cleanupFlag          bool
 	rmAllFlag            bool
+	rewriteManifestFlag  bool
 )
 
 func Main(name string) {
@@ -183,8 +184,8 @@
 	// uninstall accept --all-targets but with different defaults.
 	cmdUninstall.Flags.BoolVar(&allFlag, "all-targets", false, "apply to all targets for the specified profile(s)")
 
-	// list accepts --show-manifest, --available, --dir, --default, --versions
-	cmdList.Flags.BoolVar(&showManifestFlag, "show-manifest", false, "print out the manifest file")
+	// list accepts --show-profiles-manifest, --available, --dir, --default, --versions
+	cmdList.Flags.BoolVar(&showManifestFlag, "show-profiles-manifest", false, "print out the manifest file")
 	cmdList.Flags.BoolVar(&availableFlag, "available", false, "print the list of available profiles")
 	cmdList.Flags.StringVar(&infoFlag, "info", "", infoUsage())
 
@@ -196,7 +197,8 @@
 	// cleanup accepts the following flags:
 	cmdCleanup.Flags.BoolVar(&cleanupFlag, "gc", false, "uninstall profile targets that are older than the current default")
 	cmdCleanup.Flags.BoolVar(&specificVersionsFlag, "ensure-specific-versions-are-set", false, "ensure that profile targets have a specific version set")
-	cmdCleanup.Flags.BoolVar(&rmAllFlag, "rm-all", false, "remove profile manifest and all profile generated output files.")
+	cmdCleanup.Flags.BoolVar(&rmAllFlag, "rm-all", false, "remove profiles manifest and all profile generated output files.")
+	cmdCleanup.Flags.BoolVar(&rewriteManifestFlag, "rewrite-profiles-manifest", false, "rewrite the profiles manifest file to use the latest schema version")
 }
 
 func runList(env *cmdline.Env, args []string) error {
@@ -518,12 +520,16 @@
 		}
 		for _, target := range profile.Targets() {
 			if len(target.Version()) == 0 {
-				prior := target
+				prior := *target
 				version, err := mgr.VersionInfo().Select(target.Version())
 				if err != nil {
 					return err
 				}
 				target.SetVersion(version)
+				profiles.RemoveProfileTarget(name, prior)
+				if err := profiles.AddProfileTarget(name, *target); err != nil {
+					return err
+				}
 				if verboseFlag {
 					fmt.Fprintf(ctx.Stdout(), "%s %s had no version, now set to: %s\n", name, prior, target)
 				}
@@ -534,11 +540,18 @@
 }
 
 func runRmAll(ctx *tool.Context) error {
-	if err := ctx.Run().Remove(manifestFlag); err != nil {
-		return err
+	s := ctx.NewSeq()
+	if exists, err := s.FileExists(manifestFlag); err != nil || exists {
+		if err := s.Remove(manifestFlag).Done(); err != nil {
+			return err
+		}
 	}
-	if err := ctx.Run().RemoveAll(rootPath.Expand()); err != nil {
-		return err
+	rp := rootPath.Expand()
+	if exists, err := s.DirectoryExists(rp); err != nil || exists {
+		if err := s.Run("chmod", "-R", "u+w", rp).
+			RemoveAll(rp).Done(); err != nil {
+			return err
+		}
 	}
 	return nil
 }
@@ -581,6 +594,9 @@
 		// Don't write out the profiles manifest file again.
 		return nil
 	}
+	if rewriteManifestFlag {
+		dirty = true
+	}
 	if !dirty {
 		return fmt.Errorf("at least one option must be specified")
 	}
@@ -647,7 +663,7 @@
 		return err
 	}
 	if allFlag && targetFlag.IsSet() {
-		return fmt.Errorf("don't specify a target (%v) in conjunction with --all-targets", targetFlag)
+		fmt.Fprintf(ctx.Stdout(), "ignore target (%v) when used in conjunction with --all-targets\n", targetFlag)
 	}
 	if allFlag {
 		for _, name := range args {
diff --git a/profiles/env_test.go b/profiles/env_test.go
index 535bfdd..f057c3c 100644
--- a/profiles/env_test.go
+++ b/profiles/env_test.go
@@ -32,7 +32,10 @@
 	}
 	ch.Vars = envvar.VarsFromOS()
 	ch.Delete("CGO_CFLAGS")
-	native, _ := profiles.NewTarget("native")
+	native, err := profiles.NewTarget("amd64-darwin")
+	if err != nil {
+		t.Fatal(err)
+	}
 	ch.MergeEnvFromProfiles(profiles.JiriMergePolicies(), native, "go", "syncbase")
 	if got, want := ch.Get("CGO_CFLAGS"), "-IX -IY -IA -IB"; got != want {
 		t.Errorf("got %v, want %v", got, want)
@@ -66,7 +69,10 @@
 		t.Fatal(err)
 	}
 	ch.Vars = envvar.VarsFromSlice([]string{})
-	t1Target, _ := profiles.NewTarget("cpu1-os1@1")
+	t1Target, err := profiles.NewTarget("cpu1-os1@1")
+	if err != nil {
+		t.Fatal(err)
+	}
 	ch.MergeEnvFromProfiles(map[string]profiles.MergePolicy{
 		"A": profiles.AppendFlag,
 		"B": profiles.UseLast,
diff --git a/profiles/manifest.go b/profiles/manifest.go
index 4631ac6..cb1c505 100644
--- a/profiles/manifest.go
+++ b/profiles/manifest.go
@@ -83,7 +83,7 @@
 }
 
 func newDB() *profileDB {
-	return &profileDB{db: make(map[string]*Profile), version: 0}
+	return &profileDB{db: make(map[string]*Profile), version: V4}
 }
 
 var (
@@ -283,7 +283,7 @@
 	defer pdb.Unlock()
 
 	var schema profilesSchema
-	schema.Version = V3
+	schema.Version = V4
 	for i, name := range pdb.profilesUnlocked() {
 		profile := pdb.db[name]
 		schema.Profiles = append(schema.Profiles, &profileSchema{
diff --git a/profiles/manifest_test.go b/profiles/manifest_test.go
index 8a8d2dd..aa6ef33 100644
--- a/profiles/manifest_test.go
+++ b/profiles/manifest_test.go
@@ -16,6 +16,7 @@
 	"testing"
 
 	"v.io/jiri/profiles"
+	"v.io/jiri/project"
 	"v.io/jiri/tool"
 )
 
@@ -121,7 +122,7 @@
 	}
 }
 
-func TestBackwardsCompatibility(t *testing.T) {
+func TestReadingV0(t *testing.T) {
 	profiles.Clear()
 
 	getProfiles := func() []*profiles.Profile {
@@ -162,7 +163,7 @@
 		t.Fatal(err)
 	}
 
-	if got, want := profiles.SchemaVersion(), profiles.V3; got != want {
+	if got, want := profiles.SchemaVersion(), profiles.V4; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
 	nprofiles := getProfiles()
@@ -180,3 +181,54 @@
 		}
 	}
 }
+
+func handleRelativePath(root profiles.RelativePath, s string) string {
+	// Handle the transition from absolute to relative paths.
+	if filepath.IsAbs(s) {
+		return s
+	}
+	return root.RootJoin(s).Expand()
+}
+
+func TestReadingV3AndV4(t *testing.T) {
+	ctx := tool.NewDefaultContext()
+	root, err := project.JiriRoot()
+	if err != nil {
+		t.Fatal(err)
+	}
+	for i, c := range []struct {
+		filename, prefix, variable string
+		version                    profiles.Version
+	}{
+		{"v3.xml", "", "", profiles.V3},
+		{"v4.xml", root, "${JIRI_ROOT}", profiles.V4},
+	} {
+		ch, err := profiles.NewConfigHelper(ctx, profiles.UseProfiles, filepath.Join("testdata", c.filename))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if got, want := profiles.SchemaVersion(), c.version; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+		target, err := profiles.NewTarget("cpu1-os1@1")
+		if err != nil {
+			t.Fatal(err)
+		}
+		p := profiles.LookupProfile("a")
+		// We need to expand the variable here for a V4 profile if we want
+		// to get the full absolute path.
+		if got, want := p.Root, c.variable+"/an/absolute/root"; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+		lt := profiles.LookupProfileTarget("a", target)
+		if got, want := lt.InstallationDir, c.variable+"/an/absolute/dir"; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+		// The merged environment variables are expanded appropriately
+		// internally by MergeEnvFromProfiles.
+		ch.MergeEnvFromProfiles(profiles.JiriMergePolicies(), target, "a")
+		if got, want := ch.Get("ABS"), "-I"+c.prefix+"/an/absolute/path"; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+	}
+}
diff --git a/profiles/target.go b/profiles/target.go
index cffcbd9..5bf831c 100644
--- a/profiles/target.go
+++ b/profiles/target.go
@@ -323,10 +323,6 @@
 // only a single target available in targets then that one target is considered
 // as matching.
 func FindTarget(targets OrderedTargets, target *Target) *Target {
-	if len(targets) == 1 && !target.IsSet() {
-		tmp := *targets[0]
-		return &tmp
-	}
 	for _, t := range targets {
 		if target.Match(t) {
 			tmp := *t
diff --git a/profiles/testdata/m1.xml b/profiles/testdata/m1.xml
index d54ea6f..c14d319 100644
--- a/profiles/testdata/m1.xml
+++ b/profiles/testdata/m1.xml
@@ -1,4 +1,4 @@
-<profiles version="3">
+<profiles version="4">
   <profile name="a" root="">
     <target tag="" arch="cpu1" os="os1" installation-directory="" version="1">
       <envvars></envvars>
diff --git a/profiles/testdata/v3.xml b/profiles/testdata/v3.xml
new file mode 100644
index 0000000..5b6a3cb
--- /dev/null
+++ b/profiles/testdata/v3.xml
@@ -0,0 +1,13 @@
+<profiles version="3">
+  <profile name="a" root="/an/absolute/root">
+    <target arch="cpu1" os="os1" installation-directory="/an/absolute/dir" version="1">
+      <envvars>
+        <var>ABS=-I/an/absolute/path</var>
+      </envvars>
+      <command-line>
+        <var>A=B</var>
+        <var>C=D</var>
+      </command-line>
+    </target>
+  </profile>
+</profiles>
diff --git a/profiles/testdata/v4.xml b/profiles/testdata/v4.xml
new file mode 100644
index 0000000..83fddb7
--- /dev/null
+++ b/profiles/testdata/v4.xml
@@ -0,0 +1,13 @@
+<profiles version="4">
+  <profile name="a" root="${JIRI_ROOT}/an/absolute/root">
+    <target arch="cpu1" os="os1" installation-directory="${JIRI_ROOT}/an/absolute/dir" version="1">
+      <envvars>
+        <var>ABS=-I${JIRI_ROOT}/an/absolute/path</var>
+      </envvars>
+      <command-line>
+        <var>A=B</var>
+        <var>C=D</var>
+      </command-line>
+    </target>
+  </profile>
+</profiles>
diff --git a/runutil/.api b/runutil/.api
index d303875..ba929ec 100644
--- a/runutil/.api
+++ b/runutil/.api
@@ -1,3 +1,4 @@
+pkg runutil, func IsFNLHost() bool
 pkg runutil, func LookPath(string, map[string]string) (string, error)
 pkg runutil, func NewRun(map[string]string, io.Reader, io.Writer, io.Writer, bool, bool, bool) *Run
 pkg runutil, func NewSequence(map[string]string, io.Reader, io.Writer, io.Writer, bool, bool, bool) *Sequence
@@ -6,6 +7,8 @@
 pkg runutil, method (*Run) Chmod(string, os.FileMode) error
 pkg runutil, method (*Run) Command(string, ...string) error
 pkg runutil, method (*Run) CommandWithOpts(Opts, string, ...string) error
+pkg runutil, method (*Run) Copy(*os.File, io.Reader) (int64, error)
+pkg runutil, method (*Run) Create(string) (*os.File, error)
 pkg runutil, method (*Run) DirectoryExists(string) bool
 pkg runutil, method (*Run) FileExists(string) bool
 pkg runutil, method (*Run) Function(func() error, string, ...interface{}) error
@@ -13,6 +16,7 @@
 pkg runutil, method (*Run) IsDir(string) (bool, error)
 pkg runutil, method (*Run) MkdirAll(string, os.FileMode) error
 pkg runutil, method (*Run) Open(string) (*os.File, error)
+pkg runutil, method (*Run) OpenFile(string, int, os.FileMode) (*os.File, error)
 pkg runutil, method (*Run) Output([]string)
 pkg runutil, method (*Run) OutputWithOpts(Opts, []string)
 pkg runutil, method (*Run) ReadDir(string) ([]os.FileInfo, error)
@@ -23,6 +27,7 @@
 pkg runutil, method (*Run) Stat(string) (os.FileInfo, error)
 pkg runutil, method (*Run) Symlink(string, string) error
 pkg runutil, method (*Run) TempDir(string, string) (string, error)
+pkg runutil, method (*Run) TempFile(string, string) (*os.File, error)
 pkg runutil, method (*Run) TimedCommand(time.Duration, string, ...string) error
 pkg runutil, method (*Run) TimedCommandWithOpts(time.Duration, Opts, string, ...string) error
 pkg runutil, method (*Run) WriteFile(string, []byte, os.FileMode) error
@@ -36,6 +41,7 @@
 pkg runutil, method (*Sequence) FileExists(string) (bool, error)
 pkg runutil, method (*Sequence) GetOpts() Opts
 pkg runutil, method (*Sequence) IsDir(string) (bool, error)
+pkg runutil, method (*Sequence) Last(string, ...string) error
 pkg runutil, method (*Sequence) MkdirAll(string, os.FileMode) *Sequence
 pkg runutil, method (*Sequence) Open(string) (*os.File, error)
 pkg runutil, method (*Sequence) Opts(Opts) *Sequence
diff --git a/runutil/executor.go b/runutil/executor.go
index 44a18e9..31f2fff 100644
--- a/runutil/executor.go
+++ b/runutil/executor.go
@@ -204,13 +204,11 @@
 	}
 	fmt.Fprintf(e.opts.Stderr, "Waiting for command to exit: %q\n", command.Args)
 	// Give the process some time to shut down cleanly.
-	for i := 0; i < 10; i++ {
-		select {
-		case <-time.After(time.Second):
-			if err := syscall.Kill(pid, 0); err != nil {
-				return
-			}
+	for i := 0; i < 50; i++ {
+		if err := syscall.Kill(pid, 0); err != nil {
+			return
 		}
+		time.Sleep(200 * time.Millisecond)
 	}
 	// If it still exists, send SIGKILL to it.
 	if err := syscall.Kill(pid, 0); err == nil {
diff --git a/runutil/sequence.go b/runutil/sequence.go
index 1d1b564..68c00b9 100644
--- a/runutil/sequence.go
+++ b/runutil/sequence.go
@@ -5,8 +5,13 @@
 package runutil
 
 import (
+	"bytes"
+	"fmt"
 	"io"
+	"io/ioutil"
 	"os"
+	"path/filepath"
+	"runtime"
 	"time"
 )
 
@@ -19,23 +24,31 @@
 // methods and the result of that first error is returned by the
 // Done method or any of the other 'terminating methods' (see below).
 //
+// Unless directed to specific stdout and stderr io.Writers using Capture(), the
+// stdout and stderr output from the command is discarded, except in verbose
+// mode or upon error: when in verbose mode (set either via NewSequence or an
+// Opt) or when the command fails, all the command's output (stdout and stderr)
+// is written to the stdout io.Writer configured either via NewSequence or an
+// Opt.  In addition, in verbose mode, command execution logging is written to
+// the stdout and stderr io.Writers configured via NewSequence.
+//
 // Modifier methods are provided that influence the behaviour of the
-// next invocation of the Run method to set timeouts (Timeout) and to
-// capture output (Capture), an additional modifier method (Opts) is
-// provided the set the options for the remaining methods and is generally
-// used to control logging.
+// next invocation of the Run method to set timeouts (Timeout), to
+// capture output (Capture), an set options  (Opts).
 // For example, the following will result in a timeout error.
 //
 // err := s.Timed(time.Second).Run("sleep","10").Done()
+// err := s.Timed(time.Second).Last("sleep","10")
 //
 // A sequence of commands must be terminated with a call to a 'terminating'
-// method. The simplest is the Done method used in the examples above, but there
-// are other methods which typically return results in addition to error, such
-// as ReadFile(filename string) ([]byte, error). Here the usage would be:
+// method. The simplest are the Done or Last methods used in the examples above,
+// but there are other methods which typically return results in addition to
+// error, such as ReadFile(filename string) ([]byte, error). Here the usage
+// would be:
 //
 // o.Stdout, _ = os.Create("foo")
 // data, err := s.Opts(o).Run("echo","b").ReadFile("foo")
-// // data == "foo"
+// // data == "b"
 //
 // Note that terminating functions, even those that take an action, may
 // return an error generated by a previous method.
@@ -46,8 +59,10 @@
 type Sequence struct {
 	r              *Run
 	err            error
+	caller         string
 	stdout, stderr io.Writer
 	opts           *Opts
+	dirs           []string
 	timeout        time.Duration
 }
 
@@ -57,9 +72,9 @@
 	return &Sequence{r: NewRun(env, stdin, stdout, stderr, color, dryRun, verbose)}
 }
 
-// Capture arranges for the next call to Run to write its stdout and stderr
-// output to the supplied io.Writers. This will be cleared and not used for
-// any calls to Run beyond the next one.
+// Capture arranges for the next call to Run or Last to write its stdout and
+// stderr output to the supplied io.Writers. This will be cleared and not used
+// for any calls to Run or Last beyond the next one.
 func (s *Sequence) Capture(stdout, stderr io.Writer) *Sequence {
 	if s.err != nil {
 		return s
@@ -68,8 +83,9 @@
 	return s
 }
 
-// Opts arranges for the next call to Run to use the supplied options.
-// They will be cleared and not used for any calls to Run beyond the next one.
+// Opts arranges for the next call to Run or Last to use the supplied options.
+// This will be cleared and not used for any calls to Run or Last beyond the
+// next one.
 func (s *Sequence) Opts(opts Opts) *Sequence {
 	if s.err != nil {
 		return s
@@ -78,9 +94,9 @@
 	return s
 }
 
-// Timeout arranges for the next call to Run to be subject to the specified
-// timeout. The timeout will be cleared and not used any calls to Run beyond
-// the next one.
+// Timeout arranges for the next call to Run or Last to be subject to the
+// specified timeout. The timeout will be cleared and not used any calls to Run
+// or Last beyond the next one.
 func (s *Sequence) Timeout(timeout time.Duration) *Sequence {
 	if s.err != nil {
 		return s
@@ -97,6 +113,9 @@
 }
 
 func (s *Sequence) Error() error {
+	if s.err != nil && len(s.caller) > 0 {
+		return fmt.Errorf("%s: %v", s.caller, s.err)
+	}
 	return s.err
 }
 
@@ -104,6 +123,19 @@
 	s.opts = &opts
 }
 
+func fmtError(depth int, err error, detail string) string {
+	_, file, line, _ := runtime.Caller(depth + 1)
+	return fmt.Sprintf("%s:%d: %s", filepath.Base(file), line, detail)
+}
+
+func (s *Sequence) setError(err error, detail string) {
+	if err == nil || s.err != nil {
+		return
+	}
+	s.err = err
+	s.caller = fmtError(2, err, detail)
+}
+
 func (s *Sequence) reset() {
 	s.stdout, s.stderr, s.opts = nil, nil, nil
 	s.timeout = 0
@@ -128,7 +160,34 @@
 
 func (s *Sequence) initAndDefer() func() {
 	if s.stdout == nil && s.stderr == nil {
-		return func() {}
+		f, err := ioutil.TempFile("", "seq")
+		if err != nil {
+			return func() {}
+		}
+		opts := s.GetOpts()
+		stdout := opts.Stdout
+		if stdout == nil {
+			return func() {}
+		}
+		opts.Stdout = f
+		opts.Stderr = f
+		s.setOpts(opts)
+		return func() {
+			filename := f.Name()
+			f.Close()
+			if opts.Verbose || s.err != nil {
+				// TODO(cnicolaou): probably best to stream this out rather
+				// than buffer the whole file into memory.
+				if data, err := ioutil.ReadFile(filename); err == nil {
+					fmt.Fprint(stdout, string(data))
+					if wd, err := os.Getwd(); err == nil {
+						fmt.Fprintf(stdout, "Current Directory: %v\n", wd)
+					}
+				}
+			}
+			os.Remove(filename)
+			s.opts = nil
+		}
 	}
 	opts := s.GetOpts()
 	rStdin, wStdin := io.Pipe()
@@ -152,16 +211,87 @@
 	}
 }
 
+func fmtArgs(args ...interface{}) string {
+	if len(args) == 0 {
+		return ""
+	}
+	out := &bytes.Buffer{}
+	for _, a := range args {
+		if _, ok := a.(string); ok {
+			out.WriteString(fmt.Sprintf(" ,%q", a))
+		} else {
+			out.WriteString(fmt.Sprintf(" ,%s", a))
+		}
+	}
+	return out.String()
+}
+
+func fmtStringArgs(args ...string) string {
+	if len(args) == 0 {
+		return ""
+	}
+	out := &bytes.Buffer{}
+	for _, a := range args {
+		out.WriteString(", \"")
+		out.WriteString(a)
+		out.WriteString("\"")
+	}
+	return out.String()
+}
+
+// Pushd pushes the current directory onto a stack and changes directory
+// to the specified one. Calling any terminating function will pop back
+// to the first element in the stack on completion of that function.
+func (s *Sequence) Pushd(dir string) *Sequence {
+	cwd, err := os.Getwd()
+	if err != nil {
+		s.setError(err, "Pushd("+dir+"): os.Getwd")
+		return s
+	}
+	s.dirs = append(s.dirs, cwd)
+	s.setError(s.r.Chdir(dir), "Pushd("+dir+")")
+	return s
+}
+
+// Popd popds the last directory from the directory stack and chdir's to it.
+// Calling any termination function will pop back to the first element in
+// the stack on completion of that function.
+func (s *Sequence) Popd() *Sequence {
+	if s.err != nil {
+		return s
+	}
+	if len(s.dirs) == 0 {
+		s.setError(fmt.Errorf("directory stack is empty"), "Popd()")
+		return s
+	}
+	last := s.dirs[len(s.dirs)-1]
+	s.dirs = s.dirs[:len(s.dirs)-1]
+	s.setError(s.r.Chdir(last), "Popd() -> "+last)
+	return s
+}
+
 // Run runs the given command as a subprocess.
 func (s *Sequence) Run(path string, args ...string) *Sequence {
 	if s.err != nil {
 		return s
 	}
 	defer s.initAndDefer()()
-	s.err = s.r.command(s.timeout, s.GetOpts(), path, args...)
+	s.setError(s.r.command(s.timeout, s.GetOpts(), path, args...), fmt.Sprintf("Run(%q%s)", path, fmtStringArgs(args...)))
 	return s
 }
 
+// Last runs the given command as a subprocess and returns an error
+// immediately terminating the sequence, it is equivalent to
+// calling s.Run(path, args...).Done().
+func (s *Sequence) Last(path string, args ...string) error {
+	if s.err != nil {
+		return s.Done()
+	}
+	defer s.initAndDefer()()
+	s.setError(s.r.command(s.timeout, s.GetOpts(), path, args...), fmt.Sprintf("Last(%q%s)", path, fmtStringArgs(args...)))
+	return s.Done()
+}
+
 // Call runs the given function. Note that Capture and Timeout have no
 // effect on invocations of Call, but Opts can control logging.
 func (s *Sequence) Call(fn func() error, format string, args ...interface{}) *Sequence {
@@ -169,7 +299,7 @@
 		return s
 	}
 	defer s.initAndDefer()()
-	s.err = s.r.FunctionWithOpts(s.GetOpts(), fn, format, args...)
+	s.setError(s.r.FunctionWithOpts(s.GetOpts(), fn, format, args...), fmt.Sprintf("Call(%s,%s%s)", fn, format, fmtArgs(args)))
 	return s
 }
 
@@ -179,7 +309,7 @@
 	if s.err != nil {
 		return s
 	}
-	s.err = s.r.Chdir(dir)
+	s.setError(s.r.Chdir(dir), "Chdir("+dir+")")
 	return s
 }
 
@@ -190,7 +320,7 @@
 		return s
 	}
 	if err := s.r.Chmod(dir, mode); err != nil {
-		s.err = err
+		s.setError(err, fmt.Sprintf("Chmod(%s, %s)", dir, mode))
 	}
 	return s
 }
@@ -201,7 +331,7 @@
 	if s.err != nil {
 		return s
 	}
-	s.err = s.r.MkdirAll(dir, mode)
+	s.setError(s.r.MkdirAll(dir, mode), fmt.Sprintf("MkdirAll(%s, %s)", dir, mode))
 	return s
 }
 
@@ -211,7 +341,7 @@
 	if s.err != nil {
 		return s
 	}
-	s.err = s.r.RemoveAll(dir)
+	s.setError(s.r.RemoveAll(dir), fmt.Sprintf("RemoveAll(%s)", dir))
 	return s
 }
 
@@ -221,7 +351,7 @@
 	if s.err != nil {
 		return s
 	}
-	s.err = s.r.Remove(file)
+	s.setError(s.r.Remove(file), fmt.Sprintf("Remove(%s)", file))
 	return s
 }
 
@@ -231,7 +361,7 @@
 	if s.err != nil {
 		return s
 	}
-	s.err = s.r.Rename(src, dst)
+	s.setError(s.r.Rename(src, dst), fmt.Sprintf("Rename(%s, %s)", src, dst))
 	return s
 }
 
@@ -241,7 +371,7 @@
 	if s.err != nil {
 		return s
 	}
-	s.err = s.r.Symlink(src, dst)
+	s.setError(s.r.Symlink(src, dst), fmt.Sprintf("Symlink(%s, %s)", src, dst))
 	return s
 }
 
@@ -255,12 +385,32 @@
 	return s
 }
 
-// Done returns the error stored in the Sequence. Done is a terminating function.
+// Done returns the error stored in the Sequence and pops back to the first
+// entry in the directory stack if Pushd has been called. Done is a terminating
+// function. There is no need to ensure that Done is called before returning
+// from a function that uses a sequence unless it is necessary to pop the
+// stack.
 func (s *Sequence) Done() error {
-	err := s.err
+	rerr := s.Error()
 	s.err = nil
+	s.caller = ""
 	s.reset()
-	return err
+	if len(s.dirs) > 0 {
+		cwd := s.dirs[0]
+		s.dirs = nil
+		if err := s.r.Chdir(cwd); err != nil {
+			detail := "Done: Chdir(" + cwd + ")"
+			if rerr == nil {
+				s.setError(err, detail)
+			} else {
+				// In the unlikely event that Chdir fails in addition to an
+				// earlier error, we append an appropriate error message.
+				s.err = fmt.Errorf("%v\n%v", rerr, fmtError(1, err, detail))
+			}
+			return s.Error()
+		}
+	}
+	return rerr
 }
 
 // Open is a wrapper around os.Open that handles options such as
@@ -270,7 +420,7 @@
 		return nil, s.Done()
 	}
 	f, err := s.r.Open(name)
-	s.err = err
+	s.setError(err, fmt.Sprintf("Open(%s)", name))
 	return f, s.Done()
 }
 
@@ -281,7 +431,7 @@
 		return nil, s.Done()
 	}
 	fi, err := s.r.ReadDir(dirname)
-	s.err = err
+	s.setError(err, fmt.Sprintf("ReadDir(%s)", dirname))
 	return fi, s.Done()
 }
 
@@ -292,10 +442,21 @@
 		return nil, s.Done()
 	}
 	data, err := s.r.ReadFile(filename)
-	s.err = err
+	s.setError(err, fmt.Sprintf("ReadFile(%s)", filename))
 	return data, s.Done()
 }
 
+// WriteFile is a wrapper around ioutil.WriteFile that handles options
+// such as "verbose" or "dry run".
+func (s *Sequence) WriteFile(filename string, data []byte, perm os.FileMode) *Sequence {
+	if s.err != nil {
+		return s
+	}
+	err := s.r.WriteFile(filename, data, perm)
+	s.setError(err, fmt.Sprintf("WriteFile(%s, %10s,  %s)", filename, data, perm))
+	return s
+}
+
 // Stat is a wrapper around os.Stat that handles options such as
 // "verbose" or "dry run". Stat is a terminating function.
 func (s *Sequence) Stat(name string) (os.FileInfo, error) {
@@ -303,7 +464,7 @@
 		return nil, s.Done()
 	}
 	fi, err := s.r.Stat(name)
-	s.err = err
+	s.setError(err, fmt.Sprintf("Stat(%s)", name))
 	return fi, s.Done()
 }
 
@@ -314,18 +475,18 @@
 		return "", s.Done()
 	}
 	name, err := s.r.TempDir(dir, prefix)
-	s.err = err
+	s.setError(err, fmt.Sprintf("TempDir(%s,%s)", dir, prefix))
 	return name, s.Done()
 }
 
 // IsDir is a wrapper around os.Stat with appropriate logging.
 // IsDir is a terminating function.
-func (s *Sequence) IsDir(name string) (bool, error) {
+func (s *Sequence) IsDir(dirname string) (bool, error) {
 	if s.err != nil {
 		return false, s.Done()
 	}
-	t, err := s.r.IsDir(name)
-	s.err = err
+	t, err := s.r.IsDir(dirname)
+	s.setError(err, fmt.Sprintf("IsDir(%s)", dirname))
 	return t, s.Done()
 }
 
diff --git a/runutil/sequence_test.go b/runutil/sequence_test.go
index 566f500..62df6f0 100644
--- a/runutil/sequence_test.go
+++ b/runutil/sequence_test.go
@@ -9,28 +9,181 @@
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
 	"testing"
 	"time"
 
 	"v.io/jiri/runutil"
 )
 
+func rmLineNumbers(s string) string {
+	re := regexp.MustCompile("(.*\\.go):\\d+:(.*)")
+	return re.ReplaceAllString(s, "$1:-:$2")
+}
+
+func sanitizeTimestamps(s string) string {
+	re := regexp.MustCompile(`\[(\d\d:\d\d:\d\d.\d\d)\]`)
+	return re.ReplaceAllString(s, "[hh:mm:ss.xx]")
+}
+
 func ExampleSequence() {
 	seq := runutil.NewSequence(nil, os.Stdin, ioutil.Discard, ioutil.Discard, false, false, true)
 	err := seq.
 		Capture(os.Stdout, nil).Run("echo", "a").
-		Capture(os.Stdout, nil).Run("echo", "b").
-		Done()
+		Capture(os.Stdout, nil).Last("echo", "b")
 	err = seq.
 		Run("echo", "c").
 		Run("xxxxxxx").
-		Capture(os.Stdout, nil).Run("echo", "d").
-		Done()
-	fmt.Println(err)
+		Capture(os.Stdout, nil).Last("echo", "d")
+	// Get rid of the line#s in the error output.
+	fmt.Println(rmLineNumbers(err.Error()))
 	// Output:
 	// a
 	// b
-	// exec: "xxxxxxx": executable file not found in $PATH
+	// sequence_test.go:-: Run("xxxxxxx"): exec: "xxxxxxx": executable file not found in $PATH
+}
+
+// TestStdoutStderr exercises the various possible configurations for stdout and
+// stderr (via NewSequence, Opts, or Capture) as well as the verbose flag.
+func TestStdoutStderr(t *testing.T) {
+	cwd, err := os.Getwd()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Case 1: we only specify stdout/stderr at constructor time.
+	//
+	// Verbose mode: All the command's output and execution logging goes to
+	// stdout, execution error messages to stderr.
+	//
+	// Non-Verbose mode: No stdout output; execution error messages to
+	// stderr.
+	for _, verbose := range []bool{false, true} {
+		var cnstrStdout, cnstrStderr bytes.Buffer
+		seq := runutil.NewSequence(nil, os.Stdin, &cnstrStdout, &cnstrStderr, false, false, verbose)
+		seq.Run("bash", "-c", "echo a; echo b >&2").
+			Timeout(time.Microsecond).
+			Run("sleep", "10000")
+		want := "Current Directory: " + cwd + "\n"
+		if verbose {
+			want = `[hh:mm:ss.xx] >> bash -c "echo a; echo b >&2"
+[hh:mm:ss.xx] >> OK
+a
+b
+Current Directory: ` + cwd + `
+[hh:mm:ss.xx] >> sleep 10000
+[hh:mm:ss.xx] >> TIMED OUT
+Current Directory: ` + cwd + `
+`
+		}
+		if got := sanitizeTimestamps(cnstrStdout.String()); want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := cnstrStderr.String(), "Waiting for command to exit: [\"sleep\" \"10000\"]\n"; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+	}
+
+	// Case 2: we specify stdout/stderr at constructor time, and also via
+	// Opts.  The verbose setting from opts takes precedence and controls
+	// the output that goes both to constructor stdout and to opts stdout.
+	//
+	// Verbose mode: The command execution logging goes to constructor
+	// stdout, command execution errors go to constructor stderr, and the
+	// actual command output goes to opts stdout. Nothing goes to opts
+	// stderr.
+	//
+	// Non-Verbose mode: No stdout output; execution error messages to
+	// constructor stderr.
+	for _, verbose := range []bool{false, true} {
+		var cnstrStdout, cnstrStderr, optsStdout, optsStderr bytes.Buffer
+		cstrVerbose := false // irellevant, the opts verbose flag takes precedence.
+		seq := runutil.NewSequence(nil, os.Stdin, &cnstrStdout, &cnstrStderr, false, false, cstrVerbose)
+		opts := runutil.Opts{Stdout: &optsStdout, Stderr: &optsStderr, Verbose: verbose}
+		seq.Opts(opts).
+			Run("bash", "-c", "echo a; echo b >&2").
+			Opts(opts).
+			Timeout(time.Microsecond).
+			Run("sleep", "10000")
+		want := ""
+		if verbose {
+			want = `[hh:mm:ss.xx] >> bash -c "echo a; echo b >&2"
+[hh:mm:ss.xx] >> OK
+[hh:mm:ss.xx] >> sleep 10000
+[hh:mm:ss.xx] >> TIMED OUT
+`
+		}
+		if got := sanitizeTimestamps(cnstrStdout.String()); want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := cnstrStderr.String(), "Waiting for command to exit: [\"sleep\" \"10000\"]\n"; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		want = "Current Directory: " + cwd + "\n"
+		if verbose {
+			want = "a\nb\nCurrent Directory: " + cwd + "\nCurrent Directory: " + cwd + "\n"
+		}
+		if got := optsStdout.String(); want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := optsStderr.String(), ""; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+	}
+
+	// Case 3: we specify stdout/stderr at constructor time, also via Opts,
+	// and also via Capture.  The verbose setting from opts takes
+	// precedence.
+	//
+	// Verbose mode: The command execution log goes to constructor stdout,
+	// command execution errors go to constructor stderr, and the
+	// stdout/stderr output from the command goes to capture stdout/stderr
+	// respectively.  Nothing goes to opts stdout/stderr.
+	//
+	// Non-Verbose mode: The stdout/stderr output from the command goes to
+	// capture stdout/stderr respectively.  No command execution log, but
+	// the command execution errors go to constructor stderr.  Nothing goes
+	// to opts stdout/stderr.
+	for _, verbose := range []bool{false, true} {
+		var cnstrStdout, cnstrStderr, optsStdout, optsStderr, captureStdout, captureStderr bytes.Buffer
+		cstrVerbose := false // irellevant, the opts verbose flag takes precedence.
+		seq := runutil.NewSequence(nil, os.Stdin, &cnstrStdout, &cnstrStderr, false, false, cstrVerbose)
+		opts := runutil.Opts{Stdout: &optsStdout, Stderr: &optsStderr, Verbose: verbose}
+		seq.Opts(opts).
+			Capture(&captureStdout, &captureStderr).
+			Run("bash", "-c", "echo a; echo b >&2").
+			Opts(opts).
+			Timeout(time.Microsecond).
+			Run("sleep", "10000")
+		want := ""
+		if verbose {
+			want = `[hh:mm:ss.xx] >> bash -c "echo a; echo b >&2"
+[hh:mm:ss.xx] >> OK
+[hh:mm:ss.xx] >> sleep 10000
+[hh:mm:ss.xx] >> TIMED OUT
+`
+		}
+		if got := sanitizeTimestamps(cnstrStdout.String()); want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := cnstrStderr.String(), "Waiting for command to exit: [\"sleep\" \"10000\"]\n"; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := optsStdout.String(), "Current Directory: "+cwd+"\n"; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := optsStderr.String(), ""; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := captureStdout.String(), "a\n"; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+		if got, want := captureStderr.String(), "b\n"; want != got {
+			t.Errorf("verbose: %t, got %v, want %v", verbose, got, want)
+		}
+	}
 }
 
 func TestSequence(t *testing.T) {
@@ -66,14 +219,13 @@
 		t.Errorf("got %v, want %v", got, want)
 	}
 	out.Reset()
-	err = seq.Run("./bound-to-fail").Done()
+	err = seq.Run("./bound-to-fail", "fail").Done()
 	if err == nil {
 		t.Fatalf("should have experience an error")
 	}
-	if got, want := err.Error(), "fork/exec ./bound-to-fail: no such file or directory"; got != want {
+	if got, want := rmLineNumbers(err.Error()), "sequence_test.go:-: Run(\"./bound-to-fail\", \"fail\"): fork/exec ./bound-to-fail: no such file or directory"; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
-
 	err = seq.
 		Capture(&out, nil).Run("echo", "works, despite previous error").Done()
 	if err != nil {
@@ -83,9 +235,8 @@
 		t.Errorf("got %v, want %v", got, want)
 	}
 	out.Reset()
-
 	err = seq.Timeout(time.Second).Run("sleep", "10").Done()
-	if got, want := err.Error(), "command timed out"; got != want {
+	if got, want := rmLineNumbers(err.Error()), "sequence_test.go:-: Run(\"sleep\", \"10\"): command timed out"; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
 
@@ -102,8 +253,7 @@
 	}
 	err := seq.
 		Capture(&out, nil).Opts(opts).Run("sh", "-c", "echo $MYTEST").
-		Capture(&out, nil).Run("sh", "-c", "echo $MYTEST").
-		Done()
+		Capture(&out, nil).Last("sh", "-c", "echo $MYTEST")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -124,6 +274,50 @@
 	}
 }
 
+func TestSequenceOutputOnError(t *testing.T) {
+	var out bytes.Buffer
+	// Only the output from the command that generates an error is written
+	// to out when not in verbose mode.
+	seq := runutil.NewSequence(nil, os.Stdin, &out, os.Stderr, false, false, false)
+	err := seq.Run("sh", "-c", "echo not me").
+		Run("sh", "-c", "echo ooh; echo ah; echo me; exit 1").
+		Last("sh", "-c", "echo not me either")
+	if err == nil {
+		t.Errorf("expected an error")
+	}
+	if got, want := out.String(), "oh\nah"; !strings.Contains(got, want) {
+		t.Errorf("got %v doesn't contain %v", got, want)
+	}
+	if got, notWant := out.String(), "not me either"; strings.Contains(got, notWant) {
+		t.Errorf("got %v contains %v", got, notWant)
+	}
+
+	out.Reset()
+	err = seq.Run("sh", "-c", "echo hard to not include me").
+		Run("sh", "-c", "echo ooh; echo ah; echo me").
+		Last("sh", "-c", "echo not me either")
+	if err != nil {
+		t.Error(err)
+	}
+	if got, want := len(out.String()), 0; got != want {
+		t.Logf(out.String())
+		t.Errorf("got %v, want %v", got, want)
+	}
+
+	out.Reset()
+	// All output is written to out when in verbose mode.
+	seq = runutil.NewSequence(nil, os.Stdin, &out, os.Stderr, false, false, true)
+	err = seq.Run("sh", "-c", "echo AA").
+		Run("sh", "-c", "echo BB; exit 1").
+		Last("sh", "-c", "echo CC")
+	if got, want := strings.Count(out.String(), "\n"), 8; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	if got, want := strings.Count(out.String(), "AA"), 2; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+}
+
 type timestamped struct {
 	times []time.Time
 	data  [][]byte
@@ -173,11 +367,65 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	if got, want := string(data), "aha\n"; got != want {
+	cwd, err := os.Getwd()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if got, want := string(data), "aha\nCurrent Directory: "+cwd+"\n"; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
 }
 
+func getwd(t *testing.T) string {
+	here, err := os.Getwd()
+	if err != nil {
+		t.Fatal(err)
+	}
+	return here
+}
+
+func TestSequencePushPop(t *testing.T) {
+	here := getwd(t)
+	s := runutil.NewSequence(nil, os.Stdin, os.Stdout, os.Stderr, false, false, true)
+	components := []string{here, "test", "a", "b", "c"}
+	tree := filepath.Join(components...)
+	s.MkdirAll(tree, os.FileMode(0755))
+	if err := s.Error(); err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(filepath.Join(here, "test"))
+
+	td := ""
+	for _, d := range components {
+		s.Pushd(d)
+		td = filepath.Join(td, d)
+		if got, want := getwd(t), td; got != want {
+			t.Errorf("got %v, want %v", got, want)
+		}
+	}
+	s.Done()
+	if got, want := getwd(t), here; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+
+	s.Pushd("test").Pushd("a").Pushd("b")
+	if got, want := getwd(t), filepath.Join(here, "test", "a", "b"); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	err := s.Pushd("x").Done()
+	if err == nil {
+		t.Fatal(fmt.Errorf("expected an error"))
+	}
+	// Make sure the stack is unwound on error.
+	if got, want := getwd(t), here; got != want {
+		t.Errorf("got %v, want %v", got, want)
+		if err := os.Chdir(here); err != nil {
+			panic(fmt.Sprintf("failed to chdir back to %s", here))
+		}
+
+	}
+}
+
 // TODO(cnicolaou):
 // - tests for functions
 // - tests for terminating functions, make sure they clean up correctly.
diff --git a/runutil/start_test.go b/runutil/start_test.go
index 2682983..ef23759 100644
--- a/runutil/start_test.go
+++ b/runutil/start_test.go
@@ -9,7 +9,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"strings"
+	"syscall"
 	"testing"
 	"time"
 )
@@ -17,23 +17,28 @@
 // TestStartCommandOK tests start.Command() returns immediately without waiting
 // for the command to complete.
 func TestStartCommandOK(t *testing.T) {
-	var out bytes.Buffer
-	start := NewStart(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
-	bin, err := buildTestProgram(NewRun(nil, os.Stdin, &out, ioutil.Discard, false, false, true), "slow_hello2")
+	start := NewStart(nil, os.Stdin, ioutil.Discard, ioutil.Discard, false, false, true)
+	bin, err := buildTestProgram(NewRun(nil, os.Stdin, ioutil.Discard, ioutil.Discard, false, false, true), "slow_hello2")
 	if bin != "" {
 		defer os.RemoveAll(filepath.Dir(bin))
 	}
 	if err != nil {
 		t.Fatalf("%v", err)
 	}
-	if _, err := start.Command(bin); err != nil {
-		t.Fatalf(`Command("go run ./testdata/slow_hello2.go") failed: %v`, err)
+	cmd, err := start.Command(bin)
+	if err != nil {
+		t.Fatalf(`Command("go run ./testdata/slow_hello2.go") failed to start: %v`, err)
 	}
-	// Note that the output shouldn't have "hello!!" because start.Command won't
-	// wait for the command to finish.
-	output := removeTimestamps(t, &out)
-	if strings.Index(output, "hello!!") != -1 {
-		t.Fatalf("output shouldn't contain 'hello!!':\n%v", output)
+	pid := cmd.Process.Pid
+	// Wait a sec and check that the child process is still around.
+	time.Sleep(time.Second)
+	if err := syscall.Kill(pid, 0); err != nil {
+		t.Fatalf(`Command("go run ./testdata/slow_hello2.go") already exited`)
+	}
+	// We're satisfied.  Go ahead and kill the child to avoid leaving it
+	// running after the test completes.
+	if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
+		t.Fatalf(`Command("go run ./testdata/slow_hello2.go") couldn't be killed`)
 	}
 }
 
@@ -42,10 +47,13 @@
 	start := NewStart(nil, os.Stdin, &runOut, ioutil.Discard, false, false, true)
 	opts := start.Opts()
 	opts.Stdout = &cmdOut
-	if _, err := start.CommandWithOpts(opts, "go", "run", "./testdata/ok_hello.go"); err != nil {
+	cmd, err := start.CommandWithOpts(opts, "go", "run", "./testdata/ok_hello.go")
+	if err != nil {
+		t.Fatalf(`Command("go run ./testdata/ok_hello.go") failed to start: %v`, err)
+	}
+	if err := cmd.Wait(); err != nil {
 		t.Fatalf(`Command("go run ./testdata/ok_hello.go") failed: %v`, err)
 	}
-	time.Sleep(time.Second * 3)
 	if got, want := removeTimestamps(t, &cmdOut), "hello\n"; got != want {
 		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
 	}
diff --git a/tool/context.go b/tool/context.go
index fc2da35..0efecbb 100644
--- a/tool/context.go
+++ b/tool/context.go
@@ -89,12 +89,10 @@
 func NewContext(opts ContextOpts) *Context {
 	initOpts(newContextOpts(), &opts)
 	run := runutil.NewRun(opts.Env, opts.Stdin, opts.Stdout, opts.Stderr, *opts.Color, *opts.DryRun, *opts.Verbose)
-	seq := runutil.NewSequence(opts.Env, opts.Stdin, opts.Stdout, opts.Stderr, *opts.Color, *opts.DryRun, *opts.Verbose)
 	start := runutil.NewStart(opts.Env, opts.Stdin, opts.Stdout, opts.Stderr, *opts.Color, *opts.DryRun, *opts.Verbose)
 	return &Context{
 		opts:  opts,
 		run:   run,
-		seq:   seq,
 		start: start,
 	}
 }
@@ -197,9 +195,10 @@
 	return ctx.run
 }
 
-// Seq returns the sequence instance of the context.
-func (ctx Context) Seq() *runutil.Sequence {
-	return ctx.seq
+// NewSeq returns a new instance of Sequence initialized using the options
+// stored in the context.
+func (ctx Context) NewSeq() *runutil.Sequence {
+	return runutil.NewSequence(ctx.opts.Env, ctx.opts.Stdin, ctx.opts.Stdout, ctx.opts.Stderr, *ctx.opts.Color, *ctx.opts.DryRun, *ctx.opts.Verbose)
 }
 
 // Start returns the start instance of the context.