Compare commits
1 Commits
72bd5960a7
...
5e4d61d6d6
Author | SHA1 | Date | |
---|---|---|---|
5e4d61d6d6 |
@ -1,6 +0,0 @@
|
||||
package build
|
||||
|
||||
var (
|
||||
Name = "treefmt"
|
||||
Version = "v0.0.1+dev"
|
||||
)
|
4
cache/cache.go → internal/cache/cache.go
vendored
4
cache/cache.go → internal/cache/cache.go
vendored
@ -9,9 +9,9 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/format"
|
||||
"git.numtide.com/numtide/treefmt/walk"
|
||||
"git.numtide.com/numtide/treefmt/internal/walk"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/internal/format"
|
||||
"github.com/charmbracelet/log"
|
||||
|
||||
"github.com/adrg/xdg"
|
@ -1,7 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"git.numtide.com/numtide/treefmt/walk"
|
||||
"git.numtide.com/numtide/treefmt/internal/walk"
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
@ -11,7 +11,6 @@ var Cli = Format{}
|
||||
type Format struct {
|
||||
AllowMissingFormatter bool `default:"false" help:"Do not exit with error if a configured formatter is missing"`
|
||||
WorkingDirectory kong.ChangeDirFlag `default:"." short:"C" help:"Run as if treefmt was started in the specified working directory instead of the current working directory"`
|
||||
NoCache bool `help:"Ignore the evaluation cache entirely. Useful for CI"`
|
||||
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"`
|
||||
FailOnChange bool `help:"Exit with error if any changes were made. Useful for CI."`
|
||||
@ -19,7 +18,6 @@ type Format struct {
|
||||
TreeRoot string `type:"existingdir" default:"."`
|
||||
Walk walk.Type `enum:"auto,git,filesystem" default:"auto" help:"The method used to traverse the files within --tree-root. Currently supports 'auto', 'git' or 'filesystem'."`
|
||||
Verbosity int `name:"verbose" short:"v" type:"counter" default:"0" env:"LOG_LEVEL" help:"Set the verbosity of logs e.g. -vv."`
|
||||
Version bool `name:"version" short:"V" help:"Print version"`
|
||||
|
||||
Paths []string `name:"paths" arg:"" type:"path" optional:"" help:"Paths to format. Defaults to formatting the whole tree."`
|
||||
Stdin bool `help:"Format the context passed in via stdin"`
|
@ -5,17 +5,18 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/cache"
|
||||
"git.numtide.com/numtide/treefmt/config"
|
||||
format2 "git.numtide.com/numtide/treefmt/format"
|
||||
"git.numtide.com/numtide/treefmt/walk"
|
||||
"git.numtide.com/numtide/treefmt/internal/walk"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/internal/config"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/internal/cache"
|
||||
"git.numtide.com/numtide/treefmt/internal/format"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@ -46,7 +47,7 @@ func (f *Format) Run() error {
|
||||
return fmt.Errorf("%w: failed to read config file", err)
|
||||
}
|
||||
|
||||
globalExcludes, err := format2.CompileGlobs(cfg.Global.Excludes)
|
||||
globalExcludes, err := format.CompileGlobs(cfg.Global.Excludes)
|
||||
|
||||
// create optional formatter filter set
|
||||
formatterSet := make(map[string]bool)
|
||||
@ -67,7 +68,7 @@ func (f *Format) Run() error {
|
||||
}
|
||||
}
|
||||
|
||||
formatters := make(map[string]*format2.Formatter)
|
||||
formatters := make(map[string]*format.Formatter)
|
||||
|
||||
// detect broken dependencies
|
||||
for name, formatterCfg := range cfg.Formatters {
|
||||
@ -114,8 +115,8 @@ func (f *Format) Run() error {
|
||||
continue
|
||||
}
|
||||
|
||||
formatter, err := format2.NewFormatter(name, formatterCfg, globalExcludes)
|
||||
if errors.Is(err, format2.ErrCommandNotFound) && Cli.AllowMissingFormatter {
|
||||
formatter, err := format.NewFormatter(name, formatterCfg, globalExcludes)
|
||||
if errors.Is(err, format.ErrCommandNotFound) && Cli.AllowMissingFormatter {
|
||||
l.Debugf("formatter not found: %v", name)
|
||||
continue
|
||||
} else if err != nil {
|
||||
@ -146,7 +147,7 @@ func (f *Format) Run() error {
|
||||
//
|
||||
completedCh := make(chan string, 1024)
|
||||
|
||||
ctx = format2.SetCompletedChannel(ctx, completedCh)
|
||||
ctx = format.SetCompletedChannel(ctx, completedCh)
|
||||
|
||||
//
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
@ -169,20 +170,6 @@ func (f *Format) Run() error {
|
||||
|
||||
var changes int
|
||||
|
||||
processBatch := func() error {
|
||||
if Cli.NoCache {
|
||||
changes += len(batch)
|
||||
} else {
|
||||
count, err := cache.Update(batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
changes += count
|
||||
}
|
||||
batch = batch[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
@ -194,17 +181,22 @@ func (f *Format) Run() error {
|
||||
}
|
||||
batch = append(batch, path)
|
||||
if len(batch) == batchSize {
|
||||
if err = processBatch(); err != nil {
|
||||
count, err := cache.Update(batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
changes += count
|
||||
batch = batch[:0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// final flush
|
||||
if err = processBatch(); err != nil {
|
||||
count, err := cache.Update(batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
changes += count
|
||||
|
||||
if Cli.FailOnChange && changes != 0 {
|
||||
return ErrFailOnChange
|
||||
@ -261,22 +253,6 @@ func (f *Format) Run() error {
|
||||
}
|
||||
|
||||
defer close(pathsCh)
|
||||
|
||||
if Cli.NoCache {
|
||||
return walker.Walk(ctx, func(path string, info fs.FileInfo, err error) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
// ignore symlinks and directories
|
||||
if !(info.IsDir() || info.Mode()&os.ModeSymlink == os.ModeSymlink) {
|
||||
pathsCh <- path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return cache.ChangeSet(ctx, walker, pathsCh)
|
||||
})
|
||||
|
@ -8,15 +8,15 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
config2 "git.numtide.com/numtide/treefmt/config"
|
||||
"git.numtide.com/numtide/treefmt/format"
|
||||
"git.numtide.com/numtide/treefmt/test"
|
||||
"git.numtide.com/numtide/treefmt/internal/config"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/internal/test"
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/cache"
|
||||
"github.com/go-git/go-git/v5/storage/filesystem"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/internal/format"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -26,8 +26,8 @@ func TestAllowMissingFormatter(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
configPath := tempDir + "/treefmt.toml"
|
||||
|
||||
test.WriteConfig(t, configPath, config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
test.WriteConfig(t, configPath, config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"foo-fmt": {
|
||||
Command: "foo-fmt",
|
||||
},
|
||||
@ -47,8 +47,8 @@ func TestDependencyCycle(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
configPath := tempDir + "/treefmt.toml"
|
||||
|
||||
test.WriteConfig(t, configPath, config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
test.WriteConfig(t, configPath, config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"a": {Command: "echo", Before: "b"},
|
||||
"b": {Command: "echo", Before: "c"},
|
||||
"c": {Command: "echo", Before: "a"},
|
||||
@ -68,8 +68,8 @@ func TestSpecifyingFormatters(t *testing.T) {
|
||||
tempDir := test.TempExamples(t)
|
||||
configPath := tempDir + "/treefmt.toml"
|
||||
|
||||
test.WriteConfig(t, configPath, config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
test.WriteConfig(t, configPath, config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"elm": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*.elm"},
|
||||
@ -117,8 +117,8 @@ func TestIncludesAndExcludes(t *testing.T) {
|
||||
configPath := tempDir + "/echo.toml"
|
||||
|
||||
// test without any excludes
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"echo": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*"},
|
||||
@ -189,8 +189,8 @@ func TestCache(t *testing.T) {
|
||||
configPath := tempDir + "/echo.toml"
|
||||
|
||||
// test without any excludes
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"echo": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*"},
|
||||
@ -206,20 +206,6 @@ func TestCache(t *testing.T) {
|
||||
out, err = cmd(t, "--config-file", configPath, "--tree-root", tempDir)
|
||||
as.NoError(err)
|
||||
as.Contains(string(out), "0 files changed")
|
||||
|
||||
// clear cache
|
||||
out, err = cmd(t, "--config-file", configPath, "--tree-root", tempDir, "-c")
|
||||
as.NoError(err)
|
||||
as.Contains(string(out), fmt.Sprintf("%d files changed", 29))
|
||||
|
||||
out, err = cmd(t, "--config-file", configPath, "--tree-root", tempDir)
|
||||
as.NoError(err)
|
||||
as.Contains(string(out), "0 files changed")
|
||||
|
||||
// no cache
|
||||
out, err = cmd(t, "--config-file", configPath, "--tree-root", tempDir, "--no-cache")
|
||||
as.NoError(err)
|
||||
as.Contains(string(out), fmt.Sprintf("%d files changed", 29))
|
||||
}
|
||||
|
||||
func TestChangeWorkingDirectory(t *testing.T) {
|
||||
@ -238,8 +224,8 @@ func TestChangeWorkingDirectory(t *testing.T) {
|
||||
configPath := tempDir + "/treefmt.toml"
|
||||
|
||||
// test without any excludes
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"echo": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*"},
|
||||
@ -263,8 +249,8 @@ func TestFailOnChange(t *testing.T) {
|
||||
configPath := tempDir + "/echo.toml"
|
||||
|
||||
// test without any excludes
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"echo": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*"},
|
||||
@ -299,8 +285,8 @@ func TestBustCacheOnFormatterChange(t *testing.T) {
|
||||
as.NoError(os.Setenv("PATH", binPath+":"+os.Getenv("PATH")))
|
||||
|
||||
// start with 2 formatters
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"python": {
|
||||
Command: "black",
|
||||
Includes: []string{"*.py"},
|
||||
@ -344,7 +330,7 @@ func TestBustCacheOnFormatterChange(t *testing.T) {
|
||||
as.Contains(string(out), "0 files changed")
|
||||
|
||||
// add go formatter
|
||||
cfg.Formatters["go"] = &config2.Formatter{
|
||||
cfg.Formatters["go"] = &config.Formatter{
|
||||
Command: "gofmt",
|
||||
Options: []string{"-w"},
|
||||
Includes: []string{"*.go"},
|
||||
@ -394,8 +380,8 @@ func TestGitWorktree(t *testing.T) {
|
||||
configPath := filepath.Join(tempDir, "/treefmt.toml")
|
||||
|
||||
// basic config
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"echo": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*"},
|
||||
@ -449,8 +435,8 @@ func TestOrderingFormatters(t *testing.T) {
|
||||
configPath := path.Join(tempDir, "treefmt.toml")
|
||||
|
||||
// missing child
|
||||
test.WriteConfig(t, configPath, config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
test.WriteConfig(t, configPath, config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"hs-a": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*.hs"},
|
||||
@ -463,8 +449,8 @@ func TestOrderingFormatters(t *testing.T) {
|
||||
as.ErrorContains(err, "formatter hs-a is before hs-b but config for hs-b was not found")
|
||||
|
||||
// multiple roots
|
||||
test.WriteConfig(t, configPath, config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
test.WriteConfig(t, configPath, config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"hs-a": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*.hs"},
|
||||
@ -515,8 +501,8 @@ func TestPathsArg(t *testing.T) {
|
||||
as.NoError(os.Chdir(tempDir))
|
||||
|
||||
// basic config
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"echo": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*"},
|
||||
@ -559,8 +545,8 @@ func TestStdIn(t *testing.T) {
|
||||
as.NoError(os.Chdir(tempDir))
|
||||
|
||||
// basic config
|
||||
cfg := config2.Config{
|
||||
Formatters: map[string]*config2.Formatter{
|
||||
cfg := config.Config{
|
||||
Formatters: map[string]*config.Formatter{
|
||||
"echo": {
|
||||
Command: "echo",
|
||||
Includes: []string{"*"},
|
@ -7,8 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/test"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/internal/test"
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
@ -9,7 +9,7 @@ import (
|
||||
func TestReadConfigFile(t *testing.T) {
|
||||
as := require.New(t)
|
||||
|
||||
cfg, err := ReadFile("../test/treefmt.toml")
|
||||
cfg, err := ReadFile("../../test/treefmt.toml")
|
||||
as.NoError(err, "failed to read config file")
|
||||
|
||||
as.NotNil(cfg)
|
@ -7,7 +7,7 @@ import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/config"
|
||||
"git.numtide.com/numtide/treefmt/internal/config"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/gobwas/glob"
|
@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/config"
|
||||
"git.numtide.com/numtide/treefmt/internal/config"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
cp "github.com/otiai10/copy"
|
||||
@ -25,7 +25,7 @@ func WriteConfig(t *testing.T, path string, cfg config.Config) {
|
||||
|
||||
func TempExamples(t *testing.T) string {
|
||||
tempDir := t.TempDir()
|
||||
require.NoError(t, cp.Copy("../test/examples", tempDir), "failed to copy test data to temp dir")
|
||||
require.NoError(t, cp.Copy("../../test/examples", tempDir), "failed to copy test data to temp dir")
|
||||
return tempDir
|
||||
}
|
||||
|
17
main.go
17
main.go
@ -1,26 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.numtide.com/numtide/treefmt/build"
|
||||
"git.numtide.com/numtide/treefmt/cli"
|
||||
"git.numtide.com/numtide/treefmt/internal/cli"
|
||||
"github.com/alecthomas/kong"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// This is to maintain compatibility with 1.0.0 which allows specifying the version with a `treefmt --version` flag
|
||||
// on the 'default' command. With Kong it would be better to have `treefmt version` so it would be treated as a
|
||||
// separate command. As it is, we would need to weaken some of the `existingdir` and `existingfile` checks kong is
|
||||
// doing for us in the default format command.
|
||||
for _, arg := range os.Args {
|
||||
if arg == "--version" || arg == "-V" {
|
||||
fmt.Printf("%s %s\n", build.Name, build.Version)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx := kong.Parse(&cli.Cli)
|
||||
ctx.FatalIfErrorf(ctx.Run())
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
packages = rec {
|
||||
treefmt = inputs'.gomod2nix.legacyPackages.buildGoApplication rec {
|
||||
pname = "treefmt";
|
||||
version = "2.0.0+dev";
|
||||
version = "0.0.1+dev";
|
||||
|
||||
# ensure we are using the same version of go to build with
|
||||
inherit (pkgs) go;
|
||||
|
Reference in New Issue
Block a user