func Analyse(ctx context.Context, logger logger.Logger, exec Executer, cloner Cloner, configReader ConfigReader, refReader RefReader, config Config, analysis *db.Analysis) error {
	start := time.Now()
	defer func() {
		analysis.TotalDuration = db.Duration(time.Since(start))
	}()
	logger = logger.With("area", "analyser")
	deltaStart := time.Now()  
	if err := cloner.Clone(ctx, exec); err != nil {
		return errors.WithMessage(err, "could not clone")
	}
	analysis.CloneDuration = db.Duration(time.Since(deltaStart))
	repoConfig, err := configReader.Read(ctx, exec)
	if err != nil {
		return errors.WithMessage(err, "could not configure repository")
	}
	envArgs := [][]string{
		{"go", "env"},
		{"go", "version"},
		{"cat", "/proc/self/limits"},
		{"lsb_release", "--description"},
	}
	for _, arg := range envArgs {
		out, err := exec.Execute(ctx, arg)
		if err != nil {
			return fmt.Errorf("could not execute %v: %s\n%s", arg, err, out)
		}
	}
	if err := installAPTPackages(ctx, exec, repoConfig.APTPackages); err != nil {
		return errors.WithMessage(err, "could not install packages")
	}
	baseRef, err := refReader.Base(ctx, exec)
	if err != nil {
		return errors.Wrap(err, "could not get base ref")
	}
	patch, err := getPatch(ctx, exec, baseRef, config.HeadRef)
	if err != nil {
		return errors.Wrap(err, "could not get patch")
	}
	deltaStart = time.Now()
	args := []string{"install-deps.sh"}
	out, err := exec.Execute(ctx, args)
	if err != nil {
		return fmt.Errorf("could not execute %v: %s\n%s", args, err, out)
	}
	analysis.DepsDuration = db.Duration(time.Since(deltaStart))
	logger.With("step", "install-deps.sh").Info(string(bytes.TrimSpace(out)))
	args = []string{"pwd"}
	out, err = exec.Execute(ctx, args)
	if err != nil {
		return fmt.Errorf("could not execute %v: %s\n%s", args, err, out)
	}
	pwd := string(bytes.TrimSpace(out))
	for _, tool := range repoConfig.Tools {
		deltaStart = time.Now()
		args := []string{tool.Path}
		for _, arg := range strings.Fields(tool.Args) {
			switch arg {
			case ArgBaseBranch:  
				arg = baseRef
			}
			args = append(args, arg)
		}
		out, err := exec.Execute(ctx, args)
		switch err.(type) {
		case nil, *NonZeroError:
		default:
			return fmt.Errorf("could not execute %v: %s\n%s", args, err, out)
		}
		logger.With("step", tool.Name).Info("ran tool")
		checker := revgrep.Checker{
			Patch:   bytes.NewReader(patch),
			Regexp:  tool.Regexp,
			AbsPath: pwd,
		}
		revIssues, err := checker.Check(bytes.NewReader(out), ioutil.Discard)
		if err != nil {
			return err
		}
		logger.Infof("revgrep found %v issues", len(revIssues))
		var issues []db.Issue
		for _, issue := range revIssues {
			args = []string{"isFileGenerated", pwd, issue.File}
			out, err := exec.Execute(ctx, args)
			logger.With("step", "isFileGenerated").Info(string(bytes.TrimSpace(out)))
			switch err {
			case nil:
				continue  
			default:
				if etype, ok := err.(*NonZeroError); ok && etype.ExitCode == 1 {
					break  
				}
				return fmt.Errorf("could not execute %v: %s\n%s", args, err, out)
			}
			issues = append(issues, db.Issue{
				Path:    issue.File,
				Line:    issue.LineNo,
				HunkPos: issue.HunkPos,
				Issue:   fmt.Sprintf("%s: %s", tool.Name, issue.Message),
			})
		}
		analysis.Tools[tool.ID] = db.AnalysisTool{
			Duration: db.Duration(time.Since(deltaStart)),
			Issues:   issues,
		}
	}
	return nil
}
Analyse downloads a repository set in config in an environment provided by
exec, running the series of tools. Writes results to provided analysis,
or an error. The repository is expected to contain at least one Go package.
package analyser
import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"strings"
	"time"
	"github.com/bradleyfalzon/gopherci/internal/db"
	"github.com/bradleyfalzon/gopherci/internal/logger"
	"github.com/bradleyfalzon/revgrep"
	"github.com/pkg/errors"
)
const (
	ArgBaseBranch = "%BASE_BRANCH%"
)
type Analyser interface {
	NewExecuter(ctx context.Context, goSrcPath string) (Executer, error)
}
type Config struct {
	HeadRef string
}
type Executer interface {
	Execute(context.Context, []string) ([]byte, error)
	Stop(context.Context) error
}
type NonZeroError struct {
	args     []string
	ExitCode int  
}
func (e *NonZeroError) Error() string {
	return fmt.Sprintf("%v returned exit code %v", e.args, e.ExitCode)
}