mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00:00 
			
		
		
		
	* Add copyright Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add gitea-vet and fix non-compliance Signed-off-by: jolheiser <john.olheiser@gmail.com> * Combine tools.go into build.go and clean up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove extra GO111MODULE=on Signed-off-by: jolheiser <john.olheiser@gmail.com>
		
			
				
	
	
		
			127 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			127 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package gocommand is a helper for calling the go command.
 | |
| package gocommand
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // An Invocation represents a call to the go command.
 | |
| type Invocation struct {
 | |
| 	Verb       string
 | |
| 	Args       []string
 | |
| 	BuildFlags []string
 | |
| 	Env        []string
 | |
| 	WorkingDir string
 | |
| 	Logf       func(format string, args ...interface{})
 | |
| }
 | |
| 
 | |
| // Run runs the invocation, returning its stdout and an error suitable for
 | |
| // human consumption, including stderr.
 | |
| func (i *Invocation) Run(ctx context.Context) (*bytes.Buffer, error) {
 | |
| 	stdout, _, friendly, _ := i.RunRaw(ctx)
 | |
| 	return stdout, friendly
 | |
| }
 | |
| 
 | |
| // RunRaw is like RunPiped, but also returns the raw stderr and error for callers
 | |
| // that want to do low-level error handling/recovery.
 | |
| func (i *Invocation) RunRaw(ctx context.Context) (stdout *bytes.Buffer, stderr *bytes.Buffer, friendlyError error, rawError error) {
 | |
| 	stdout = &bytes.Buffer{}
 | |
| 	stderr = &bytes.Buffer{}
 | |
| 	rawError = i.RunPiped(ctx, stdout, stderr)
 | |
| 	if rawError != nil {
 | |
| 		// Check for 'go' executable not being found.
 | |
| 		if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
 | |
| 			friendlyError = fmt.Errorf("go command required, not found: %v", ee)
 | |
| 		}
 | |
| 		if ctx.Err() != nil {
 | |
| 			friendlyError = ctx.Err()
 | |
| 		}
 | |
| 		friendlyError = fmt.Errorf("err: %v: stderr: %s", rawError, stderr)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // RunPiped is like Run, but relies on the given stdout/stderr
 | |
| func (i *Invocation) RunPiped(ctx context.Context, stdout, stderr io.Writer) error {
 | |
| 	log := i.Logf
 | |
| 	if log == nil {
 | |
| 		log = func(string, ...interface{}) {}
 | |
| 	}
 | |
| 
 | |
| 	goArgs := []string{i.Verb}
 | |
| 	switch i.Verb {
 | |
| 	case "mod":
 | |
| 		// mod needs the sub-verb before build flags.
 | |
| 		goArgs = append(goArgs, i.Args[0])
 | |
| 		goArgs = append(goArgs, i.BuildFlags...)
 | |
| 		goArgs = append(goArgs, i.Args[1:]...)
 | |
| 	case "env":
 | |
| 		// env doesn't take build flags.
 | |
| 		goArgs = append(goArgs, i.Args...)
 | |
| 	default:
 | |
| 		goArgs = append(goArgs, i.BuildFlags...)
 | |
| 		goArgs = append(goArgs, i.Args...)
 | |
| 	}
 | |
| 	cmd := exec.Command("go", goArgs...)
 | |
| 	cmd.Stdout = stdout
 | |
| 	cmd.Stderr = stderr
 | |
| 	// On darwin the cwd gets resolved to the real path, which breaks anything that
 | |
| 	// expects the working directory to keep the original path, including the
 | |
| 	// go command when dealing with modules.
 | |
| 	// The Go stdlib has a special feature where if the cwd and the PWD are the
 | |
| 	// same node then it trusts the PWD, so by setting it in the env for the child
 | |
| 	// process we fix up all the paths returned by the go command.
 | |
| 	cmd.Env = append(append([]string{}, i.Env...), "PWD="+i.WorkingDir)
 | |
| 	cmd.Dir = i.WorkingDir
 | |
| 
 | |
| 	defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
 | |
| 
 | |
| 	return runCmdContext(ctx, cmd)
 | |
| }
 | |
| 
 | |
| // runCmdContext is like exec.CommandContext except it sends os.Interrupt
 | |
| // before os.Kill.
 | |
| func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
 | |
| 	if err := cmd.Start(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	resChan := make(chan error, 1)
 | |
| 	go func() {
 | |
| 		resChan <- cmd.Wait()
 | |
| 	}()
 | |
| 
 | |
| 	select {
 | |
| 	case err := <-resChan:
 | |
| 		return err
 | |
| 	case <-ctx.Done():
 | |
| 	}
 | |
| 	// Cancelled. Interrupt and see if it ends voluntarily.
 | |
| 	cmd.Process.Signal(os.Interrupt)
 | |
| 	select {
 | |
| 	case err := <-resChan:
 | |
| 		return err
 | |
| 	case <-time.After(time.Second):
 | |
| 	}
 | |
| 	// Didn't shut down in response to interrupt. Kill it hard.
 | |
| 	cmd.Process.Kill()
 | |
| 	return <-resChan
 | |
| }
 | |
| 
 | |
| func cmdDebugStr(cmd *exec.Cmd) string {
 | |
| 	env := make(map[string]string)
 | |
| 	for _, kv := range cmd.Env {
 | |
| 		split := strings.Split(kv, "=")
 | |
| 		k, v := split[0], split[1]
 | |
| 		env[k] = v
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], cmd.Args)
 | |
| }
 |