Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22.15.0
22.16.0
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
to Task after the `--` (the same as `CLI_ARGS`, but an array instead of a
string). (#2138, #2139, #2140 by @pd93).
- Added `toYaml` and `fromYaml` templating functions (#2217, #2219 by @pd93).
- Added `task` field the `--list --json` output (#2256 by @aleksandersh).
- Added the ability to
[pin included taskfiles](https://taskfile.dev/next/experiments/remote-taskfiles/#manual-checksum-pinning)
by specifying a checksum. This works with both local and remote Taskfiles
(#2222, #2223 by @pd93).
- When using the
[Remote Taskfiles experiment](https://github.com/go-task/task/issues/1317),
any credentials used in the URL will now be redacted in Task's output (#2100,
#2220 by @pd93).
- Fixed fuzzy suggestions not working when misspelling a task name (#2192, #2200
by @vmaerten).
- Fixed a bug where taskfiles in directories containing spaces created
Expand Down
1 change: 1 addition & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (
CodeTaskfileNetworkTimeout
CodeTaskfileInvalid
CodeTaskfileCycle
CodeTaskfileDoesNotMatchChecksum
)

// Task related exit codes
Expand Down
21 changes: 21 additions & 0 deletions errors/errors_taskfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,24 @@ func (err TaskfileCycleError) Error() string {
func (err TaskfileCycleError) Code() int {
return CodeTaskfileCycle
}

// TaskfileDoesNotMatchChecksum is returned when a Taskfile's checksum does not
// match the one pinned in the parent Taskfile.
type TaskfileDoesNotMatchChecksum struct {
URI string
ExpectedChecksum string
ActualChecksum string
}

func (err *TaskfileDoesNotMatchChecksum) Error() string {
return fmt.Sprintf(
"task: The checksum of the Taskfile at %q does not match!\ngot: %q\nwant: %q",
err.URI,
err.ActualChecksum,
err.ExpectedChecksum,
)
}

func (err *TaskfileDoesNotMatchChecksum) Code() int {
return CodeTaskfileDoesNotMatchChecksum
}
20 changes: 20 additions & 0 deletions executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -958,3 +958,23 @@ func TestFuzzyModel(t *testing.T) {
WithTask("install"),
)
}

func TestIncludeChecksum(t *testing.T) {
t.Parallel()

NewExecutorTest(t,
WithName("correct"),
WithExecutorOptions(
task.WithDir("testdata/includes_checksum/correct"),
),
)

NewExecutorTest(t,
WithName("incorrect"),
WithExecutorOptions(
task.WithDir("testdata/includes_checksum/incorrect"),
),
WithSetupError(),
WithPostProcessFn(PPRemoveAbsolutePaths),
)
}
20 changes: 20 additions & 0 deletions formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,23 @@ func TestListDescInterpolation(t *testing.T) {
}),
)
}

func TestJsonListFormat(t *testing.T) {
t.Parallel()

fp, err := filepath.Abs("testdata/json_list_format/Taskfile.yml")
require.NoError(t, err)
NewFormatterTest(t,
WithExecutorOptions(
task.WithDir("testdata/json_list_format"),
),
WithListOptions(task.ListOptions{
FormatTaskListAsJSON: true,
}),
WithFixtureTemplateData(struct {
TaskfileLocation string
}{
TaskfileLocation: fp,
}),
)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.23.0
require (
github.com/Ladicle/tabwriter v1.0.0
github.com/Masterminds/semver/v3 v3.3.1
github.com/alecthomas/chroma/v2 v2.17.2
github.com/alecthomas/chroma/v2 v2.18.0
github.com/chainguard-dev/git-urls v1.0.2
github.com/davecgh/go-spew v1.1.1
github.com/dominikbraun/graph v0.23.0
Expand Down
12 changes: 2 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNx
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.17.0 h1:3r2Cgk+nXNICMBxIFGnTRTbQFUwMiLisW+9uos0TtUI=
github.com/alecthomas/chroma/v2 v2.17.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI=
github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
Expand Down Expand Up @@ -143,8 +141,6 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbR
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -156,13 +152,9 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand Down
1 change: 1 addition & 0 deletions help.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Ta
g.Go(func() error {
o.Tasks[i] = editors.Task{
Name: tasks[i].Name(),
Task: tasks[i].Task,
Desc: tasks[i].Desc,
Summary: tasks[i].Summary,
Aliases: aliases,
Expand Down
1 change: 1 addition & 0 deletions internal/editors/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type (
// Task describes a single task
Task struct {
Name string `json:"name"`
Task string `json:"task"`
Desc string `json:"desc"`
Summary string `json:"summary"`
Aliases []string `json:"aliases"`
Expand Down
17 changes: 6 additions & 11 deletions internal/fsnotifyext/fsnotify_dedup.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package fsnotifyext

import (
"math"
"sync"
"time"

"github.com/fsnotify/fsnotify"
Expand All @@ -11,7 +10,6 @@ import (
type Deduper struct {
w *fsnotify.Watcher
waitTime time.Duration
mutex sync.Mutex
}

func NewDeduper(w *fsnotify.Watcher, waitTime time.Duration) *Deduper {
Expand All @@ -21,31 +19,28 @@ func NewDeduper(w *fsnotify.Watcher, waitTime time.Duration) *Deduper {
}
}

func (d *Deduper) GetChan() chan fsnotify.Event {
// GetChan returns a chan of deduplicated [fsnotify.Event].
//
// [fsnotify.Chmod] operations will be skipped.
func (d *Deduper) GetChan() <-chan fsnotify.Event {
channel := make(chan fsnotify.Event)
timers := make(map[string]*time.Timer)

go func() {
timers := make(map[string]*time.Timer)
for {
event, ok := <-d.w.Events
switch {
case !ok:
return
case event.Op == fsnotify.Chmod:
case event.Has(fsnotify.Chmod):
continue
}

d.mutex.Lock()
timer, ok := timers[event.String()]
d.mutex.Unlock()

if !ok {
timer = time.AfterFunc(math.MaxInt64, func() { channel <- event })
timer.Stop()

d.mutex.Lock()
timers[event.String()] = timer
d.mutex.Unlock()
}

timer.Reset(d.waitTime)
Expand Down
38 changes: 32 additions & 6 deletions task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ type (
FormatterTestOption
}
TaskTest struct {
name string
experiments map[*experiments.Experiment]int
postProcessFns []PostProcessFn
name string
experiments map[*experiments.Experiment]int
postProcessFns []PostProcessFn
fixtureTemplateData any
}
)

Expand Down Expand Up @@ -79,7 +80,11 @@ func (tt *TaskTest) writeFixture(
if goldenFileSuffix != "" {
goldenFileName += "-" + goldenFileSuffix
}
g.Assert(t, goldenFileName, b)
if tt.fixtureTemplateData != nil {
g.AssertWithTemplate(t, goldenFileName, tt.fixtureTemplateData, b)
} else {
g.Assert(t, goldenFileName, b)
}
}

// writeFixtureBuffer is a wrapper for writing the main output of the task to a
Expand Down Expand Up @@ -234,6 +239,26 @@ func (opt *setupErrorTestOption) applyToFormatterTest(t *FormatterTest) {
t.wantSetupError = true
}

// WithFixtureTemplateData sets up data defined in the golden file using golang
// template. Useful if the golden file can change depending on the test.
// Example template: {{ .Value }}
// Example data definition: struct{ Value string }{Value: "value"}
func WithFixtureTemplateData(data any) TestOption {
return &fixtureTemplateDataTestOption{data: data}
}

type fixtureTemplateDataTestOption struct {
data any
}

func (opt *fixtureTemplateDataTestOption) applyToExecutorTest(t *ExecutorTest) {
t.fixtureTemplateData = opt.data
}

func (opt *fixtureTemplateDataTestOption) applyToFormatterTest(t *FormatterTest) {
t.fixtureTemplateData = opt.data
}

// Post-processing

// A PostProcessFn is a function that can be applied to the output of a test
Expand Down Expand Up @@ -702,6 +727,7 @@ func TestIncludesRemote(t *testing.T) {
enableExperimentForTest(t, &experiments.RemoteTaskfiles, 1)

dir := "testdata/includes_remote"
os.RemoveAll(filepath.Join(dir, ".task", "remote"))

srv := httptest.NewServer(http.FileServer(http.Dir(dir)))
defer srv.Close()
Expand Down Expand Up @@ -777,8 +803,8 @@ func TestIncludesRemote(t *testing.T) {
},
}

for j, e := range executors {
t.Run(fmt.Sprint(j), func(t *testing.T) {
for _, e := range executors {
t.Run(e.name, func(t *testing.T) {
require.NoError(t, e.executor.Setup())

for k, taskCall := range taskCalls {
Expand Down
5 changes: 5 additions & 0 deletions taskfile/ast/include.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type (
AdvancedImport bool
Vars *Vars
Flatten bool
Checksum string
}
// Includes is an ordered map of namespaces to includes.
Includes struct {
Expand Down Expand Up @@ -165,6 +166,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error {
Aliases []string
Excludes []string
Vars *Vars
Checksum string
}
if err := node.Decode(&includedTaskfile); err != nil {
return errors.NewTaskfileDecodeError(err, node)
Expand All @@ -178,6 +180,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error {
include.AdvancedImport = true
include.Vars = includedTaskfile.Vars
include.Flatten = includedTaskfile.Flatten
include.Checksum = includedTaskfile.Checksum
return nil
}

Expand All @@ -200,5 +203,7 @@ func (include *Include) DeepCopy() *Include {
AdvancedImport: include.AdvancedImport,
Vars: include.Vars.DeepCopy(),
Flatten: include.Flatten,
Aliases: deepcopy.Slice(include.Aliases),
Checksum: include.Checksum,
}
}
2 changes: 2 additions & 0 deletions taskfile/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Node interface {
Parent() Node
Location() string
Dir() string
Checksum() string
Verify(checksum string) bool
ResolveEntrypoint(entrypoint string) (string, error)
ResolveDir(dir string) (string, error)
}
Expand Down
35 changes: 25 additions & 10 deletions taskfile/node_base.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
package taskfile

type (
NodeOption func(*BaseNode)
// BaseNode is a generic node that implements the Parent() methods of the
NodeOption func(*baseNode)
// baseNode is a generic node that implements the Parent() methods of the
// NodeReader interface. It does not implement the Read() method and it
// designed to be embedded in other node types so that this boilerplate code
// does not need to be repeated.
BaseNode struct {
parent Node
dir string
baseNode struct {
parent Node
dir string
checksum string
}
)

func NewBaseNode(dir string, opts ...NodeOption) *BaseNode {
node := &BaseNode{
func NewBaseNode(dir string, opts ...NodeOption) *baseNode {
node := &baseNode{
parent: nil,
dir: dir,
}
Expand All @@ -27,15 +28,29 @@ func NewBaseNode(dir string, opts ...NodeOption) *BaseNode {
}

func WithParent(parent Node) NodeOption {
return func(node *BaseNode) {
return func(node *baseNode) {
node.parent = parent
}
}

func (node *BaseNode) Parent() Node {
func WithChecksum(checksum string) NodeOption {
return func(node *baseNode) {
node.checksum = checksum
}
}

func (node *baseNode) Parent() Node {
return node.parent
}

func (node *BaseNode) Dir() string {
func (node *baseNode) Dir() string {
return node.dir
}

func (node *baseNode) Checksum() string {
return node.checksum
}

func (node *baseNode) Verify(checksum string) bool {
return node.checksum == "" || node.checksum == checksum
}
Loading