diff --git a/.vscode/settings-sample.json b/.vscode/settings-sample.json index 1373475cf1..6584cd0774 100644 --- a/.vscode/settings-sample.json +++ b/.vscode/settings-sample.json @@ -5,6 +5,12 @@ "Taskfile.yaml", "taskfile.yml", "taskfile.yaml" + ], + "./website/src/public/schema-taskrc.json": [ + ".taskrc.yml", + ".taskrc.yaml", + "taskrc.yml", + "taskrc.yaml" ] }, "gopls": { diff --git a/CHANGELOG.md b/CHANGELOG.md index ead5170aa6..7876c8e674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ - Added support for configuring output flags (`--output`, `--output-group-begin`, `--output-group-end`, `--output-group-error-only`) via the `TASK_OUTPUT*` environment variables (#2873 by @liiight). +- Added a `--temp-dir` flag (with `TASK_TEMP_DIR` env var and `temp-dir` taskrc + config) to customise the directory where Task stores temporary files such as + checksums. Relative paths are resolved against the root Taskfile (#2891 by + @kjasn). ## v3.51.1 - 2026-05-16 diff --git a/executor.go b/executor.go index 43b8bacffe..783f18ed0d 100644 --- a/executor.go +++ b/executor.go @@ -29,6 +29,7 @@ type ( Dir string Entrypoint string TempDir TempDir + TempDirPath string Force bool ForceAll bool Insecure bool @@ -165,6 +166,22 @@ func (o *tempDirOption) ApplyToExecutor(e *Executor) { e.TempDir = o.tempDir } +// WithTempDirPath sets an unresolved path used to build [Executor.TempDir] +// during [Executor.Setup]. Relative paths are resolved from the root Taskfile +// directory. Use [WithTempDir] when the remote and fingerprint directories have +// already been resolved. +func WithTempDirPath(path string) ExecutorOption { + return &tempDirPathOption{path: path} +} + +type tempDirPathOption struct { + path string +} + +func (o *tempDirPathOption) ApplyToExecutor(e *Executor) { + e.TempDirPath = o.path +} + // WithForce ensures that the [Executor] always runs a task, even when // fingerprinting or prompts would normally stop it. func WithForce(force bool) ExecutorOption { diff --git a/internal/flags/flags.go b/internal/flags/flags.go index bfae0eb901..f1bf6c767f 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -87,6 +87,7 @@ var ( Cert string CertKey string Interactive bool + TempDir string ) func init() { @@ -143,6 +144,7 @@ func init() { pflag.BoolVarP(&ExitCode, "exit-code", "x", false, "Pass-through the exit code of the task command.") pflag.StringVarP(&Dir, "dir", "d", "", "Sets the directory in which Task will execute and look for a Taskfile.") pflag.StringVarP(&Entrypoint, "taskfile", "t", "", `Choose which Taskfile to run. Defaults to "Taskfile.yml".`) + pflag.StringVar(&TempDir, "temp-dir", getConfig(config, "TEMP_DIR", func() *string { return config.TempDir }, ""), "Sets the directory used to store Task temporary files, such as checksums. Relative paths are relative to the root Taskfile.") pflag.StringVarP(&Output.Name, "output", "o", getConfig(config, "OUTPUT", func() *string { return nil }, ""), "Sets output style: [interleaved|group|prefixed].") pflag.StringVar(&Output.Group.Begin, "output-group-begin", getConfig(config, "OUTPUT_GROUP_BEGIN", func() *string { return nil }, ""), "Message template to print before a task's grouped output.") pflag.StringVar(&Output.Group.End, "output-group-end", getConfig(config, "OUTPUT_GROUP_END", func() *string { return nil }, ""), "Message template to print after a task's grouped output.") @@ -308,6 +310,7 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) { task.WithTaskSorter(sorter), task.WithVersionCheck(true), task.WithFailfast(Failfast), + task.WithTempDirPath(TempDir), ) } diff --git a/setup.go b/setup.go index d9f57d461c..f24f8b8eb3 100644 --- a/setup.go +++ b/setup.go @@ -1,6 +1,7 @@ package task import ( + "cmp" "context" "fmt" "os" @@ -13,7 +14,6 @@ import ( "github.com/sajari/fuzzy" "github.com/go-task/task/v3/errors" - "github.com/go-task/task/v3/internal/env" "github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/internal/logger" @@ -133,13 +133,9 @@ func (e *Executor) setupTempDir() error { return nil } - tempDir := env.GetTaskEnv("TEMP_DIR") - if tempDir == "" { - e.TempDir = TempDir{ - Remote: filepathext.SmartJoin(e.Dir, ".task"), - Fingerprint: filepathext.SmartJoin(e.Dir, ".task"), - } - } else if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") { + // e.TempDirPath carries the resolved CLI precedence (flag > TASK_TEMP_DIR > taskrc). + tempDir := cmp.Or(e.TempDirPath, ".task") + if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") { tempDir, err := execext.ExpandLiteral(tempDir) if err != nil { return err diff --git a/taskrc/ast/taskrc.go b/taskrc/ast/taskrc.go index d1f5d3a43b..895b8f7ee8 100644 --- a/taskrc/ast/taskrc.go +++ b/taskrc/ast/taskrc.go @@ -19,6 +19,7 @@ type TaskRC struct { Interactive *bool `yaml:"interactive"` Remote Remote `yaml:"remote"` Failfast bool `yaml:"failfast"` + TempDir *string `yaml:"temp-dir"` Experiments map[string]int `yaml:"experiments"` } @@ -70,4 +71,5 @@ func (t *TaskRC) Merge(other *TaskRC) { t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency) t.Interactive = cmp.Or(other.Interactive, t.Interactive) t.Failfast = cmp.Or(other.Failfast, t.Failfast) + t.TempDir = cmp.Or(other.TempDir, t.TempDir) } diff --git a/taskrc/taskrc_test.go b/taskrc/taskrc_test.go index 902f65def9..dde9f9c58c 100644 --- a/taskrc/taskrc_test.go +++ b/taskrc/taskrc_test.go @@ -112,6 +112,40 @@ func TestGetConfig_OnlyLocal(t *testing.T) { //nolint:paralleltest // cannot run }, cfg) } +func TestGetConfig_TempDir(t *testing.T) { //nolint:paralleltest // cannot run in parallel + _, _, localDir := setupDirs(t) + + writeFile(t, localDir, ".taskrc.yml", ` +temp-dir: .task-cache +`) + + cfg, err := GetConfig(localDir) + require.NoError(t, err) + require.NotNil(t, cfg) + require.NotNil(t, cfg.TempDir) + assert.Equal(t, ".task-cache", *cfg.TempDir) +} + +func TestGetConfig_TempDirMergePrecedence(t *testing.T) { //nolint:paralleltest // cannot run in parallel + xdgConfigDir, homeDir, localDir := setupDirs(t) + + writeFile(t, xdgConfigDir, "taskrc.yml", ` +temp-dir: xdg-cache +`) + writeFile(t, homeDir, ".taskrc.yml", ` +temp-dir: home-cache +`) + writeFile(t, localDir, ".taskrc.yml", ` +temp-dir: local-cache +`) + + cfg, err := GetConfig(localDir) + require.NoError(t, err) + require.NotNil(t, cfg) + require.NotNil(t, cfg.TempDir) + assert.Equal(t, "local-cache", *cfg.TempDir) +} + func TestGetConfig_All(t *testing.T) { //nolint:paralleltest // cannot run in parallel xdgConfigDir, homeDir, localDir := setupDirs(t) diff --git a/website/src/docs/reference/cli.md b/website/src/docs/reference/cli.md index 48169537da..47b5c7d128 100644 --- a/website/src/docs/reference/cli.md +++ b/website/src/docs/reference/cli.md @@ -220,6 +220,18 @@ Run the global Taskfile from `$HOME/Taskfile.{yml,yaml}`. task backup --global ``` +#### `--temp-dir ` + +Set the directory used to store Task temporary files, such as checksums. +Relative paths are relative to the root Taskfile. + +- **Config equivalent**: [`temp-dir`](./config.md#temp-dir) +- **Environment variable**: [`TASK_TEMP_DIR`](./environment.md#task-temp-dir) + +```bash +task build --temp-dir .task-cache +``` + ### Output Control #### `-o, --output ` diff --git a/website/src/docs/reference/config.md b/website/src/docs/reference/config.md index 62d12bc9f5..7222b71375 100644 --- a/website/src/docs/reference/config.md +++ b/website/src/docs/reference/config.md @@ -166,6 +166,18 @@ failfast: true interactive: true ``` +### `temp-dir` + +- **Type**: `string` +- **Default**: `./.task` +- **Description**: Directory to store Task temporary files, such as checksums + and temporary metadata. Relative paths are relative to the root Taskfile. +- **Environment variable**: [`TASK_TEMP_DIR`](./environment.md#task-temp-dir) + +```yaml +temp-dir: .task +``` + ## Example Configuration Here's a complete example of a `.taskrc.yml` file with all available options: @@ -177,6 +189,7 @@ silent: false color: true disable-fuzzy: false concurrency: 2 +temp-dir: .task # Enable experimental features experiments: diff --git a/website/src/public/schema-taskrc.json b/website/src/public/schema-taskrc.json index 55cebf505a..f25cb5dc12 100644 --- a/website/src/public/schema-taskrc.json +++ b/website/src/public/schema-taskrc.json @@ -88,6 +88,10 @@ "description": "Prompt for missing required variables instead of failing. Requires a TTY.", "type": "boolean", "default": false + }, + "temp-dir": { + "type": "string", + "description": "Directory to store Task temporary files, such as checksums and temporary metadata. Relative paths are relative to the root Taskfile." } }, "additionalProperties": false