mirror of
https://github.com/go-task/task.git
synced 2025-11-23 22:24:45 +02:00
feat(summary): add vars, env, and requires display
This commit is contained in:
@@ -61,13 +61,14 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
|
||||
newVar := templater.ReplaceVar(v, cache)
|
||||
// If the variable should not be evaluated, but is nil, set it to an empty string
|
||||
// This stops empty interface errors when using the templater to replace values later
|
||||
// Preserve the Sh field so it can be displayed in summary
|
||||
if !evaluateShVars && newVar.Value == nil {
|
||||
result.Set(k, ast.Var{Value: ""})
|
||||
result.Set(k, ast.Var{Value: "", Sh: newVar.Sh})
|
||||
return nil
|
||||
}
|
||||
// If the variable should not be evaluated and it is set, we can set it and return
|
||||
if !evaluateShVars {
|
||||
result.Set(k, ast.Var{Value: newVar.Value})
|
||||
result.Set(k, ast.Var{Value: newVar.Value, Sh: newVar.Sh})
|
||||
return nil
|
||||
}
|
||||
// Now we can check for errors since we've handled all the cases when we don't want to evaluate
|
||||
|
||||
@@ -621,6 +621,30 @@ func TestAlias(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestSummaryWithVarsAndRequires(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Test basic case from prompt.md - vars and requires
|
||||
NewExecutorTest(t,
|
||||
WithName("vars-and-requires"),
|
||||
WithExecutorOptions(
|
||||
task.WithDir("testdata/summary-vars-requires"),
|
||||
task.WithSummary(true),
|
||||
),
|
||||
WithTask("mytask"),
|
||||
)
|
||||
|
||||
// Test with shell variables
|
||||
NewExecutorTest(t,
|
||||
WithName("shell-vars"),
|
||||
WithExecutorOptions(
|
||||
task.WithDir("testdata/summary-vars-requires"),
|
||||
task.WithSummary(true),
|
||||
),
|
||||
WithTask("with-sh-var"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestLabel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package summary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-task/task/v3/internal/logger"
|
||||
@@ -29,6 +31,9 @@ func PrintSpaceBetweenSummaries(l *logger.Logger, i int) {
|
||||
func PrintTask(l *logger.Logger, t *ast.Task) {
|
||||
printTaskName(l, t)
|
||||
printTaskDescribingText(t, l)
|
||||
printTaskVars(l, t)
|
||||
printTaskEnv(l, t)
|
||||
printTaskRequires(l, t)
|
||||
printTaskDependencies(l, t)
|
||||
printTaskAliases(l, t)
|
||||
printTaskCommands(l, t)
|
||||
@@ -118,3 +123,186 @@ func printTaskCommands(l *logger.Logger, t *ast.Task) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printTaskVars prints the variables defined in a task in YAML format.
|
||||
// It displays the vars section with proper indentation and formatting.
|
||||
// Filters out OS environment variables, auto-generated Task variables, and Taskfile env vars.
|
||||
// Returns early if the task has no variables defined.
|
||||
func printTaskVars(l *logger.Logger, t *ast.Task) {
|
||||
if t.Vars == nil || t.Vars.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a set of OS environment variable names to filter them out
|
||||
osEnvVars := getEnvVarNames()
|
||||
|
||||
// Create a set of Taskfile env variable names to avoid duplication
|
||||
taskfileEnvVars := make(map[string]bool)
|
||||
if t.Env != nil {
|
||||
for key := range t.Env.All() {
|
||||
taskfileEnvVars[key] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are any non-environment variables to display
|
||||
hasNonEnvVars := false
|
||||
for key := range t.Vars.All() {
|
||||
if !isEnvVar(key, osEnvVars) && !taskfileEnvVars[key] {
|
||||
hasNonEnvVars = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasNonEnvVars {
|
||||
return
|
||||
}
|
||||
|
||||
l.Outf(logger.Default, "\n")
|
||||
l.Outf(logger.Default, "vars:\n")
|
||||
|
||||
for key, value := range t.Vars.All() {
|
||||
// Only display variables that are not from OS environment or Taskfile env
|
||||
if !isEnvVar(key, osEnvVars) && !taskfileEnvVars[key] {
|
||||
formattedValue := formatVarValue(value)
|
||||
l.Outf(logger.Yellow, " %s: %s\n", key, formattedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printTaskEnv prints the environment variables defined in a task in YAML format.
|
||||
// It displays the env section with proper indentation and formatting.
|
||||
// Filters out OS environment variables and auto-generated Task variables.
|
||||
// Returns early if the task has no environment variables defined.
|
||||
func printTaskEnv(l *logger.Logger, t *ast.Task) {
|
||||
if t.Env == nil || t.Env.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a set of OS environment variable names to filter them out
|
||||
envVars := getEnvVarNames()
|
||||
|
||||
// Check if there are any non-environment variables to display
|
||||
hasNonEnvVars := false
|
||||
for key := range t.Env.All() {
|
||||
if !isEnvVar(key, envVars) {
|
||||
hasNonEnvVars = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasNonEnvVars {
|
||||
return
|
||||
}
|
||||
|
||||
l.Outf(logger.Default, "\n")
|
||||
l.Outf(logger.Default, "env:\n")
|
||||
|
||||
for key, value := range t.Env.All() {
|
||||
// Only display variables that are not from OS environment
|
||||
if !isEnvVar(key, envVars) {
|
||||
formattedValue := formatVarValue(value)
|
||||
l.Outf(logger.Yellow, " %s: %s\n", key, formattedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatVarValue formats a variable value based on its type.
|
||||
// Handles static values, shell commands (sh:), references (ref:), and maps.
|
||||
func formatVarValue(v ast.Var) string {
|
||||
// Shell command - check this first before Value
|
||||
// because dynamic vars may have both Sh and an empty Value
|
||||
if v.Sh != nil {
|
||||
return fmt.Sprintf("sh: %s", *v.Sh)
|
||||
}
|
||||
|
||||
// Reference
|
||||
if v.Ref != "" {
|
||||
return fmt.Sprintf("ref: %s", v.Ref)
|
||||
}
|
||||
|
||||
// Static value
|
||||
if v.Value != nil {
|
||||
// Check if it's a map or complex type
|
||||
if m, ok := v.Value.(map[string]any); ok {
|
||||
return formatMap(m, 4)
|
||||
}
|
||||
// Simple string value
|
||||
return fmt.Sprintf(`"%v"`, v.Value)
|
||||
}
|
||||
|
||||
return `""`
|
||||
}
|
||||
|
||||
// formatMap formats a map value with proper indentation for YAML.
|
||||
func formatMap(m map[string]any, indent int) string {
|
||||
if len(m) == 0 {
|
||||
return "{}"
|
||||
}
|
||||
|
||||
var result strings.Builder
|
||||
result.WriteString("\n")
|
||||
spaces := strings.Repeat(" ", indent)
|
||||
|
||||
for k, v := range m {
|
||||
result.WriteString(fmt.Sprintf("%s%s: %v\n", spaces, k, v))
|
||||
}
|
||||
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// printTaskRequires prints the required variables for a task in YAML format.
|
||||
// It displays the requires section with proper indentation and formatting.
|
||||
// Returns early if the task has no required variables.
|
||||
func printTaskRequires(l *logger.Logger, t *ast.Task) {
|
||||
if t.Requires == nil || len(t.Requires.Vars) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
l.Outf(logger.Default, "\n")
|
||||
l.Outf(logger.Default, "requires:\n")
|
||||
l.Outf(logger.Default, " vars:\n")
|
||||
|
||||
for _, v := range t.Requires.Vars {
|
||||
// If the variable has enum constraints, format accordingly
|
||||
if len(v.Enum) > 0 {
|
||||
l.Outf(logger.Yellow, " - %s:\n", v.Name)
|
||||
l.Outf(logger.Yellow, " enum:\n")
|
||||
for _, enumValue := range v.Enum {
|
||||
l.Outf(logger.Yellow, " - %s\n", enumValue)
|
||||
}
|
||||
} else {
|
||||
// Simple required variable
|
||||
l.Outf(logger.Yellow, " - %s\n", v.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getEnvVarNames returns a set of all OS environment variable names.
|
||||
func getEnvVarNames() map[string]bool {
|
||||
envMap := make(map[string]bool)
|
||||
for _, e := range os.Environ() {
|
||||
parts := strings.SplitN(e, "=", 2)
|
||||
if len(parts) > 0 {
|
||||
envMap[parts[0]] = true
|
||||
}
|
||||
}
|
||||
return envMap
|
||||
}
|
||||
|
||||
// isEnvVar checks if a variable is from OS environment or auto-generated by Task.
|
||||
func isEnvVar(key string, envVars map[string]bool) bool {
|
||||
// Filter out auto-generated Task variables
|
||||
if strings.HasPrefix(key, "TASK_") ||
|
||||
strings.HasPrefix(key, "CLI_") ||
|
||||
strings.HasPrefix(key, "ROOT_") ||
|
||||
key == "TASK" ||
|
||||
key == "TASKFILE" ||
|
||||
key == "TASKFILE_DIR" ||
|
||||
key == "USER_WORKING_DIR" ||
|
||||
key == "ALIAS" ||
|
||||
key == "MATCH" {
|
||||
return true
|
||||
}
|
||||
// Filter out OS environment variables
|
||||
return envVars[key]
|
||||
}
|
||||
|
||||
21
testdata/summary-vars-requires/Taskfile-with-env.yml
vendored
Normal file
21
testdata/summary-vars-requires/Taskfile-with-env.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
version: 3
|
||||
|
||||
vars:
|
||||
GLOBAL_VAR: "I am a global var"
|
||||
|
||||
env:
|
||||
GLOBAL_ENV: "I am a global env"
|
||||
|
||||
tasks:
|
||||
test-env:
|
||||
desc: Task with vars and env
|
||||
vars:
|
||||
LOCAL_VAR: "I am a local var"
|
||||
env:
|
||||
LOCAL_ENV: "I am a local env"
|
||||
DATABASE_URL: "postgres://localhost/mydb"
|
||||
requires:
|
||||
vars:
|
||||
- API_KEY
|
||||
cmds:
|
||||
- echo "Testing env vars"
|
||||
16
testdata/summary-vars-requires/Taskfile-with-globals.yml
vendored
Normal file
16
testdata/summary-vars-requires/Taskfile-with-globals.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
version: 3
|
||||
|
||||
vars:
|
||||
GLOBAL_VAR: "I am global"
|
||||
ANOTHER_GLOBAL: "Also global"
|
||||
|
||||
tasks:
|
||||
test-globals:
|
||||
desc: Task with global and local vars
|
||||
vars:
|
||||
LOCAL_VAR: "I am local"
|
||||
requires:
|
||||
vars:
|
||||
- REQUIRED_VAR
|
||||
cmds:
|
||||
- echo {{ .GLOBAL_VAR }} {{ .LOCAL_VAR }}
|
||||
36
testdata/summary-vars-requires/Taskfile.yml
vendored
Normal file
36
testdata/summary-vars-requires/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
mytask:
|
||||
desc: It does things
|
||||
summary: |
|
||||
It does things and has optional and required variables.
|
||||
vars:
|
||||
OPTIONAL_VAR: "hello"
|
||||
requires:
|
||||
vars:
|
||||
- REQUIRED_VAR
|
||||
cmds:
|
||||
- cmd: echo {{ .OPTIONAL_VAR }} {{ .REQUIRED_VAR }}
|
||||
|
||||
with-sh-var:
|
||||
desc: Task with shell variable
|
||||
vars:
|
||||
DYNAMIC_VAR:
|
||||
sh: echo "world"
|
||||
STATIC_VAR: "hello"
|
||||
cmds:
|
||||
- echo {{ .DYNAMIC_VAR }}
|
||||
|
||||
no-vars:
|
||||
desc: Task without variables
|
||||
cmds:
|
||||
- echo "no vars here"
|
||||
|
||||
only-requires:
|
||||
desc: Task with only requires
|
||||
requires:
|
||||
vars:
|
||||
- NEEDED_VAR
|
||||
cmds:
|
||||
- echo {{ .NEEDED_VAR }}
|
||||
10
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-shell-vars.golden
vendored
Normal file
10
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-shell-vars.golden
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
task: with-sh-var
|
||||
|
||||
Task with shell variable
|
||||
|
||||
vars:
|
||||
DYNAMIC_VAR: sh: echo "world"
|
||||
STATIC_VAR: "hello"
|
||||
|
||||
commands:
|
||||
- echo
|
||||
13
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-vars-and-requires.golden
vendored
Normal file
13
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-vars-and-requires.golden
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
task: mytask
|
||||
|
||||
It does things and has optional and required variables.
|
||||
|
||||
vars:
|
||||
OPTIONAL_VAR: "hello"
|
||||
|
||||
requires:
|
||||
vars:
|
||||
- REQUIRED_VAR
|
||||
|
||||
commands:
|
||||
- echo hello
|
||||
Reference in New Issue
Block a user