From 684dc16077dcbc90e21ba3bc534c3e644493ea63 Mon Sep 17 00:00:00 2001 From: Or Carmi Date: Thu, 4 Jun 2026 13:51:25 +0300 Subject: [PATCH] feat: configure output flags via environment variables Add environment variable equivalents for the output-related flags so grouped output can be enabled once at the CI/job level instead of on every task invocation: - TASK_OUTPUT (--output) - TASK_OUTPUT_GROUP_BEGIN (--output-group-begin) - TASK_OUTPUT_GROUP_END (--output-group-end) - TASK_OUTPUT_GROUP_ERROR_ONLY (--output-group-error-only) These are wired through the existing getConfig helper, so the usual precedence applies: CLI flag > env var > default. Closes #2872 --- internal/flags/flags.go | 8 +-- internal/flags/flags_test.go | 67 +++++++++++++++++++++++ website/src/docs/reference/cli.md | 8 +++ website/src/docs/reference/environment.md | 28 ++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 internal/flags/flags_test.go diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 51bec00468..bfae0eb901 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -143,10 +143,10 @@ 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.StringVarP(&Output.Name, "output", "o", "", "Sets output style: [interleaved|group|prefixed].") - pflag.StringVar(&Output.Group.Begin, "output-group-begin", "", "Message template to print before a task's grouped output.") - pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.") - pflag.BoolVar(&Output.Group.ErrorOnly, "output-group-error-only", false, "Swallow output from successful tasks.") + 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.") + pflag.BoolVar(&Output.Group.ErrorOnly, "output-group-error-only", getConfig(config, "OUTPUT_GROUP_ERROR_ONLY", func() *bool { return nil }, false), "Swallow output from successful tasks.") pflag.BoolVarP(&Color, "color", "c", getConfig(config, "COLOR", func() *bool { return config.Color }, true), "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.") pflag.IntVarP(&Concurrency, "concurrency", "C", getConfig(config, "CONCURRENCY", func() *int { return config.Concurrency }, 0), "Limit number of tasks to run concurrently.") pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.") diff --git a/internal/flags/flags_test.go b/internal/flags/flags_test.go new file mode 100644 index 0000000000..24b10733d3 --- /dev/null +++ b/internal/flags/flags_test.go @@ -0,0 +1,67 @@ +package flags + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + taskrcast "github.com/go-task/task/v3/taskrc/ast" +) + +func TestGetConfigOutputEnvVars(t *testing.T) { + t.Run("TASK_OUTPUT sets the output name", func(t *testing.T) { + t.Setenv("TASK_OUTPUT", "group") + got := getConfig[string](nil, "OUTPUT", func() *string { return nil }, "") + require.Equal(t, "group", got) + }) + + t.Run("TASK_OUTPUT_GROUP_BEGIN sets the begin template", func(t *testing.T) { + t.Setenv("TASK_OUTPUT_GROUP_BEGIN", "::group::{{.TASK}}") + got := getConfig[string](nil, "OUTPUT_GROUP_BEGIN", func() *string { return nil }, "") + require.Equal(t, "::group::{{.TASK}}", got) + }) + + t.Run("TASK_OUTPUT_GROUP_END sets the end template", func(t *testing.T) { + t.Setenv("TASK_OUTPUT_GROUP_END", "::endgroup::") + got := getConfig[string](nil, "OUTPUT_GROUP_END", func() *string { return nil }, "") + require.Equal(t, "::endgroup::", got) + }) + + t.Run("TASK_OUTPUT_GROUP_ERROR_ONLY sets the error-only flag", func(t *testing.T) { + t.Setenv("TASK_OUTPUT_GROUP_ERROR_ONLY", "true") + got := getConfig[bool](nil, "OUTPUT_GROUP_ERROR_ONLY", func() *bool { return nil }, false) + require.True(t, got) + }) + + t.Run("falls back to the default when the env var is unset", func(t *testing.T) { + require.Equal(t, "", getConfig[string](nil, "OUTPUT", func() *string { return nil }, "")) + require.False(t, getConfig[bool](nil, "OUTPUT_GROUP_ERROR_ONLY", func() *bool { return nil }, false)) + }) + + t.Run("invalid bool env var falls back to the default", func(t *testing.T) { + t.Setenv("TASK_OUTPUT_GROUP_ERROR_ONLY", "not-a-bool") + require.False(t, getConfig[bool](nil, "OUTPUT_GROUP_ERROR_ONLY", func() *bool { return nil }, false)) + }) +} + +func TestGetConfigPrecedence(t *testing.T) { + t.Run("env var takes precedence over the taskrc config", func(t *testing.T) { + t.Setenv("TASK_VERBOSE", "true") + config := &taskrcast.TaskRC{Verbose: boolPtr(false)} + require.True(t, getConfig[bool](config, "VERBOSE", func() *bool { return config.Verbose }, false)) + }) + + t.Run("taskrc config is used when the env var is unset", func(t *testing.T) { + config := &taskrcast.TaskRC{Verbose: boolPtr(true)} + require.True(t, getConfig[bool](config, "VERBOSE", func() *bool { return config.Verbose }, false)) + }) + + t.Run("duration env vars are parsed", func(t *testing.T) { + t.Setenv("TASK_REMOTE_TIMEOUT", "30s") + got := getConfig[time.Duration](nil, "REMOTE_TIMEOUT", func() *time.Duration { return nil }, time.Second*10) + require.Equal(t, 30*time.Second, got) + }) +} + +func boolPtr(b bool) *bool { return &b } diff --git a/website/src/docs/reference/cli.md b/website/src/docs/reference/cli.md index aeb417f1f4..48169537da 100644 --- a/website/src/docs/reference/cli.md +++ b/website/src/docs/reference/cli.md @@ -226,6 +226,8 @@ task backup --global Set output style. Available modes: `interleaved`, `group`, `prefixed`. +- **Environment variable**: [`TASK_OUTPUT`](./environment.md#task-output) + ```bash task test --output group ``` @@ -234,6 +236,8 @@ task test --output group Message template to print before grouped output. +- **Environment variable**: [`TASK_OUTPUT_GROUP_BEGIN`](./environment.md#task-output-group-begin) + ```bash task test --output group --output-group-begin "::group::{{.TASK}}" ``` @@ -242,6 +246,8 @@ task test --output group --output-group-begin "::group::{{.TASK}}" Message template to print after grouped output. +- **Environment variable**: [`TASK_OUTPUT_GROUP_END`](./environment.md#task-output-group-end) + ```bash task test --output group --output-group-end "::endgroup::" ``` @@ -250,6 +256,8 @@ task test --output group --output-group-end "::endgroup::" Only show command output on non-zero exit codes. +- **Environment variable**: [`TASK_OUTPUT_GROUP_ERROR_ONLY`](./environment.md#task-output-group-error-only) + ```bash task test --output group --output-group-error-only ``` diff --git a/website/src/docs/reference/environment.md b/website/src/docs/reference/environment.md index 45937e9238..e5fdf42ae2 100644 --- a/website/src/docs/reference/environment.md +++ b/website/src/docs/reference/environment.md @@ -81,6 +81,34 @@ variables. The priority order is: CLI flags > environment variables > config fil - **Default**: `false` - **Description**: Prompt for missing required variables +### `TASK_OUTPUT` + +- **Type**: `string` (`interleaved`, `group`, `prefixed`) +- **Description**: Sets the output style +- **CLI equivalent**: [`--output`](./cli.md#--output-string) + +### `TASK_OUTPUT_GROUP_BEGIN` + +- **Type**: `string` +- **Description**: Message template to print before a task's grouped output. + Only applies when the output style is `group`. +- **CLI equivalent**: [`--output-group-begin`](./cli.md#--output-group-begin-template) + +### `TASK_OUTPUT_GROUP_END` + +- **Type**: `string` +- **Description**: Message template to print after a task's grouped output. + Only applies when the output style is `group`. +- **CLI equivalent**: [`--output-group-end`](./cli.md#--output-group-end-template) + +### `TASK_OUTPUT_GROUP_ERROR_ONLY` + +- **Type**: `boolean` (`true`, `false`, `1`, `0`) +- **Default**: `false` +- **Description**: Swallow output from successful tasks. Only applies when the + output style is `group`. +- **CLI equivalent**: [`--output-group-error-only`](./cli.md#--output-group-error-only) + ### `TASK_TEMP_DIR` Defines the location of Task's temporary directory which is used for storing