• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2023 Google LLC
2//
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// This task driver runs various Go linters via Bazel, and fails on any errors or Git diffs.
7package main
8
9import (
10	"context"
11	"flag"
12	"fmt"
13	"path/filepath"
14	"strings"
15
16	sk_exec "go.skia.org/infra/go/exec"
17	"go.skia.org/infra/task_driver/go/lib/bazel"
18	"go.skia.org/infra/task_driver/go/lib/os_steps"
19	"go.skia.org/infra/task_driver/go/td"
20	"go.skia.org/skia/infra/bots/task_drivers/common"
21)
22
23var (
24	// Required properties for this task.
25	// We want the cache to be on a bigger disk than default. The root disk, where the home
26	// directory (and default Bazel cache) lives, is only 15 GB on our GCE VMs.
27	gitPath   = flag.String("git_path", "", "Location of git binary to use for diffs.")
28	projectId = flag.String("project_id", "", "ID of the Google Cloud project.")
29	taskId    = flag.String("task_id", "", "ID of this task.")
30	taskName  = flag.String("task_name", "", "Name of the task.")
31	workdir   = flag.String("workdir", ".", "Working directory, the root directory of a full Skia checkout")
32	// Optional flags.
33	local  = flag.Bool("local", false, "True if running locally (as opposed to on the CI/CQ)")
34	output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.")
35)
36
37func main() {
38	bazelFlags := common.MakeBazelFlags(common.MakeBazelFlagsOpts{
39		AdditionalArgs: true,
40	})
41
42	// StartRun calls flag.Parse()
43	ctx := td.StartRun(projectId, taskId, taskName, output, local)
44	defer td.EndRun(ctx)
45
46	bazelFlags.Validate(ctx)
47
48	if *gitPath == "" {
49		td.Fatal(ctx, fmt.Errorf("--git_path is required"))
50	}
51
52	wd, err := os_steps.Abs(ctx, *workdir)
53	if err != nil {
54		td.Fatal(ctx, err)
55	}
56	absGit, err := os_steps.Abs(ctx, *gitPath)
57	if err != nil {
58		td.Fatal(ctx, err)
59	}
60	if _, err := os_steps.Stat(ctx, absGit); err != nil {
61		fmt.Printf("Cannot stat git binary %s\n", absGit)
62		td.Fatal(ctx, err)
63	}
64	skiaPath := filepath.Join(wd, "skia")
65
66	// When running on the CI, there is not a git checkout here, so we make a temp one.
67	if !*local {
68		if err := gitInit(ctx, absGit, skiaPath); err != nil {
69			td.Fatal(ctx, err)
70		}
71	}
72
73	opts := bazel.BazelOptions{
74		CachePath: *bazelFlags.CacheDir,
75	}
76	if err := bazel.EnsureBazelRCFile(ctx, opts); err != nil {
77		td.Fatal(ctx, err)
78	}
79
80	if err := bazelRun(ctx, skiaPath, "//:gofmt", append(*bazelFlags.AdditionalArgs, "--", "-s", "-w", ".")...); err != nil {
81		td.Fatal(ctx, err)
82	}
83
84	if err := bazelRun(ctx, skiaPath, "//:errcheck", append(*bazelFlags.AdditionalArgs, "--", "-ignore", ":Close", "go.skia.org/skia/...")...); err != nil {
85		td.Fatal(ctx, err)
86	}
87
88	if err := checkGitDiff(ctx, absGit, skiaPath); err != nil {
89		td.Fatal(ctx, err)
90	}
91
92	if !*local {
93		if err := common.BazelCleanIfLowDiskSpace(ctx, *bazelFlags.CacheDir, skiaPath, "bazelisk"); err != nil {
94			td.Fatal(ctx, err)
95		}
96	}
97}
98
99// bazelRun runs the given Bazel label from the Skia path with any given args using Bazelisk.
100func bazelRun(ctx context.Context, skiaPath, label string, args ...string) error {
101	return td.Do(ctx, td.Props("bazel run "+label), func(ctx context.Context) error {
102		runCmd := &sk_exec.Command{
103			Name:       "bazelisk",
104			Args:       append([]string{"run", label}, args...),
105			InheritEnv: true, // Need to make sure bazelisk is on the path
106			Dir:        skiaPath,
107			LogStdout:  true,
108			LogStderr:  true,
109		}
110		_, err := sk_exec.RunCommand(ctx, runCmd)
111		if err != nil {
112			return err
113		}
114		return nil
115	})
116}
117
118// gitInit creates a temporary git repository with all files in the Skia path. This allows the later
119// git diff call to work properly. This is necessary because our current Swarming setup does not
120// include the .git folder when copying down files.
121func gitInit(ctx context.Context, gitPath, skiaPath string) error {
122	step := fmt.Sprintf("Setting git baseline in %s", skiaPath)
123	err := td.Do(ctx, td.Props(step), func(ctx context.Context) error {
124		initCmd := &sk_exec.Command{
125			Name:       gitPath,
126			Args:       []string{"init"},
127			InheritEnv: false,
128			Dir:        skiaPath,
129			LogStdout:  true,
130			LogStderr:  true,
131		}
132		if _, err := sk_exec.RunCommand(ctx, initCmd); err != nil {
133			return err
134		}
135		addCmd := &sk_exec.Command{
136			Name:       gitPath,
137			Args:       []string{"add", "."},
138			InheritEnv: false,
139			Dir:        skiaPath,
140			LogStdout:  true,
141			LogStderr:  true,
142		}
143		if _, err := sk_exec.RunCommand(ctx, addCmd); err != nil {
144			return err
145		}
146		commitCmd := &sk_exec.Command{
147			Name:       gitPath,
148			Args:       []string{"commit", "-m", "baseline commit"},
149			InheritEnv: false,
150			Dir:        skiaPath,
151			LogStdout:  true,
152			LogStderr:  true,
153		}
154		if _, err := sk_exec.RunCommand(ctx, commitCmd); err != nil {
155			return err
156		}
157		return nil
158	})
159	return err
160}
161
162// checkGitDiff runs git diff and returns error if the diff is non-empty.
163func checkGitDiff(ctx context.Context, gitPath, skiaPath string) error {
164	step := fmt.Sprintf("git diff %s", skiaPath)
165	return td.Do(ctx, td.Props(step), func(ctx context.Context) error {
166		runCmd := &sk_exec.Command{
167			Name:       gitPath,
168			Args:       []string{"diff", "--no-ext-diff"},
169			InheritEnv: false,
170			Dir:        skiaPath,
171			LogStdout:  true,
172			LogStderr:  true,
173		}
174		rv, err := sk_exec.RunCommand(ctx, runCmd)
175		if err != nil {
176			return err
177		}
178		if strings.TrimSpace(rv) != "" {
179			return fmt.Errorf("Non-empty diff:\n" + rv)
180		}
181		return nil
182	})
183}
184