Skip to content

Commit 0fdb5e8

Browse files
vmaertenpd93
andauthored
feat: add some config to taskrc.yml (#2389)
Co-authored-by: Pete Davison <[email protected]>
1 parent 534dfa0 commit 0fdb5e8

File tree

8 files changed

+208
-21
lines changed

8 files changed

+208
-21
lines changed

experiments/experiments.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/joho/godotenv"
1010

1111
"github.com/go-task/task/v3/taskrc"
12+
"github.com/go-task/task/v3/taskrc/ast"
1213
)
1314

1415
const envPrefix = "TASK_X_"
@@ -31,11 +32,15 @@ var (
3132
var xList []Experiment
3233

3334
func Parse(dir string) {
35+
config, _ := taskrc.GetConfig(dir)
36+
37+
ParseWithConfig(dir, config)
38+
}
39+
40+
func ParseWithConfig(dir string, config *ast.TaskRC) {
3441
// Read any .env files
3542
readDotEnv(dir)
3643

37-
config, _ := taskrc.GetConfig(dir)
38-
3944
// Initialize the experiments
4045
GentleForce = New("GENTLE_FORCE", config, 1)
4146
RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1)

internal/flags/flags.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import (
55
"log"
66
"os"
77
"path/filepath"
8-
"strconv"
98
"time"
109

1110
"github.com/spf13/pflag"
1211

1312
"github.com/go-task/task/v3"
1413
"github.com/go-task/task/v3/errors"
1514
"github.com/go-task/task/v3/experiments"
16-
"github.com/go-task/task/v3/internal/env"
1715
"github.com/go-task/task/v3/internal/sort"
1816
"github.com/go-task/task/v3/taskfile/ast"
17+
"github.com/go-task/task/v3/taskrc"
18+
taskrcast "github.com/go-task/task/v3/taskrc/ast"
1919
)
2020

2121
const usage = `Usage: task [flags...] [task...]
@@ -95,7 +95,9 @@ func init() {
9595

9696
// Parse the experiments
9797
dir = cmp.Or(dir, filepath.Dir(entrypoint))
98-
experiments.Parse(dir)
98+
99+
config, _ := taskrc.GetConfig(dir)
100+
experiments.ParseWithConfig(dir, config)
99101

100102
// Parse the rest of the flags
101103
log.SetFlags(0)
@@ -104,10 +106,7 @@ func init() {
104106
log.Print(usage)
105107
pflag.PrintDefaults()
106108
}
107-
offline, err := strconv.ParseBool(cmp.Or(env.GetTaskEnv("OFFLINE"), "false"))
108-
if err != nil {
109-
offline = false
110-
}
109+
111110
pflag.BoolVar(&Version, "version", false, "Show Task version.")
112111
pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
113112
pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
@@ -118,9 +117,9 @@ func init() {
118117
pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
119118
pflag.BoolVar(&Status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.")
120119
pflag.BoolVar(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON")
121-
pflag.BoolVar(&Insecure, "insecure", false, "Forces Task to download Taskfiles over insecure connections.")
120+
pflag.BoolVar(&Insecure, "insecure", getConfig(config, config.Remote.Insecure, false), "Forces Task to download Taskfiles over insecure connections.")
122121
pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.")
123-
pflag.BoolVarP(&Verbose, "verbose", "v", false, "Enables verbose mode.")
122+
pflag.BoolVarP(&Verbose, "verbose", "v", getConfig(config, config.Verbose, false), "Enables verbose mode.")
124123
pflag.BoolVarP(&Silent, "silent", "s", false, "Disables echoing.")
125124
pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.")
126125
pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
@@ -134,7 +133,7 @@ func init() {
134133
pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.")
135134
pflag.BoolVar(&Output.Group.ErrorOnly, "output-group-error-only", false, "Swallow output from successful tasks.")
136135
pflag.BoolVarP(&Color, "color", "c", true, "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.")
137-
pflag.IntVarP(&Concurrency, "concurrency", "C", 0, "Limit number of tasks to run concurrently.")
136+
pflag.IntVarP(&Concurrency, "concurrency", "C", getConfig(config, config.Concurrency, 0), "Limit number of tasks to run concurrently.")
138137
pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.")
139138
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
140139
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
@@ -150,12 +149,11 @@ func init() {
150149
// Remote Taskfiles experiment will adds the "download" and "offline" flags
151150
if experiments.RemoteTaskfiles.Enabled() {
152151
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
153-
pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached Taskfiles.")
154-
pflag.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.")
152+
pflag.BoolVar(&Offline, "offline", getConfig(config, config.Remote.Offline, false), "Forces Task to only use local or cached Taskfiles.")
153+
pflag.DurationVar(&Timeout, "timeout", getConfig(config, config.Remote.Timeout, time.Second*10), "Timeout for downloading remote Taskfiles.")
155154
pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.")
156-
pflag.DurationVar(&CacheExpiryDuration, "expiry", 0, "Expiry duration for cached remote Taskfiles.")
155+
pflag.DurationVar(&CacheExpiryDuration, "expiry", getConfig(config, config.Remote.Timeout, 0), "Expiry duration for cached remote Taskfiles.")
157156
}
158-
159157
pflag.Parse()
160158
}
161159

@@ -251,3 +249,15 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
251249
task.WithVersionCheck(true),
252250
)
253251
}
252+
253+
// getConfig extracts a config value directly from a pointer field with a fallback default
254+
func getConfig[T any](config *taskrcast.TaskRC, field *T, fallback T) T {
255+
if config == nil {
256+
return fallback
257+
}
258+
259+
if field != nil {
260+
return *field
261+
}
262+
return fallback
263+
}

taskrc/ast/taskrc.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,48 @@
11
package ast
22

33
import (
4+
"cmp"
45
"maps"
6+
"time"
57

68
"github.com/Masterminds/semver/v3"
79
)
810

911
type TaskRC struct {
1012
Version *semver.Version `yaml:"version"`
13+
Verbose *bool `yaml:"verbose"`
14+
Concurrency *int `yaml:"concurrency"`
15+
Remote Remote `yaml:"remote"`
1116
Experiments map[string]int `yaml:"experiments"`
1217
}
1318

19+
type Remote struct {
20+
Insecure *bool `yaml:"insecure"`
21+
Offline *bool `yaml:"offline"`
22+
Timeout *time.Duration `yaml:"timeout"`
23+
CacheExpiry *time.Duration `yaml:"cache-expiry"`
24+
}
25+
1426
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
1527
func (t *TaskRC) Merge(other *TaskRC) {
1628
if other == nil {
1729
return
1830
}
19-
if t.Version == nil && other.Version != nil {
20-
t.Version = other.Version
21-
}
31+
32+
t.Version = cmp.Or(other.Version, t.Version)
33+
2234
if t.Experiments == nil && other.Experiments != nil {
2335
t.Experiments = other.Experiments
2436
} else if t.Experiments != nil && other.Experiments != nil {
2537
maps.Copy(t.Experiments, other.Experiments)
2638
}
39+
40+
// Merge Remote fields
41+
t.Remote.Insecure = cmp.Or(other.Remote.Insecure, t.Remote.Insecure)
42+
t.Remote.Offline = cmp.Or(other.Remote.Offline, t.Remote.Offline)
43+
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
44+
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
45+
46+
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
47+
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
2748
}

website/.vitepress/config.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { resolve } from 'path';
55
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
66
import {
77
groupIconMdPlugin,
8-
groupIconVitePlugin
8+
groupIconVitePlugin,
9+
localIconLoader
910
} from 'vitepress-plugin-group-icons';
1011
import { team } from './team.ts';
1112
import { taskDescription, taskName } from './meta.ts';
@@ -99,7 +100,20 @@ export default defineConfig({
99100
}
100101
},
101102
vite: {
102-
plugins: [groupIconVitePlugin()],
103+
plugins: [
104+
groupIconVitePlugin({
105+
customIcon: {
106+
'.taskrc.yml': localIconLoader(
107+
import.meta.url,
108+
'./theme/icons/task.svg'
109+
),
110+
'Taskfile.yml': localIconLoader(
111+
import.meta.url,
112+
'./theme/icons/task.svg'
113+
)
114+
}
115+
})
116+
],
103117
resolve: {
104118
alias: [
105119
{
Lines changed: 1 addition & 0 deletions
Loading

website/src/docs/experiments/remote-taskfiles.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,64 @@ You can force Task to ignore the cache and download the latest version by using
290290
the `--download` flag.
291291

292292
You can use the `--clear-cache` flag to clear all cached remote files.
293+
294+
## Configuration
295+
This experiment adds a new `remote` section to the [configuration file](../reference/config.md).
296+
297+
- **Type**: `object`
298+
- **Description**: Remote configuration settings for handling remote Taskfiles
299+
300+
```yaml
301+
remote:
302+
insecure: false
303+
offline: false
304+
timeout: "30s"
305+
cache-expiry: "24h"
306+
```
307+
308+
#### `insecure`
309+
310+
- **Type**: `boolean`
311+
- **Default**: `false`
312+
- **Description**: Allow insecure connections when fetching remote Taskfiles
313+
314+
```yaml
315+
remote:
316+
insecure: true
317+
```
318+
319+
#### `offline`
320+
321+
- **Type**: `boolean`
322+
- **Default**: `false`
323+
- **Description**: Work in offline mode, preventing remote Taskfile fetching
324+
325+
```yaml
326+
remote:
327+
offline: true
328+
```
329+
330+
#### `timeout`
331+
332+
- **Type**: `string`
333+
- **Default**: Not specified
334+
- **Pattern**: `^[0-9]+(ns|us|µs|ms|s|m|h)$`
335+
- **Description**: Timeout duration for remote operations (e.g., '30s', '5m')
336+
337+
```yaml
338+
remote:
339+
timeout: "1m"
340+
```
341+
342+
#### `cache-expiry`
343+
344+
- **Type**: `string`
345+
- **Default**: Not specified
346+
- **Pattern**: `^[0-9]+(ns|us|µs|ms|s|m|h)$`
347+
- **Description**: Cache expiry duration for remote Taskfiles (e.g., '1h', '24h')
348+
349+
```yaml
350+
remote:
351+
cache-expiry: "6h"
352+
```
353+

website/src/docs/reference/config.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,44 @@ option_3: foo # Taken from $XDG_CONFIG_HOME/task/.taskrc.yml
7373
The experiments section allows you to enable Task's experimental features. These
7474
options are not enumerated here. Instead, please refer to our
7575
[experiments documentation](../experiments/index.md) for more information.
76+
77+
```yaml
78+
experiments:
79+
feature_name: 1
80+
another_feature: 2
81+
```
82+
83+
### `verbose`
84+
85+
- **Type**: `boolean`
86+
- **Default**: `false`
87+
- **Description**: Enable verbose output for all tasks
88+
- **CLI equivalent**: [`-v, --verbose`](./cli.md#-v---verbose)
89+
90+
```yaml
91+
verbose: true
92+
```
93+
94+
### `concurrency`
95+
96+
- **Type**: `integer`
97+
- **Minimum**: `1`
98+
- **Description**: Number of concurrent tasks to run
99+
- **CLI equivalent**: [`-C, --concurrency`](./cli.md#-c---concurrency-number)
100+
101+
```yaml
102+
concurrency: 4
103+
```
104+
105+
## Example Configuration
106+
107+
Here's a complete example of a `.taskrc.yml` file with all available options:
108+
109+
```yaml
110+
# Global settings
111+
verbose: true
112+
concurrency: 2
113+
114+
# Enable experimental features
115+
experiments:
116+
REMOTE_TASKFILES: 1

website/src/public/schema-taskrc.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,40 @@
2020
"enum": [0, 1]
2121
}
2222
}
23+
},
24+
"remote": {
25+
"type": "object",
26+
"description": "Remote configuration settings",
27+
"properties": {
28+
"insecure": {
29+
"type": "boolean",
30+
"description": "Forces Task to download Taskfiles over insecure connections."
31+
},
32+
"offline": {
33+
"type": "boolean",
34+
"description": "Forces Task to only use local or cached Taskfiles."
35+
},
36+
"timeout": {
37+
"type": "string",
38+
"description": "Timeout for downloading remote Taskfiles (e.g., '30s', '5m')",
39+
"pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$"
40+
},
41+
"cache-expiry": {
42+
"type": "string",
43+
"description": "Expiry duration for cached remote Taskfiles (e.g., '1h', '24h')",
44+
"pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$"
45+
}
46+
},
47+
"additionalProperties": false
48+
},
49+
"verbose": {
50+
"type": "boolean",
51+
"description": "Enable verbose output"
52+
},
53+
"concurrency": {
54+
"type": "integer",
55+
"description": "Number of concurrent tasks to run",
56+
"minimum": 1
2357
}
2458
},
2559
"additionalProperties": false

0 commit comments

Comments
 (0)