Merge pull request #42 from lyyyuna/error-improve

Enhance error handling
This commit is contained in:
qiniu-bot 2020-06-18 23:06:24 +08:00 committed by GitHub
commit a5504c17d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 334 additions and 151 deletions

View File

@ -17,7 +17,9 @@
package cmd
import (
"log"
"os"
log "github.com/sirupsen/logrus"
"github.com/qiniu/goc/pkg/build"
"github.com/qiniu/goc/pkg/cover"
@ -28,7 +30,7 @@ var buildCmd = &cobra.Command{
Use: "build",
Short: "Do cover for all go files and execute go build command",
Long: `
First of all, this build command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target in this temporary directory, finally go build command will be executed and binaries generated to their original place.
Build command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target, binaries will be generated to their original place.
`,
Example: `
# Build the current binary with cover variables injected. The binary will be generated in the current folder.
@ -44,7 +46,11 @@ goc build --output /to/this/path
goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'"
`,
Run: func(cmd *cobra.Command, args []string) {
runBuild()
wd, err := os.Getwd()
if err != nil {
log.Fatalf("Fail to build: %v", err)
}
runBuild(args, wd)
},
}
@ -52,14 +58,14 @@ var buildOutput string
func init() {
addBuildFlags(buildCmd.Flags())
buildCmd.Flags().StringVar(&buildOutput, "output", "", "it forces build to write the resulting executable or object to the named output file or directory")
buildCmd.Flags().StringVar(&buildOutput, "output", "", "it forces build to write the resulting executable to the named output file")
rootCmd.AddCommand(buildCmd)
}
func runBuild() {
gocBuild, err := build.NewBuild(buildFlags, packages, buildOutput)
func runBuild(args []string, wd string) {
gocBuild, err := build.NewBuild(buildFlags, args, wd, buildOutput)
if err != nil {
log.Fatalf("Fail to NewBuild: %v", err)
log.Fatalf("Fail to build: %v", err)
}
// remove temporary directory if needed
defer gocBuild.Clean()

View File

@ -17,13 +17,14 @@
package cmd
import (
"github.com/stretchr/testify/assert"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var baseDir string
@ -38,14 +39,14 @@ func TestGeneratedBinary(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
gopath := ""
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
buildFlags, packages, buildOutput = "", ".", ""
runBuild()
buildFlags, buildOutput = "", ""
args := []string{"."}
runBuild(args, workingDir)
obj := filepath.Join(".", "simple-project")
obj := filepath.Join(workingDir, "simple-project")
fInfo, err := os.Lstat(obj)
assert.Equal(t, err, nil, "the binary should be generated.")
assert.Equal(t, startTime.Before(fInfo.ModTime()), true, obj+"new binary should be generated, not the old one")

View File

@ -11,7 +11,7 @@ var (
mode string
debugGoc bool
buildFlags string
packages string
// packages string
appArgs string
goRunExecFlag string
@ -35,14 +35,12 @@ func addCommonFlags(cmdset *pflag.FlagSet) {
func addBuildFlags(cmdset *pflag.FlagSet) {
addCommonFlags(cmdset)
cmdset.StringVar(&packages, "packages", ".", "specify the package name, only . and ./... are supported")
// bind to viper
viper.BindPFlags(cmdset)
}
func addRunFlags(cmdset *pflag.FlagSet) {
addBuildFlags(cmdset)
cmdset.Lookup("packages").Usage = "specify the package name, only ., ./... and *.go are supported"
cmdset.StringVar(&appArgs, "appargs", "", "specify the application's arguments")
cmdset.StringVar(&goRunExecFlag, "exec", "", "same as -exec flag in 'go run' command")
cmdset.StringVar(&goRunArguments, "arguments", "", "same as 'arguments' in 'go run' command")

View File

@ -17,6 +17,8 @@
package cmd
import (
"os"
"github.com/qiniu/goc/pkg/build"
"github.com/qiniu/goc/pkg/cover"
log "github.com/sirupsen/logrus"
@ -27,7 +29,7 @@ var installCmd = &cobra.Command{
Use: "install",
Short: "Do cover for all go files and execute go install command",
Long: `
First of all, this install command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target in this temporary directory, finally go install command will be executed and binaries generated to their original place.
Install command will copy the project code and its necessary dependencies to a temporary directory, then do cover for the target, binaries will be generated to their original place.
`,
Example: `
# Install all binaries with cover variables injected. The binary will be installed in $GOPATH/bin or $HOME/go/bin if directory existed.
@ -40,7 +42,11 @@ goc install --center=http://127.0.0.1:7777
goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'"
`,
Run: func(cmd *cobra.Command, args []string) {
runInstall()
wd, err := os.Getwd()
if err != nil {
log.Fatalf("Fail to build: %v", err)
}
runInstall(args, wd)
},
}
@ -49,10 +55,10 @@ func init() {
rootCmd.AddCommand(installCmd)
}
func runInstall() {
gocBuild, err := build.NewInstall(buildFlags, packages)
func runInstall(args []string, wd string) {
gocBuild, err := build.NewInstall(buildFlags, args, wd)
if err != nil {
log.Fatalf("Fail to NewInstall: %v", err)
log.Fatalf("Fail to install: %v", err)
}
// remove temporary directory if needed
defer gocBuild.Clean()

View File

@ -17,13 +17,14 @@
package cmd
import (
"github.com/stretchr/testify/assert"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestInstalledBinaryForMod(t *testing.T) {
@ -32,12 +33,12 @@ func TestInstalledBinaryForMod(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../tests/samples/simple_project", "testhome")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
buildFlags, packages, buildOutput = "", ".", ""
runInstall()
buildFlags, buildOutput = "", ""
args := []string{"."}
runInstall(args, workingDir)
obj := filepath.Join(gopath, "bin", "simple-project")
fInfo, err := os.Lstat(obj)
@ -60,12 +61,12 @@ func TestInstalledBinaryForLegacy(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project")
gopath := filepath.Join(baseDir, "../tests/samples/simple_gopath_project")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "off")
buildFlags, packages, buildOutput = "", ".", ""
runInstall()
buildFlags, buildOutput = "", ""
args := []string{"."}
runInstall(args, workingDir)
obj := filepath.Join(gopath, "bin", "simple_gopath_project")
fInfo, err := os.Lstat(obj)

View File

@ -42,6 +42,6 @@ goc list [flags]
}
func init() {
listCmd.Flags().StringVarP(&center, "center", "", "http://127.0.0.1:7777", "cover profile host center")
addBasicFlags(listCmd.Flags())
rootCmd.AddCommand(listCmd)
}

View File

@ -17,7 +17,6 @@
package cmd
import (
"io/ioutil"
"path/filepath"
"runtime"
"strconv"
@ -37,6 +36,7 @@ Find more information at:
`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.SetReportCaller(true)
log.SetLevel(log.InfoLevel)
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
CallerPrettyfier: func(f *runtime.Frame) (string, string) {
@ -49,7 +49,13 @@ Find more information at:
})
if debugGoc == false {
// we only need log in debug mode
log.SetOutput(ioutil.Discard)
log.SetLevel(log.FatalLevel)
log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true,
CallerPrettyfier: func(f *runtime.Frame) (string, string) {
return "", ""
},
})
}
},
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"io/ioutil"
"net"
"os"
"github.com/qiniu/goc/pkg/build"
"github.com/qiniu/goc/pkg/cover"
@ -36,7 +37,14 @@ It is exactly behave as 'go run .' in addition of some internal goc features.`,
goc run .
`,
Run: func(cmd *cobra.Command, args []string) {
gocBuild, _ := build.NewBuild(buildFlags, packages, buildOutput)
wd, err := os.Getwd()
if err != nil {
log.Fatalf("Fail to build: %v", err)
}
gocBuild, err := build.NewBuild(buildFlags, args, wd, buildOutput)
if err != nil {
log.Fatalf("Fail to run: %v", err)
}
gocBuild.GoRunExecFlag = goRunExecFlag
gocBuild.GoRunArguments = goRunArguments
defer gocBuild.Clean()
@ -53,7 +61,9 @@ goc run .
// execute covers for the target source with original buildFlags and new GOPATH( tmp:original )
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, gocServer)
gocBuild.Run()
if err := gocBuild.Run(); err != nil {
log.Fatalf("Fail to run: %v", err)
}
},
}

View File

@ -32,6 +32,7 @@ type Build struct {
Pkgs map[string]*cover.Package // Pkg list parsed from "go list -json ./..." command
NewGOPATH string // the new GOPATH
OriGOPATH string // the original GOPATH
WorkingDir string // the working directory
TmpDir string // the temporary directory to build the project
TmpWorkingDir string // the working directory in the temporary directory, which is corresponding to the current directory in the project directory
IsMod bool // determine whether it is a Mod project
@ -53,17 +54,23 @@ type Build struct {
// NewBuild creates a Build struct which can build from goc temporary directory,
// and generate binary in current working directory
func NewBuild(buildflags string, packages string, outputDir string) (*Build, error) {
func NewBuild(buildflags string, args []string, workingDir string, outputDir string) (*Build, error) {
if err := checkParameters(args, workingDir); err != nil {
return nil, err
}
// buildflags = buildflags + " -o " + outputDir
b := &Build{
BuildFlags: buildflags,
Packages: packages,
Packages: strings.Join(args, " "),
WorkingDir: workingDir,
}
if false == b.validatePackageForBuild() {
log.Errorln(ErrWrongPackageTypeForBuild)
return nil, ErrWrongPackageTypeForBuild
}
b.MvProjectsToTmp()
if err := b.MvProjectsToTmp(); err != nil {
return nil, err
}
dir, err := b.determineOutputDir(outputDir)
b.Target = dir
if err != nil {
@ -90,13 +97,13 @@ func (b *Build) Build() error {
err := cmd.Start()
if err != nil {
log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v: %w", cmd.Args, err)
return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
}
if err = cmd.Wait(); err != nil {
log.Errorf("go build failed. The error is: %v", err)
return fmt.Errorf("go build faileds: %w", err)
return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
}
log.Println("Go build exit successful.")
log.Infoln("Go build exit successful.")
return nil
}
@ -107,57 +114,42 @@ func (b *Build) determineOutputDir(outputDir string) (string, error) {
log.Errorf("Can only be called after Build.MvProjectsToTmp(): %v", ErrWrongCallSequence)
return "", fmt.Errorf("can only be called after Build.MvProjectsToTmp(): %w", ErrWrongCallSequence)
}
curWorkingDir, err := os.Getwd()
if err != nil {
log.Errorf("Cannot get current working directory: %v", err)
return "", fmt.Errorf("cannot get current working directory: %w", err)
}
if outputDir == "" {
_, last := filepath.Split(curWorkingDir)
_, last := filepath.Split(b.WorkingDir)
if b.IsMod {
// in mod, special rule
// replace "_" with "-" in the import path
last = strings.ReplaceAll(last, "_", "-")
}
return filepath.Join(curWorkingDir, last), nil
return filepath.Join(b.WorkingDir, last), nil
}
abs, err := filepath.Abs(outputDir)
if err != nil {
log.Errorf("Fail to transform the path: %v to absolute path: %v", outputDir, err)
return "", fmt.Errorf("fail to transform the path %v to absolute path: %w", outputDir, err)
return "", err
}
return abs, nil
}
// validatePackageForBuild only allow . as package name
func (b *Build) validatePackageForBuild() bool {
if b.Packages == "." {
if b.Packages == "." || b.Packages == "" {
return true
}
return false
}
// Run excutes the main package in addition with the internal goc features
func (b *Build) Run() {
cmd := exec.Command("/bin/bash", "-c", "go run "+b.BuildFlags+" "+b.GoRunExecFlag+" "+b.Packages+" "+b.GoRunArguments)
cmd.Dir = b.TmpWorkingDir
if b.NewGOPATH != "" {
// Change to temp GOPATH for go install command
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", b.NewGOPATH))
func checkParameters(args []string, workingDir string) error {
if len(args) > 1 {
log.Errorln(ErrTooManyArgs)
return ErrTooManyArgs
}
log.Printf("go build cmd is: %v", cmd.Args)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
log.Fatalf("Fail to start command: %v. The error is: %v", cmd.Args, err)
}
if err = cmd.Wait(); err != nil {
log.Fatalf("Fail to execute command: %v. The error is: %v", cmd.Args, err)
if workingDir == "" {
return ErrInvalidWorkingDir
}
log.Infof("Working directory: %v", workingDir)
return nil
}

View File

@ -17,10 +17,13 @@
package build
import (
"github.com/stretchr/testify/assert"
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestInvalidPackage(t *testing.T) {
@ -28,26 +31,62 @@ func TestInvalidPackage(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := ""
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
_, err := NewBuild("", "example.com/simple-project", "")
assert.Equal(t, err, ErrWrongPackageTypeForBuild, "the package name should be invalid")
_, err := NewBuild("", []string{"example.com/simple-project"}, workingDir, "")
if !assert.Equal(t, err, ErrWrongPackageTypeForBuild) {
assert.FailNow(t, "the package name should be invalid")
}
}
func TestBasicBuildForModProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := ""
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
fmt.Println(workingDir)
buildFlags, args, buildOutput := "", []string{"."}, ""
gocBuild, err := NewBuild(buildFlags, args, workingDir, buildOutput)
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "should create temporary directory successfully")
}
err = gocBuild.Build()
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "temporary directory should build successfully")
}
}
func TestCheckParameters(t *testing.T) {
err := checkParameters([]string{"aa", "bb"}, "aa")
assert.Equal(t, err, ErrTooManyArgs, "too many arguments should failed")
err = checkParameters([]string{"aa"}, "")
assert.Equal(t, err, ErrInvalidWorkingDir, "empty working directory should failed")
}
func TestDetermineOutputDir(t *testing.T) {
b := &Build{}
_, err := b.determineOutputDir("")
assert.Equal(t, errors.Is(err, ErrWrongCallSequence), true, "called before Build.MvProjectsToTmp() should fail")
b.TmpDir = "fake"
_, err = b.determineOutputDir("xx")
assert.Equal(t, err, nil, "should return a directory")
}
func TestInvalidPackageNameForBuild(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../../tests/samples/simple_project", "testhome")
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
buildFlags, packages, buildOutput := "", ".", ""
gocBuild, err := NewBuild(buildFlags, packages, buildOutput)
assert.Equal(t, err, nil, "should create temporary directory successfully")
err = gocBuild.Build()
assert.Equal(t, err, nil, "temporary directory should build successfully")
buildFlags, packages := "", []string{"main.go"}
_, err := NewBuild(buildFlags, packages, workingDir, "")
if !assert.Equal(t, err, ErrWrongPackageTypeForBuild) {
assert.FailNow(t, "should not success with non . or ./... package")
}
}

View File

@ -9,6 +9,8 @@ var (
ErrGocShouldExecInProject = errors.New("goc not executed in project directory")
ErrWrongPackageTypeForInstall = errors.New("packages only support \".\" and \"./...\"")
ErrWrongPackageTypeForBuild = errors.New("packages only support \".\"")
ErrTooManyArgs = errors.New("too many args")
ErrInvalidWorkingDir = errors.New("the working directory is invalid")
ErrWrongCallSequence = errors.New("function should be called in a specified sequence")
ErrNoplaceToInstall = errors.New("no go env")
ErrNoplaceToInstall = errors.New("dont know where to install")
)

View File

@ -20,21 +20,28 @@ import (
"fmt"
"os"
"os/exec"
"strings"
log "github.com/sirupsen/logrus"
)
// NewInstall creates a Build struct which can install from goc temporary directory
func NewInstall(buildflags string, packages string) (*Build, error) {
func NewInstall(buildflags string, args []string, workingDir string) (*Build, error) {
if err := checkParameters(args, workingDir); err != nil {
return nil, err
}
b := &Build{
BuildFlags: buildflags,
Packages: packages,
Packages: strings.Join(args, " "),
WorkingDir: workingDir,
}
if false == b.validatePackageForInstall() {
log.Errorln(ErrWrongPackageTypeForInstall)
return nil, ErrWrongPackageTypeForInstall
}
b.MvProjectsToTmp()
if err := b.MvProjectsToTmp(); err != nil {
return nil, err
}
return b, nil
}
@ -61,18 +68,18 @@ func (b *Build) Install() error {
err = cmd.Start()
if err != nil {
log.Errorf("Fail to execute: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v: %w", cmd.Args, err)
return err
}
if err = cmd.Wait(); err != nil {
log.Errorf("go install failed. The error is: %v", err)
return fmt.Errorf("go install failed: %w", err)
return err
}
log.Infof("Go install successful. Binary installed in: %v", whereToInstall)
return nil
}
func (b *Build) validatePackageForInstall() bool {
if b.Packages == "." || b.Packages == "./..." {
if b.Packages == "." || b.Packages == "" || b.Packages == "./..." {
return true
}
return false

View File

@ -1,24 +1,42 @@
package build
import (
"github.com/stretchr/testify/assert"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestBasicInstallForModProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../tests/samples/simple_project", "testhome")
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../../tests/samples/simple_project", "testhome")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
buildFlags, packages := "", "."
gocBuild, err := NewInstall(buildFlags, packages)
assert.Equal(t, err, nil, "should create temporary directory successfully")
buildFlags, packages := "", []string{"."}
gocBuild, err := NewInstall(buildFlags, packages, workingDir)
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "should create temporary directory successfully")
}
err = gocBuild.Install()
assert.Equal(t, err, nil, "temporary directory should build successfully")
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "temporary directory should build successfully")
}
}
func TestInvalidPackageNameForInstall(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := filepath.Join(baseDir, "../../tests/samples/simple_project", "testhome")
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
buildFlags, packages := "", []string{"main.go"}
_, err := NewInstall(buildFlags, packages, workingDir)
if !assert.Equal(t, err, ErrWrongPackageTypeForInstall) {
assert.FailNow(t, "should not success with non . or ./... package")
}
}

View File

@ -50,13 +50,6 @@ func (b *Build) cpLegacyProject() {
// only cp dependency in root(current gopath),
// skip deps in other GOPATHs
func (b *Build) cpDepPackages(pkg *cover.Package, visited map[string]bool) {
/*
oriGOPATH := os.Getenv("GOPATH")
if oriGOPATH == "" {
oriGOPATH = filepath.Join(os.Getenv("HOME"), "go")
}
gopaths := strings.Split(oriGOPATH, ":")
*/
gopath := pkg.Root
for _, dep := range pkg.Deps {
src := filepath.Join(gopath, "src", dep)

52
pkg/build/run.go Normal file
View File

@ -0,0 +1,52 @@
/*
Copyright 2020 Qiniu Cloud (qiniu.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package build
import (
"fmt"
"os"
"os/exec"
log "github.com/sirupsen/logrus"
)
// Run excutes the main package in addition with the internal goc features
func (b *Build) Run() error {
cmd := exec.Command("/bin/bash", "-c", "go run "+b.BuildFlags+" "+b.GoRunExecFlag+" "+b.Packages+" "+b.GoRunArguments)
cmd.Dir = b.TmpWorkingDir
if b.NewGOPATH != "" {
// Change to temp GOPATH for go install command
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", b.NewGOPATH))
}
log.Infof("go build cmd is: %v", cmd.Args)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
log.Errorf("Fail to start command: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
}
if err = cmd.Wait(); err != nil {
log.Errorf("Fail to go run: %v. The error is: %v", cmd.Args, err)
return fmt.Errorf("fail to execute: %v, err: %w", cmd.Args, err)
}
return nil
}

View File

@ -29,15 +29,24 @@ import (
"github.com/spf13/viper"
)
func (b *Build) MvProjectsToTmp() {
func (b *Build) MvProjectsToTmp() error {
listArgs := []string{"-json"}
if len(b.BuildFlags) != 0 {
listArgs = append(listArgs, b.BuildFlags)
}
listArgs = append(listArgs, "./...")
b.Pkgs = cover.ListPackages(".", strings.Join(listArgs, " "), "")
var err error
b.Pkgs, err = cover.ListPackages(b.WorkingDir, strings.Join(listArgs, " "), "")
if err != nil {
log.Errorln(err)
return err
}
b.mvProjectsToTmp()
err = b.mvProjectsToTmp()
if err != nil {
log.Errorf("Fail to move the project to temporary directory")
return err
}
b.OriGOPATH = os.Getenv("GOPATH")
if b.IsMod == true {
b.NewGOPATH = ""
@ -53,22 +62,17 @@ func (b *Build) MvProjectsToTmp() {
if b.Root == "" && b.IsMod == false {
b.NewGOPATH = b.OriGOPATH
}
log.Printf("New GOPATH: %v", b.NewGOPATH)
return
log.Infof("New GOPATH: %v", b.NewGOPATH)
return nil
}
func (b *Build) mvProjectsToTmp() error {
path, err := os.Getwd()
if err != nil {
log.Errorf("Cannot get current working directory, the error is: %v", err)
return err
}
b.TmpDir = filepath.Join(os.TempDir(), TmpFolderName(path))
b.TmpDir = filepath.Join(os.TempDir(), TmpFolderName(b.WorkingDir))
// Delete previous tmp folder and its content
os.RemoveAll(b.TmpDir)
// Create a new tmp folder
err = os.MkdirAll(filepath.Join(b.TmpDir, "src"), os.ModePerm)
err := os.MkdirAll(filepath.Join(b.TmpDir, "src"), os.ModePerm)
if err != nil {
log.Errorf("Fail to create the temporary build directory. The err is: %v", err)
return err
@ -77,6 +81,7 @@ func (b *Build) mvProjectsToTmp() error {
// traverse pkg list to get project meta info
b.IsMod, b.Root, err = b.traversePkgsList()
log.Infof("mod project? %v", b.IsMod)
if errors.Is(err, ErrShouldNotReached) {
return fmt.Errorf("mvProjectsToTmp with a empty project: %w", err)
}
@ -84,7 +89,7 @@ func (b *Build) mvProjectsToTmp() error {
b.TmpWorkingDir, err = b.getTmpwd()
if err != nil {
log.Errorf("fail to get workding directory in temporary directory: %v", err)
return fmt.Errorf("fail to get workding directory in temporary directory: %w", err)
return fmt.Errorf("getTmpwd failed with error: %w", err)
}
// issue #14
// if b.Root == "", then the project is non-standard project
@ -109,7 +114,7 @@ func TmpFolderName(path string) string {
sum := sha256.Sum256([]byte(path))
h := fmt.Sprintf("%x", sum[:6])
return "goc-" + h
return "goc-build-" + h
}
// traversePkgsList travse the Build.Pkgs list
@ -128,7 +133,7 @@ func (b *Build) traversePkgsList() (isMod bool, root string, err error) {
isMod = true
return
}
log.Error("should not reach here")
log.Error(ErrShouldNotReached)
err = ErrShouldNotReached
return
}
@ -137,19 +142,13 @@ func (b *Build) traversePkgsList() (isMod bool, root string, err error) {
// and store it in the Build.tmpWorkdingDir
func (b *Build) getTmpwd() (string, error) {
for _, pkg := range b.Pkgs {
path, err := os.Getwd()
if err != nil {
log.Errorf("cannot get current working directory: %v", err)
return "", fmt.Errorf("cannot get current working directory: %w", err)
}
index := -1
var parentPath string
if b.IsMod == false {
index = strings.Index(path, pkg.Root)
index = strings.Index(b.WorkingDir, pkg.Root)
parentPath = pkg.Root
} else {
index = strings.Index(path, pkg.Module.Dir)
index = strings.Index(b.WorkingDir, pkg.Module.Dir)
parentPath = pkg.Module.Dir
}
@ -157,7 +156,7 @@ func (b *Build) getTmpwd() (string, error) {
return "", ErrGocShouldExecInProject
}
// b.TmpWorkingDir = filepath.Join(b.TmpDir, path[len(parentPath):])
return filepath.Join(b.TmpDir, path[len(parentPath):]), nil
return filepath.Join(b.TmpDir, b.WorkingDir[len(parentPath):]), nil
}
return "", ErrShouldNotReached
@ -168,16 +167,14 @@ func (b *Build) findWhereToInstall() (string, error) {
return GOBIN, nil
}
// old GOPATH dir
GOPATH := os.Getenv("GOPATH")
if false == b.IsMod {
if b.Root == "" {
return "", ErrNoplaceToInstall
}
return filepath.Join(b.Root, "bin"), nil
}
if GOPATH != "" {
return filepath.Join(strings.Split(GOPATH, ":")[0], "bin"), nil
if b.OriGOPATH != "" {
return filepath.Join(strings.Split(b.OriGOPATH, ":")[0], "bin"), nil
}
return filepath.Join(os.Getenv("HOME"), "go", "bin"), nil
}

View File

@ -34,12 +34,10 @@ func TestNewDirParseInLegacyProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project")
gopath := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project")
os.Chdir(workingDir)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "off")
b, _ := NewInstall("", ".")
b, _ := NewInstall("", []string{"."}, workingDir)
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
}
@ -48,7 +46,7 @@ func TestNewDirParseInLegacyProject(t *testing.T) {
t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir)
}
b, _ = NewBuild("", ".", "")
b, _ = NewBuild("", []string{"."}, workingDir, "")
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
}
@ -62,12 +60,11 @@ func TestNewDirParseInModProject(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_project")
gopath := ""
os.Chdir(workingDir)
fmt.Println(gopath)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
b, _ := NewInstall("", ".")
b, _ := NewInstall("", []string{"."}, workingDir)
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
}
@ -76,7 +73,7 @@ func TestNewDirParseInModProject(t *testing.T) {
t.Fatalf("The New GOPATH is wrong. newgopath: %v, tmpdir: %v", b.NewGOPATH, b.TmpDir)
}
b, _ = NewBuild("", ".", "")
b, _ = NewBuild("", []string{"."}, workingDir, "")
if -1 == strings.Index(b.TmpWorkingDir, b.TmpDir) {
t.Fatalf("Directory parse error. newwd: %v, tmpdir: %v", b.TmpWorkingDir, b.TmpDir)
}
@ -91,12 +88,11 @@ func TestLegacyProjectNotInGoPATH(t *testing.T) {
workingDir := filepath.Join(baseDir, "../../tests/samples/simple_gopath_project/src/qiniu.com/simple_gopath_project")
gopath := ""
os.Chdir(workingDir)
fmt.Println(gopath)
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "off")
b, _ := NewBuild("", ".", "")
b, _ := NewBuild("", []string{"."}, workingDir, "")
if b.OriGOPATH != b.NewGOPATH {
t.Fatalf("New GOPATH should be same with old GOPATH, for this kind of project. New: %v, old: %v", b.NewGOPATH, b.OriGOPATH)
}

View File

@ -21,6 +21,7 @@ import (
"bytes"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -37,6 +38,11 @@ import (
"github.com/sirupsen/logrus"
)
var (
ErrCoverPkgFailed = errors.New("fail to inject code to project")
ErrCoverListFailed = errors.New("fail to list package dependencies")
)
// TestCover is a collection of all counters
type TestCover struct {
Mode string
@ -114,9 +120,10 @@ type PackageError struct {
}
//Execute execute go tool cover for all the .go files in the target folder
func Execute(args, newGopath, target, mode, center string) {
func Execute(args, newGopath, target, mode, center string) error {
if !isDirExist(target) {
log.Fatalf("target directory %s not exist", target)
log.Errorf("Target directory %s not exist", target)
return ErrCoverPkgFailed
}
listArgs := []string{"-json"}
@ -124,7 +131,11 @@ func Execute(args, newGopath, target, mode, center string) {
listArgs = append(listArgs, args)
}
listArgs = append(listArgs, "./...")
pkgs := ListPackages(target, strings.Join(listArgs, " "), newGopath)
pkgs, err := ListPackages(target, strings.Join(listArgs, " "), newGopath)
if err != nil {
log.Errorf("Fail to list all packages, the error: %v", err)
return err
}
var seen = make(map[string]*PackageCover)
var seenCache = make(map[string]*PackageCover)
@ -134,7 +145,8 @@ func Execute(args, newGopath, target, mode, center string) {
// inject the main package
mainCover, err := AddCounters(pkg, mode, newGopath)
if err != nil {
log.Fatalf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err)
log.Errorf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err)
return ErrCoverPkgFailed
}
// new a testcover for this service
@ -158,7 +170,7 @@ func Execute(args, newGopath, target, mode, center string) {
if hasInternalPath(dep) {
//scan exist cache cover to tc.CacheCover
if cache, ok := seenCache[dep]; ok {
log.Printf("cache cover exist: %s", cache.Package.ImportPath)
log.Infof("cache cover exist: %s", cache.Package.ImportPath)
tc.CacheCover[cache.Package.Dir] = cache
continue
}
@ -166,7 +178,8 @@ func Execute(args, newGopath, target, mode, center string) {
// add counter for internal package
inPkgCover, err := AddCounters(depPkg, mode, newGopath)
if err != nil {
log.Fatalf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err)
log.Errorf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err)
return ErrCoverPkgFailed
}
parentDir := getInternalParent(depPkg.Dir)
parentImportPath := getInternalParent(depPkg.ImportPath)
@ -212,7 +225,8 @@ func Execute(args, newGopath, target, mode, center string) {
packageCover, err := AddCounters(depPkg, mode, newGopath)
if err != nil {
log.Fatalf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err)
log.Errorf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err)
return err
}
tc.DepsCover = append(tc.DepsCover, packageCover)
seen[dep] = packageCover
@ -220,21 +234,25 @@ func Execute(args, newGopath, target, mode, center string) {
}
if errs := InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 {
log.Fatalf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs)
log.Errorf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs)
return ErrCoverPkgFailed
}
// inject Http Cover APIs
var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir)
if err := InjectCountersHandlers(tc, httpCoverApis); err != nil {
log.Fatalf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err)
}
log.Errorf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err)
return ErrCoverPkgFailed
}
}
}
return nil
}
// ListPackages list all packages under specific via go list command
// The argument newgopath is if you need to go list in a different GOPATH
func ListPackages(dir string, args string, newgopath string) map[string]*Package {
func ListPackages(dir string, args string, newgopath string) (map[string]*Package, error) {
cmd := exec.Command("/bin/bash", "-c", "go list "+args)
log.Printf("go list cmd is: %v", cmd.Args)
cmd.Dir = dir
@ -243,7 +261,8 @@ func ListPackages(dir string, args string, newgopath string) map[string]*Package
}
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("excute `go list -json ./...` command failed, err: %v, out: %v", err, string(out))
log.Errorf("excute `go list -json ./...` command failed, err: %v, out: %v", err, string(out))
return nil, ErrCoverListFailed
}
dec := json.NewDecoder(bytes.NewReader(out))
@ -254,10 +273,12 @@ func ListPackages(dir string, args string, newgopath string) map[string]*Package
if err == io.EOF {
break
}
log.Fatalf("reading go list output: %v", err)
log.Errorf("reading go list output: %v", err)
return nil, ErrCoverListFailed
}
if pkg.Error != nil {
log.Fatalf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error)
log.Errorf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error)
return nil, ErrCoverPkgFailed
}
// for _, err := range pkg.DepsErrors {
@ -266,7 +287,7 @@ func ListPackages(dir string, args string, newgopath string) map[string]*Package
pkgs[pkg.ImportPath] = &pkg
}
return pkgs
return pkgs, nil
}
// AddCounters add counters for all go files under the package

View File

@ -27,6 +27,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/otiai10/copy"
"github.com/stretchr/testify/assert"
)
@ -304,3 +305,40 @@ func TestFindInternal(t *testing.T) {
}
}
}
func TestExecuteForSimpleModProject(t *testing.T) {
workingDir := "../../tests/samples/simple_project"
gopath := ""
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
testDir := filepath.Join(os.TempDir(), "goc-build-test")
copy.Copy(workingDir, testDir)
Execute("", gopath, testDir, "count", "http://127.0.0.1:7777")
_, err := os.Lstat(filepath.Join(testDir, "http_cover_apis_auto_generated.go"))
if !assert.Equal(t, err, nil) {
assert.FailNow(t, "should generate http_cover_apis_auto_generated.go")
}
}
func TestListPackagesForSimpleModProject(t *testing.T) {
workingDir := "../../tests/samples/simple_project"
gopath := ""
os.Setenv("GOPATH", gopath)
os.Setenv("GO111MODULE", "on")
pkgs, _ := ListPackages(workingDir, "-json ./...", "")
if !assert.Equal(t, len(pkgs), 1) {
assert.FailNow(t, "should only have one pkg")
}
if pkg, ok := pkgs["example.com/simple-project"]; ok {
assert.Equal(t, pkg.Module.Path, "example.com/simple-project")
} else {
assert.FailNow(t, "cannot get the pkg: example.com/simple-project")
}
}