diff --git a/cmd/task/task.go b/cmd/task/task.go index 16c8ff19..e59273b4 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + fp "path/filepath" "strings" "github.com/spf13/pflag" @@ -13,6 +14,7 @@ import ( "github.com/go-task/task/v3/args" "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/internal/flags" "github.com/go-task/task/v3/internal/logger" "github.com/go-task/task/v3/internal/sort" @@ -77,18 +79,28 @@ func run() error { if err != nil { return err } - - if err := task.InitTaskfile(os.Stdout, wd); err != nil { + args, _, err := getArgs() + if err != nil { + return err + } + path := wd + if len(args) > 0 { + name := args[0] + if filepathext.IsExtOnly(name) { + name = filepathext.SmartJoin(fp.Dir(name), "Taskfile"+fp.Ext(name)) + } + path = filepathext.SmartJoin(wd, name) + } + finalPath, err := task.InitTaskfile(os.Stdout, path) + if err != nil { return err } - if !flags.Silent { if flags.Verbose { log.Outf(logger.Default, "%s\n", task.DefaultTaskfile) } - log.Outf(logger.Green, "%s created in the current directory\n", task.DefaultTaskFilename) + log.Outf(logger.Green, "Taskfile created: %s\n", filepathext.TryAbsToRel(finalPath)) } - return nil } diff --git a/init.go b/init.go index 69c31b91..04f0c0b7 100644 --- a/init.go +++ b/init.go @@ -24,17 +24,29 @@ tasks: const DefaultTaskFilename = "Taskfile.yml" -// InitTaskfile creates a new Taskfile -func InitTaskfile(w io.Writer, dir string) error { - f := filepathext.SmartJoin(dir, DefaultTaskFilename) - - if _, err := os.Stat(f); err == nil { - return errors.TaskfileAlreadyExistsError{} +// InitTaskfile creates a new Taskfile at path. +// +// path can be either a file path or a directory path. +// If path is a directory, path/Taskfile.yml will be created. +// +// The final file path is always returned and may be different from the input path. +func InitTaskfile(w io.Writer, path string) (string, error) { + fi, err := os.Stat(path) + if err == nil && !fi.IsDir() { + return path, errors.TaskfileAlreadyExistsError{} } - if err := os.WriteFile(f, []byte(DefaultTaskfile), 0o644); err != nil { - return err + if fi != nil && fi.IsDir() { + path = filepathext.SmartJoin(path, DefaultTaskFilename) + // path was a directory, so check if Taskfile.yml exists in it + if _, err := os.Stat(path); err == nil { + return path, errors.TaskfileAlreadyExistsError{} + } } - return nil + if err := os.WriteFile(path, []byte(DefaultTaskfile), 0o644); err != nil { + return path, err + } + + return path, nil } diff --git a/init_test.go b/init_test.go index 5f4ccc79..ce401c08 100644 --- a/init_test.go +++ b/init_test.go @@ -9,7 +9,7 @@ import ( "github.com/go-task/task/v3/internal/filepathext" ) -func TestInit(t *testing.T) { +func TestInitDir(t *testing.T) { t.Parallel() const dir = "testdata/init" @@ -20,12 +20,34 @@ func TestInit(t *testing.T) { t.Errorf("Taskfile.yml should not exist") } - if err := task.InitTaskfile(io.Discard, dir); err != nil { + if _, err := task.InitTaskfile(io.Discard, dir); err != nil { t.Error(err) } if _, err := os.Stat(file); err != nil { t.Errorf("Taskfile.yml should exist") } + + _ = os.Remove(file) +} + +func TestInitFile(t *testing.T) { + t.Parallel() + + const dir = "testdata/init" + file := filepathext.SmartJoin(dir, "Tasks.yml") + + _ = os.Remove(file) + if _, err := os.Stat(file); err == nil { + t.Errorf("Tasks.yml should not exist") + } + + if _, err := task.InitTaskfile(io.Discard, file); err != nil { + t.Error(err) + } + + if _, err := os.Stat(file); err != nil { + t.Errorf("Tasks.yml should exist") + } _ = os.Remove(file) } diff --git a/internal/filepathext/filepathext.go b/internal/filepathext/filepathext.go index db64695c..f2a1ba15 100644 --- a/internal/filepathext/filepathext.go +++ b/internal/filepathext/filepathext.go @@ -55,3 +55,9 @@ func TryAbsToRel(abs string) string { return rel } + +// IsExtOnly checks whether path points to a file with no name but with +// an extension, i.e. ".yaml" +func IsExtOnly(path string) bool { + return filepath.Base(path) == filepath.Ext(path) +} diff --git a/website/docs/reference/cli.mdx b/website/docs/reference/cli.mdx index e1963418..4afe84b8 100644 --- a/website/docs/reference/cli.mdx +++ b/website/docs/reference/cli.mdx @@ -16,10 +16,10 @@ task [--flags] [tasks...] [-- CLI_ARGS...] If `--` is given, all remaining arguments will be assigned to a special `CLI_ARGS` variable -## Flags - ::: +## Flags + | Short | Flag | Type | Default | Description | | ----- | --------------------------- | -------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c` | `--color` | `bool` | `true` | Colored output. Enabled by default. Set flag to `false` or use `NO_COLOR=1` to disable. | @@ -45,7 +45,7 @@ If `--` is given, all remaining arguments will be assigned to a special | `-y` | `--yes` | `bool` | `false` | Assume "yes" as answer to all prompts. | | | `--status` | `bool` | `false` | Exits with non-zero exit code if any of the given tasks is not up-to-date. | | | `--summary` | `bool` | `false` | Show summary about a task. | -| `-t` | `--taskfile` | `string` | `Taskfile.yml` or `Taskfile.yaml` | | +| `-t` | `--taskfile` | `string` | | Taskfile path to run.
Check the list of default filenames [here](../usage/#supported-file-names). | | `-v` | `--verbose` | `bool` | `false` | Enables verbose mode. | | | `--version` | `bool` | `false` | Show Task version. | | `-w` | `--watch` | `bool` | `false` | Enables watch of the given task.