xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/releaser/run.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1*9bb1b549SSpandan Das// Copyright 2021 The Bazel Authors. All rights reserved.
2*9bb1b549SSpandan Das//
3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License");
4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License.
5*9bb1b549SSpandan Das// You may obtain a copy of the License at
6*9bb1b549SSpandan Das//
7*9bb1b549SSpandan Das//    http://www.apache.org/licenses/LICENSE-2.0
8*9bb1b549SSpandan Das//
9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software
10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS,
11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and
13*9bb1b549SSpandan Das// limitations under the License.
14*9bb1b549SSpandan Das
15*9bb1b549SSpandan Daspackage main
16*9bb1b549SSpandan Das
17*9bb1b549SSpandan Dasimport (
18*9bb1b549SSpandan Das	"bytes"
19*9bb1b549SSpandan Das	"context"
20*9bb1b549SSpandan Das	"fmt"
21*9bb1b549SSpandan Das	"os"
22*9bb1b549SSpandan Das	"os/exec"
23*9bb1b549SSpandan Das	"strings"
24*9bb1b549SSpandan Das)
25*9bb1b549SSpandan Das
26*9bb1b549SSpandan Das// runForError runs a command without showing its output. If the command fails,
27*9bb1b549SSpandan Das// runForError returns an error containing its stderr.
28*9bb1b549SSpandan Dasfunc runForError(ctx context.Context, dir string, name string, args ...string) error {
29*9bb1b549SSpandan Das	stderr := &bytes.Buffer{}
30*9bb1b549SSpandan Das	cmd := exec.CommandContext(ctx, name, args...)
31*9bb1b549SSpandan Das	cmd.Env = envWithoutBazel()
32*9bb1b549SSpandan Das	cmd.Dir = dir
33*9bb1b549SSpandan Das	cmd.Stdout = nil
34*9bb1b549SSpandan Das	cmd.Stderr = stderr
35*9bb1b549SSpandan Das	err := cmd.Run()
36*9bb1b549SSpandan Das	return cleanCmdError(err, name, args, stderr.Bytes())
37*9bb1b549SSpandan Das}
38*9bb1b549SSpandan Das
39*9bb1b549SSpandan Das// runForOutput runs a command and returns its output. If the command fails,
40*9bb1b549SSpandan Das// runForOutput returns an error containing its stderr. The command's output
41*9bb1b549SSpandan Das// is returned whether it failed or not.
42*9bb1b549SSpandan Dasfunc runForOutput(ctx context.Context, dir string, name string, args ...string) ([]byte, error) {
43*9bb1b549SSpandan Das	stdout := &bytes.Buffer{}
44*9bb1b549SSpandan Das	stderr := &bytes.Buffer{}
45*9bb1b549SSpandan Das	cmd := exec.CommandContext(ctx, name, args...)
46*9bb1b549SSpandan Das	cmd.Env = envWithoutBazel()
47*9bb1b549SSpandan Das	cmd.Dir = dir
48*9bb1b549SSpandan Das	cmd.Stdout = stdout
49*9bb1b549SSpandan Das	cmd.Stderr = stderr
50*9bb1b549SSpandan Das	err := cmd.Run()
51*9bb1b549SSpandan Das	return stdout.Bytes(), cleanCmdError(err, name, args, stderr.Bytes())
52*9bb1b549SSpandan Das}
53*9bb1b549SSpandan Das
54*9bb1b549SSpandan Das// envWithoutBazel runs the current process's environment without variables
55*9bb1b549SSpandan Das// starting with "BUILD_" added by 'bazel run'. These can confuse subprocesses.
56*9bb1b549SSpandan Dasfunc envWithoutBazel() []string {
57*9bb1b549SSpandan Das	env := os.Environ()
58*9bb1b549SSpandan Das	filtered := make([]string, 0, len(env))
59*9bb1b549SSpandan Das	for _, e := range env {
60*9bb1b549SSpandan Das		if strings.HasPrefix(e, "BUILD_") {
61*9bb1b549SSpandan Das			continue
62*9bb1b549SSpandan Das		}
63*9bb1b549SSpandan Das		filtered = append(filtered, e)
64*9bb1b549SSpandan Das	}
65*9bb1b549SSpandan Das	return filtered
66*9bb1b549SSpandan Das}
67*9bb1b549SSpandan Das
68*9bb1b549SSpandan Das// cleanCmdError wraps an error returned by exec.Cmd.Run with the command that
69*9bb1b549SSpandan Das// was run and its stderr output.
70*9bb1b549SSpandan Dasfunc cleanCmdError(err error, name string, args []string, stderr []byte) error {
71*9bb1b549SSpandan Das	if err == nil {
72*9bb1b549SSpandan Das		return nil
73*9bb1b549SSpandan Das	}
74*9bb1b549SSpandan Das	return &commandError{
75*9bb1b549SSpandan Das		argv: append([]string{name}, args...),
76*9bb1b549SSpandan Das		err:  err,
77*9bb1b549SSpandan Das	}
78*9bb1b549SSpandan Das}
79*9bb1b549SSpandan Das
80*9bb1b549SSpandan Dastype commandError struct {
81*9bb1b549SSpandan Das	argv   []string
82*9bb1b549SSpandan Das	stderr []byte
83*9bb1b549SSpandan Das	err    error
84*9bb1b549SSpandan Das}
85*9bb1b549SSpandan Das
86*9bb1b549SSpandan Dasfunc (e *commandError) Error() string {
87*9bb1b549SSpandan Das	return fmt.Sprintf("running %s: %v\n%s", strings.Join(e.argv, " "), e.err, bytes.TrimSpace(e.stderr))
88*9bb1b549SSpandan Das}
89*9bb1b549SSpandan Das
90*9bb1b549SSpandan Dasfunc (e *commandError) Unwrap() error {
91*9bb1b549SSpandan Das	return e.err
92*9bb1b549SSpandan Das}
93