blob: 45c9175ab6fde4cb10b8ad548e94719004e6f821 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
)
// vetCheck runs the "vet" tool on the source code in req.Body.
// In case of no errors it returns an empty, non-nil *response.
// Otherwise &response.Errors contains found errors.
//
// Deprecated: this is the handler for the legacy /vet endpoint; use
// the /compile (compileAndRun) handler instead with the WithVet
// boolean set. This code path doesn't support modules and only exists
// as a temporary compatibility bridge to older javascript clients.
func vetCheck(ctx context.Context, req *request) (*response, error) {
tmpDir, err := ioutil.TempDir("", "vet")
if err != nil {
return nil, fmt.Errorf("error creating temp directory: %v", err)
}
defer os.RemoveAll(tmpDir)
in := filepath.Join(tmpDir, progName)
if err := ioutil.WriteFile(in, []byte(req.Body), 0400); err != nil {
return nil, fmt.Errorf("error creating temp file %q: %v", in, err)
}
vetOutput, err := vetCheckInDir(ctx, tmpDir, os.Getenv("GOPATH"))
if err != nil {
// This is about errors running vet, not vet returning output.
return nil, err
}
return &response{Errors: vetOutput}, nil
}
// vetCheckInDir runs go vet in the provided directory, using the
// provided GOPATH value. The returned error is only about whether
// go vet was able to run, not whether vet reported problem. The
// returned value is ("", nil) if vet successfully found nothing,
// and (non-empty, nil) if vet ran and found issues.
func vetCheckInDir(ctx context.Context, dir, goPath string) (output string, execErr error) {
start := time.Now()
defer func() {
status := "success"
if execErr != nil {
status = "error"
}
// Ignore error. The only error can be invalid tag key or value
// length, which we know are safe.
stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(kGoVetSuccess, status)},
mGoVetLatency.M(float64(time.Since(start))/float64(time.Millisecond)))
}()
cmd := exec.Command("go", "vet")
cmd.Dir = dir
// Linux go binary is not built with CGO_ENABLED=0.
// Prevent vet to compile packages in cgo mode.
// See #26307.
cmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GOPATH="+goPath)
cmd.Env = append(cmd.Env,
"GO111MODULE=on",
"GOPROXY="+playgroundGoproxy(),
)
out, err := cmd.CombinedOutput()
if err == nil {
return "", nil
}
if _, ok := err.(*exec.ExitError); !ok {
return "", fmt.Errorf("error vetting go source: %v", err)
}
// Rewrite compiler errors to refer to progName
// instead of '/tmp/sandbox1234/main.go'.
errs := strings.Replace(string(out), dir, "", -1)
// Remove vet's package name banner.
if strings.HasPrefix(errs, "#") {
if nl := strings.Index(errs, "\n"); nl != -1 {
errs = errs[nl+1:]
}
}
return errs, nil
}