parent
b6405d0714
commit
9b84155265
1
go.mod
1
go.mod
|
@ -9,6 +9,7 @@ require (
|
||||||
github.com/charmbracelet/log v0.3.1
|
github.com/charmbracelet/log v0.3.1
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/juju/errors v1.0.0
|
github.com/juju/errors v1.0.0
|
||||||
|
github.com/otiai10/copy v1.14.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1
|
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||||
github.com/ztrue/shutdown v0.1.1
|
github.com/ztrue/shutdown v0.1.1
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -40,6 +40,10 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||||
|
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||||
|
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||||
|
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
|
||||||
|
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import "github.com/charmbracelet/log"
|
import (
|
||||||
|
"github.com/charmbracelet/log"
|
||||||
|
)
|
||||||
|
|
||||||
var Cli = Options{}
|
var Cli = Options{}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
AllowMissingFormatter bool `default:"false" help:"Do not exit with error if a configured formatter is missing"`
|
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"`
|
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"`
|
ConfigFile string `type:"existingfile" default:"./treefmt.toml"`
|
||||||
TreeRoot string `type:"existingdir" default:"."`
|
Formatters []string `help:"Specify formatters to apply. Defaults to all formatters"`
|
||||||
Verbosity int `name:"verbose" short:"v" type:"counter" default:"0" env:"LOG_LEVEL" help:"Set the verbosity of logs e.g. -vv"`
|
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:"."`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Options) ConfigureLogger() {
|
func (c *Options) Configure() {
|
||||||
log.SetReportTimestamp(false)
|
log.SetReportTimestamp(false)
|
||||||
|
|
||||||
if c.Verbosity == 0 {
|
if c.Verbosity == 0 {
|
||||||
|
|
|
@ -19,7 +19,7 @@ type Format struct{}
|
||||||
func (f *Format) Run() error {
|
func (f *Format) Run() error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
Cli.ConfigureLogger()
|
Cli.Configure()
|
||||||
|
|
||||||
l := log.WithPrefix("format")
|
l := log.WithPrefix("format")
|
||||||
|
|
||||||
|
@ -42,8 +42,34 @@ func (f *Format) Run() error {
|
||||||
return errors.Annotate(err, "failed to read config file")
|
return errors.Annotate(err, "failed to read config file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create optional formatter filter set
|
||||||
|
formatterSet := make(map[string]bool)
|
||||||
|
for _, name := range Cli.Formatters {
|
||||||
|
_, ok := cfg.Formatters[name]
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("formatter not found in config: %v", name)
|
||||||
|
}
|
||||||
|
formatterSet[name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
includeFormatter := func(name string) bool {
|
||||||
|
if len(formatterSet) == 0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
_, include := formatterSet[name]
|
||||||
|
return include
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// init formatters
|
// init formatters
|
||||||
for name, formatter := range cfg.Formatters {
|
for name, formatter := range cfg.Formatters {
|
||||||
|
if !includeFormatter(name) {
|
||||||
|
// remove this formatter
|
||||||
|
delete(cfg.Formatters, name)
|
||||||
|
l.Debugf("formatter %v is not in formatter list %v, skipping", name, Cli.Formatters)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
err = formatter.Init(name)
|
err = formatter.Init(name)
|
||||||
if err == format.ErrFormatterNotFound && Cli.AllowMissingFormatter {
|
if err == format.ErrFormatterNotFound && Cli.AllowMissingFormatter {
|
||||||
l.Debugf("formatter not found: %v", name)
|
l.Debugf("formatter not found: %v", name)
|
||||||
|
@ -126,7 +152,7 @@ func (f *Format) Run() error {
|
||||||
}
|
}
|
||||||
changes += count
|
changes += count
|
||||||
|
|
||||||
println(fmt.Sprintf("%v files changed in %v", changes, time.Now().Sub(start)))
|
fmt.Printf("%v files changed in %v", changes, time.Now().Sub(start))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.numtide.com/numtide/treefmt/internal/format"
|
"git.numtide.com/numtide/treefmt/internal/format"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
|
"github.com/juju/errors"
|
||||||
|
cp "github.com/otiai10/copy"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -37,10 +41,56 @@ func newKong(t *testing.T, cli interface{}, options ...kong.Option) *kong.Kong {
|
||||||
return parser
|
return parser
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCli(t *testing.T, args ...string) (*kong.Context, error) {
|
func tempFile(t *testing.T, path string) *os.File {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create temporary file: %v", err)
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmd(t *testing.T, args ...string) ([]byte, error) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// create a new kong context
|
||||||
p := newKong(t, &Cli)
|
p := newKong(t, &Cli)
|
||||||
return p.Parse(args)
|
ctx, err := p.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
tempOut := tempFile(t, filepath.Join(tempDir, "combined_output"))
|
||||||
|
|
||||||
|
// capture standard outputs before swapping them
|
||||||
|
stdout := os.Stdout
|
||||||
|
stderr := os.Stderr
|
||||||
|
|
||||||
|
// swap them temporarily
|
||||||
|
os.Stdout = tempOut
|
||||||
|
os.Stderr = tempOut
|
||||||
|
|
||||||
|
// run the command
|
||||||
|
if err = ctx.Run(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset and read the temporary output
|
||||||
|
if _, err = tempOut.Seek(0, 0); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "failed to reset temp output for reading")
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := io.ReadAll(tempOut)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotate(err, "failed to read temp output")
|
||||||
|
}
|
||||||
|
|
||||||
|
// swap outputs back
|
||||||
|
os.Stdout = stdout
|
||||||
|
os.Stderr = stderr
|
||||||
|
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAllowMissingFormatter(t *testing.T) {
|
func TestAllowMissingFormatter(t *testing.T) {
|
||||||
|
@ -57,12 +107,59 @@ func TestAllowMissingFormatter(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx, err := newCli(t, "--config-file", configPath, "--tree-root", tempDir)
|
_, err := cmd(t, "--config-file", configPath, "--tree-root", tempDir)
|
||||||
as.NoError(err)
|
as.ErrorIs(err, format.ErrFormatterNotFound)
|
||||||
as.Error(ctx.Run(), format.ErrFormatterNotFound)
|
|
||||||
|
|
||||||
ctx, err = newCli(t, "--config-file", configPath, "--tree-root", tempDir, "--allow-missing-formatter")
|
_, err = cmd(t, "--config-file", configPath, "--tree-root", tempDir, "--allow-missing-formatter")
|
||||||
as.NoError(err)
|
as.NoError(err)
|
||||||
|
}
|
||||||
as.NoError(ctx.Run())
|
|
||||||
|
func TestSpecifyingFormatters(t *testing.T) {
|
||||||
|
as := require.New(t)
|
||||||
|
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
configPath := tempDir + "/treefmt.toml"
|
||||||
|
|
||||||
|
as.NoError(cp.Copy("../../test/examples", tempDir), "failed to copy test data to temp dir")
|
||||||
|
|
||||||
|
writeConfig(t, configPath, format.Config{
|
||||||
|
Formatters: map[string]*format.Formatter{
|
||||||
|
"elm": {
|
||||||
|
Command: "echo",
|
||||||
|
Includes: []string{"*.elm"},
|
||||||
|
},
|
||||||
|
"nix": {
|
||||||
|
Command: "echo",
|
||||||
|
Includes: []string{"*.nix"},
|
||||||
|
},
|
||||||
|
"ruby": {
|
||||||
|
Command: "echo",
|
||||||
|
Includes: []string{"*.rb"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
out, err := cmd(t, "--clear-cache", "--config-file", configPath, "--tree-root", tempDir)
|
||||||
|
as.NoError(err)
|
||||||
|
as.Contains(string(out), "3 files changed")
|
||||||
|
|
||||||
|
out, err = cmd(t, "--clear-cache", "--config-file", configPath, "--tree-root", tempDir, "--formatters", "elm,nix")
|
||||||
|
as.NoError(err)
|
||||||
|
as.Contains(string(out), "2 files changed")
|
||||||
|
|
||||||
|
out, err = cmd(t, "--clear-cache", "--config-file", configPath, "--tree-root", tempDir, "--formatters", "ruby,nix")
|
||||||
|
as.NoError(err)
|
||||||
|
as.Contains(string(out), "2 files changed")
|
||||||
|
|
||||||
|
out, err = cmd(t, "--clear-cache", "--config-file", configPath, "--tree-root", tempDir, "--formatters", "nix")
|
||||||
|
as.NoError(err)
|
||||||
|
as.Contains(string(out), "1 files changed")
|
||||||
|
|
||||||
|
// test bad names
|
||||||
|
|
||||||
|
out, err = cmd(t, "--clear-cache", "--config-file", configPath, "--tree-root", tempDir, "--formatters", "foo")
|
||||||
|
as.Errorf(err, "formatter not found in config: foo")
|
||||||
|
|
||||||
|
out, err = cmd(t, "--clear-cache", "--config-file", configPath, "--tree-root", tempDir, "--formatters", "bar,foo")
|
||||||
|
as.Errorf(err, "formatter not found in config: bar")
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue
Block a user