mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
release/dist: add a helper to run commands
The helper suppresses output if the command runs successfully. If the command fails, it dumps the buffered output to stdout before returning the error. This means the happy path isn't swamped by debug noise or xcode being intensely verbose about what kind of day it's having, but you still get debug output when something goes wrong. Updates tailscale/corp#9045 Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
f18beaa1e4
commit
0df11253ec
56
release/dist/dist.go
vendored
56
release/dist/dist.go
vendored
@ -5,6 +5,7 @@
|
|||||||
package dist
|
package dist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -146,11 +147,11 @@ func (b *Build) Extra(key any, constructor func() any) any {
|
|||||||
// GoPkg returns the path on disk of pkg.
|
// GoPkg returns the path on disk of pkg.
|
||||||
// The module of pkg must be imported in b.Repo's go.mod.
|
// The module of pkg must be imported in b.Repo's go.mod.
|
||||||
func (b *Build) GoPkg(pkg string) (string, error) {
|
func (b *Build) GoPkg(pkg string) (string, error) {
|
||||||
bs, err := exec.Command(b.Go, "list", "-f", "{{.Dir}}", pkg).Output()
|
out, err := b.Command(b.Repo, b.Go, "list", "-f", "{{.Dir}}", pkg).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("finding package %q: %w", pkg, err)
|
return "", fmt.Errorf("finding package %q: %w", pkg, err)
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(bs)), nil
|
return strings.TrimSpace(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TmpDir creates and returns a new empty temporary directory.
|
// TmpDir creates and returns a new empty temporary directory.
|
||||||
@ -178,7 +179,7 @@ func (b *Build) BuildGoBinary(path string, env map[string]string) (string, error
|
|||||||
// and do other initialization. Running `go version` once takes care of
|
// and do other initialization. Running `go version` once takes care of
|
||||||
// all of that and avoids that initialization happening concurrently
|
// all of that and avoids that initialization happening concurrently
|
||||||
// later on in builds.
|
// later on in builds.
|
||||||
_, err := exec.Command(b.Go, "version").Output()
|
_, err := b.Command(b.Repo, b.Go, "version").CombinedOutput()
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -197,15 +198,10 @@ func (b *Build) BuildGoBinary(path string, env map[string]string) (string, error
|
|||||||
sort.Strings(envStrs)
|
sort.Strings(envStrs)
|
||||||
log.Printf("Building %s (with env %s)", path, strings.Join(envStrs, " "))
|
log.Printf("Building %s (with env %s)", path, strings.Join(envStrs, " "))
|
||||||
buildDir := b.TmpDir()
|
buildDir := b.TmpDir()
|
||||||
cmd := exec.Command(b.Go, "build", "-o", buildDir, path)
|
cmd := b.Command(b.Repo, b.Go, "build", "-o", buildDir, path)
|
||||||
cmd.Dir = b.Repo
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
for k, v := range env {
|
for k, v := range env {
|
||||||
cmd.Env = append(cmd.Env, k+"="+v)
|
cmd.Cmd.Env = append(cmd.Cmd.Env, k+"="+v)
|
||||||
}
|
}
|
||||||
cmd.Env = append(cmd.Env, "TS_USE_GOCROSS=1")
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -217,6 +213,46 @@ func (b *Build) BuildGoBinary(path string, env map[string]string) (string, error
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command prepares an exec.Cmd to run [cmd, args...] in dir.
|
||||||
|
func (b *Build) Command(dir, cmd string, args ...string) *Command {
|
||||||
|
ret := &Command{
|
||||||
|
Cmd: exec.Command(cmd, args...),
|
||||||
|
}
|
||||||
|
ret.Cmd.Stdout = &ret.Output
|
||||||
|
ret.Cmd.Stderr = &ret.Output
|
||||||
|
// dist always wants to use gocross if any Go is involved.
|
||||||
|
ret.Cmd.Env = append(os.Environ(), "TS_USE_GOCROSS=1")
|
||||||
|
ret.Cmd.Dir = dir
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command runs an exec.Cmd and returns its exit status. If the command fails,
|
||||||
|
// its output is printed to os.Stdout, otherwise it's suppressed.
|
||||||
|
type Command struct {
|
||||||
|
Cmd *exec.Cmd
|
||||||
|
Output bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run is like c.Cmd.Run, but if the command fails, its output is printed to
|
||||||
|
// os.Stdout before returning the error.
|
||||||
|
func (c *Command) Run() error {
|
||||||
|
err := c.Cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
// Command failed, dump its output.
|
||||||
|
os.Stdout.Write(c.Output.Bytes())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CombinedOutput is like c.Cmd.CombinedOutput, but returns the output as a
|
||||||
|
// string instead of a byte slice.
|
||||||
|
func (c *Command) CombinedOutput() (string, error) {
|
||||||
|
c.Cmd.Stdout = nil
|
||||||
|
c.Cmd.Stderr = nil
|
||||||
|
bs, err := c.Cmd.CombinedOutput()
|
||||||
|
return string(bs), err
|
||||||
|
}
|
||||||
|
|
||||||
func findModRoot(path string) (string, error) {
|
func findModRoot(path string) (string, error) {
|
||||||
for {
|
for {
|
||||||
modpath := filepath.Join(path, "go.mod")
|
modpath := filepath.Join(path, "go.mod")
|
||||||
|
Loading…
Reference in New Issue
Block a user