[cli] Add `shac fmt` command
Identical to `shac fix`, but only runs and applies fixes from checks
annotated with `formatter = True`.
Change-Id: Ic0c963203dc098b1618fb7f6e1f8b165dd444959
Reviewed-on: https://fuchsia-review.googlesource.com/c/shac-project/shac/+/881734
Commit-Queue: Oliver Newman <olivernewman@google.com>
Reviewed-by: Marc-Antoine Ruel <maruel@google.com>
diff --git a/internal/cli/fix.go b/internal/cli/fix.go
index d333d1f..064b48a 100644
--- a/internal/cli/fix.go
+++ b/internal/cli/fix.go
@@ -31,7 +31,7 @@
}
func (*fixCmd) Description() string {
- return "Run checks and make suggested fixes."
+ return "Run non-formatter checks and make suggested fixes."
}
func (c *fixCmd) SetFlags(f *flag.FlagSet) {
@@ -43,5 +43,6 @@
return errors.New("unsupported arguments")
}
o := c.options()
+ o.Filter = engine.OnlyNonFormatters
return engine.Fix(ctx, &o)
}
diff --git a/internal/cli/fmt.go b/internal/cli/fmt.go
new file mode 100644
index 0000000..019249b
--- /dev/null
+++ b/internal/cli/fmt.go
@@ -0,0 +1,48 @@
+// Copyright 2023 The Shac Authors
+//
+// 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 cli
+
+import (
+ "context"
+ "errors"
+
+ flag "github.com/spf13/pflag"
+ "go.fuchsia.dev/shac-project/shac/internal/engine"
+)
+
+type fmtCmd struct {
+ commandBase
+}
+
+func (*fmtCmd) Name() string {
+ return "fmt"
+}
+
+func (*fmtCmd) Description() string {
+ return "Auto-format files."
+}
+
+func (c *fmtCmd) SetFlags(f *flag.FlagSet) {
+ c.commandBase.SetFlags(f)
+}
+
+func (c *fmtCmd) Execute(ctx context.Context, args []string) error {
+ if len(args) != 0 {
+ return errors.New("unsupported arguments")
+ }
+ o := c.options()
+ o.Filter = engine.OnlyFormatters
+ return engine.Fix(ctx, &o)
+}
diff --git a/internal/cli/main.go b/internal/cli/main.go
index f4c2e19..115de4c 100644
--- a/internal/cli/main.go
+++ b/internal/cli/main.go
@@ -68,7 +68,10 @@
ctx := context.Background()
subcommands := [...]subcommand{
+ // Ordered roughly by importance, because ordering here corresponds to
+ // the order in which subcommands will be listed in `shac help`.
&checkCmd{},
+ &fmtCmd{},
&fixCmd{},
&docCmd{},
&helpCmd{},
diff --git a/internal/cli/main_test.go b/internal/cli/main_test.go
index e744b96..9c2afb8 100644
--- a/internal/cli/main_test.go
+++ b/internal/cli/main_test.go
@@ -31,6 +31,7 @@
{[]string{"shac", "--help"}, "Usage of shac:\n"},
{[]string{"shac", "check", "--help"}, "Usage of shac check:\n"},
{[]string{"shac", "fix", "--help"}, "Usage of shac fix:\n"},
+ {[]string{"shac", "fmt", "--help"}, "Usage of shac fmt:\n"},
{[]string{"shac", "doc", "--help"}, "Usage of shac doc:\n"},
}
for i, line := range data {
diff --git a/internal/engine/run.go b/internal/engine/run.go
index b801600..7832c95 100644
--- a/internal/engine/run.go
+++ b/internal/engine/run.go
@@ -71,6 +71,21 @@
_ struct{}
}
+// CheckFilter controls which checks get run by `Run`. It returns true for
+// checks that should be run, false for checks that should be skipped.
+type CheckFilter func(registeredCheck) bool
+
+// OnlyFormatters causes only checks marked with `formatter = True` to be run.
+func OnlyFormatters(c registeredCheck) bool {
+ return c.formatter
+}
+
+// OnlyNonFormatters causes only checks *not* marked with `formatter = True` to
+// be run.
+func OnlyNonFormatters(c registeredCheck) bool {
+ return !c.formatter
+}
+
// Level is one of "notice", "warning" or "error".
//
// A check is only considered failed if it emits at least one finding with
@@ -132,6 +147,8 @@
AllFiles bool
// Recurse tells the engine to run all Main files found in subdirectories.
Recurse bool
+ // Filter controls which checks run.
+ Filter CheckFilter
// main source file to run. Defaults to shac.star. Only used in unit tests.
main string
@@ -201,14 +218,14 @@
if err != nil {
return err
}
- err = runInner(ctx, root, tmpdir, main, o.Report, doc.AllowNetwork, doc.WritableRoot, o.Recurse, scm, packages)
+ err = runInner(ctx, root, tmpdir, main, o.Report, doc.AllowNetwork, doc.WritableRoot, o.Recurse, o.Filter, scm, packages)
if err2 := os.RemoveAll(tmpdir); err == nil {
err = err2
}
return err
}
-func runInner(ctx context.Context, root, tmpdir, main string, r Report, allowNetwork, writableRoot, recurse bool, scm scmCheckout, packages map[string]fs.FS) error {
+func runInner(ctx context.Context, root, tmpdir, main string, r Report, allowNetwork, writableRoot, recurse bool, filter CheckFilter, scm scmCheckout, packages map[string]fs.FS) error {
sb, err := sandbox.New(tmpdir)
if err != nil {
return err
@@ -244,6 +261,7 @@
r: r,
allowNetwork: allowNetwork,
writableRoot: writableRoot,
+ filter: filter,
main: main,
root: root,
subdir: d,
@@ -263,6 +281,7 @@
r: r,
allowNetwork: allowNetwork,
writableRoot: writableRoot,
+ filter: filter,
main: main,
root: root,
tmpdir: filepath.Join(tmpdir, "0"),
@@ -350,6 +369,8 @@
// Checks are executed sequentially after all Starlark code is loaded and not
// mutated. They run checks and emit results (results and comments).
checks []registeredCheck
+ // filter controls which checks run. If nil, all checks will run.
+ filter CheckFilter
// Set when fail() is called. This happens only during the first phase, thus
// no mutex is needed.
@@ -415,6 +436,9 @@
args := starlark.Tuple{getCtx(path.Join(s.root, s.subdir))}
args.Freeze()
for i := range s.checks {
+ if s.filter != nil && !s.filter(s.checks[i]) {
+ continue
+ }
i := i
ch <- func() error {
start := time.Now()