blob: c041bbc6986d5df62eea1a121a014021f56e2636 [file] [log] [blame]
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"path"
"strings"
"go.skia.org/infra/go/exec"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/task_driver/go/lib/checkout"
"go.skia.org/infra/task_driver/go/lib/os_steps"
"go.skia.org/infra/task_driver/go/td"
"go.skia.org/infra/task_scheduler/go/types"
)
var (
// Required properties for this task.
projectId = flag.String("project_id", "", "ID of the Google Cloud project.")
taskId = flag.String("task_id", "", "ID of this task.")
taskName = flag.String("task_name", "", "Name of the task.")
repo = flag.String("repo", "", "URL of the repo.")
revision = flag.String("revision", "", "Git revision to test.")
patchIssue = flag.String("patch_issue", "", "Issue ID, required if this is a try job.")
patchSet = flag.String("patch_set", "", "Patch Set ID, required if this is a try job.")
patchServer = flag.String("patch_server", "", "Code review server, required if this is a try job.")
// Optional flags.
workdir = flag.String("workdir", ".", "Working directory")
local = flag.Bool("local", false, "True if running locally (as opposed to in production)")
output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.")
)
// goVars returns the target directory for the infra repo and the full set of
// environment variables which should be used for running Go commands.
func goVars(ctx context.Context, workdir string) (string, []string) {
goPath := path.Join(workdir, "go_deps")
goSrc := path.Join(goPath, "src")
if err := os_steps.MkdirAll(ctx, goSrc); err != nil {
td.Fatal(ctx, err)
}
goRoot := path.Join(workdir, "go", "go")
goBin := path.Join(goRoot, "bin")
checkoutRoot := path.Join(goSrc, "go.skia.org")
infraDir := path.Join(checkoutRoot, "infra")
depotToolsDir := path.Join(workdir, "depot_tools")
PATH := strings.Join([]string{
goBin,
path.Join(goPath, "bin"),
path.Join(workdir, "gcloud_linux", "bin"),
path.Join(workdir, "protoc", "bin"),
path.Join(workdir, "node", "node", "bin"),
os.Getenv("PATH"),
depotToolsDir,
}, string(os.PathListSeparator))
env := []string{
"CHROME_HEADLESS=1",
fmt.Sprintf("GOCACHE=%s", path.Join(workdir, "cache", "go_cache")),
fmt.Sprintf("GOROOT=%s", goRoot),
fmt.Sprintf("GOPATH=%s", goPath),
"GIT_USER_AGENT=git/1.9.1", // I don't think this version matters.
fmt.Sprintf("PATH=%s", PATH),
fmt.Sprintf("SKIABOT_TEST_DEPOT_TOOLS=%s", depotToolsDir),
"TMPDIR=",
}
return infraDir, env
}
func syncMissingGoDeps(ctx context.Context, infraDir string) error {
ctx = td.StartStep(ctx, td.Props("Sync missing Go DEPS"))
defer td.EndStep(ctx)
// Determine if any dependencies are missing from the go_deps asset. This
// happens whenever we add a dependency on a new package and will be resolved
// automatically the next time that go_deps is rolled. For now, explicitly sync
// the missing dependencies.
script := path.Join(infraDir, "scripts", "find_missing_go_deps.py")
missing, err := exec.RunCwd(ctx, ".", "python", script, "--json")
if err != nil {
return td.FailStep(ctx, err)
}
missing = strings.TrimSpace(missing)
if len(missing) > 0 {
var missingList []string
if err := json.Unmarshal([]byte(missing), &missingList); err != nil {
return td.FailStep(ctx, err)
}
for _, pkg := range missingList {
if _, err := exec.RunCwd(ctx, ".", "go", "get", pkg); err != nil {
return td.FailStep(ctx, err)
}
}
}
return nil
}
func main() {
// Setup.
ctx := td.StartRun(projectId, taskId, taskName, output, local)
defer td.EndRun(ctx)
if *repo == "" {
td.Fatalf(ctx, "--repo is required.")
}
if *revision == "" {
td.Fatalf(ctx, "--revision is required.")
}
// Setup Go.
wd, err := os_steps.Abs(ctx, *workdir)
if err != nil {
td.Fatal(ctx, err)
}
infraDir, goEnv := goVars(ctx, wd)
// Check out code.
*repo = strings.TrimSuffix(*repo, ".git")
repoState := types.RepoState{
Repo: *repo,
Revision: *revision,
Patch: types.Patch{
Issue: *patchIssue,
Patchset: *patchSet,
Server: *patchServer,
},
}
if _, err := checkout.EnsureGitCheckout(ctx, infraDir, repoState); err != nil {
td.Fatal(ctx, err)
}
// For Large/Race, start the Cloud Datastore emulator.
if strings.Contains(*taskName, "Large") || strings.Contains(*taskName, "Race") {
d := path.Join(infraDir, "go", "ds", "emulator")
if _, err := exec.RunCwd(ctx, d, "./run_emulator", "start"); err != nil {
td.Fatal(ctx, err)
}
goEnv = append(goEnv, "DATASTORE_EMULATOR_HOST=localhost:8891")
goEnv = append(goEnv, "BIGTABLE_EMULATOR_HOST=localhost:8892")
goEnv = append(goEnv, "PUBSUB_EMULATOR_HOST=localhost:8893")
defer func() {
if _, err := exec.RunCwd(ctx, d, "./run_emulator", "stop"); err != nil {
td.Fatal(ctx, err)
}
}()
}
// The remainder of the steps want the Go environment variables.
if err := td.Do(ctx, td.Props("Set Go Environment").Env(goEnv), func(ctx context.Context) (rvErr error) {
// Print Go info.
out, err := exec.RunCwd(ctx, ".", "which", "go")
if err != nil {
return err
}
sklog.Infof("Using Go from %s", out)
out, err = exec.RunCwd(ctx, ".", "go", "version")
if err != nil {
return err
}
sklog.Infof("Go version %s", out)
// More prerequisites.
if !strings.Contains(*taskName, "Race") {
if _, err := exec.RunCwd(ctx, ".", "sudo", "npm", "i", "-g", "bower@1.8.2"); err != nil {
return err
}
}
if _, err := exec.RunCwd(ctx, path.Join(infraDir, "go", "database"), "./setup_test_db"); err != nil {
return err
}
if err := syncMissingGoDeps(ctx, infraDir); err != nil {
return err
}
// Run the tests.
cmd := []string{"go", "run", "./run_unittests.go", "--alsologtostderr"}
if strings.Contains(*taskName, "Race") {
cmd = append(cmd, "--race", "--large", "--medium", "--small")
} else if strings.Contains(*taskName, "Large") {
cmd = append(cmd, "--large")
} else if strings.Contains(*taskName, "Medium") {
cmd = append(cmd, "--medium")
} else {
cmd = append(cmd, "--small")
}
if _, err := exec.RunCwd(ctx, infraDir, cmd...); err != nil {
return err
}
return nil
}); err != nil {
td.Fatal(ctx, err)
}
}