feat(completion): add missing flags and dynamic experimental feature detection (#2532)

This commit is contained in:
Valentin Maerten
2025-11-29 12:16:58 +01:00
committed by GitHub
parent 4ab1958df1
commit a085d62727
4 changed files with 219 additions and 53 deletions

View File

@@ -10,6 +10,8 @@
`env:`, and `requires:` sections. Dynamic variables show their shell command `env:`, and `requires:` sections. Dynamic variables show their shell command
(e.g., `sh: echo "hello"`) instead of the evaluated value (#2486 ,#2524 by (e.g., `sh: echo "hello"`) instead of the evaluated value (#2486 ,#2524 by
@vmaerten). @vmaerten).
- Improved shell completion scripts (Zsh, Fish, PowerShell) by adding missing
flags and dynamic experimental feature detection (#2532 by @vmaerten).
## v3.45.5 - 2025-11-11 ## v3.45.5 - 2025-11-11

View File

@@ -1,5 +1,32 @@
set -l GO_TASK_PROGNAME task set -l GO_TASK_PROGNAME task
# Cache variables for experiments (global)
set -g __task_experiments_cache ""
set -g __task_experiments_cache_time 0
# Helper function to get experiments with 1-second cache
function __task_get_experiments
set -l now (date +%s)
set -l ttl 1 # Cache for 1 second only
# Return cached value if still valid
if test (math "$now - $__task_experiments_cache_time") -lt $ttl
printf '%s\n' $__task_experiments_cache
return
end
# Refresh cache
set -g __task_experiments_cache (task --experiments 2>/dev/null)
set -g __task_experiments_cache_time $now
printf '%s\n' $__task_experiments_cache
end
# Helper function to check if an experiment is enabled
function __task_is_experiment_enabled
set -l experiment $argv[1]
__task_get_experiments | string match -qr "^\* $experiment:.*on"
end
function __task_get_tasks --description "Prints all available tasks with their description" --inherit-variable GO_TASK_PROGNAME function __task_get_tasks --description "Prints all available tasks with their description" --inherit-variable GO_TASK_PROGNAME
# Check if the global task is requested # Check if the global task is requested
set -l global_task false set -l global_task false
@@ -36,19 +63,49 @@ end
complete -c $GO_TASK_PROGNAME -d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was complete -c $GO_TASK_PROGNAME -d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was
specified.' -xa "(__task_get_tasks)" specified.' -xa "(__task_get_tasks)"
complete -c $GO_TASK_PROGNAME -s c -l color -d 'colored output (default true)' # Standard flags
complete -c $GO_TASK_PROGNAME -s d -l dir -d 'sets directory of execution' complete -c $GO_TASK_PROGNAME -s a -l list-all -d 'list all tasks'
complete -c $GO_TASK_PROGNAME -l dry -d 'compiles and prints tasks in the order that they would be run, without executing them' complete -c $GO_TASK_PROGNAME -s c -l color -d 'colored output (default true)'
complete -c $GO_TASK_PROGNAME -s f -l force -d 'forces execution even when the task is up-to-date' complete -c $GO_TASK_PROGNAME -s C -l concurrency -d 'limit number of concurrent tasks'
complete -c $GO_TASK_PROGNAME -s h -l help -d 'shows Task usage' complete -c $GO_TASK_PROGNAME -l completion -d 'generate shell completion script' -xa "bash zsh fish powershell"
complete -c $GO_TASK_PROGNAME -s i -l init -d 'creates a new Taskfile.yml in the current folder' complete -c $GO_TASK_PROGNAME -s d -l dir -d 'set directory of execution'
complete -c $GO_TASK_PROGNAME -s l -l list -d 'lists tasks with description of current Taskfile' complete -c $GO_TASK_PROGNAME -s n -l dry -d 'compile and print tasks without executing'
complete -c $GO_TASK_PROGNAME -s o -l output -d 'sets output style: [interleaved|group|prefixed]' -xa "interleaved group prefixed" complete -c $GO_TASK_PROGNAME -s x -l exit-code -d 'pass-through exit code of task command'
complete -c $GO_TASK_PROGNAME -s p -l parallel -d 'executes tasks provided on command line in parallel' complete -c $GO_TASK_PROGNAME -l experiments -d 'list available experiments'
complete -c $GO_TASK_PROGNAME -s s -l silent -d 'disables echoing' complete -c $GO_TASK_PROGNAME -s f -l force -d 'force execution even when up-to-date'
complete -c $GO_TASK_PROGNAME -l status -d 'exits with non-zero exit code if any of the given tasks is not up-to-date' complete -c $GO_TASK_PROGNAME -s g -l global -d 'run global Taskfile from home directory'
complete -c $GO_TASK_PROGNAME -l summary -d 'show summary about a task' complete -c $GO_TASK_PROGNAME -s h -l help -d 'show help'
complete -c $GO_TASK_PROGNAME -s t -l taskfile -d 'choose which Taskfile to run. Defaults to "Taskfile.yml"' complete -c $GO_TASK_PROGNAME -s i -l init -d 'create new Taskfile'
complete -c $GO_TASK_PROGNAME -s v -l verbose -d 'enables verbose mode' complete -c $GO_TASK_PROGNAME -l insecure -d 'allow insecure Taskfile downloads'
complete -c $GO_TASK_PROGNAME -l version -d 'show Task version' complete -c $GO_TASK_PROGNAME -s I -l interval -d 'interval to watch for changes'
complete -c $GO_TASK_PROGNAME -s w -l watch -d 'enables watch of the given task' complete -c $GO_TASK_PROGNAME -s j -l json -d 'format task list as JSON'
complete -c $GO_TASK_PROGNAME -s l -l list -d 'list tasks with descriptions'
complete -c $GO_TASK_PROGNAME -l nested -d 'nest namespaces when listing as JSON'
complete -c $GO_TASK_PROGNAME -l no-status -d 'ignore status when listing as JSON'
complete -c $GO_TASK_PROGNAME -s o -l output -d 'set output style' -xa "interleaved group prefixed"
complete -c $GO_TASK_PROGNAME -l output-group-begin -d 'message template before grouped output'
complete -c $GO_TASK_PROGNAME -l output-group-end -d 'message template after grouped output'
complete -c $GO_TASK_PROGNAME -l output-group-error-only -d 'hide output from successful tasks'
complete -c $GO_TASK_PROGNAME -s p -l parallel -d 'execute tasks in parallel'
complete -c $GO_TASK_PROGNAME -s s -l silent -d 'disable echoing'
complete -c $GO_TASK_PROGNAME -l sort -d 'set task sorting order' -xa "default alphanumeric none"
complete -c $GO_TASK_PROGNAME -l status -d 'exit non-zero if tasks not up-to-date'
complete -c $GO_TASK_PROGNAME -l summary -d 'show task summary'
complete -c $GO_TASK_PROGNAME -s t -l taskfile -d 'choose Taskfile to run'
complete -c $GO_TASK_PROGNAME -s v -l verbose -d 'verbose output'
complete -c $GO_TASK_PROGNAME -l version -d 'show version'
complete -c $GO_TASK_PROGNAME -s w -l watch -d 'watch mode, re-run on changes'
complete -c $GO_TASK_PROGNAME -s y -l yes -d 'assume yes to all prompts'
# Experimental flags (dynamically checked at completion time via -n condition)
# GentleForce experiment
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled GENTLE_FORCE" -l force-all -d 'force execution of task and all dependencies'
# RemoteTaskfiles experiment - Options
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l offline -d 'use only local or cached Taskfiles'
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l timeout -d 'timeout for remote Taskfile downloads'
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l expiry -d 'cache expiry duration'
# RemoteTaskfiles experiment - Operations
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l download -d 'download remote Taskfile'
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l clear-cache -d 'clear remote Taskfile cache'

View File

@@ -5,22 +5,78 @@ Register-ArgumentCompleter -CommandName task -ScriptBlock {
if ($commandName.StartsWith('-')) { if ($commandName.StartsWith('-')) {
$completions = @( $completions = @(
[CompletionResult]::new('--list-all ', '--list-all ', [CompletionResultType]::ParameterName, 'list all tasks'), # Standard flags (alphabetical order)
[CompletionResult]::new('--color ', '--color', [CompletionResultType]::ParameterName, '--color'), [CompletionResult]::new('-a', '-a', [CompletionResultType]::ParameterName, 'list all tasks'),
[CompletionResult]::new('--concurrency=', '--concurrency=', [CompletionResultType]::ParameterName, 'concurrency'), [CompletionResult]::new('--list-all', '--list-all', [CompletionResultType]::ParameterName, 'list all tasks'),
[CompletionResult]::new('--interval=', '--interval=', [CompletionResultType]::ParameterName, 'interval'), [CompletionResult]::new('-c', '-c', [CompletionResultType]::ParameterName, 'colored output'),
[CompletionResult]::new('--output=interleaved ', '--output=interleaved', [CompletionResultType]::ParameterName, '--output='), [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'colored output'),
[CompletionResult]::new('--output=group ', '--output=group', [CompletionResultType]::ParameterName, '--output='), [CompletionResult]::new('-C', '-C', [CompletionResultType]::ParameterName, 'limit concurrent tasks'),
[CompletionResult]::new('--output=prefixed ', '--output=prefixed', [CompletionResultType]::ParameterName, '--output='), [CompletionResult]::new('--concurrency', '--concurrency', [CompletionResultType]::ParameterName, 'limit concurrent tasks'),
[CompletionResult]::new('--dry ', '--dry', [CompletionResultType]::ParameterName, '--dry'), [CompletionResult]::new('--completion', '--completion', [CompletionResultType]::ParameterName, 'generate shell completion'),
[CompletionResult]::new('--force ', '--force', [CompletionResultType]::ParameterName, '--force'), [CompletionResult]::new('-d', '-d', [CompletionResultType]::ParameterName, 'set directory'),
[CompletionResult]::new('--parallel ', '--parallel', [CompletionResultType]::ParameterName, '--parallel'), [CompletionResult]::new('--dir', '--dir', [CompletionResultType]::ParameterName, 'set directory'),
[CompletionResult]::new('--silent ', '--silent', [CompletionResultType]::ParameterName, '--silent'), [CompletionResult]::new('-n', '-n', [CompletionResultType]::ParameterName, 'dry run'),
[CompletionResult]::new('--status ', '--status', [CompletionResultType]::ParameterName, '--status'), [CompletionResult]::new('--dry', '--dry', [CompletionResultType]::ParameterName, 'dry run'),
[CompletionResult]::new('--verbose ', '--verbose', [CompletionResultType]::ParameterName, '--verbose'), [CompletionResult]::new('-x', '-x', [CompletionResultType]::ParameterName, 'pass-through exit code'),
[CompletionResult]::new('--watch ', '--watch', [CompletionResultType]::ParameterName, '--watch') [CompletionResult]::new('--exit-code', '--exit-code', [CompletionResultType]::ParameterName, 'pass-through exit code'),
[CompletionResult]::new('--experiments', '--experiments', [CompletionResultType]::ParameterName, 'list experiments'),
[CompletionResult]::new('-f', '-f', [CompletionResultType]::ParameterName, 'force execution'),
[CompletionResult]::new('--force', '--force', [CompletionResultType]::ParameterName, 'force execution'),
[CompletionResult]::new('-g', '-g', [CompletionResultType]::ParameterName, 'run global Taskfile'),
[CompletionResult]::new('--global', '--global', [CompletionResultType]::ParameterName, 'run global Taskfile'),
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'show help'),
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'show help'),
[CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'create new Taskfile'),
[CompletionResult]::new('--init', '--init', [CompletionResultType]::ParameterName, 'create new Taskfile'),
[CompletionResult]::new('--insecure', '--insecure', [CompletionResultType]::ParameterName, 'allow insecure downloads'),
[CompletionResult]::new('-I', '-I', [CompletionResultType]::ParameterName, 'watch interval'),
[CompletionResult]::new('--interval', '--interval', [CompletionResultType]::ParameterName, 'watch interval'),
[CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'format as JSON'),
[CompletionResult]::new('--json', '--json', [CompletionResultType]::ParameterName, 'format as JSON'),
[CompletionResult]::new('-l', '-l', [CompletionResultType]::ParameterName, 'list tasks'),
[CompletionResult]::new('--list', '--list', [CompletionResultType]::ParameterName, 'list tasks'),
[CompletionResult]::new('--nested', '--nested', [CompletionResultType]::ParameterName, 'nest namespaces in JSON'),
[CompletionResult]::new('--no-status', '--no-status', [CompletionResultType]::ParameterName, 'ignore status in JSON'),
[CompletionResult]::new('-o', '-o', [CompletionResultType]::ParameterName, 'set output style'),
[CompletionResult]::new('--output', '--output', [CompletionResultType]::ParameterName, 'set output style'),
[CompletionResult]::new('--output-group-begin', '--output-group-begin', [CompletionResultType]::ParameterName, 'template before group'),
[CompletionResult]::new('--output-group-end', '--output-group-end', [CompletionResultType]::ParameterName, 'template after group'),
[CompletionResult]::new('--output-group-error-only', '--output-group-error-only', [CompletionResultType]::ParameterName, 'hide successful output'),
[CompletionResult]::new('-p', '-p', [CompletionResultType]::ParameterName, 'execute in parallel'),
[CompletionResult]::new('--parallel', '--parallel', [CompletionResultType]::ParameterName, 'execute in parallel'),
[CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'silent mode'),
[CompletionResult]::new('--silent', '--silent', [CompletionResultType]::ParameterName, 'silent mode'),
[CompletionResult]::new('--sort', '--sort', [CompletionResultType]::ParameterName, 'task sorting order'),
[CompletionResult]::new('--status', '--status', [CompletionResultType]::ParameterName, 'check task status'),
[CompletionResult]::new('--summary', '--summary', [CompletionResultType]::ParameterName, 'show task summary'),
[CompletionResult]::new('-t', '-t', [CompletionResultType]::ParameterName, 'choose Taskfile'),
[CompletionResult]::new('--taskfile', '--taskfile', [CompletionResultType]::ParameterName, 'choose Taskfile'),
[CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'verbose output'),
[CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'verbose output'),
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'show version'),
[CompletionResult]::new('-w', '-w', [CompletionResultType]::ParameterName, 'watch mode'),
[CompletionResult]::new('--watch', '--watch', [CompletionResultType]::ParameterName, 'watch mode'),
[CompletionResult]::new('-y', '-y', [CompletionResultType]::ParameterName, 'assume yes'),
[CompletionResult]::new('--yes', '--yes', [CompletionResultType]::ParameterName, 'assume yes')
) )
# Experimental flags (dynamically added based on enabled experiments)
$experiments = & task --experiments 2>$null | Out-String
if ($experiments -match '\* GENTLE_FORCE:.*on') {
$completions += [CompletionResult]::new('--force-all', '--force-all', [CompletionResultType]::ParameterName, 'force all dependencies')
}
if ($experiments -match '\* REMOTE_TASKFILES:.*on') {
# Options
$completions += [CompletionResult]::new('--offline', '--offline', [CompletionResultType]::ParameterName, 'use cached Taskfiles')
$completions += [CompletionResult]::new('--timeout', '--timeout', [CompletionResultType]::ParameterName, 'download timeout')
$completions += [CompletionResult]::new('--expiry', '--expiry', [CompletionResultType]::ParameterName, 'cache expiry')
# Operations
$completions += [CompletionResult]::new('--download', '--download', [CompletionResultType]::ParameterName, 'download remote Taskfile')
$completions += [CompletionResult]::new('--clear-cache', '--clear-cache', [CompletionResultType]::ParameterName, 'clear cache')
}
return $completions.Where{ $_.CompletionText.StartsWith($commandName) } return $completions.Where{ $_.CompletionText.StartsWith($commandName) }
} }

View File

@@ -4,6 +4,12 @@ typeset -A opt_args
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}" _GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
# Check if an experiment is enabled
function __task_is_experiment_enabled() {
local experiment=$1
task --experiments 2>/dev/null | grep -q "^\* ${experiment}:.*on"
}
# Listing commands from Taskfile.yml # Listing commands from Taskfile.yml
function __task_list() { function __task_list() {
local -a scripts cmd local -a scripts cmd
@@ -36,29 +42,74 @@ function __task_list() {
} }
_task() { _task() {
_arguments \ local -a standard_args operation_args
'(-C --concurrency)'{-C,--concurrency}'[limit number of concurrent tasks]: ' \
'(-p --parallel)'{-p,--parallel}'[run command-line tasks in parallel]' \ standard_args=(
'(-f --force)'{-f,--force}'[run even if task is up-to-date]' \ '(-C --concurrency)'{-C,--concurrency}'[limit number of concurrent tasks]: '
'(-c --color)'{-c,--color}'[colored output]' \ '(-p --parallel)'{-p,--parallel}'[run command-line tasks in parallel]'
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs' \ '(-f --force)'{-f,--force}'[run even if task is up-to-date]'
'(--dry)--dry[dry-run mode, compile and print tasks only]' \ '(-c --color)'{-c,--color}'[colored output]'
'(-o --output)'{-o,--output}'[set output style]:style:(interleaved group prefixed)' \ '(--completion)--completion[generate shell completion script]:shell:(bash zsh fish powershell)'
'(--output-group-begin)--output-group-begin[message template before grouped output]:template text: ' \ '(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs'
'(--output-group-end)--output-group-end[message template after grouped output]:template text: ' \ '(-n --dry)'{-n,--dry}'[compiles and prints tasks without executing]'
'(-s --silent)'{-s,--silent}'[disable echoing]' \ '(--dry)--dry[dry-run mode, compile and print tasks only]'
'(--status)--status[exit non-zero if supplied tasks not up-to-date]' \ '(-x --exit-code)'{-x,--exit-code}'[pass-through exit code of task command]'
'(--summary)--summary[show summary\: field from tasks instead of running them]' \ '(--experiments)--experiments[list available experiments]'
'(-t --taskfile)'{-t,--taskfile}'[specify a different taskfile]:taskfile:_files' \ '(-g --global)'{-g,--global}'[run global Taskfile from home directory]'
'(-v --verbose)'{-v,--verbose}'[verbose mode]' \ '(--insecure)--insecure[allow insecure Taskfile downloads]'
'(-w --watch)'{-w,--watch}'[watch-mode for given tasks, re-run when inputs change]' \ '(-I --interval)'{-I,--interval}'[interval to watch for changes]:duration: '
+ '(operation)' \ '(-j --json)'{-j,--json}'[format task list as JSON]'
{-l,--list}'[list describable tasks]' \ '(--nested)--nested[nest namespaces when listing as JSON]'
{-a,--list-all}'[list all tasks]' \ '(--no-status)--no-status[ignore status when listing as JSON]'
{-i,--init}'[create new Taskfile.yml]' \ '(-o --output)'{-o,--output}'[set output style]:style:(interleaved group prefixed)'
'(-*)'{-h,--help}'[show help]' \ '(--output-group-begin)--output-group-begin[message template before grouped output]:template text: '
'(-*)--version[show version and exit]' \ '(--output-group-end)--output-group-end[message template after grouped output]:template text: '
'*: :__task_list' '(--output-group-error-only)--output-group-error-only[hide output from successful tasks]'
'(-s --silent)'{-s,--silent}'[disable echoing]'
'(--sort)--sort[set task sorting order]:order:(default alphanumeric none)'
'(--status)--status[exit non-zero if supplied tasks not up-to-date]'
'(--summary)--summary[show summary\: field from tasks instead of running them]'
'(-t --taskfile)'{-t,--taskfile}'[specify a different taskfile]:taskfile:_files'
'(-v --verbose)'{-v,--verbose}'[verbose mode]'
'(-w --watch)'{-w,--watch}'[watch-mode for given tasks, re-run when inputs change]'
'(-y --yes)'{-y,--yes}'[assume yes to all prompts]'
)
# Experimental flags (dynamically added based on enabled experiments)
# Options (modify behavior)
if __task_is_experiment_enabled "GENTLE_FORCE"; then
standard_args+=('(--force-all)--force-all[force execution of task and all dependencies]')
fi
if __task_is_experiment_enabled "REMOTE_TASKFILES"; then
standard_args+=(
'(--offline)--offline[use only local or cached Taskfiles]'
'(--timeout)--timeout[timeout for remote Taskfile downloads]:duration: '
'(--expiry)--expiry[cache expiry duration]:duration: '
)
fi
operation_args=(
+ '(operation)'
{-l,--list}'[list describable tasks]'
{-a,--list-all}'[list all tasks]'
{-i,--init}'[create new Taskfile.yml]'
'(-*)'{-h,--help}'[show help]'
'(-*)--version[show version and exit]'
)
# Experimental operations (dynamically added based on enabled experiments)
if __task_is_experiment_enabled "REMOTE_TASKFILES"; then
operation_args+=(
'--download[download remote Taskfile]'
'--clear-cache[clear remote Taskfile cache]'
)
fi
# Task names completion (must be last)
operation_args+=('*: :__task_list')
_arguments $standard_args $operation_args
} }
# don't run the completion function when being source-ed or eval-ed # don't run the completion function when being source-ed or eval-ed