Merge "NewContextFromEnv should set the env variables.."
diff --git a/project/project.go b/project/project.go
index 1feca1c..2892bc9 100644
--- a/project/project.go
+++ b/project/project.go
@@ -1565,6 +1565,9 @@
 }
 
 func (op nullOperation) Run(jirix *jiri.X, manifest *Manifest) error {
+	if err := writeMetadata(jirix, op.project, op.project.Path); err != nil {
+		return err
+	}
 	return addProjectToManifest(jirix, manifest, op.project)
 }
 
diff --git a/runutil/.api b/runutil/.api
index 318b8e8..9969ef5 100644
--- a/runutil/.api
+++ b/runutil/.api
@@ -1,4 +1,9 @@
+pkg runutil, func GetOriginalError(error) error
+pkg runutil, func IsExist(error) bool
 pkg runutil, func IsFNLHost() bool
+pkg runutil, func IsNotExist(error) bool
+pkg runutil, func IsPermission(error) bool
+pkg runutil, func IsTimeout(error) 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
@@ -44,6 +49,7 @@
 pkg runutil, method (*Sequence) Env(map[string]string) *Sequence
 pkg runutil, method (*Sequence) Error() error
 pkg runutil, method (*Sequence) FileExists(string) (bool, error)
+pkg runutil, method (*Sequence) Fprintf(io.Writer, string, ...interface{}) *Sequence
 pkg runutil, method (*Sequence) IsDir(string) (bool, error)
 pkg runutil, method (*Sequence) Last(string, ...string) error
 pkg runutil, method (*Sequence) Lstat(string) (os.FileInfo, error)
@@ -65,6 +71,7 @@
 pkg runutil, method (*Sequence) Symlink(string, string) *Sequence
 pkg runutil, method (*Sequence) TempDir(string, string) (string, error)
 pkg runutil, method (*Sequence) Timeout(time.Duration) *Sequence
+pkg runutil, method (*Sequence) Verbose(bool) *Sequence
 pkg runutil, method (*Sequence) WriteFile(string, []byte, os.FileMode) *Sequence
 pkg runutil, method (*Start) Command(string, ...string) (*exec.Cmd, error)
 pkg runutil, method (*Start) CommandWithOpts(Opts, string, ...string) (*exec.Cmd, error)
@@ -82,4 +89,3 @@
 pkg runutil, type Run struct
 pkg runutil, type Sequence struct
 pkg runutil, type Start struct
-pkg runutil, var CommandTimedOutErr error
diff --git a/runutil/executor.go b/runutil/executor.go
index 31f2fff..79ed5ad 100644
--- a/runutil/executor.go
+++ b/runutil/executor.go
@@ -178,7 +178,7 @@
 		if opts.Verbose {
 			e.printf(e.opts.Stdout, "TIMED OUT")
 		}
-		return CommandTimedOutErr
+		return commandTimedOutErr
 	case err := <-done:
 		if err != nil {
 			if opts.Verbose {
diff --git a/runutil/run.go b/runutil/run.go
index 60d876c..6b276e6 100644
--- a/runutil/run.go
+++ b/runutil/run.go
@@ -14,7 +14,7 @@
 )
 
 var (
-	CommandTimedOutErr = fmt.Errorf("command timed out")
+	commandTimedOutErr = fmt.Errorf("command timed out")
 )
 
 type Run struct {
diff --git a/runutil/run_test.go b/runutil/run_test.go
index a408e59..f253505 100644
--- a/runutil/run_test.go
+++ b/runutil/run_test.go
@@ -113,7 +113,7 @@
 	}
 	if err := run.TimedCommand(timedCommandTimeout, bin); err == nil {
 		t.Fatalf(`TimedCommand("go run ./testdata/slow_hello.go") did not fail when it should`)
-	} else if got, want := err, CommandTimedOutErr; got != want {
+	} else if got, want := IsTimeout(err), true; got != want {
 		t.Fatalf("unexpected error: got %v, want %v", got, want)
 	}
 	if got, want := removeTimestamps(t, &out), fmt.Sprintf(">> %s\nhello\n>> TIMED OUT\n", bin); got != want {
@@ -151,7 +151,7 @@
 	opts.Stdout = &cmdOut
 	if err := run.TimedCommandWithOpts(timedCommandTimeout, opts, bin); err == nil {
 		t.Fatalf(`TimedCommandWithOpts("go run ./testdata/slow_hello.go") did not fail when it should`)
-	} else if got, want := err, CommandTimedOutErr; got != want {
+	} else if got, want := IsTimeout(err), true; got != want {
 		t.Fatalf("unexpected error: got %v, want %v", got, want)
 	}
 	if got, want := removeTimestamps(t, &runOut), fmt.Sprintf(">> %s\n>> TIMED OUT\n", bin); got != want {
diff --git a/runutil/sequence.go b/runutil/sequence.go
index 47fbf64..01f6941 100644
--- a/runutil/sequence.go
+++ b/runutil/sequence.go
@@ -24,6 +24,8 @@
 // The first method to encounter an error short circuits any following
 // methods and the result of that first error is returned by the
 // Done method or any of the other 'terminating methods' (see below).
+// Sequence is not thread safe. It also good practice to use a new
+// instance of a Sequence in defer's.
 //
 // Unless directed to specific stdout and stderr io.Writers using Capture(),
 // the stdout and stderr output from the command is discarded, unless an error
@@ -70,17 +72,18 @@
 	dirs                         []string
 	verbosity                    *bool
 	timeout                      time.Duration
+	serializedWriterLock         sync.Mutex
 }
 
 // NewSequence creates an instance of Sequence with default values for its
 // environment, stdin, stderr, stdout and other supported options.
 func NewSequence(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, dryRun, verbose bool) *Sequence {
-	return &Sequence{
-		r:             NewRun(env, stdin, stdout, stderr, color, dryRun, verbose),
-		defaultStdin:  stdin,
-		defaultStdout: stdout,
-		defaultStderr: stderr,
+	s := &Sequence{
+		r:            NewRun(env, stdin, stdout, stderr, color, dryRun, verbose),
+		defaultStdin: stdin,
 	}
+	s.defaultStdout, s.defaultStderr = s.serializeWriter(stdout), s.serializeWriter(stderr)
+	return s
 }
 
 // Capture arranges for the next call to Run or Last to write its stdout and
@@ -172,6 +175,15 @@
 	return s.err
 }
 
+// GetOriginalError gets the original error wrapped in the given err.
+// If the given err is not a wrappedError, just return itself.
+func GetOriginalError(err error) error {
+	if we, ok := err.(*wrappedError); ok {
+		return we.oe
+	}
+	return err
+}
+
 // IsExist returns a boolean indicating whether the error is known
 // to report that a file or directory already exists.
 func IsExist(err error) bool {
@@ -199,6 +211,15 @@
 	return os.IsPermission(err)
 }
 
+// IsTimeout returns a boolean indicating whether the error is a result of
+// a timeout.
+func IsTimeout(err error) bool {
+	if we, ok := err.(*wrappedError); ok {
+		return we.oe == commandTimedOutErr
+	}
+	return err == commandTimedOutErr
+}
+
 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)
@@ -253,17 +274,24 @@
 	}
 }
 
-type lockedWriter struct {
-	sync.Mutex
-	f io.Writer
+type sharedLockWriter struct {
+	mu *sync.Mutex
+	f  io.Writer
 }
 
-func (lw *lockedWriter) Write(d []byte) (int, error) {
-	lw.Lock()
-	defer lw.Unlock()
+func (lw *sharedLockWriter) Write(d []byte) (int, error) {
+	lw.mu.Lock()
+	defer lw.mu.Unlock()
 	return lw.f.Write(d)
 }
 
+func (s *Sequence) serializeWriter(a io.Writer) io.Writer {
+	if a != nil {
+		return &sharedLockWriter{&s.serializedWriterLock, a}
+	}
+	return nil
+}
+
 func (s *Sequence) initAndDefer() func() {
 	if s.stdout == nil && s.stderr == nil {
 		fout, err := ioutil.TempFile("", "seq")
@@ -271,8 +299,7 @@
 			return func() {}
 		}
 		opts := s.getOpts()
-		opts.Stdout = fout
-		opts.Stderr = fout
+		opts.Stdout, opts.Stderr = s.serializeWriter(fout), s.serializeWriter(fout)
 		opts.Env = s.env
 		if s.reading {
 			opts.Stdin = s.stdin
@@ -303,13 +330,8 @@
 		opts.Stdin = s.stdin
 	}
 	var stdinCh, stderrCh chan error
-	stdout := s.stdout
-	stderr := s.stderr
+	stdout, stderr := s.serializeWriter(s.stdout), s.serializeWriter(s.stderr)
 	if stdout != nil {
-		if stdout == stderr {
-			stdout = &lockedWriter{f: stdout}
-			stderr = &lockedWriter{f: stderr}
-		}
 		stdinCh = make(chan error)
 		go copy(stdout, rStdout, stdinCh)
 	} else {
@@ -407,9 +429,10 @@
 	if s.err != nil {
 		return s.Done()
 	}
+	defer 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()
+	return s.err
 }
 
 // Call runs the given function. Note that Capture and Timeout have no
diff --git a/runutil/sequence_test.go b/runutil/sequence_test.go
index 279e967..dc298df 100644
--- a/runutil/sequence_test.go
+++ b/runutil/sequence_test.go
@@ -276,6 +276,14 @@
 		t.Errorf("got %v, want %v", got, want)
 	}
 	out.Reset()
+
+	err = seq.Last("sh", "-c", "echo should see an error; exit 1")
+	if err == nil {
+		t.Errorf("expected an error")
+	}
+	if got, want := out.String(), "should see an error"; !strings.Contains(got, want) {
+		t.Errorf("got %v, want %v", got, want)
+	}
 }
 
 type timestamped struct {