gosh: Make Cleanup pop to the top of the dir stack.

Here's how I've written one of my tests:

func TestFoo(t *testing.T) {
  sh := gosh.NewShell(gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf})
  defer sh.Cleanup
  tmpDir := sh.MakeTempDir()
  sh.Pushd(tmpDir)
  defer sh.Popd()
  sh.Cmd("binary", "fails").Run()
}

The problem with this pattern is that if "binary" exits with a
non-0 exit code, sh.Err will be non-nil after Run, but the
deferred sh.Popd will then panic.

It should be easy for me to fix; I can just remove the deferred
sh.Popd.  But the problem is that the cwd is tmpDir, which is
deleted by Cleanup.  Any subsequent test that calls sh.Pushd will
fail, since os.Getwd will fail.

My fix in this CL is pop to the top of the dir stack, so even if
the user forgets to call Popd(), Cleanup will put us back in the
directory where we started.  This seems reasonable.

Change-Id: I7592cc7baab270b041ceb5e5a75cdf46b0cbe940
diff --git a/gosh/shell.go b/gosh/shell.go
index 370f4e3..dbe8fbb 100644
--- a/gosh/shell.go
+++ b/gosh/shell.go
@@ -539,6 +539,13 @@
 			sh.logf("os.RemoveAll(%q) failed: %v\n", tempDir, err)
 		}
 	}
+	// Change back to the top of the dir stack.
+	if len(sh.dirStack) > 0 {
+		dir := sh.dirStack[0]
+		if err := os.Chdir(dir); err != nil {
+			sh.logf("os.Chdir(%q) failed: %v\n", dir, err)
+		}
+	}
 	// Call any registered cleanup functions in LIFO order.
 	for i := len(sh.cleanupFns) - 1; i >= 0; i-- {
 		sh.cleanupFns[i]()
diff --git a/gosh/shell_test.go b/gosh/shell_test.go
index 80fcfc2..89a9555 100644
--- a/gosh/shell_test.go
+++ b/gosh/shell_test.go
@@ -103,6 +103,25 @@
 	eq(t, calledErrorf, true)
 }
 
+func TestPushdNoPopdCleanup(t *testing.T) {
+	startDir, err := os.Getwd()
+	ok(t, err)
+
+	sh := gosh.NewShell(gosh.Opts{Errorf: makeErrorf(t), Logf: t.Logf})
+	tmpDir := sh.MakeTempDir()
+	sh.Pushd(tmpDir)
+	cwd, err := os.Getwd()
+	ok(t, err)
+	eq(t, cwd, tmpDir)
+	// There is no matching popd; the cwd is tmpDir, which is deleted by Cleanup.
+	// Cleanup needs to put us back in startDir, otherwise all subsequent Pushd
+	// calls will fail.
+	sh.Cleanup()
+	cwd, err = os.Getwd()
+	ok(t, err)
+	eq(t, cwd, startDir)
+}
+
 func TestCmds(t *testing.T) {
 	sh := gosh.NewShell(gosh.Opts{Errorf: makeErrorf(t), Logf: t.Logf})
 	defer sh.Cleanup()