mirror of
				https://github.com/go-task/task.git
				synced 2025-10-30 23:58:01 +02:00 
			
		
		
		
	feat: add some config to taskrc.yml (#2389)
Co-authored-by: Pete Davison <pd93.uk@outlook.com>
This commit is contained in:
		| @@ -9,6 +9,7 @@ import ( | ||||
| 	"github.com/joho/godotenv" | ||||
|  | ||||
| 	"github.com/go-task/task/v3/taskrc" | ||||
| 	"github.com/go-task/task/v3/taskrc/ast" | ||||
| ) | ||||
|  | ||||
| const envPrefix = "TASK_X_" | ||||
| @@ -31,11 +32,15 @@ var ( | ||||
| var xList []Experiment | ||||
|  | ||||
| func Parse(dir string) { | ||||
| 	config, _ := taskrc.GetConfig(dir) | ||||
|  | ||||
| 	ParseWithConfig(dir, config) | ||||
| } | ||||
|  | ||||
| func ParseWithConfig(dir string, config *ast.TaskRC) { | ||||
| 	// Read any .env files | ||||
| 	readDotEnv(dir) | ||||
|  | ||||
| 	config, _ := taskrc.GetConfig(dir) | ||||
|  | ||||
| 	// Initialize the experiments | ||||
| 	GentleForce = New("GENTLE_FORCE", config, 1) | ||||
| 	RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1) | ||||
|   | ||||
| @@ -5,7 +5,6 @@ import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/spf13/pflag" | ||||
| @@ -13,9 +12,10 @@ import ( | ||||
| 	"github.com/go-task/task/v3" | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/experiments" | ||||
| 	"github.com/go-task/task/v3/internal/env" | ||||
| 	"github.com/go-task/task/v3/internal/sort" | ||||
| 	"github.com/go-task/task/v3/taskfile/ast" | ||||
| 	"github.com/go-task/task/v3/taskrc" | ||||
| 	taskrcast "github.com/go-task/task/v3/taskrc/ast" | ||||
| ) | ||||
|  | ||||
| const usage = `Usage: task [flags...] [task...] | ||||
| @@ -95,7 +95,9 @@ func init() { | ||||
|  | ||||
| 	// Parse the experiments | ||||
| 	dir = cmp.Or(dir, filepath.Dir(entrypoint)) | ||||
| 	experiments.Parse(dir) | ||||
|  | ||||
| 	config, _ := taskrc.GetConfig(dir) | ||||
| 	experiments.ParseWithConfig(dir, config) | ||||
|  | ||||
| 	// Parse the rest of the flags | ||||
| 	log.SetFlags(0) | ||||
| @@ -104,10 +106,7 @@ func init() { | ||||
| 		log.Print(usage) | ||||
| 		pflag.PrintDefaults() | ||||
| 	} | ||||
| 	offline, err := strconv.ParseBool(cmp.Or(env.GetTaskEnv("OFFLINE"), "false")) | ||||
| 	if err != nil { | ||||
| 		offline = false | ||||
| 	} | ||||
|  | ||||
| 	pflag.BoolVar(&Version, "version", false, "Show Task version.") | ||||
| 	pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.") | ||||
| 	pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.") | ||||
| @@ -118,9 +117,9 @@ func init() { | ||||
| 	pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].") | ||||
| 	pflag.BoolVar(&Status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.") | ||||
| 	pflag.BoolVar(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON") | ||||
| 	pflag.BoolVar(&Insecure, "insecure", false, "Forces Task to download Taskfiles over insecure connections.") | ||||
| 	pflag.BoolVar(&Insecure, "insecure", getConfig(config, config.Remote.Insecure, false), "Forces Task to download Taskfiles over insecure connections.") | ||||
| 	pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.") | ||||
| 	pflag.BoolVarP(&Verbose, "verbose", "v", false, "Enables verbose mode.") | ||||
| 	pflag.BoolVarP(&Verbose, "verbose", "v", getConfig(config, config.Verbose, false), "Enables verbose mode.") | ||||
| 	pflag.BoolVarP(&Silent, "silent", "s", false, "Disables echoing.") | ||||
| 	pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.") | ||||
| 	pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.") | ||||
| @@ -134,7 +133,7 @@ func init() { | ||||
| 	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.BoolVarP(&Color, "color", "c", true, "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.") | ||||
| 	pflag.IntVarP(&Concurrency, "concurrency", "C", 0, "Limit number of tasks to run concurrently.") | ||||
| 	pflag.IntVarP(&Concurrency, "concurrency", "C", getConfig(config, config.Concurrency, 0), "Limit number of tasks to run concurrently.") | ||||
| 	pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.") | ||||
| 	pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.") | ||||
| 	pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.") | ||||
| @@ -150,12 +149,11 @@ func init() { | ||||
| 	// Remote Taskfiles experiment will adds the "download" and "offline" flags | ||||
| 	if experiments.RemoteTaskfiles.Enabled() { | ||||
| 		pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.") | ||||
| 		pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached Taskfiles.") | ||||
| 		pflag.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.") | ||||
| 		pflag.BoolVar(&Offline, "offline", getConfig(config, config.Remote.Offline, false), "Forces Task to only use local or cached Taskfiles.") | ||||
| 		pflag.DurationVar(&Timeout, "timeout", getConfig(config, config.Remote.Timeout, time.Second*10), "Timeout for downloading remote Taskfiles.") | ||||
| 		pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.") | ||||
| 		pflag.DurationVar(&CacheExpiryDuration, "expiry", 0, "Expiry duration for cached remote Taskfiles.") | ||||
| 		pflag.DurationVar(&CacheExpiryDuration, "expiry", getConfig(config, config.Remote.Timeout, 0), "Expiry duration for cached remote Taskfiles.") | ||||
| 	} | ||||
|  | ||||
| 	pflag.Parse() | ||||
| } | ||||
|  | ||||
| @@ -251,3 +249,15 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) { | ||||
| 		task.WithVersionCheck(true), | ||||
| 	) | ||||
| } | ||||
|  | ||||
| // getConfig extracts a config value directly from a pointer field with a fallback default | ||||
| func getConfig[T any](config *taskrcast.TaskRC, field *T, fallback T) T { | ||||
| 	if config == nil { | ||||
| 		return fallback | ||||
| 	} | ||||
|  | ||||
| 	if field != nil { | ||||
| 		return *field | ||||
| 	} | ||||
| 	return fallback | ||||
| } | ||||
|   | ||||
| @@ -1,27 +1,48 @@ | ||||
| package ast | ||||
|  | ||||
| import ( | ||||
| 	"cmp" | ||||
| 	"maps" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/Masterminds/semver/v3" | ||||
| ) | ||||
|  | ||||
| type TaskRC struct { | ||||
| 	Version     *semver.Version `yaml:"version"` | ||||
| 	Verbose     *bool           `yaml:"verbose"` | ||||
| 	Concurrency *int            `yaml:"concurrency"` | ||||
| 	Remote      Remote          `yaml:"remote"` | ||||
| 	Experiments map[string]int  `yaml:"experiments"` | ||||
| } | ||||
|  | ||||
| type Remote struct { | ||||
| 	Insecure    *bool          `yaml:"insecure"` | ||||
| 	Offline     *bool          `yaml:"offline"` | ||||
| 	Timeout     *time.Duration `yaml:"timeout"` | ||||
| 	CacheExpiry *time.Duration `yaml:"cache-expiry"` | ||||
| } | ||||
|  | ||||
| // Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC. | ||||
| func (t *TaskRC) Merge(other *TaskRC) { | ||||
| 	if other == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	if t.Version == nil && other.Version != nil { | ||||
| 		t.Version = other.Version | ||||
| 	} | ||||
|  | ||||
| 	t.Version = cmp.Or(other.Version, t.Version) | ||||
|  | ||||
| 	if t.Experiments == nil && other.Experiments != nil { | ||||
| 		t.Experiments = other.Experiments | ||||
| 	} else if t.Experiments != nil && other.Experiments != nil { | ||||
| 		maps.Copy(t.Experiments, other.Experiments) | ||||
| 	} | ||||
|  | ||||
| 	// Merge Remote fields | ||||
| 	t.Remote.Insecure = cmp.Or(other.Remote.Insecure, t.Remote.Insecure) | ||||
| 	t.Remote.Offline = cmp.Or(other.Remote.Offline, t.Remote.Offline) | ||||
| 	t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout) | ||||
| 	t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry) | ||||
|  | ||||
| 	t.Verbose = cmp.Or(other.Verbose, t.Verbose) | ||||
| 	t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency) | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,8 @@ import { resolve } from 'path'; | ||||
| import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'; | ||||
| import { | ||||
|   groupIconMdPlugin, | ||||
|   groupIconVitePlugin | ||||
|   groupIconVitePlugin, | ||||
|   localIconLoader | ||||
| } from 'vitepress-plugin-group-icons'; | ||||
| import { team } from './team.ts'; | ||||
| import { taskDescription, taskName } from './meta.ts'; | ||||
| @@ -99,7 +100,20 @@ export default defineConfig({ | ||||
|     } | ||||
|   }, | ||||
|   vite: { | ||||
|     plugins: [groupIconVitePlugin()], | ||||
|     plugins: [ | ||||
|       groupIconVitePlugin({ | ||||
|         customIcon: { | ||||
|           '.taskrc.yml': localIconLoader( | ||||
|             import.meta.url, | ||||
|             './theme/icons/task.svg' | ||||
|           ), | ||||
|           'Taskfile.yml': localIconLoader( | ||||
|             import.meta.url, | ||||
|             './theme/icons/task.svg' | ||||
|           ) | ||||
|         } | ||||
|       }) | ||||
|     ], | ||||
|     resolve: { | ||||
|       alias: [ | ||||
|         { | ||||
|   | ||||
							
								
								
									
										1
									
								
								website/.vitepress/theme/icons/task.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								website/.vitepress/theme/icons/task.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 375 375"><path fill="#29beb0" d="M 187.570312 190.933594 L 187.570312 375 L 30.070312 279.535156 L 30.070312 95.464844 Z"/><path fill="#69d2c8" d="M 187.570312 190.933594 L 187.570312 375 L 345.070312 279.535156 L 345.070312 95.464844 Z"/><path fill="#94dfd8" d="M 187.570312 190.933594 L 30.070312 95.464844 L 187.570312 0 L 345.070312 95.464844 Z"/></svg> | ||||
| After Width: | Height: | Size: 435 B | 
| @@ -290,3 +290,64 @@ You can force Task to ignore the cache and download the latest version by using | ||||
| the `--download` flag. | ||||
|  | ||||
| You can use the `--clear-cache` flag to clear all cached remote files. | ||||
|  | ||||
| ## Configuration | ||||
| This experiment adds a new `remote` section to the [configuration file](../reference/config.md). | ||||
|  | ||||
| - **Type**: `object` | ||||
| - **Description**: Remote configuration settings for handling remote Taskfiles | ||||
|  | ||||
| ```yaml | ||||
| remote: | ||||
|   insecure: false | ||||
|   offline: false | ||||
|   timeout: "30s" | ||||
|   cache-expiry: "24h" | ||||
| ``` | ||||
|  | ||||
| #### `insecure` | ||||
|  | ||||
| - **Type**: `boolean` | ||||
| - **Default**: `false` | ||||
| - **Description**: Allow insecure connections when fetching remote Taskfiles | ||||
|  | ||||
| ```yaml | ||||
| remote: | ||||
|   insecure: true | ||||
| ``` | ||||
|  | ||||
| #### `offline` | ||||
|  | ||||
| - **Type**: `boolean` | ||||
| - **Default**: `false` | ||||
| - **Description**: Work in offline mode, preventing remote Taskfile fetching | ||||
|  | ||||
| ```yaml | ||||
| remote: | ||||
|   offline: true | ||||
| ``` | ||||
|  | ||||
| #### `timeout` | ||||
|  | ||||
| - **Type**: `string` | ||||
| - **Default**: Not specified | ||||
| - **Pattern**: `^[0-9]+(ns|us|µs|ms|s|m|h)$` | ||||
| - **Description**: Timeout duration for remote operations (e.g., '30s', '5m') | ||||
|  | ||||
| ```yaml | ||||
| remote: | ||||
|   timeout: "1m" | ||||
| ``` | ||||
|  | ||||
| #### `cache-expiry` | ||||
|  | ||||
| - **Type**: `string` | ||||
| - **Default**: Not specified | ||||
| - **Pattern**: `^[0-9]+(ns|us|µs|ms|s|m|h)$` | ||||
| - **Description**: Cache expiry duration for remote Taskfiles (e.g., '1h', '24h') | ||||
|  | ||||
| ```yaml | ||||
| remote: | ||||
|   cache-expiry: "6h" | ||||
| ``` | ||||
|  | ||||
|   | ||||
| @@ -73,3 +73,44 @@ option_3: foo # Taken from $XDG_CONFIG_HOME/task/.taskrc.yml | ||||
| The experiments section allows you to enable Task's experimental features. These | ||||
| options are not enumerated here. Instead, please refer to our | ||||
| [experiments documentation](../experiments/index.md) for more information. | ||||
|  | ||||
| ```yaml | ||||
| experiments: | ||||
|   feature_name: 1 | ||||
|   another_feature: 2 | ||||
| ``` | ||||
|  | ||||
| ### `verbose` | ||||
|  | ||||
| - **Type**: `boolean` | ||||
| - **Default**: `false` | ||||
| - **Description**: Enable verbose output for all tasks | ||||
| - **CLI equivalent**: [`-v, --verbose`](./cli.md#-v---verbose) | ||||
|  | ||||
| ```yaml | ||||
| verbose: true | ||||
| ``` | ||||
|  | ||||
| ### `concurrency` | ||||
|  | ||||
| - **Type**: `integer` | ||||
| - **Minimum**: `1` | ||||
| - **Description**: Number of concurrent tasks to run | ||||
| - **CLI equivalent**: [`-C, --concurrency`](./cli.md#-c---concurrency-number) | ||||
|  | ||||
| ```yaml | ||||
| concurrency: 4 | ||||
| ``` | ||||
|  | ||||
| ## Example Configuration | ||||
|  | ||||
| Here's a complete example of a `.taskrc.yml` file with all available options: | ||||
|  | ||||
| ```yaml | ||||
| # Global settings | ||||
| verbose: true | ||||
| concurrency: 2 | ||||
|  | ||||
| # Enable experimental features | ||||
| experiments: | ||||
|   REMOTE_TASKFILES: 1 | ||||
|   | ||||
| @@ -20,6 +20,40 @@ | ||||
|           "enum": [0, 1] | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "remote": { | ||||
|       "type": "object", | ||||
|       "description": "Remote configuration settings", | ||||
|       "properties": { | ||||
|         "insecure": { | ||||
|           "type": "boolean", | ||||
|           "description": "Forces Task to download Taskfiles over insecure connections." | ||||
|         }, | ||||
|         "offline": { | ||||
|           "type": "boolean", | ||||
|           "description": "Forces Task to only use local or cached Taskfiles." | ||||
|         }, | ||||
|         "timeout": { | ||||
|           "type": "string", | ||||
|           "description": "Timeout for downloading remote Taskfiles (e.g., '30s', '5m')", | ||||
|           "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$" | ||||
|         }, | ||||
|         "cache-expiry": { | ||||
|           "type": "string", | ||||
|           "description": "Expiry duration for cached remote Taskfiles (e.g., '1h', '24h')", | ||||
|           "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$" | ||||
|         } | ||||
|       }, | ||||
|       "additionalProperties": false | ||||
|     }, | ||||
|     "verbose": { | ||||
|       "type": "boolean", | ||||
|       "description": "Enable verbose output" | ||||
|     }, | ||||
|     "concurrency": { | ||||
|       "type": "integer", | ||||
|       "description": "Number of concurrent tasks to run", | ||||
|       "minimum": 1 | ||||
|     } | ||||
|   }, | ||||
|   "additionalProperties": false | ||||
|   | ||||
		Reference in New Issue
	
	Block a user