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 {