Files
go-task/taskfile/ast/taskfile.go
Valentin Maerten fe542d5418 feat: add gitignore option to exclude ignored files from sources/generates
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.
2026-06-07 15:02:39 +02:00

129 lines
2.9 KiB
Go

package ast
import (
"fmt"
"time"
"github.com/Masterminds/semver/v3"
"go.yaml.in/yaml/v3"
"github.com/go-task/task/v3/errors"
)
// NamespaceSeparator contains the character that separates namespaces
const NamespaceSeparator = ":"
var V3 = semver.MustParse("3")
// ErrIncludedTaskfilesCantHaveDotenvs is returned when a included Taskfile contains dotenvs
var ErrIncludedTaskfilesCantHaveDotenvs = errors.New("task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile")
// Taskfile is the abstract syntax tree for a Taskfile
type Taskfile struct {
Location string
Version *semver.Version
Output Output
Method string
Includes *Includes
Set []string
Shopt []string
Vars *Vars
Env *Vars
Tasks *Tasks
Silent bool
Dotenv []string
Run string
Interval time.Duration
Gitignore bool
}
// Merge merges the second Taskfile into the first
func (t1 *Taskfile) Merge(t2 *Taskfile, include *Include) error {
if !t1.Version.Equal(t2.Version) {
return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
}
if len(t2.Dotenv) > 0 {
return ErrIncludedTaskfilesCantHaveDotenvs
}
if t2.Output.IsSet() {
t1.Output = t2.Output
}
if t1.Includes == nil {
t1.Includes = NewIncludes()
}
if t1.Vars == nil {
t1.Vars = NewVars()
}
if t1.Env == nil {
t1.Env = NewVars()
}
if t1.Tasks == nil {
t1.Tasks = NewTasks()
}
if t2.Silent {
for _, t := range t2.Tasks.All(nil) {
if t.Silent == nil {
v := true
t.Silent = &v
}
}
}
t1.Vars.Merge(t2.Vars, include)
t1.Env.Merge(t2.Env, include)
return t1.Tasks.Merge(t2.Tasks, include, t1.Vars)
}
func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
switch node.Kind {
case yaml.MappingNode:
var taskfile struct {
Version *semver.Version
Output Output
Method string
Includes *Includes
Set []string
Shopt []string
Vars *Vars
Env *Vars
Tasks *Tasks
Silent bool
Dotenv []string
Run string
Interval time.Duration
Gitignore bool
}
if err := node.Decode(&taskfile); err != nil {
return errors.NewTaskfileDecodeError(err, node)
}
tf.Version = taskfile.Version
tf.Output = taskfile.Output
tf.Method = taskfile.Method
tf.Includes = taskfile.Includes
tf.Set = taskfile.Set
tf.Shopt = taskfile.Shopt
tf.Vars = taskfile.Vars
tf.Env = taskfile.Env
tf.Tasks = taskfile.Tasks
tf.Silent = taskfile.Silent
tf.Dotenv = taskfile.Dotenv
tf.Run = taskfile.Run
tf.Interval = taskfile.Interval
tf.Gitignore = taskfile.Gitignore
if tf.Includes == nil {
tf.Includes = NewIncludes()
}
if tf.Vars == nil {
tf.Vars = NewVars()
}
if tf.Env == nil {
tf.Env = NewVars()
}
if tf.Tasks == nil {
tf.Tasks = NewTasks()
}
return nil
}
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("taskfile")
}