mirror of
https://github.com/go-task/task.git
synced 2026-06-23 12:45:52 +00:00
When `gitignore: true` is set at the Taskfile or task level, files matching .gitignore rules are automatically excluded from sources and generates glob resolution. This prevents rebuilds triggered by changes to files that are in .gitignore (build artifacts, generated files, etc.). Uses go-git to load .gitignore patterns including nested .gitignore files, .git/info/exclude, and global gitignore configuration.
260 lines
6.8 KiB
Go
260 lines
6.8 KiB
Go
package ast
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"go.yaml.in/yaml/v3"
|
|
|
|
"github.com/go-task/task/v3/errors"
|
|
"github.com/go-task/task/v3/internal/deepcopy"
|
|
)
|
|
|
|
// Task represents a task
|
|
type Task struct {
|
|
Task string `hash:"ignore"`
|
|
Cmds []*Cmd
|
|
Deps []*Dep
|
|
Label string
|
|
Desc string
|
|
Prompt Prompt
|
|
Summary string
|
|
Requires *Requires
|
|
Aliases []string
|
|
Sources []*Glob
|
|
Generates []*Glob
|
|
Status []string
|
|
Preconditions []*Precondition
|
|
Dir string
|
|
Set []string
|
|
Shopt []string
|
|
Vars *Vars
|
|
Env *Vars
|
|
Dotenv []string
|
|
Silent *bool
|
|
Interactive bool
|
|
Internal bool
|
|
Method string
|
|
Prefix string `hash:"ignore"`
|
|
IgnoreError bool
|
|
Gitignore *bool
|
|
Run string
|
|
Platforms []*Platform
|
|
If string
|
|
Watch bool
|
|
Location *Location
|
|
Failfast bool
|
|
// Populated during merging
|
|
Namespace string `hash:"ignore"`
|
|
IncludeVars *Vars
|
|
IncludedTaskfileVars *Vars
|
|
|
|
FullName string `hash:"ignore"`
|
|
}
|
|
|
|
func (t *Task) Name() string {
|
|
if t.Label != "" {
|
|
return t.Label
|
|
}
|
|
if t.FullName != "" {
|
|
return t.FullName
|
|
}
|
|
return t.Task
|
|
}
|
|
|
|
func (t *Task) LocalName() string {
|
|
name := t.FullName
|
|
name = strings.TrimPrefix(name, t.Namespace)
|
|
name = strings.TrimPrefix(name, ":")
|
|
return name
|
|
}
|
|
|
|
// IsSilent returns true if the task has silent mode explicitly enabled.
|
|
// Returns false if Silent is nil (not set) or explicitly set to false.
|
|
func (t *Task) IsSilent() bool {
|
|
return t.Silent != nil && *t.Silent
|
|
}
|
|
|
|
// IsGitignore returns true if the task has gitignore filtering explicitly enabled.
|
|
// Returns false if Gitignore is nil (not set) or explicitly set to false.
|
|
func (t *Task) IsGitignore() bool {
|
|
return t.Gitignore != nil && *t.Gitignore
|
|
}
|
|
|
|
// WildcardMatch will check if the given string matches the name of the Task and returns any wildcard values.
|
|
func (t *Task) WildcardMatch(name string) (bool, []string) {
|
|
names := append([]string{t.Task}, t.Aliases...)
|
|
|
|
for _, taskName := range names {
|
|
regexStr := fmt.Sprintf("^%s$", strings.ReplaceAll(taskName, "*", "(.*)"))
|
|
regex := regexp.MustCompile(regexStr)
|
|
wildcards := regex.FindStringSubmatch(name)
|
|
|
|
if len(wildcards) == 0 {
|
|
continue
|
|
}
|
|
|
|
// Remove the first match, which is the full string
|
|
wildcards = wildcards[1:]
|
|
wildcardCount := strings.Count(taskName, "*")
|
|
|
|
if len(wildcards) != wildcardCount {
|
|
continue
|
|
}
|
|
|
|
return true, wildcards
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|
switch node.Kind {
|
|
|
|
// Shortcut syntax for a task with a single command
|
|
case yaml.ScalarNode:
|
|
var cmd Cmd
|
|
if err := node.Decode(&cmd); err != nil {
|
|
return errors.NewTaskfileDecodeError(err, node)
|
|
}
|
|
t.Cmds = append(t.Cmds, &cmd)
|
|
return nil
|
|
|
|
// Shortcut syntax for a simple task with a list of commands
|
|
case yaml.SequenceNode:
|
|
var cmds []*Cmd
|
|
if err := node.Decode(&cmds); err != nil {
|
|
return errors.NewTaskfileDecodeError(err, node)
|
|
}
|
|
t.Cmds = cmds
|
|
return nil
|
|
|
|
// Full task object
|
|
case yaml.MappingNode:
|
|
var task struct {
|
|
Cmds []*Cmd
|
|
Cmd *Cmd
|
|
Deps []*Dep
|
|
Label string
|
|
Desc string
|
|
Prompt Prompt
|
|
Summary string
|
|
Aliases []string
|
|
Sources []*Glob
|
|
Generates []*Glob
|
|
Status []string
|
|
Preconditions []*Precondition
|
|
Dir string
|
|
Set []string
|
|
Shopt []string
|
|
Vars *Vars
|
|
Env *Vars
|
|
Dotenv []string
|
|
Silent *bool `yaml:"silent,omitempty"`
|
|
Interactive bool
|
|
Internal bool
|
|
Method string
|
|
Prefix string
|
|
IgnoreError bool `yaml:"ignore_error"`
|
|
Gitignore *bool `yaml:"gitignore,omitempty"`
|
|
Run string
|
|
Platforms []*Platform
|
|
If string
|
|
Requires *Requires
|
|
Watch bool
|
|
Failfast bool
|
|
}
|
|
if err := node.Decode(&task); err != nil {
|
|
return errors.NewTaskfileDecodeError(err, node)
|
|
}
|
|
if task.Cmd != nil {
|
|
if task.Cmds != nil {
|
|
return errors.NewTaskfileDecodeError(nil, node).WithMessage("task cannot have both cmd and cmds")
|
|
}
|
|
t.Cmds = []*Cmd{task.Cmd}
|
|
} else {
|
|
t.Cmds = task.Cmds
|
|
}
|
|
t.Deps = task.Deps
|
|
t.Label = task.Label
|
|
t.Desc = task.Desc
|
|
t.Prompt = task.Prompt
|
|
t.Summary = task.Summary
|
|
t.Aliases = task.Aliases
|
|
t.Sources = task.Sources
|
|
t.Generates = task.Generates
|
|
t.Status = task.Status
|
|
t.Preconditions = task.Preconditions
|
|
t.Dir = task.Dir
|
|
t.Set = task.Set
|
|
t.Shopt = task.Shopt
|
|
t.Vars = task.Vars
|
|
t.Env = task.Env
|
|
t.Dotenv = task.Dotenv
|
|
t.Silent = deepcopy.Scalar(task.Silent)
|
|
t.Interactive = task.Interactive
|
|
t.Internal = task.Internal
|
|
t.Method = task.Method
|
|
t.Prefix = task.Prefix
|
|
t.IgnoreError = task.IgnoreError
|
|
t.Gitignore = deepcopy.Scalar(task.Gitignore)
|
|
t.Run = task.Run
|
|
t.Platforms = task.Platforms
|
|
t.If = task.If
|
|
t.Requires = task.Requires
|
|
t.Watch = task.Watch
|
|
t.Failfast = task.Failfast
|
|
return nil
|
|
}
|
|
|
|
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("task")
|
|
}
|
|
|
|
// DeepCopy creates a new instance of Task and copies
|
|
// data by value from the source struct.
|
|
func (t *Task) DeepCopy() *Task {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
c := &Task{
|
|
Task: t.Task,
|
|
Cmds: deepcopy.Slice(t.Cmds),
|
|
Deps: deepcopy.Slice(t.Deps),
|
|
Label: t.Label,
|
|
Desc: t.Desc,
|
|
Prompt: t.Prompt,
|
|
Summary: t.Summary,
|
|
Aliases: deepcopy.Slice(t.Aliases),
|
|
Sources: deepcopy.Slice(t.Sources),
|
|
Generates: deepcopy.Slice(t.Generates),
|
|
Status: deepcopy.Slice(t.Status),
|
|
Preconditions: deepcopy.Slice(t.Preconditions),
|
|
Dir: t.Dir,
|
|
Set: deepcopy.Slice(t.Set),
|
|
Shopt: deepcopy.Slice(t.Shopt),
|
|
Vars: t.Vars.DeepCopy(),
|
|
Env: t.Env.DeepCopy(),
|
|
Dotenv: deepcopy.Slice(t.Dotenv),
|
|
Silent: deepcopy.Scalar(t.Silent),
|
|
Interactive: t.Interactive,
|
|
Internal: t.Internal,
|
|
Method: t.Method,
|
|
Prefix: t.Prefix,
|
|
IgnoreError: t.IgnoreError,
|
|
Gitignore: deepcopy.Scalar(t.Gitignore),
|
|
Run: t.Run,
|
|
IncludeVars: t.IncludeVars.DeepCopy(),
|
|
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
|
Platforms: deepcopy.Slice(t.Platforms),
|
|
If: t.If,
|
|
Location: t.Location.DeepCopy(),
|
|
Requires: t.Requires.DeepCopy(),
|
|
Namespace: t.Namespace,
|
|
FullName: t.FullName,
|
|
Watch: t.Watch,
|
|
Failfast: t.Failfast,
|
|
}
|
|
return c
|
|
}
|