From 4c45d2aa7e322ca40b6d7c1f42bc933ed16ffbb2 Mon Sep 17 00:00:00 2001 From: Brian McGee Date: Sat, 23 Dec 2023 15:00:39 +0000 Subject: [PATCH] feat: allow missing formatters (#6) Closes #3 Reviewed-on: https://git.numtide.com/numtide/treefmt/pulls/6 Co-authored-by: Brian McGee Co-committed-by: Brian McGee --- internal/cache/cache.go | 3 ++ internal/cli/cli.go | 9 +++-- internal/cli/format.go | 9 ++++- internal/cli/format_test.go | 68 ++++++++++++++++++++++++++++++++++ internal/format/config_test.go | 7 +++- internal/format/format.go | 11 +++++- test/treefmt.toml | 5 ++- 7 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 internal/cli/format_test.go diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 2cacf78..30ba389 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -61,6 +61,9 @@ func Open(treeRoot string, clean bool) (err error) { } func Close() error { + if db == nil { + return nil + } return db.Close() } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 11431a8..6303029 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -5,10 +5,11 @@ import "github.com/charmbracelet/log" var Cli = Options{} type Options struct { - Verbosity int `name:"verbose" short:"v" type:"counter" default:"0" env:"LOG_LEVEL" help:"Set the verbosity of logs e.g. -vv"` - ConfigFile string `type:"existingfile" default:"./treefmt.toml"` - TreeRoot string `type:"existingdir" default:"."` - ClearCache bool `short:"c" help:"Reset the evaluation cache. Use in case the cache is not precise enough"` + AllowMissingFormatter bool `default:"false" help:"Do not exit with error if a configured formatter is missing"` + ClearCache bool `short:"c" help:"Reset the evaluation cache. Use in case the cache is not precise enough"` + ConfigFile string `type:"existingfile" default:"./treefmt.toml"` + TreeRoot string `type:"existingdir" default:"."` + Verbosity int `name:"verbose" short:"v" type:"counter" default:"0" env:"LOG_LEVEL" help:"Set the verbosity of logs e.g. -vv"` Format Format `cmd:"" default:"."` } diff --git a/internal/cli/format.go b/internal/cli/format.go index 83c1e5f..009d8da 100644 --- a/internal/cli/format.go +++ b/internal/cli/format.go @@ -44,7 +44,12 @@ func (f *Format) Run() error { // init formatters for name, formatter := range cfg.Formatters { - if err = formatter.Init(name); err != nil { + err = formatter.Init(name) + if err == format.ErrFormatterNotFound && Cli.AllowMissingFormatter { + l.Debugf("formatter not found: %v", name) + // remove this formatter + delete(cfg.Formatters, name) + } else if err != nil { return errors.Annotatef(err, "failed to initialise formatter: %v", name) } } @@ -155,5 +160,7 @@ func (f *Format) Run() error { return cache.ChangeSet(ctx, Cli.TreeRoot, pathsCh) }) + // shutdown.Listen(syscall.SIGINT, syscall.SIGTERM) + return eg.Wait() } diff --git a/internal/cli/format_test.go b/internal/cli/format_test.go new file mode 100644 index 0000000..44f0737 --- /dev/null +++ b/internal/cli/format_test.go @@ -0,0 +1,68 @@ +package cli + +import ( + "os" + "testing" + + "git.numtide.com/numtide/treefmt/internal/format" + "github.com/BurntSushi/toml" + "github.com/alecthomas/kong" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func writeConfig(t *testing.T, path string, cfg format.Config) { + t.Helper() + f, err := os.Create(path) + if err != nil { + t.Fatalf("failed to create a new config file: %v", err) + } + encoder := toml.NewEncoder(f) + if err = encoder.Encode(cfg); err != nil { + t.Fatalf("failed to write to config file: %v", err) + } +} + +func newKong(t *testing.T, cli interface{}, options ...kong.Option) *kong.Kong { + t.Helper() + options = append([]kong.Option{ + kong.Name("test"), + kong.Exit(func(int) { + t.Helper() + t.Fatalf("unexpected exit()") + }), + }, options...) + parser, err := kong.New(cli, options...) + assert.NoError(t, err) + return parser +} + +func newCli(t *testing.T, args ...string) (*kong.Context, error) { + t.Helper() + p := newKong(t, &Cli) + return p.Parse(args) +} + +func TestAllowMissingFormatter(t *testing.T) { + as := require.New(t) + + tempDir := t.TempDir() + configPath := tempDir + "/treefmt.toml" + + writeConfig(t, configPath, format.Config{ + Formatters: map[string]*format.Formatter{ + "foo-fmt": { + Command: "foo-fmt", + }, + }, + }) + + ctx, err := newCli(t, "--config-file", configPath, "--tree-root", tempDir) + as.NoError(err) + as.Error(ctx.Run(), format.ErrFormatterNotFound) + + ctx, err = newCli(t, "--config-file", configPath, "--tree-root", tempDir, "--allow-missing-formatter") + as.NoError(err) + + as.NoError(ctx.Run()) +} diff --git a/internal/format/config_test.go b/internal/format/config_test.go index 81115b6..1650aaf 100644 --- a/internal/format/config_test.go +++ b/internal/format/config_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestConfig(t *testing.T) { +func TestReadConfigFile(t *testing.T) { as := require.New(t) cfg, err := ReadConfigFile("../../test/treefmt.toml") @@ -119,4 +119,9 @@ shfmt -i 2 -s -w "$@" as.Equal([]string{"fmt"}, terraform.Options) as.Equal([]string{"*.tf"}, terraform.Includes) as.Nil(terraform.Excludes) + + // missing + foo, ok := cfg.Formatters["foo-fmt"] + as.True(ok, "foo formatter not found") + as.Equal("foo-fmt", foo.Command) } diff --git a/internal/format/format.go b/internal/format/format.go index 76a9483..404b433 100644 --- a/internal/format/format.go +++ b/internal/format/format.go @@ -10,6 +10,10 @@ import ( "github.com/juju/errors" ) +const ( + ErrFormatterNotFound = errors.ConstError("formatter not found") +) + type Formatter struct { Name string Command string @@ -32,8 +36,13 @@ type Formatter struct { func (f *Formatter) Init(name string) error { f.Name = name - f.log = log.WithPrefix("format | " + name) + // test if the formatter is available + if err := exec.Command(f.Command, "--help").Run(); err != nil { + return ErrFormatterNotFound + } + + f.log = log.WithPrefix("format | " + name) f.inbox = make(chan string, 1024) f.batchSize = 1024 diff --git a/test/treefmt.toml b/test/treefmt.toml index 9fac6e5..c78bd78 100644 --- a/test/treefmt.toml +++ b/test/treefmt.toml @@ -79,4 +79,7 @@ includes = ["*.sh"] # see https://github.com/numtide/treefmt/issues/97 command = "terraform" options = ["fmt"] -includes = ["*.tf"] \ No newline at end of file +includes = ["*.tf"] + +[formatter.foo-fmt] +command = "foo-fmt" \ No newline at end of file