feat: allow missing formatters #6
3
internal/cache/cache.go
vendored
3
internal/cache/cache.go
vendored
@ -61,6 +61,9 @@ func Open(treeRoot string, clean bool) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Close() error {
|
func Close() error {
|
||||||
|
if db == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return db.Close()
|
return db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,10 +5,11 @@ import "github.com/charmbracelet/log"
|
|||||||
var Cli = Options{}
|
var Cli = Options{}
|
||||||
|
|
||||||
type Options struct {
|
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"`
|
AllowMissingFormatter bool `default:"false" help:"Do not exit with error if a configured formatter is missing"`
|
||||||
ConfigFile string `type:"existingfile" default:"./treefmt.toml"`
|
ClearCache bool `short:"c" help:"Reset the evaluation cache. Use in case the cache is not precise enough"`
|
||||||
TreeRoot string `type:"existingdir" default:"."`
|
ConfigFile string `type:"existingfile" default:"./treefmt.toml"`
|
||||||
ClearCache bool `short:"c" help:"Reset the evaluation cache. Use in case the cache is not precise enough"`
|
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:"."`
|
Format Format `cmd:"" default:"."`
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,12 @@ func (f *Format) Run() error {
|
|||||||
|
|
||||||
// init formatters
|
// init formatters
|
||||||
for name, formatter := range cfg.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)
|
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)
|
return cache.ChangeSet(ctx, Cli.TreeRoot, pathsCh)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// shutdown.Listen(syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
return eg.Wait()
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
68
internal/cli/format_test.go
Normal file
68
internal/cli/format_test.go
Normal file
@ -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())
|
||||||
|
}
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfig(t *testing.T) {
|
func TestReadConfigFile(t *testing.T) {
|
||||||
as := require.New(t)
|
as := require.New(t)
|
||||||
|
|
||||||
cfg, err := ReadConfigFile("../../test/treefmt.toml")
|
cfg, err := ReadConfigFile("../../test/treefmt.toml")
|
||||||
@ -119,4 +119,9 @@ shfmt -i 2 -s -w "$@"
|
|||||||
as.Equal([]string{"fmt"}, terraform.Options)
|
as.Equal([]string{"fmt"}, terraform.Options)
|
||||||
as.Equal([]string{"*.tf"}, terraform.Includes)
|
as.Equal([]string{"*.tf"}, terraform.Includes)
|
||||||
as.Nil(terraform.Excludes)
|
as.Nil(terraform.Excludes)
|
||||||
|
|
||||||
|
// missing
|
||||||
|
foo, ok := cfg.Formatters["foo-fmt"]
|
||||||
|
as.True(ok, "foo formatter not found")
|
||||||
|
as.Equal("foo-fmt", foo.Command)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,10 @@ import (
|
|||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrFormatterNotFound = errors.ConstError("formatter not found")
|
||||||
|
)
|
||||||
|
|
||||||
type Formatter struct {
|
type Formatter struct {
|
||||||
Name string
|
Name string
|
||||||
Command string
|
Command string
|
||||||
@ -32,8 +36,13 @@ type Formatter struct {
|
|||||||
|
|
||||||
func (f *Formatter) Init(name string) error {
|
func (f *Formatter) Init(name string) error {
|
||||||
f.Name = name
|
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.inbox = make(chan string, 1024)
|
||||||
|
|
||||||
f.batchSize = 1024
|
f.batchSize = 1024
|
||||||
|
@ -79,4 +79,7 @@ includes = ["*.sh"]
|
|||||||
# see https://github.com/numtide/treefmt/issues/97
|
# see https://github.com/numtide/treefmt/issues/97
|
||||||
command = "terraform"
|
command = "terraform"
|
||||||
options = ["fmt"]
|
options = ["fmt"]
|
||||||
includes = ["*.tf"]
|
includes = ["*.tf"]
|
||||||
|
|
||||||
|
[formatter.foo-fmt]
|
||||||
|
command = "foo-fmt"
|
Reference in New Issue
Block a user