From 4834ac743cfddeb48193afa5c69a43dca4a5f0de Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sat, 19 Apr 2025 11:54:27 +0000 Subject: [PATCH 01/30] chore: changelog for #2166 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4143aba4f4..0f0dd2b200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,9 @@ - Added an `--expiry` flag which sets the TTL for a remote file cache. By default the value will be 0 (caching disabled). If Task is running in offline mode or fails to make a connection, it will fallback on the cache. +- `.taskrc` files can now be used from subdirectories and will be searched for + recursively up the file tree in the same way that Taskfiles are (#2159, #2166 + by @pd93). - The default taskfile (output when using the `--init` flag) is now an embedded file in the binary instead of being stored in the code (#2112 by @pd93). - Improved the way we report the Task version when using the `--version` flag or From 7eebf6e7047376ac99694349491fedd8183fd5df Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sat, 19 Apr 2025 11:54:48 +0000 Subject: [PATCH 02/30] chore: delete unused exp package --- internal/exp/maps.go | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 internal/exp/maps.go diff --git a/internal/exp/maps.go b/internal/exp/maps.go deleted file mode 100644 index 9d6607504c..0000000000 --- a/internal/exp/maps.go +++ /dev/null @@ -1,26 +0,0 @@ -// This package is intended as a place to copy functions from the -// golang.org/x/exp package. Copying these functions allows us to rely on our -// own code instead of an external package that may change unpredictably in the -// future. -// -// It also prevents problems with transitive dependencies whereby a -// package that imports Task (and therefore our version of golang.org/x/exp) -// cannot import a different version of golang.org/x/exp. -// -// Finally, it serves as a place to track functions that may be able to be -// removed in the future if they are added to the standard library. This is also -// why this package is under the internal directory since these functions are -// not intended to be used outside of Task. -package exp - -import "cmp" - -// Keys is a copy of https://pkg.go.dev/golang.org/x/exp@v0.0.0-20240103183307-be819d1f06fc/maps#Keys. -// This is not yet included in the standard library. See https://github.com/golang/go/issues/61538. -func Keys[K cmp.Ordered, V any](m map[K]V) []K { - var keys []K - for key := range m { - keys = append(keys, key) - } - return keys -} From 672b39413fe8b3c277727c704e6746ea48b3f325 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sat, 19 Apr 2025 12:55:22 +0100 Subject: [PATCH 03/30] feat: mockery v3 (#2110) --- .mockery.yaml | 12 +- Taskfile.yml | 17 +- internal/fingerprint/checker_mock.go | 320 +++++++++++++++++++++++++++ internal/fingerprint/task_test.go | 33 ++- internal/mocks/sources_checkable.go | 226 ------------------- internal/mocks/status_checkable.go | 92 -------- 6 files changed, 356 insertions(+), 344 deletions(-) create mode 100644 internal/fingerprint/checker_mock.go delete mode 100644 internal/mocks/sources_checkable.go delete mode 100644 internal/mocks/status_checkable.go diff --git a/.mockery.yaml b/.mockery.yaml index 2d095f31c0..d329845ec2 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -1,4 +1,8 @@ -with-expecter: true -keeptree: true -case: underscore -output: ./internal/mocks +all: False +template: testify +filename: '{{base (trimSuffix ".go" .InterfaceFile)}}_mock.go' +packages: + github.com/go-task/task/v3/internal/fingerprint: + interfaces: + SourcesCheckable: + StatusCheckable: diff --git a/Taskfile.yml b/Taskfile.yml index ae4d82de7a..bb27299af8 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -32,16 +32,23 @@ tasks: - go install -v ./cmd/task generate: - desc: Runs Mockery to create mocks aliases: [gen, g] + desc: Runs all generate tasks + cmds: + - task: generate:mocks + - task: generate:fixtures + + generate:mocks: + desc: Runs Mockery to create mocks + aliases: [gen:mocks, g:mocks] deps: [install:mockery] sources: - "internal/fingerprint/checker.go" generates: - "internal/mocks/*.go" cmds: - - "{{.BIN}}/mockery --dir ./internal/fingerprint --name SourcesCheckable" - - "{{.BIN}}/mockery --dir ./internal/fingerprint --name StatusCheckable" + - find . -type f -name *_mock.go -delete + - "{{.BIN}}/mockery" generate:fixtures: desc: Runs tests and generates golden fixture files @@ -53,13 +60,13 @@ tasks: install:mockery: desc: Installs mockgen; a tool to generate mock files vars: - MOCKERY_VERSION: v2.24.0 + MOCKERY_VERSION: v3.2.2 env: GOBIN: "{{.BIN}}" status: - go version -m {{.BIN}}/mockery | grep github.com/vektra/mockery | grep {{.MOCKERY_VERSION}} cmds: - - go install github.com/vektra/mockery/v2@{{.MOCKERY_VERSION}} + - GOBIN="{{.BIN}}" go install github.com/vektra/mockery/v3@{{.MOCKERY_VERSION}} mod: desc: Downloads and tidy Go modules diff --git a/internal/fingerprint/checker_mock.go b/internal/fingerprint/checker_mock.go new file mode 100644 index 0000000000..cd572440cf --- /dev/null +++ b/internal/fingerprint/checker_mock.go @@ -0,0 +1,320 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package fingerprint + +import ( + "context" + + "github.com/go-task/task/v3/taskfile/ast" + mock "github.com/stretchr/testify/mock" +) + +// NewMockStatusCheckable creates a new instance of MockStatusCheckable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockStatusCheckable(t interface { + mock.TestingT + Cleanup(func()) +}) *MockStatusCheckable { + mock := &MockStatusCheckable{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockStatusCheckable is an autogenerated mock type for the StatusCheckable type +type MockStatusCheckable struct { + mock.Mock +} + +type MockStatusCheckable_Expecter struct { + mock *mock.Mock +} + +func (_m *MockStatusCheckable) EXPECT() *MockStatusCheckable_Expecter { + return &MockStatusCheckable_Expecter{mock: &_m.Mock} +} + +// IsUpToDate provides a mock function for the type MockStatusCheckable +func (_mock *MockStatusCheckable) IsUpToDate(ctx context.Context, t *ast.Task) (bool, error) { + ret := _mock.Called(ctx, t) + + if len(ret) == 0 { + panic("no return value specified for IsUpToDate") + } + + var r0 bool + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, *ast.Task) (bool, error)); ok { + return returnFunc(ctx, t) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, *ast.Task) bool); ok { + r0 = returnFunc(ctx, t) + } else { + r0 = ret.Get(0).(bool) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, *ast.Task) error); ok { + r1 = returnFunc(ctx, t) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockStatusCheckable_IsUpToDate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUpToDate' +type MockStatusCheckable_IsUpToDate_Call struct { + *mock.Call +} + +// IsUpToDate is a helper method to define mock.On call +// - ctx +// - t +func (_e *MockStatusCheckable_Expecter) IsUpToDate(ctx interface{}, t interface{}) *MockStatusCheckable_IsUpToDate_Call { + return &MockStatusCheckable_IsUpToDate_Call{Call: _e.mock.On("IsUpToDate", ctx, t)} +} + +func (_c *MockStatusCheckable_IsUpToDate_Call) Run(run func(ctx context.Context, t *ast.Task)) *MockStatusCheckable_IsUpToDate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*ast.Task)) + }) + return _c +} + +func (_c *MockStatusCheckable_IsUpToDate_Call) Return(b bool, err error) *MockStatusCheckable_IsUpToDate_Call { + _c.Call.Return(b, err) + return _c +} + +func (_c *MockStatusCheckable_IsUpToDate_Call) RunAndReturn(run func(ctx context.Context, t *ast.Task) (bool, error)) *MockStatusCheckable_IsUpToDate_Call { + _c.Call.Return(run) + return _c +} + +// NewMockSourcesCheckable creates a new instance of MockSourcesCheckable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockSourcesCheckable(t interface { + mock.TestingT + Cleanup(func()) +}) *MockSourcesCheckable { + mock := &MockSourcesCheckable{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockSourcesCheckable is an autogenerated mock type for the SourcesCheckable type +type MockSourcesCheckable struct { + mock.Mock +} + +type MockSourcesCheckable_Expecter struct { + mock *mock.Mock +} + +func (_m *MockSourcesCheckable) EXPECT() *MockSourcesCheckable_Expecter { + return &MockSourcesCheckable_Expecter{mock: &_m.Mock} +} + +// IsUpToDate provides a mock function for the type MockSourcesCheckable +func (_mock *MockSourcesCheckable) IsUpToDate(t *ast.Task) (bool, error) { + ret := _mock.Called(t) + + if len(ret) == 0 { + panic("no return value specified for IsUpToDate") + } + + var r0 bool + var r1 error + if returnFunc, ok := ret.Get(0).(func(*ast.Task) (bool, error)); ok { + return returnFunc(t) + } + if returnFunc, ok := ret.Get(0).(func(*ast.Task) bool); ok { + r0 = returnFunc(t) + } else { + r0 = ret.Get(0).(bool) + } + if returnFunc, ok := ret.Get(1).(func(*ast.Task) error); ok { + r1 = returnFunc(t) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockSourcesCheckable_IsUpToDate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUpToDate' +type MockSourcesCheckable_IsUpToDate_Call struct { + *mock.Call +} + +// IsUpToDate is a helper method to define mock.On call +// - t +func (_e *MockSourcesCheckable_Expecter) IsUpToDate(t interface{}) *MockSourcesCheckable_IsUpToDate_Call { + return &MockSourcesCheckable_IsUpToDate_Call{Call: _e.mock.On("IsUpToDate", t)} +} + +func (_c *MockSourcesCheckable_IsUpToDate_Call) Run(run func(t *ast.Task)) *MockSourcesCheckable_IsUpToDate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*ast.Task)) + }) + return _c +} + +func (_c *MockSourcesCheckable_IsUpToDate_Call) Return(b bool, err error) *MockSourcesCheckable_IsUpToDate_Call { + _c.Call.Return(b, err) + return _c +} + +func (_c *MockSourcesCheckable_IsUpToDate_Call) RunAndReturn(run func(t *ast.Task) (bool, error)) *MockSourcesCheckable_IsUpToDate_Call { + _c.Call.Return(run) + return _c +} + +// Kind provides a mock function for the type MockSourcesCheckable +func (_mock *MockSourcesCheckable) Kind() string { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Kind") + } + + var r0 string + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(string) + } + return r0 +} + +// MockSourcesCheckable_Kind_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Kind' +type MockSourcesCheckable_Kind_Call struct { + *mock.Call +} + +// Kind is a helper method to define mock.On call +func (_e *MockSourcesCheckable_Expecter) Kind() *MockSourcesCheckable_Kind_Call { + return &MockSourcesCheckable_Kind_Call{Call: _e.mock.On("Kind")} +} + +func (_c *MockSourcesCheckable_Kind_Call) Run(run func()) *MockSourcesCheckable_Kind_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockSourcesCheckable_Kind_Call) Return(s string) *MockSourcesCheckable_Kind_Call { + _c.Call.Return(s) + return _c +} + +func (_c *MockSourcesCheckable_Kind_Call) RunAndReturn(run func() string) *MockSourcesCheckable_Kind_Call { + _c.Call.Return(run) + return _c +} + +// OnError provides a mock function for the type MockSourcesCheckable +func (_mock *MockSourcesCheckable) OnError(t *ast.Task) error { + ret := _mock.Called(t) + + if len(ret) == 0 { + panic("no return value specified for OnError") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(*ast.Task) error); ok { + r0 = returnFunc(t) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockSourcesCheckable_OnError_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnError' +type MockSourcesCheckable_OnError_Call struct { + *mock.Call +} + +// OnError is a helper method to define mock.On call +// - t +func (_e *MockSourcesCheckable_Expecter) OnError(t interface{}) *MockSourcesCheckable_OnError_Call { + return &MockSourcesCheckable_OnError_Call{Call: _e.mock.On("OnError", t)} +} + +func (_c *MockSourcesCheckable_OnError_Call) Run(run func(t *ast.Task)) *MockSourcesCheckable_OnError_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*ast.Task)) + }) + return _c +} + +func (_c *MockSourcesCheckable_OnError_Call) Return(err error) *MockSourcesCheckable_OnError_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockSourcesCheckable_OnError_Call) RunAndReturn(run func(t *ast.Task) error) *MockSourcesCheckable_OnError_Call { + _c.Call.Return(run) + return _c +} + +// Value provides a mock function for the type MockSourcesCheckable +func (_mock *MockSourcesCheckable) Value(t *ast.Task) (any, error) { + ret := _mock.Called(t) + + if len(ret) == 0 { + panic("no return value specified for Value") + } + + var r0 any + var r1 error + if returnFunc, ok := ret.Get(0).(func(*ast.Task) (any, error)); ok { + return returnFunc(t) + } + if returnFunc, ok := ret.Get(0).(func(*ast.Task) any); ok { + r0 = returnFunc(t) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(any) + } + } + if returnFunc, ok := ret.Get(1).(func(*ast.Task) error); ok { + r1 = returnFunc(t) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockSourcesCheckable_Value_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Value' +type MockSourcesCheckable_Value_Call struct { + *mock.Call +} + +// Value is a helper method to define mock.On call +// - t +func (_e *MockSourcesCheckable_Expecter) Value(t interface{}) *MockSourcesCheckable_Value_Call { + return &MockSourcesCheckable_Value_Call{Call: _e.mock.On("Value", t)} +} + +func (_c *MockSourcesCheckable_Value_Call) Run(run func(t *ast.Task)) *MockSourcesCheckable_Value_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*ast.Task)) + }) + return _c +} + +func (_c *MockSourcesCheckable_Value_Call) Return(v any, err error) *MockSourcesCheckable_Value_Call { + _c.Call.Return(v, err) + return _c +} + +func (_c *MockSourcesCheckable_Value_Call) RunAndReturn(run func(t *ast.Task) (any, error)) *MockSourcesCheckable_Value_Call { + _c.Call.Return(run) + return _c +} diff --git a/internal/fingerprint/task_test.go b/internal/fingerprint/task_test.go index 09fb235f06..5c81c010bb 100644 --- a/internal/fingerprint/task_test.go +++ b/internal/fingerprint/task_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/go-task/task/v3/internal/mocks" "github.com/go-task/task/v3/taskfile/ast" ) @@ -31,8 +30,8 @@ func TestIsTaskUpToDate(t *testing.T) { tests := []struct { name string task *ast.Task - setupMockStatusChecker func(m *mocks.StatusCheckable) - setupMockSourcesChecker func(m *mocks.SourcesCheckable) + setupMockStatusChecker func(m *MockStatusCheckable) + setupMockSourcesChecker func(m *MockSourcesCheckable) expected bool }{ { @@ -52,7 +51,7 @@ func TestIsTaskUpToDate(t *testing.T) { Sources: []*ast.Glob{{Glob: "sources"}}, }, setupMockStatusChecker: nil, - setupMockSourcesChecker: func(m *mocks.SourcesCheckable) { + setupMockSourcesChecker: func(m *MockSourcesCheckable) { m.EXPECT().IsUpToDate(mock.Anything).Return(true, nil) }, expected: true, @@ -64,7 +63,7 @@ func TestIsTaskUpToDate(t *testing.T) { Sources: []*ast.Glob{{Glob: "sources"}}, }, setupMockStatusChecker: nil, - setupMockSourcesChecker: func(m *mocks.SourcesCheckable) { + setupMockSourcesChecker: func(m *MockSourcesCheckable) { m.EXPECT().IsUpToDate(mock.Anything).Return(false, nil) }, expected: false, @@ -75,7 +74,7 @@ func TestIsTaskUpToDate(t *testing.T) { Status: []string{"status"}, Sources: nil, }, - setupMockStatusChecker: func(m *mocks.StatusCheckable) { + setupMockStatusChecker: func(m *MockStatusCheckable) { m.EXPECT().IsUpToDate(mock.Anything, mock.Anything).Return(true, nil) }, setupMockSourcesChecker: nil, @@ -87,10 +86,10 @@ func TestIsTaskUpToDate(t *testing.T) { Status: []string{"status"}, Sources: []*ast.Glob{{Glob: "sources"}}, }, - setupMockStatusChecker: func(m *mocks.StatusCheckable) { + setupMockStatusChecker: func(m *MockStatusCheckable) { m.EXPECT().IsUpToDate(mock.Anything, mock.Anything).Return(true, nil) }, - setupMockSourcesChecker: func(m *mocks.SourcesCheckable) { + setupMockSourcesChecker: func(m *MockSourcesCheckable) { m.EXPECT().IsUpToDate(mock.Anything).Return(true, nil) }, expected: true, @@ -101,10 +100,10 @@ func TestIsTaskUpToDate(t *testing.T) { Status: []string{"status"}, Sources: []*ast.Glob{{Glob: "sources"}}, }, - setupMockStatusChecker: func(m *mocks.StatusCheckable) { + setupMockStatusChecker: func(m *MockStatusCheckable) { m.EXPECT().IsUpToDate(mock.Anything, mock.Anything).Return(true, nil) }, - setupMockSourcesChecker: func(m *mocks.SourcesCheckable) { + setupMockSourcesChecker: func(m *MockSourcesCheckable) { m.EXPECT().IsUpToDate(mock.Anything).Return(false, nil) }, expected: false, @@ -115,7 +114,7 @@ func TestIsTaskUpToDate(t *testing.T) { Status: []string{"status"}, Sources: nil, }, - setupMockStatusChecker: func(m *mocks.StatusCheckable) { + setupMockStatusChecker: func(m *MockStatusCheckable) { m.EXPECT().IsUpToDate(mock.Anything, mock.Anything).Return(false, nil) }, setupMockSourcesChecker: nil, @@ -127,10 +126,10 @@ func TestIsTaskUpToDate(t *testing.T) { Status: []string{"status"}, Sources: []*ast.Glob{{Glob: "sources"}}, }, - setupMockStatusChecker: func(m *mocks.StatusCheckable) { + setupMockStatusChecker: func(m *MockStatusCheckable) { m.EXPECT().IsUpToDate(mock.Anything, mock.Anything).Return(false, nil) }, - setupMockSourcesChecker: func(m *mocks.SourcesCheckable) { + setupMockSourcesChecker: func(m *MockSourcesCheckable) { m.EXPECT().IsUpToDate(mock.Anything).Return(true, nil) }, expected: false, @@ -141,10 +140,10 @@ func TestIsTaskUpToDate(t *testing.T) { Status: []string{"status"}, Sources: []*ast.Glob{{Glob: "sources"}}, }, - setupMockStatusChecker: func(m *mocks.StatusCheckable) { + setupMockStatusChecker: func(m *MockStatusCheckable) { m.EXPECT().IsUpToDate(mock.Anything, mock.Anything).Return(false, nil) }, - setupMockSourcesChecker: func(m *mocks.SourcesCheckable) { + setupMockSourcesChecker: func(m *MockSourcesCheckable) { m.EXPECT().IsUpToDate(mock.Anything).Return(false, nil) }, expected: false, @@ -154,12 +153,12 @@ func TestIsTaskUpToDate(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - mockStatusChecker := mocks.NewStatusCheckable(t) + mockStatusChecker := NewMockStatusCheckable(t) if tt.setupMockStatusChecker != nil { tt.setupMockStatusChecker(mockStatusChecker) } - mockSourcesChecker := mocks.NewSourcesCheckable(t) + mockSourcesChecker := NewMockSourcesCheckable(t) if tt.setupMockSourcesChecker != nil { tt.setupMockSourcesChecker(mockSourcesChecker) } diff --git a/internal/mocks/sources_checkable.go b/internal/mocks/sources_checkable.go deleted file mode 100644 index e234364f1b..0000000000 --- a/internal/mocks/sources_checkable.go +++ /dev/null @@ -1,226 +0,0 @@ -// Code generated by mockery v2.24.0. DO NOT EDIT. - -package mocks - -import ( - ast "github.com/go-task/task/v3/taskfile/ast" - - mock "github.com/stretchr/testify/mock" -) - -// SourcesCheckable is an autogenerated mock type for the SourcesCheckable type -type SourcesCheckable struct { - mock.Mock -} - -type SourcesCheckable_Expecter struct { - mock *mock.Mock -} - -func (_m *SourcesCheckable) EXPECT() *SourcesCheckable_Expecter { - return &SourcesCheckable_Expecter{mock: &_m.Mock} -} - -// IsUpToDate provides a mock function with given fields: t -func (_m *SourcesCheckable) IsUpToDate(t *ast.Task) (bool, error) { - ret := _m.Called(t) - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(*ast.Task) (bool, error)); ok { - return rf(t) - } - if rf, ok := ret.Get(0).(func(*ast.Task) bool); ok { - r0 = rf(t) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(*ast.Task) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SourcesCheckable_IsUpToDate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUpToDate' -type SourcesCheckable_IsUpToDate_Call struct { - *mock.Call -} - -// IsUpToDate is a helper method to define mock.On call -// - t *ast.Task -func (_e *SourcesCheckable_Expecter) IsUpToDate(t interface{}) *SourcesCheckable_IsUpToDate_Call { - return &SourcesCheckable_IsUpToDate_Call{Call: _e.mock.On("IsUpToDate", t)} -} - -func (_c *SourcesCheckable_IsUpToDate_Call) Run(run func(t *ast.Task)) *SourcesCheckable_IsUpToDate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*ast.Task)) - }) - return _c -} - -func (_c *SourcesCheckable_IsUpToDate_Call) Return(_a0 bool, _a1 error) *SourcesCheckable_IsUpToDate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *SourcesCheckable_IsUpToDate_Call) RunAndReturn(run func(*ast.Task) (bool, error)) *SourcesCheckable_IsUpToDate_Call { - _c.Call.Return(run) - return _c -} - -// Kind provides a mock function with given fields: -func (_m *SourcesCheckable) Kind() string { - ret := _m.Called() - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// SourcesCheckable_Kind_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Kind' -type SourcesCheckable_Kind_Call struct { - *mock.Call -} - -// Kind is a helper method to define mock.On call -func (_e *SourcesCheckable_Expecter) Kind() *SourcesCheckable_Kind_Call { - return &SourcesCheckable_Kind_Call{Call: _e.mock.On("Kind")} -} - -func (_c *SourcesCheckable_Kind_Call) Run(run func()) *SourcesCheckable_Kind_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *SourcesCheckable_Kind_Call) Return(_a0 string) *SourcesCheckable_Kind_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *SourcesCheckable_Kind_Call) RunAndReturn(run func() string) *SourcesCheckable_Kind_Call { - _c.Call.Return(run) - return _c -} - -// OnError provides a mock function with given fields: t -func (_m *SourcesCheckable) OnError(t *ast.Task) error { - ret := _m.Called(t) - - var r0 error - if rf, ok := ret.Get(0).(func(*ast.Task) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SourcesCheckable_OnError_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnError' -type SourcesCheckable_OnError_Call struct { - *mock.Call -} - -// OnError is a helper method to define mock.On call -// - t *ast.Task -func (_e *SourcesCheckable_Expecter) OnError(t interface{}) *SourcesCheckable_OnError_Call { - return &SourcesCheckable_OnError_Call{Call: _e.mock.On("OnError", t)} -} - -func (_c *SourcesCheckable_OnError_Call) Run(run func(t *ast.Task)) *SourcesCheckable_OnError_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*ast.Task)) - }) - return _c -} - -func (_c *SourcesCheckable_OnError_Call) Return(_a0 error) *SourcesCheckable_OnError_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *SourcesCheckable_OnError_Call) RunAndReturn(run func(*ast.Task) error) *SourcesCheckable_OnError_Call { - _c.Call.Return(run) - return _c -} - -// Value provides a mock function with given fields: t -func (_m *SourcesCheckable) Value(t *ast.Task) (interface{}, error) { - ret := _m.Called(t) - - var r0 interface{} - var r1 error - if rf, ok := ret.Get(0).(func(*ast.Task) (interface{}, error)); ok { - return rf(t) - } - if rf, ok := ret.Get(0).(func(*ast.Task) interface{}); ok { - r0 = rf(t) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(interface{}) - } - } - - if rf, ok := ret.Get(1).(func(*ast.Task) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SourcesCheckable_Value_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Value' -type SourcesCheckable_Value_Call struct { - *mock.Call -} - -// Value is a helper method to define mock.On call -// - t *ast.Task -func (_e *SourcesCheckable_Expecter) Value(t interface{}) *SourcesCheckable_Value_Call { - return &SourcesCheckable_Value_Call{Call: _e.mock.On("Value", t)} -} - -func (_c *SourcesCheckable_Value_Call) Run(run func(t *ast.Task)) *SourcesCheckable_Value_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*ast.Task)) - }) - return _c -} - -func (_c *SourcesCheckable_Value_Call) Return(_a0 interface{}, _a1 error) *SourcesCheckable_Value_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *SourcesCheckable_Value_Call) RunAndReturn(run func(*ast.Task) (interface{}, error)) *SourcesCheckable_Value_Call { - _c.Call.Return(run) - return _c -} - -type mockConstructorTestingTNewSourcesCheckable interface { - mock.TestingT - Cleanup(func()) -} - -// NewSourcesCheckable creates a new instance of SourcesCheckable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSourcesCheckable(t mockConstructorTestingTNewSourcesCheckable) *SourcesCheckable { - mock := &SourcesCheckable{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/mocks/status_checkable.go b/internal/mocks/status_checkable.go deleted file mode 100644 index d63bd829df..0000000000 --- a/internal/mocks/status_checkable.go +++ /dev/null @@ -1,92 +0,0 @@ -// Code generated by mockery v2.24.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - ast "github.com/go-task/task/v3/taskfile/ast" - - mock "github.com/stretchr/testify/mock" -) - -// StatusCheckable is an autogenerated mock type for the StatusCheckable type -type StatusCheckable struct { - mock.Mock -} - -type StatusCheckable_Expecter struct { - mock *mock.Mock -} - -func (_m *StatusCheckable) EXPECT() *StatusCheckable_Expecter { - return &StatusCheckable_Expecter{mock: &_m.Mock} -} - -// IsUpToDate provides a mock function with given fields: ctx, t -func (_m *StatusCheckable) IsUpToDate(ctx context.Context, t *ast.Task) (bool, error) { - ret := _m.Called(ctx, t) - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *ast.Task) (bool, error)); ok { - return rf(ctx, t) - } - if rf, ok := ret.Get(0).(func(context.Context, *ast.Task) bool); ok { - r0 = rf(ctx, t) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context, *ast.Task) error); ok { - r1 = rf(ctx, t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// StatusCheckable_IsUpToDate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsUpToDate' -type StatusCheckable_IsUpToDate_Call struct { - *mock.Call -} - -// IsUpToDate is a helper method to define mock.On call -// - ctx context.Context -// - t *ast.Task -func (_e *StatusCheckable_Expecter) IsUpToDate(ctx interface{}, t interface{}) *StatusCheckable_IsUpToDate_Call { - return &StatusCheckable_IsUpToDate_Call{Call: _e.mock.On("IsUpToDate", ctx, t)} -} - -func (_c *StatusCheckable_IsUpToDate_Call) Run(run func(ctx context.Context, t *ast.Task)) *StatusCheckable_IsUpToDate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*ast.Task)) - }) - return _c -} - -func (_c *StatusCheckable_IsUpToDate_Call) Return(_a0 bool, _a1 error) *StatusCheckable_IsUpToDate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *StatusCheckable_IsUpToDate_Call) RunAndReturn(run func(context.Context, *ast.Task) (bool, error)) *StatusCheckable_IsUpToDate_Call { - _c.Call.Return(run) - return _c -} - -type mockConstructorTestingTNewStatusCheckable interface { - mock.TestingT - Cleanup(func()) -} - -// NewStatusCheckable creates a new instance of StatusCheckable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewStatusCheckable(t mockConstructorTestingTNewStatusCheckable) *StatusCheckable { - mock := &StatusCheckable{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} From 84cd4dfdade56c205210f079ea7158a07f5451b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 13:52:23 +0200 Subject: [PATCH 04/30] chore(deps): update all non-major dependencies (#2188) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ website/yarn.lock | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 7dc74392e7..142c675c7f 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/fatih/color v1.18.0 github.com/fsnotify/fsnotify v1.9.0 github.com/go-git/go-billy/v5 v5.6.2 - github.com/go-git/go-git/v5 v5.15.0 + github.com/go-git/go-git/v5 v5.16.0 github.com/go-task/slim-sprig/v3 v3.0.0 github.com/go-task/template v0.1.0 github.com/joho/godotenv v1.5.1 diff --git a/go.sum b/go.sum index a100547c2d..3095a5984b 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,8 @@ github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= github.com/go-git/go-git/v5 v5.15.0 h1:f5Qn0W0F7ry1iN0ZwIU5m/n7/BKB4hiZfc+zlZx7ly0= github.com/go-git/go-git/v5 v5.15.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ= +github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= diff --git a/website/yarn.lock b/website/yarn.lock index b3f25326eb..540e89ed37 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2437,9 +2437,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^19.0.0": - version "19.1.1" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.1.tgz#f7f2bb4a0a8d22b9ee4b8f6e27f0c78d76eb7f9b" - integrity sha512-ePapxDL7qrgqSF67s0h9m412d9DbXyC1n59O2st+9rjuuamWsZuD2w55rqY12CbzsZ7uVXb5Nw0gEp9Z8MMutQ== + version "19.1.2" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.2.tgz#11df86f66f188f212c90ecb537327ec68bfd593f" + integrity sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw== dependencies: csstype "^3.0.2" From 7169bf6434eacf526a4903aeafd3095240a08a51 Mon Sep 17 00:00:00 2001 From: Valentin Maerten Date: Mon, 21 Apr 2025 18:43:20 +0200 Subject: [PATCH 05/30] fix: interpolate vars in defer (#2173) --- task.go | 2 ++ task_test.go | 3 +++ testdata/deferred/Taskfile.yml | 15 +++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/task.go b/task.go index 40f7661afa..01dccc13f9 100644 --- a/task.go +++ b/task.go @@ -297,6 +297,8 @@ func (e *Executor) runDeferred(t *ast.Task, call *Call, i int, deferredExitCode } cmd.Cmd = templater.ReplaceWithExtra(cmd.Cmd, cache, extra) + cmd.Task = templater.ReplaceWithExtra(cmd.Task, cache, extra) + cmd.Vars = templater.ReplaceVarsWithExtra(cmd.Vars, cache, extra) if err := e.runCommand(ctx, t, call, i); err != nil { e.Logger.VerboseErrf(logger.Yellow, "task: ignored error in deferred cmd: %s\n", err.Error()) diff --git a/task_test.go b/task_test.go index a2400cd336..73fa285b4f 100644 --- a/task_test.go +++ b/task_test.go @@ -1809,6 +1809,9 @@ task-1 ran successfully `) require.Error(t, e.Run(context.Background(), &task.Call{Task: "task-2"})) assert.Contains(t, buff.String(), expectedOutputOrder) + buff.Reset() + require.NoError(t, e.Run(context.Background(), &task.Call{Task: "parent"})) + assert.Contains(t, buff.String(), "child task deferred value-from-parent") } func TestExitCodeZero(t *testing.T) { diff --git a/testdata/deferred/Taskfile.yml b/testdata/deferred/Taskfile.yml index b193117c7d..9ea3d0aa52 100644 --- a/testdata/deferred/Taskfile.yml +++ b/testdata/deferred/Taskfile.yml @@ -12,3 +12,18 @@ tasks: - defer: echo 'failing' && exit 2 - echo 'cmd ran' - exit 1 + + parent: + vars: + VAR1: "value-from-parent" + cmds: + - defer: + task: child + vars: + VAR1: 'task deferred {{.VAR1}}' + - task: child + vars: + VAR1: 'task immediate {{.VAR1}}' + child: + cmds: + - cmd: echo "child {{.VAR1}}" From 0a6cd1ee4207234da0b0deb03e37de383f29d7e5 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Mon, 21 Apr 2025 13:48:25 -0300 Subject: [PATCH 06/30] chore: add changelog entry for #2173 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0dd2b200..9bc9ff901a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ - The `USER_WORKING_DIR` special now will now properly account for the `--dir` (`-d`) flag, if given (#2102, #2103 by @jaynis, #2186 by @andreynering). - Fix Fish completions when `--global` (`-g`) is given (#2134 by @atusy). +- Fixed variables not available when using `defer:` (#1909, #2173 by @vmaerten). #### Package API From c2123dc016a7e9785bcb42de2ccc99a47fe8e527 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Mon, 21 Apr 2025 13:50:40 -0300 Subject: [PATCH 07/30] v3.43.0 --- CHANGELOG.md | 2 +- internal/version/version.txt | 2 +- package-lock.json | 2 +- package.json | 2 +- website/docs/changelog.mdx | 71 ++++ .../version-latest/changelog.mdx | 71 ++++ .../experiments/map_variables.mdx | 245 ----------- .../experiments/remote_taskfiles.mdx | 195 +++++++-- .../version-latest/getting_started.mdx | 4 +- .../version-latest/installation.mdx | 5 + .../version-latest/reference/cli.mdx | 2 +- .../version-latest/reference/package.mdx | 144 ++++++- .../version-latest/reference/schema.mdx | 5 +- .../version-latest/reference/templating.mdx | 2 +- .../versioned_docs/version-latest/usage.mdx | 396 ++++++++++-------- 15 files changed, 681 insertions(+), 467 deletions(-) delete mode 100644 website/versioned_docs/version-latest/experiments/map_variables.mdx diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bc9ff901a..2eb5e1dea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## v3.43.0 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from [watcher](https://github.com/radovskyb/watcher) to diff --git a/internal/version/version.txt b/internal/version/version.txt index e339122b45..a9184766ba 100644 --- a/internal/version/version.txt +++ b/internal/version/version.txt @@ -1 +1 @@ -3.42.1 +3.43.0 diff --git a/package-lock.json b/package-lock.json index b2448dbf00..80998be1cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.42.1", + "version": "3.43.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index ffd5d164bc..97daa4b342 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.42.1", + "version": "3.43.0", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", diff --git a/website/docs/changelog.mdx b/website/docs/changelog.mdx index 3e39dd9479..5e216c8cac 100644 --- a/website/docs/changelog.mdx +++ b/website/docs/changelog.mdx @@ -5,6 +5,77 @@ sidebar_position: 14 # Changelog +## v3.43.0 - 2025-04-21 + +- Significant improvements were made to the watcher. We migrated from + [watcher](https://github.com/radovskyb/watcher) to + [fsnotify](https://github.com/fsnotify/fsnotify). The former library used + polling, which means Task had a high CPU usage when watching too many files. + `fsnotify` uses proper the APIs from each operating system to watch files, + which means a much better performance. The default interval changed from 5 + seconds to 100 milliseconds, because now it configures the wait time for + duplicated events, instead of the polling time (#2048 by @andreynering, #1508, + #985, #1179). +- The [Map Variables experiment](https://github.com/go-task/task/issues/1585) + was made generally available so you can now + [define map variables in your Taskfiles!](https://taskfile.dev/usage/#variables) + (#1585, #1547, #2081 by @pd93). +- Wildcards can now + [match multiple tasks](https://taskfile.dev/usage/#wildcard-arguments) (#2072, + #2121 by @pd93). +- Added the ability to + [loop over the files specified by the `generates` keyword](https://taskfile.dev/usage/#looping-over-your-tasks-sources-or-generated-files). + This works the same way as looping over sources (#2151 by @sedyh). +- Added the ability to resolve variables when defining an include variable + (#2108, #2113 by @pd93). +- A few changes have been made to the + [Remote Taskfiles experiment](https://github.com/go-task/task/issues/1317) + (#1402, #2176 by @pd93): + - Cached files are now prioritized over remote ones. + - Added an `--expiry` flag which sets the TTL for a remote file cache. By + default the value will be 0 (caching disabled). If Task is running in + offline mode or fails to make a connection, it will fallback on the cache. +- `.taskrc` files can now be used from subdirectories and will be searched for + recursively up the file tree in the same way that Taskfiles are (#2159, #2166 + by @pd93). +- The default taskfile (output when using the `--init` flag) is now an embedded + file in the binary instead of being stored in the code (#2112 by @pd93). +- Improved the way we report the Task version when using the `--version` flag or + `{{.TASK_VERSION}}` variable. This should now be more consistent and easier + for package maintainers to use (#2131 by @pd93). +- Fixed a bug where globstar (`**`) matching in `sources` only resolved the + first result (#2073, #2075 by @pd93). +- Fixed a bug where sorting tasks by "none" would use the default sorting + instead of leaving tasks in the order they were defined (#2124, #2125 by + @trulede). +- Fixed Fish completion on newer Fish versions (#2130 by @atusy). +- Fixed a bug where undefined/null variables resolved to an empty string instead + of `nil` (#1911, #2144 by @pd93). +- The `USER_WORKING_DIR` special now will now properly account for the `--dir` + (`-d`) flag, if given (#2102, #2103 by @jaynis, #2186 by @andreynering). +- Fix Fish completions when `--global` (`-g`) is given (#2134 by @atusy). +- Fixed variables not available when using `defer:` (#1909, #2173 by @vmaerten). + +#### Package API + +- The [`Executor`](https://pkg.go.dev/github.com/go-task/task/v3#Executor) now + uses the functional options pattern (#2085, #2147, #2148 by @pd93). +- The functional options for the + [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + and + [`taskfile.Snippet`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Snippet) + types no longer have the `Reader`/`Snippet` respective prefixes (#2148 by + @pd93). +- [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + no longer accepts a + [`taskfile.Node`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Node). + Instead nodes are passed directly into the + [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + method (#2169 by @pd93). +- [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + also now accepts a [`context.Context`](https://pkg.go.dev/context#Context) + (#2176 by @pd93). + ## v3.42.1 - 2025-03-10 - Fixed a bug where some special variables caused a type error when used global diff --git a/website/versioned_docs/version-latest/changelog.mdx b/website/versioned_docs/version-latest/changelog.mdx index 3e39dd9479..5e216c8cac 100644 --- a/website/versioned_docs/version-latest/changelog.mdx +++ b/website/versioned_docs/version-latest/changelog.mdx @@ -5,6 +5,77 @@ sidebar_position: 14 # Changelog +## v3.43.0 - 2025-04-21 + +- Significant improvements were made to the watcher. We migrated from + [watcher](https://github.com/radovskyb/watcher) to + [fsnotify](https://github.com/fsnotify/fsnotify). The former library used + polling, which means Task had a high CPU usage when watching too many files. + `fsnotify` uses proper the APIs from each operating system to watch files, + which means a much better performance. The default interval changed from 5 + seconds to 100 milliseconds, because now it configures the wait time for + duplicated events, instead of the polling time (#2048 by @andreynering, #1508, + #985, #1179). +- The [Map Variables experiment](https://github.com/go-task/task/issues/1585) + was made generally available so you can now + [define map variables in your Taskfiles!](https://taskfile.dev/usage/#variables) + (#1585, #1547, #2081 by @pd93). +- Wildcards can now + [match multiple tasks](https://taskfile.dev/usage/#wildcard-arguments) (#2072, + #2121 by @pd93). +- Added the ability to + [loop over the files specified by the `generates` keyword](https://taskfile.dev/usage/#looping-over-your-tasks-sources-or-generated-files). + This works the same way as looping over sources (#2151 by @sedyh). +- Added the ability to resolve variables when defining an include variable + (#2108, #2113 by @pd93). +- A few changes have been made to the + [Remote Taskfiles experiment](https://github.com/go-task/task/issues/1317) + (#1402, #2176 by @pd93): + - Cached files are now prioritized over remote ones. + - Added an `--expiry` flag which sets the TTL for a remote file cache. By + default the value will be 0 (caching disabled). If Task is running in + offline mode or fails to make a connection, it will fallback on the cache. +- `.taskrc` files can now be used from subdirectories and will be searched for + recursively up the file tree in the same way that Taskfiles are (#2159, #2166 + by @pd93). +- The default taskfile (output when using the `--init` flag) is now an embedded + file in the binary instead of being stored in the code (#2112 by @pd93). +- Improved the way we report the Task version when using the `--version` flag or + `{{.TASK_VERSION}}` variable. This should now be more consistent and easier + for package maintainers to use (#2131 by @pd93). +- Fixed a bug where globstar (`**`) matching in `sources` only resolved the + first result (#2073, #2075 by @pd93). +- Fixed a bug where sorting tasks by "none" would use the default sorting + instead of leaving tasks in the order they were defined (#2124, #2125 by + @trulede). +- Fixed Fish completion on newer Fish versions (#2130 by @atusy). +- Fixed a bug where undefined/null variables resolved to an empty string instead + of `nil` (#1911, #2144 by @pd93). +- The `USER_WORKING_DIR` special now will now properly account for the `--dir` + (`-d`) flag, if given (#2102, #2103 by @jaynis, #2186 by @andreynering). +- Fix Fish completions when `--global` (`-g`) is given (#2134 by @atusy). +- Fixed variables not available when using `defer:` (#1909, #2173 by @vmaerten). + +#### Package API + +- The [`Executor`](https://pkg.go.dev/github.com/go-task/task/v3#Executor) now + uses the functional options pattern (#2085, #2147, #2148 by @pd93). +- The functional options for the + [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + and + [`taskfile.Snippet`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Snippet) + types no longer have the `Reader`/`Snippet` respective prefixes (#2148 by + @pd93). +- [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + no longer accepts a + [`taskfile.Node`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Node). + Instead nodes are passed directly into the + [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + method (#2169 by @pd93). +- [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + also now accepts a [`context.Context`](https://pkg.go.dev/context#Context) + (#2176 by @pd93). + ## v3.42.1 - 2025-03-10 - Fixed a bug where some special variables caused a type error when used global diff --git a/website/versioned_docs/version-latest/experiments/map_variables.mdx b/website/versioned_docs/version-latest/experiments/map_variables.mdx deleted file mode 100644 index 477714d09e..0000000000 --- a/website/versioned_docs/version-latest/experiments/map_variables.mdx +++ /dev/null @@ -1,245 +0,0 @@ ---- -slug: /experiments/map-variables/ ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Map Variables (#1585) - -:::caution - -All experimental features are subject to breaking changes and/or removal _at any -time_. We strongly recommend that you do not use these features in a production -environment. They are intended for testing and feedback only. - -::: - -Currently, Task supports all variable types except for maps. This experiment -adds two different proposals for map variables. Click on the tabs below to -switch between them. - - - - - -:::warning - -This experiment proposal breaks the following functionality: - -- Dynamically defined variables (using the `sh` keyword) - -::: - -:::info - -To enable this experiment, set the environment variable: -`TASK_X_MAP_VARIABLES=1`. Check out [our guide to enabling experiments -][enabling-experiments] for more information. - -::: - -This proposal removes support for the `sh` and `ref` keywords in favour of a new -syntax for dynamically defined variables and references. This allows you to -define a map directly as you would for any other type: - -```yaml -version: 3 - -tasks: - foo: - vars: - FOO: {a: 1, b: 2, c: 3} # <-- Directly defined map on the `FOO` key - cmds: - - 'echo {{.FOO.a}}' -``` - -## Migration - -Taskfiles with dynamically defined variables via the `sh` subkey or references -defined with `ref` will no longer work with this experiment enabled. In order to -keep using these features, you will need to migrate your Taskfile to use the new -syntax. - -### Dynamic Variables - -Previously, you had to define dynamic variables using the `sh` subkey. With this -experiment enabled, you will need to remove the `sh` subkey and define your -command as a string that begins with a `$`. This will instruct Task to interpret -the string as a command instead of a literal value and the variable will be -populated with the output of the command. For example: - - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - CALCULATED_VAR: - sh: 'echo hello' - cmds: - - 'echo {{.CALCULATED_VAR}}' -``` - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - CALCULATED_VAR: '$echo hello' # <-- Prefix dynamic variable with a `$` - cmds: - - 'echo {{.CALCULATED_VAR}}' -``` - - - -### References - - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - VAR: 42 - VAR_REF: - ref: '.FOO' - cmds: - - 'echo {{.VAR_REF}}' -``` - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - VAR: 42 - VAR_REF: '#.FOO' # <-- Prefix reference with a `#` - cmds: - - 'echo {{.VAR_REF}}' -``` - - - -If your current Taskfile contains a string variable that begins with a `$` or a -`#`, you will now need to escape it with a backslash (`\`) to stop Task from -interpreting it as a command or reference. - - - - -:::info - -To enable this experiment, set the environment variable: -`TASK_X_MAP_VARIABLES=2`. Check out [our guide to enabling experiments -][enabling-experiments] for more information. - -::: - -This proposal maintains backwards-compatibility and the `sh` subkey and adds -another new `map` subkey for defining map variables: - -```yaml -version: 3 - -tasks: - foo: - vars: - FOO: - map: {a: 1, b: 2, c: 3} # <-- Defined using the `map' subkey instead of directly on 'FOO' - BAR: true # <-- Other types of variables are still defined directly on the key - BAZ: - sh: 'echo Hello Task' # <-- The `sh` subkey is still supported - QUX: - ref: '.BAZ' # <-- The `ref` subkey is still supported - cmds: - - 'echo {{.FOO.a}}' -``` - - - -## Looping over maps - -This experiment also adds support for looping over maps using the `for` keyword, -just like arrays. In addition to the `{{.ITEM}}` variable being populated when -looping over a map, we also make an additional `{{.KEY}}` variable available -that holds the string value of the map key. - - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - MAP: {a: 1, b: 2, c: 3} - cmds: - - for: - var: MAP - cmd: 'echo "{{.KEY}}: {{.ITEM}}"' -``` - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - map: - MAP: {a: 1, b: 2, c: 3} - cmds: - - for: - var: MAP - cmd: 'echo "{{.KEY}}: {{.ITEM}}"' -``` - -:::note - -Remember that maps are unordered, so -the order in which the items are looped over is random. - -::: - - - -{/* prettier-ignore-start */} -[enabling-experiments]: ./experiments.mdx#enabling-experiments -{/* prettier-ignore-end */} diff --git a/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx b/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx index 45e6617edf..b9c2d1f2cb 100644 --- a/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx +++ b/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx @@ -2,6 +2,9 @@ slug: /experiments/remote-taskfiles/ --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Remote Taskfiles (#1317) :::caution @@ -20,57 +23,162 @@ To enable this experiment, set the environment variable: ::: -This experiment allows you to specify a remote Taskfile URL when including a -Taskfile. For example: +:::danger +Never run remote Taskfiles from sources that you do not trust. +::: -```yaml -version: '3' +This experiment allows you to use Taskfiles which are stored in remote +locations. This applies to both the root Taskfile (aka. Entrypoint) and also +when including Taskfiles. -includes: - my-remote-namespace: https://raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml -``` +Task uses "nodes" to reference remote Taskfiles. There are a few different types +of node which you can use: + + + + +`https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml` + +This is the most basic type of remote node and works by downloading the file +from the specified URL. The file must be a valid Taskfile and can be of any +name. If a file is not found at the specified URL, Task will append each of the +[supported file names][supported-file-names] in turn until it finds a valid +file. If it still does not find a valid Taskfile, an error is returned. + + + + +`https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main` + +This type of node works by downloading the file from a Git repository over +HTTP/HTTPS. The first part of the URL is the base URL of the Git repository. +This is the same URL that you would use to clone the repo over HTTP. + +- You can optionally add the path to the Taskfile in the repository by appending +`//` to the URL. +- You can also optionally specify a branch or tag to use by appending +`?ref=` to the end of the URL. If you omit a reference, the default branch +will be used. -This works exactly the same way that including a local file does. Any tasks in -the remote Taskfile will be available to run from your main Taskfile via the -namespace `my-remote-namespace`. For example, if the remote file contains the -following: + + + +`git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main` + +This type of node works by downloading the file from a Git repository over SSH. +The first part of the URL is the user and base URL of the Git repository. This +is the same URL that you would use to clone the repo over SSH. + +To use Git over SSH, you need to make sure that your SSH agent has your private +SSH keys added so that they can be used during authentication. + +- You can optionally add the path to the Taskfile in the repository by appending +`//` to the URL. +- You can also optionally specify a branch or tag to use by appending +`?ref=` to the end of the URL. If you omit a reference, the default branch +will be used. + + + + +Task has an [example remote Taskfile][example-remote-taskfile] in our repository +that you can use for testing and that we will use throughout this document: ```yaml version: '3' tasks: + default: + cmds: + - task: hello + hello: - silent: true cmds: - - echo "Hello from the remote Taskfile!" + - echo "Hello Task!" ``` -and you run `task my-remote-namespace:hello`, it will print the text: "Hello -from the remote Taskfile!" to your console. +## Specifying a remote entrypoint -The Taskfile location is processed by the templating system, so you can -reference environment variables in your URL if you need to add authentication. -For example: +By default, Task will look for one of the [supported file +names][supported-file-names] on your local filesystem. If you want to use a +remote file instead, you can pass its URI into the `--taskfile`/`-t` flag just +like you would to specify a different local file. For example: + + + +```shell +$ task --taskfile https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml +task: [hello] echo "Hello Task!" +Hello Task! +``` + + +```shell +$ task --taskfile https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main +task: [hello] echo "Hello Task!" +Hello Task! +``` + + +```shell +$ task --taskfile git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main +task: [hello] echo "Hello Task!" +Hello Task! +``` + + + +## Including remote Taskfiles + +Including a remote file works exactly the same way that including a local file +does. You just need to replace the local path with a remote URI. Any tasks in +the remote Taskfile will be available to run from your main Taskfile. + + ```yaml version: '3' includes: - my-remote-namespace: https://{{.TOKEN}}@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml + my-remote-namespace: https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml +``` + + +```yaml +version: '3' + +includes: + my-remote-namespace: https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main +``` + + +```yaml +version: '3' + +includes: + my-remote-namespace: git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main ``` + + -`TOKEN=my-token task my-remote-namespace:hello` will be resolved by Task to -`https://my-token@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml` +```shell +$ task my-remote-namespace:hello +task: [hello] echo "Hello Task!" +Hello Task! +``` -## Git nodes +### Authenticating using environment variables -You can also include a Taskfile from a Git node. We currently support ssh-style and http / https addresses like `git@example.com/foo/bar.git//Taskfiles.yml?ref=v1` and `https://example.com/foo/bar.git//Taskfiles.yml?ref=v1`. +The Taskfile location is processed by the templating system, so you can +reference environment variables in your URL if you need to add authentication. +For example: -You need to follow this pattern : `.git//?ref=`. -The `ref` parameter, optional, can be a branch name or a tag, if not provided it'll pick up the default branch. -The `path` is the path to the Taskfile in the repository. +```yaml +version: '3' -If you want to use the SSH protocol, you need to make sure that your ssh-agent has your private ssh keys added so that they can be used during authentication. +includes: + my-remote-namespace: https://{{.TOKEN}}@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml +``` ## Security @@ -104,20 +212,26 @@ flag. Before enabling this flag, you should: Task currently supports both `http` and `https` URLs. However, the `http` requests will not execute by default unless you run the task with the `--insecure` flag. This is to protect you from accidentally running a remote -Taskfile that is via an unencrypted connection. Sources that are not protected -by TLS are vulnerable to [man-in-the-middle attacks][man-in-the-middle-attacks] -and should be avoided unless you know what you are doing. +Taskfile that is downloaded via an unencrypted connection. Sources that are not +protected by TLS are vulnerable to [man-in-the-middle +attacks][man-in-the-middle-attacks] and should be avoided unless you know what +you are doing. ## Caching & Running Offline Whenever you run a remote Taskfile, the latest copy will be downloaded from the -internet and cached locally. If for whatever reason, you lose access to the -internet, you will still be able to run your tasks by specifying the `--offline` -flag. This will tell Task to use the latest cached version of the file instead -of trying to download it. You are able to use the `--download` flag to update -the cached version of the remote files without running any tasks. You are able -to use the `--clear-cache` flag to clear all cached version of the remote files -without running any tasks. +internet and cached locally. This cached file will be used for all future +invocations of the Taskfile until the cache expires. Once it expires, Task will +download the latest copy of the file and update the cache. By default, the cache +is set to expire immediately. This means that Task will always fetch the latest +version. However, the cache expiry duration can be modified by setting the +`--expiry` flag. + +If for any reason you lose access to the internet or you are running Task in +offline mode (via the `--offline` flag or `TASK_OFFLINE` environment variable), +Task will run the any available cached files _even if they are expired_. This +means that you should never be stuck without the ability to run your tasks as +long as you have downloaded a remote Taskfile at least once. By default, Task will timeout requests to download remote files after 10 seconds and look for a cached copy instead. This timeout can be configured by setting @@ -129,7 +243,14 @@ By default, the cache is stored in the Task temp directory, represented by the override the location of the cache by setting the `TASK_REMOTE_DIR` environment variable. This way, you can share the cache between different projects. +You can force Task to ignore the cache and download the latest version +by using the `--download` flag. + +You can use the `--clear-cache` flag to clear all cached remote files. + {/* prettier-ignore-start */} [enabling-experiments]: ./experiments.mdx#enabling-experiments [man-in-the-middle-attacks]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack +[supported-file-names]: https://taskfile.dev/usage/#supported-file-names +[example-remote-taskfile]: https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml {/* prettier-ignore-end */} diff --git a/website/versioned_docs/version-latest/getting_started.mdx b/website/versioned_docs/version-latest/getting_started.mdx index bef3482eca..e79108d26d 100644 --- a/website/versioned_docs/version-latest/getting_started.mdx +++ b/website/versioned_docs/version-latest/getting_started.mdx @@ -62,8 +62,8 @@ the commands. ## Calling a task -To call the task, you simply invoke `task` followed by the name of the task you -want to run. In this case, the name of the task is `default`, so you should run: +To call the task, invoke `task` followed by the name of the task you want to +run. In this case, the name of the task is `default`, so you should run: ```shell task default diff --git a/website/versioned_docs/version-latest/installation.mdx b/website/versioned_docs/version-latest/installation.mdx index 223f274c94..b216aa3d6b 100644 --- a/website/versioned_docs/version-latest/installation.mdx +++ b/website/versioned_docs/version-latest/installation.mdx @@ -181,6 +181,11 @@ to install a specific version: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d v3.36.0 ``` +Parameters are order specific, to set both installation directory and version: +```shell +sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin v3.42.1 +``` + ### GitHub Actions If you want to install Task in GitHub Actions you can try using diff --git a/website/versioned_docs/version-latest/reference/cli.mdx b/website/versioned_docs/version-latest/reference/cli.mdx index 4afe84b836..e5094a9a42 100644 --- a/website/versioned_docs/version-latest/reference/cli.mdx +++ b/website/versioned_docs/version-latest/reference/cli.mdx @@ -24,7 +24,7 @@ If `--` is given, all remaining arguments will be assigned to a special | ----- | --------------------------- | -------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c` | `--color` | `bool` | `true` | Colored output. Enabled by default. Set flag to `false` or use `NO_COLOR=1` to disable. | | `-C` | `--concurrency` | `int` | `0` | Limit number tasks to run concurrently. Zero means unlimited. | -| `-d` | `--dir` | `string` | Working directory | Sets directory of execution. | +| `-d` | `--dir` | `string` | Working directory | Sets the directory in which Task will execute and look for a Taskfile. | | `-n` | `--dry` | `bool` | `false` | Compiles and prints tasks in the order that they would be run, without executing them. | | `-x` | `--exit-code` | `bool` | `false` | Pass-through the exit code of the task command. | | `-f` | `--force` | `bool` | `false` | Forces execution even when the task is up-to-date. | diff --git a/website/versioned_docs/version-latest/reference/package.mdx b/website/versioned_docs/version-latest/reference/package.mdx index 1f753897ab..4777f5df68 100644 --- a/website/versioned_docs/version-latest/reference/package.mdx +++ b/website/versioned_docs/version-latest/reference/package.mdx @@ -23,7 +23,145 @@ changelog entry for breaking changes to the package API. Task is primarily a CLI tool that is agnostic of any programming language. However, it is written in Go and therefore can also be used as a Go package too. This can be useful if you are already using Go in your project and you need to -extend Task's functionality in some way. +extend Task's functionality in some way. In this document, we describe the +public API surface of Task and how to use it. This may also be useful if you +want to contribute to Task or understand how it works in more detail. -The full generated documentation for the package API is available on -[pkg.go.dev](https://pkg.go.dev/github.com/go-task/task/v3). +## Key packages + +The following packages make up the most important parts of Task's package API. +Below we have listed what they are for and some of the key types available: + +### [`github.com/go-task/task/v3`] + +The core task package provides most of the main functionality for Task including +fetching and executing tasks from a Taskfile. At this time, the vast majority of +the this package's functionality is exposed via the [`task.Executor`] which +allows the user to fetch and execute tasks from a Taskfile. + +:::note +This is the package which is most likely to be the subject of breaking changes +as we refine the API. +::: + +### [`github.com/go-task/task/v3/taskfile`] + +The `taskfile` package provides utilities for _reading_ Taskfiles from various +sources. These sources can be local files, remote files, or even in-memory +strings (via stdin). + +- [`taskfile.Node`] - A reference to the location of a Taskfile. A `Node` is an + interface that has several implementations: + - [`taskfile.FileNode`] - Local files + - [`taskfile.HTTPNode`] - Remote files via HTTP/HTTPS + - [`taskfile.GitNode`] - Remote files via Git + - [`taskfile.StdinNode`] - In-memory strings (via stdin) +- [`taskfile.Reader`] - Accepts a `Node` and reads the Taskfile from it. +- [`taskfile.Snippet`] - Mostly used for rendering Taskfile errors. A snippet + stores a small part of a taskfile around a given line number and column. The + output can be syntax highlighted for CLIs and include line/column indicators. + +### [`github.com/go-task/task/v3/taskfile/ast`] + +AST stands for ["Abstract Syntax Tree"][ast]. An AST allows us to easily +represent the Taskfile syntax in Go. This package provides a way to parse +Taskfile YAML into an AST and store them in memory. + +- [`ast.TaskfileGraph`] - Represents a set of Taskfiles and their dependencies + between one another. +- [`ast.Taskfile`] - Represents a single Taskfile or a set of merged Taskfiles. +The `Taskfile` type contains all of the subtypes for the Taskfile syntax, such +as `tasks`, `includes`, `vars`, etc. These are not listed here for brevity. + +### [`github.com/go-task/task/v3/errors`] + +Contains all of the error types used in Task. All of these types implement the +[`errors.TaskError`] interface which wraps Go's standard [`error`] interface. +This allows you to call the `Code` method on the error to obtain the unique exit +code for any error. + +## Reading Taskfiles + +Start by importing the `github.com/go-task/task/v3/taskfile` package. This +provides all of the functions you need to read a Taskfile into memory: + +```go +import ( + "github.com/go-task/task/v3/taskfile" +) +``` + +Reading Taskfiles is done by using a [`taskfile.Reader`] and an implementation +of [`taskfile.Node`]. In this example we will read a local file by using the +[`taskfile.FileNode`] type. You can create this by calling the +[`taskfile.NewFileNode`] function: + +```go +node := taskfile.NewFileNode("Taskfile.yml", "./path/to/dir") +``` + +and then create a your reader by calling the [`taskfile.NewReader`] function and +passing any functional options you want to use. For example, you could pass a +debug function to the reader which will be called with debug messages: + +```go +reader := taskfile.NewReader( + taskfile.WithDebugFunc(func(s string) { + slog.Debug(s) + }), +) +``` + +Now that everything is set up, you can read the Taskfile (and any included +Taskfiles) by calling the `Read` method on the reader and pass the `Node` as an +argument: + +```go +ctx := context.Background() +tfg, err := reader.Read(ctx, node) +// handle error +``` + +This returns an instance of [`ast.TaskfileGraph`] which is a "Directed Acyclic +Graph" (DAG) of all the parsed Taskfiles. We use this graph to store and resolve +the `includes` directives in Taskfiles. However most of the time, you will want +a merged Taskfile. To do this, simply call the `Merge` method on the Taskfile +graph: + +```go +tf, err := tfg.Merge() +// handle error +``` + +This compiles the DAG into a single [`ast.Taskfile`] containing all the +namespaces and tasks from all the Taskfiles we read. + +:::note +We plan to remove AST merging in the future as it is unnecessarily complex and +causes lots of issues with scoping. +::: + +{/* prettier-ignore-start */} +[`github.com/go-task/task/v3`]: https://pkg.go.dev/github.com/go-task/task/v3 +[`github.com/go-task/task/v3/taskfile`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile +[`github.com/go-task/task/v3/taskfile/ast`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast +[`github.com/go-task/task/v3/errors`]: https://pkg.go.dev/github.com/go-task/task/v3/errors + +[`ast.TaskfileGraph`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast#TaskfileGraph +[`ast.Taskfile`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast#Taskfile +[`taskfile.Node`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Node +[`taskfile.FileNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#FileNode +[`taskfile.HTTPNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#HTTPNode +[`taskfile.GitNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#GitNode +[`taskfile.StdinNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#StdinNode +[`taskfile.NewFileNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#NewFileNode +[`taskfile.Reader`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader +[`taskfile.NewReader`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#NewReader +[`taskfile.Snippet`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Snippet +[`task.Executor`]: https://pkg.go.dev/github.com/go-task/task/v3#Executor +[`task.Formatter`]: https://pkg.go.dev/github.com/go-task/task/v3#Formatter +[`errors.TaskError`]: https://pkg.go.dev/github.com/go-task/task/v3/errors#TaskError +[`error`]: https://pkg.go.dev/builtin#error + +[ast]: https://en.wikipedia.org/wiki/Abstract_syntax_tree +{/* prettier-ignore-end */} diff --git a/website/versioned_docs/version-latest/reference/schema.mdx b/website/versioned_docs/version-latest/reference/schema.mdx index 29dc58c858..f426e3faab 100644 --- a/website/versioned_docs/version-latest/reference/schema.mdx +++ b/website/versioned_docs/version-latest/reference/schema.mdx @@ -196,9 +196,12 @@ If defined as a string this is a shell command, otherwise it is a map defining a The `for` parameter can be defined as a string, a list of strings or a map. If it is defined as a string, you can give it any of the following values: -- `source` - Will run the command for each source file defined on the task. +- `sources` - Will run the command for each source file defined on the task. (Glob patterns will be resolved, so `*.go` will run for every Go file that matches). +- `generates` - Will run the command for each file defined in the task's generates + list. (Glob patterns will be resolved, so `*.txt` will run for every text file + that matches). If it is defined as a list of strings, the command will be run for each value. diff --git a/website/versioned_docs/version-latest/reference/templating.mdx b/website/versioned_docs/version-latest/reference/templating.mdx index 98d45bae00..fc5e4fb083 100644 --- a/website/versioned_docs/version-latest/reference/templating.mdx +++ b/website/versioned_docs/version-latest/reference/templating.mdx @@ -115,7 +115,7 @@ special variable will be overridden. | `TASKFILE` | The absolute path of the included Taskfile. | | `TASKFILE_DIR` | The absolute path of the included Taskfile directory. | | `TASK_DIR` | The absolute path of the directory where the task is executed. | -| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from. | +| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from, or the value of `--dir` (`-d`) if given. | | `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. | | `TIMESTAMP` | The date object of the greatest timestamp of the files listed in `sources`. Only available within the `status` prop and if method is set to `timestamp`. | | `TASK_VERSION` | The current version of task. | diff --git a/website/versioned_docs/version-latest/usage.mdx b/website/versioned_docs/version-latest/usage.mdx index 0bfa508670..e672cf822d 100644 --- a/website/versioned_docs/version-latest/usage.mdx +++ b/website/versioned_docs/version-latest/usage.mdx @@ -61,6 +61,12 @@ In this example, we can run `cd ` and `task up` and as long as the `` directory contains a `docker-compose.yml`, the Docker composition will be brought up. +:::info + +`.USER_WORKING_DIR` will contain the value of the `--dir` (`-d`) flag, if given. + +::: + ### Running a global Taskfile If you call Task with the `--global` (alias `-g`) flag, it will look for your @@ -309,45 +315,38 @@ You can flatten the included Taskfile tasks into the main Taskfile by using the It means that the included Taskfile tasks will be available without the namespace. - - - - - ```yaml - version: '3' - - includes: - lib: - taskfile: ./Included.yml - flatten: true - - tasks: - greet: - cmds: - - echo "Greet" - - task: foo - ``` + + +```yaml +version: '3' - - +includes: + lib: + taskfile: ./Included.yml + flatten: true - ```yaml - version: '3' +tasks: + greet: + cmds: + - echo "Greet" + - task: foo +``` - tasks: - foo: - cmds: - - echo "Foo" - ``` + + +```yaml +version: '3' - +tasks: + foo: + cmds: + - echo "Foo" +``` + + If you run `task -a` it will print : @@ -368,43 +367,37 @@ Foo If multiple tasks have the same name, an error will be thrown: - - - - - ```yaml - version: '3' - includes: - lib: - taskfile: ./Included.yml - flatten: true - - tasks: - greet: - cmds: - - echo "Greet" - - task: foo - ``` + + +```yaml +version: '3' +includes: + lib: + taskfile: ./Included.yml + flatten: true - - +tasks: + greet: + cmds: + - echo "Greet" + - task: foo +``` - ```yaml - version: '3' + + - tasks: - greet: - cmds: - - echo "Foo" - ``` +```yaml +version: '3' +tasks: + greet: + cmds: + - echo "Foo" +``` - + + If you run `task -a` it will print: ```text @@ -420,35 +413,29 @@ You can do this by using the [`excludes` option](#exclude-tasks-from-being-inclu You can exclude tasks from being included by using the `excludes` option. This option takes the list of tasks to be excluded from this include. - - - - ```yaml - version: '3' - includes: - included: - taskfile: ./Included.yml - excludes: [foo] - ``` - + + - - +```yaml +version: '3' + includes: + included: + taskfile: ./Included.yml + excludes: [foo] +``` - ```yaml - version: '3' + + - tasks: - foo: echo "Foo" - bar: echo "Bar" - ``` +```yaml +version: '3' +tasks: + foo: echo "Foo" + bar: echo "Bar" +``` - + `task included:foo` will throw an error because the `foo` task is excluded but `task included:bar` will work and display `Bar`. @@ -1113,53 +1100,38 @@ variable types are supported: - `int` - `float` - `array` +- `map` :::note -Maps are not supported by default, but there is an -[experiment][map-variables] that can be enabled to add support. If -you're interested in this functionality, we would appreciate your feedback. -:pray: +Defining a map requires that you use a special `map` subkey (see example below). -In the meantime, it is technically possible to define a map using a `ref` resolver and a templating function. For example: +::: ```yaml -version: '3' +version: 3 tasks: - task-with-map: + foo: vars: - FOO: - ref: dict "a" "1" "b" "2" "c" "3" - cmds: - - echo {{.FOO}} -``` - -```txt -map[a:1 b:2 c:3] + STRING: 'Hello, World!' + BOOL: true + INT: 42 + FLOAT: 3.14 + ARRAY: [1, 2, 3] + MAP: + map: {A: 1, B: 2, C: 3} + cmds: + - 'echo {{.STRING}}' # Hello, World! + - 'echo {{.BOOL}}' # true + - 'echo {{.INT}}' # 42 + - 'echo {{.FLOAT}}' # 3.14 + - 'echo {{.ARRAY}}' # [1 2 3] + - 'echo {{.ARRAY.0}}' # 1 + - 'echo {{.MAP}}' # map[A:1 B:2 C:3] + - 'echo {{.MAP.A}}' # 1 ``` -OR by using the same technique with JSON: - -```yaml -version: '3' - -tasks: - task-with-map: - vars: - JSON: '{"a": 1, "b": 2, "c": 3}' - FOO: - ref: "fromJson .JSON" - cmds: - - echo {{.FOO}} -``` - -```txt -map[a:1 b:2 c:3] -``` - -::: - Variables can be set in many places in a Taskfile. When executing [templates][templating-reference], Task will look for variables in the order listed below (most important first): @@ -1270,13 +1242,8 @@ a value from one task to another. However, the templating engine is only able to output strings. If you want to pass something other than a string to another task then you will need to use a reference (`ref`) instead. - - - + + ```yaml version: 3 @@ -1295,7 +1262,7 @@ tasks: ``` - + ```yaml version: 3 @@ -1314,7 +1281,8 @@ tasks: - 'echo {{index .FOO 0}}' # <-- FOO is still a map so the task outputs 'A' as expected ``` - + + This also works the same way when calling `deps` and when defining a variable and can be used in any combination: @@ -1339,8 +1307,8 @@ tasks: ``` All references use the same templating syntax as regular templates, so in -addition to simply calling `.FOO`, you can also pass subkeys (`.FOO.BAR`) or -indexes (`index .FOO 0`) and use functions (`len .FOO`) as described in the +addition to calling `.FOO`, you can also pass subkeys (`.FOO.BAR`) or indexes +(`index .FOO 0`) and use functions (`len .FOO`) as described in the [templating-reference][templating-reference]: ```yaml @@ -1360,6 +1328,29 @@ tasks: - 'echo {{.FOO}}' # <-- FOO is just the letter 'A' ``` +### Parsing JSON/YAML into map variables + +If you have a raw JSON or YAML string that you want to process in Task, you can +use a combination of the `ref` keyword and the `fromJson` or `fromYaml` +templating functions to parse the string into a map variable. For example: + +```yaml +version: '3' + +tasks: + task-with-map: + vars: + JSON: '{"a": 1, "b": 2, "c": 3}' + FOO: + ref: "fromJson .JSON" + cmds: + - echo {{.FOO}} +``` + +```txt +map[a:1 b:2 c:3] +``` + ## Looping over values Task allows you to loop over certain values and execute a command for each. @@ -1433,9 +1424,13 @@ tasks: cmd: echo "{{.ITEM.OS}}/{{.ITEM.ARCH}}" ``` -### Looping over your task's sources +### Looping over your task's sources or generated files + +You are also able to loop over the sources of your task or the files it +generates: -You are also able to loop over the sources of your task: + + ```yaml version: '3' @@ -1450,14 +1445,37 @@ tasks: cmd: cat {{ .ITEM }} ``` -This will also work if you use globbing syntax in your sources. For example, if -you specify a source for `*.txt`, the loop will iterate over all files that -match that glob. + + -Source paths will always be returned as paths relative to the task directory. If -you need to convert this to an absolute path, you can use the built-in -`joinPath` function. There are some [special variables](/reference/templating/#special-variables) -that you may find useful for this. +```yaml +version: '3' + +tasks: + default: + generates: + - foo.txt + - bar.txt + cmds: + - for: generates + cmd: cat {{ .ITEM }} +``` + + + + +This will also work if you use globbing syntax in `sources` or `generates`. For +example, if you specify a source for `*.txt`, the loop will iterate over all +files that match that glob. + +Paths will always be returned as paths relative to the task directory. If you +need to convert this to an absolute path, you can use the built-in `joinPath` +function. There are some [special +variables](/reference/templating/#special-variables) that you may find useful +for this. + + + ```yaml version: '3' @@ -1475,11 +1493,33 @@ tasks: cmd: cat {{joinPath .MY_DIR .ITEM}} ``` + + + +```yaml +version: '3' + +tasks: + default: + vars: + MY_DIR: /path/to/dir + dir: '{{.MY_DIR}}' + generates: + - foo.txt + - bar.txt + cmds: + - for: generates + cmd: cat {{joinPath .MY_DIR .ITEM}} +``` + + + + ### Looping over variables -To loop over the contents of a variable, you simply need to specify the variable -you want to loop over. By default, string variables will be split on any -whitespace characters. +To loop over the contents of a variable, use the `var` key followed by the name +of the variable you want to loop over. By default, string variables will be +split on any whitespace characters. ```yaml version: '3' @@ -1508,7 +1548,7 @@ tasks: cmd: cat {{.ITEM}} ``` -You can also loop over arrays directly and maps: +You can also loop over arrays and maps directly: ```yaml version: 3 @@ -1674,36 +1714,45 @@ clear what they contain: version: '3' tasks: - echo-*: + start:*:*: vars: - TEXT: '{{index .MATCH 0}}' + SERVICE: "{{index .MATCH 0}}" + REPLICAS: "{{index .MATCH 1}}" cmds: - - echo {{.TEXT}} + - echo "Starting {{.SERVICE}} with {{.REPLICAS}} replicas" - run-*-*: + start:*: vars: - ARG_1: '{{index .MATCH 0}}' - ARG_2: '{{index .MATCH 1}}' + SERVICE: "{{index .MATCH 0}}" cmds: - - echo {{.ARG_1}} {{.ARG_2}} + - echo "Starting {{.SERVICE}}" ``` +This call matches the `start:*` task and the string "foo" is captured by the +wildcard and stored in the `.MATCH` variable. We then index the `.MATCH` array +and store the result in the `.SERVICE` variable which is then echoed out in the +cmds: + ```shell -# This call matches the "echo-*" task and the string "hello" is captured by the -# wildcard and stored in the .MATCH variable. We then index the .MATCH array and -# store the result in the .TEXT variable which is then echoed out in the cmds. -$ task echo-hello -hello -# You can use whitespace in your arguments as long as you quote the task name -$ task "echo-hello world" -hello world -# And you can pass multiple arguments -$ task run-foo-bar -foo bar -``` - -If multiple matching tasks are found, an error occurs. If you are using included -Taskfiles, tasks in parent files will be considered first. +$ task start:foo +Starting foo +``` + +You can use whitespace in your arguments as long as you quote the task name: + +```shell +$ task "start:foo bar" +Starting foo bar +``` + +If multiple matching tasks are found, the first one listed in the Taskfile will +be used. If you are using included Taskfiles, tasks in parent files will be +considered first. + +```shell +$ task start:foo:3 +Starting foo with 3 replicas +``` ## Doing task cleanup with `defer` @@ -2285,9 +2334,11 @@ With the flags `--watch` or `-w` task will watch for file changes and run the task again. This requires the `sources` attribute to be given, so task knows which files to watch. -The default watch interval is 5 seconds, but it's possible to change it by -either setting `interval: '500ms'` in the root of the Taskfile or by passing it -as an argument like `--interval=500ms`. +The default watch interval is 100 milliseconds, but it's possible to change it +by either setting `interval: '500ms'` in the root of the Taskfile or by passing +it as an argument like `--interval=500ms`. +This interval is the time Task will wait for duplicated events. It will only run +the task again once, even if multiple changes happen within the interval. Also, it's possible to set `watch: true` in a given task and it'll automatically run in watch mode: @@ -2317,6 +2368,5 @@ if called by another task, either directly or as a dependency. {/* prettier-ignore-start */} [gotemplate]: https://golang.org/pkg/text/template/ -[map-variables]: ./experiments/map_variables.mdx [templating-reference]: ./reference/templating.mdx {/* prettier-ignore-end */} From 3976e8372a4b353b6d6c065014f622c0f1f59868 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Mon, 21 Apr 2025 13:55:24 -0300 Subject: [PATCH 08/30] chore: move the `experiments` package out of the `internal/` dir Closes #2014 --- cmd/task/task.go | 2 +- executor_test.go | 2 +- {internal/experiments => experiments}/errors.go | 0 {internal/experiments => experiments}/experiment.go | 0 {internal/experiments => experiments}/experiment_test.go | 2 +- {internal/experiments => experiments}/experiments.go | 0 formatter_test.go | 2 +- internal/env/env.go | 2 +- internal/flags/flags.go | 2 +- internal/logger/logger.go | 2 +- task_test.go | 2 +- taskfile/node.go | 2 +- 12 files changed, 9 insertions(+), 9 deletions(-) rename {internal/experiments => experiments}/errors.go (100%) rename {internal/experiments => experiments}/experiment.go (100%) rename {internal/experiments => experiments}/experiment_test.go (98%) rename {internal/experiments => experiments}/experiments.go (100%) diff --git a/cmd/task/task.go b/cmd/task/task.go index 3ff649236f..1c0a42cae3 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -11,7 +11,7 @@ import ( "github.com/go-task/task/v3" "github.com/go-task/task/v3/args" "github.com/go-task/task/v3/errors" - "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/internal/flags" "github.com/go-task/task/v3/internal/logger" diff --git a/executor_test.go b/executor_test.go index a79845cb7b..8e61a9e960 100644 --- a/executor_test.go +++ b/executor_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/go-task/task/v3" - "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/taskfile/ast" ) diff --git a/internal/experiments/errors.go b/experiments/errors.go similarity index 100% rename from internal/experiments/errors.go rename to experiments/errors.go diff --git a/internal/experiments/experiment.go b/experiments/experiment.go similarity index 100% rename from internal/experiments/experiment.go rename to experiments/experiment.go diff --git a/internal/experiments/experiment_test.go b/experiments/experiment_test.go similarity index 98% rename from internal/experiments/experiment_test.go rename to experiments/experiment_test.go index 632d8e02fd..5e8a05b538 100644 --- a/internal/experiments/experiment_test.go +++ b/experiments/experiment_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/taskrc/ast" ) diff --git a/internal/experiments/experiments.go b/experiments/experiments.go similarity index 100% rename from internal/experiments/experiments.go rename to experiments/experiments.go diff --git a/formatter_test.go b/formatter_test.go index 7820b52f6c..51f10d92ff 100644 --- a/formatter_test.go +++ b/formatter_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/go-task/task/v3" - "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/taskfile/ast" ) diff --git a/internal/env/env.go b/internal/env/env.go index e2529e13c4..4b64e504b5 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/taskfile/ast" ) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 2918a89332..dab9fdf8f4 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -12,8 +12,8 @@ import ( "github.com/go-task/task/v3" "github.com/go-task/task/v3/errors" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/internal/env" - "github.com/go-task/task/v3/internal/experiments" "github.com/go-task/task/v3/internal/sort" "github.com/go-task/task/v3/taskfile/ast" ) diff --git a/internal/logger/logger.go b/internal/logger/logger.go index ad4e7c8f36..874c197ffb 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -12,8 +12,8 @@ import ( "github.com/fatih/color" "github.com/go-task/task/v3/errors" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/internal/env" - "github.com/go-task/task/v3/internal/experiments" "github.com/go-task/task/v3/internal/term" ) diff --git a/task_test.go b/task_test.go index 73fa285b4f..aeaa9317be 100644 --- a/task_test.go +++ b/task_test.go @@ -27,7 +27,7 @@ import ( "github.com/go-task/task/v3" "github.com/go-task/task/v3/errors" - "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/taskfile/ast" ) diff --git a/taskfile/node.go b/taskfile/node.go index 94f1e33d33..357ba1a522 100644 --- a/taskfile/node.go +++ b/taskfile/node.go @@ -8,7 +8,7 @@ import ( giturls "github.com/chainguard-dev/git-urls" "github.com/go-task/task/v3/errors" - "github.com/go-task/task/v3/internal/experiments" + "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/internal/fsext" ) From 3d36616e9ec7d43c41640dd2708dc8f73919a1f6 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Mon, 21 Apr 2025 13:57:43 -0300 Subject: [PATCH 09/30] v3.43.1 --- internal/version/version.txt | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/version/version.txt b/internal/version/version.txt index a9184766ba..9291fe6ef9 100644 --- a/internal/version/version.txt +++ b/internal/version/version.txt @@ -1 +1 @@ -3.43.0 +3.43.1 diff --git a/package-lock.json b/package-lock.json index 80998be1cf..874dc31687 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.43.0", + "version": "3.43.1", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 97daa4b342..31d876e920 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.43.0", + "version": "3.43.1", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", From bf4e7960cb51f9cd94227e7e80c0e065708e0340 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Mon, 21 Apr 2025 14:31:25 -0300 Subject: [PATCH 10/30] chore: show right version on changelog --- CHANGELOG.md | 2 +- website/docs/changelog.mdx | 2 +- website/versioned_docs/version-latest/changelog.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eb5e1dea0..48d3c23044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.43.0 - 2025-04-21 +## v3.43.1 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from [watcher](https://github.com/radovskyb/watcher) to diff --git a/website/docs/changelog.mdx b/website/docs/changelog.mdx index 5e216c8cac..34f611d7fb 100644 --- a/website/docs/changelog.mdx +++ b/website/docs/changelog.mdx @@ -5,7 +5,7 @@ sidebar_position: 14 # Changelog -## v3.43.0 - 2025-04-21 +## v3.43.1 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from [watcher](https://github.com/radovskyb/watcher) to diff --git a/website/versioned_docs/version-latest/changelog.mdx b/website/versioned_docs/version-latest/changelog.mdx index 5e216c8cac..34f611d7fb 100644 --- a/website/versioned_docs/version-latest/changelog.mdx +++ b/website/versioned_docs/version-latest/changelog.mdx @@ -5,7 +5,7 @@ sidebar_position: 14 # Changelog -## v3.43.0 - 2025-04-21 +## v3.43.1 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from [watcher](https://github.com/radovskyb/watcher) to From 39706105e140760b341b69e6298c6b0a7fdfacdd Mon Sep 17 00:00:00 2001 From: Valentin Maerten Date: Mon, 21 Apr 2025 21:31:18 +0200 Subject: [PATCH 11/30] fix: CLI_ARGS is a string and not an array (#2191) --- args/args.go | 8 ++++---- cmd/task/task.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/args/args.go b/args/args.go index 0e9eaab95f..8f6b5052ce 100644 --- a/args/args.go +++ b/args/args.go @@ -13,24 +13,24 @@ import ( // Get fetches the remaining arguments after CLI parsing and splits them into // two groups: the arguments before the double dash (--) and the arguments after // the double dash. -func Get() ([]string, []string, error) { +func Get() ([]string, string, error) { args := pflag.Args() doubleDashPos := pflag.CommandLine.ArgsLenAtDash() if doubleDashPos == -1 { - return args, nil, nil + return args, "", nil } var quotedCliArgs []string for _, arg := range args[doubleDashPos:] { quotedCliArg, err := syntax.Quote(arg, syntax.LangBash) if err != nil { - return nil, nil, err + return nil, "", err } quotedCliArgs = append(quotedCliArgs, quotedCliArg) } - return args[:doubleDashPos], quotedCliArgs, nil + return args[:doubleDashPos], strings.Join(quotedCliArgs, " "), nil } // Parse parses command line argument: tasks and global variables diff --git a/cmd/task/task.go b/cmd/task/task.go index 1c0a42cae3..31538afc57 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -76,7 +76,7 @@ func run() error { if err != nil { return err } - _, args, err := args.Get() + args, _, err := args.Get() if err != nil { return err } From 08056924e0fc7cff473061587855d7b22217373d Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Mon, 21 Apr 2025 16:33:30 -0300 Subject: [PATCH 12/30] chore: add changelog entry for #2191 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d3c23044..a5faf56730 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +- Fixed regresion of `CLI_ARGS` being exposed as the wrong type (#2190, #2191 by + @vmaerten). + ## v3.43.1 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from From 6cb0a5a2f2d5db23a2a882d3c9aa5f1fea37106a Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Mon, 21 Apr 2025 16:35:01 -0300 Subject: [PATCH 13/30] v3.43.2 --- CHANGELOG.md | 2 +- internal/version/version.txt | 2 +- package-lock.json | 2 +- package.json | 2 +- website/docs/changelog.mdx | 5 +++++ website/versioned_docs/version-latest/changelog.mdx | 5 +++++ 6 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5faf56730..0ac571602b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## v3.43.2 - 2025-04-21 - Fixed regresion of `CLI_ARGS` being exposed as the wrong type (#2190, #2191 by @vmaerten). diff --git a/internal/version/version.txt b/internal/version/version.txt index 9291fe6ef9..a140060640 100644 --- a/internal/version/version.txt +++ b/internal/version/version.txt @@ -1 +1 @@ -3.43.1 +3.43.2 diff --git a/package-lock.json b/package-lock.json index 874dc31687..5478c6b444 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.43.1", + "version": "3.43.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 31d876e920..ad54f40208 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.43.1", + "version": "3.43.2", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", diff --git a/website/docs/changelog.mdx b/website/docs/changelog.mdx index 34f611d7fb..980e59d02d 100644 --- a/website/docs/changelog.mdx +++ b/website/docs/changelog.mdx @@ -5,6 +5,11 @@ sidebar_position: 14 # Changelog +## v3.43.2 - 2025-04-21 + +- Fixed regresion of `CLI_ARGS` being exposed as the wrong type (#2190, #2191 by + @vmaerten). + ## v3.43.1 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from diff --git a/website/versioned_docs/version-latest/changelog.mdx b/website/versioned_docs/version-latest/changelog.mdx index 34f611d7fb..980e59d02d 100644 --- a/website/versioned_docs/version-latest/changelog.mdx +++ b/website/versioned_docs/version-latest/changelog.mdx @@ -5,6 +5,11 @@ sidebar_position: 14 # Changelog +## v3.43.2 - 2025-04-21 + +- Fixed regresion of `CLI_ARGS` being exposed as the wrong type (#2190, #2191 by + @vmaerten). + ## v3.43.1 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from From 68d50957615acc7c6b30c0eab36be06792d3e843 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 27 Apr 2025 22:14:50 +0000 Subject: [PATCH 14/30] Revert "fix: `.USER_WORKING_DIR` should contain the value of `--dir` if given (#2186)" This reverts commit 768dca053b81d18e8c842e441fb194ba13c5e151. --- executor.go | 2 -- task_test.go | 2 +- website/docs/reference/templating.mdx | 2 +- website/docs/usage.mdx | 6 ------ 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/executor.go b/executor.go index cb846464cf..8f9233ef88 100644 --- a/executor.go +++ b/executor.go @@ -4,7 +4,6 @@ import ( "context" "io" "os" - "path/filepath" "sync" "time" @@ -123,7 +122,6 @@ type dirOption struct { } func (o *dirOption) ApplyToExecutor(e *Executor) { - e.UserWorkingDir, _ = filepath.Abs(o.dir) e.Dir = o.dir } diff --git a/task_test.go b/task_test.go index aeaa9317be..5ac47c25b5 100644 --- a/task_test.go +++ b/task_test.go @@ -2151,7 +2151,7 @@ func TestUserWorkingDirectory(t *testing.T) { var buff bytes.Buffer e := task.NewExecutor( - task.WithEntrypoint("testdata/user_working_dir/Taskfile.yml"), + task.WithDir("testdata/user_working_dir"), task.WithStdout(&buff), task.WithStderr(&buff), ) diff --git a/website/docs/reference/templating.mdx b/website/docs/reference/templating.mdx index fc5e4fb083..98d45bae00 100644 --- a/website/docs/reference/templating.mdx +++ b/website/docs/reference/templating.mdx @@ -115,7 +115,7 @@ special variable will be overridden. | `TASKFILE` | The absolute path of the included Taskfile. | | `TASKFILE_DIR` | The absolute path of the included Taskfile directory. | | `TASK_DIR` | The absolute path of the directory where the task is executed. | -| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from, or the value of `--dir` (`-d`) if given. | +| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from. | | `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. | | `TIMESTAMP` | The date object of the greatest timestamp of the files listed in `sources`. Only available within the `status` prop and if method is set to `timestamp`. | | `TASK_VERSION` | The current version of task. | diff --git a/website/docs/usage.mdx b/website/docs/usage.mdx index e672cf822d..1942d9475c 100644 --- a/website/docs/usage.mdx +++ b/website/docs/usage.mdx @@ -61,12 +61,6 @@ In this example, we can run `cd ` and `task up` and as long as the `` directory contains a `docker-compose.yml`, the Docker composition will be brought up. -:::info - -`.USER_WORKING_DIR` will contain the value of the `--dir` (`-d`) flag, if given. - -::: - ### Running a global Taskfile If you call Task with the `--global` (alias `-g`) flag, it will look for your From 8162b05f595e5691e22c6ede6e07e5061d98b296 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 27 Apr 2025 22:15:49 +0000 Subject: [PATCH 15/30] Revert "feat: process variables in include vars (#2113)" This reverts commit f0414f162d8e34e7ca9da1c750fa8202aa6f5451. --- task_test.go | 4 ---- taskfile/reader.go | 2 +- testdata/include_with_vars/Taskfile.yml | 14 +++----------- ...{Taskfile.include.yml => Taskfile.include1.yml} | 0 .../include/Taskfile.include2.yml | 11 +++++++++++ .../include/Taskfile.include3.yml | 11 +++++++++++ 6 files changed, 26 insertions(+), 16 deletions(-) rename testdata/include_with_vars/include/{Taskfile.include.yml => Taskfile.include1.yml} (100%) create mode 100644 testdata/include_with_vars/include/Taskfile.include2.yml create mode 100644 testdata/include_with_vars/include/Taskfile.include3.yml diff --git a/task_test.go b/task_test.go index 5ac47c25b5..092c23e718 100644 --- a/task_test.go +++ b/task_test.go @@ -1964,10 +1964,6 @@ task: [included3:task1] echo "VAR_1 is included-default-var1" VAR_1 is included-default-var1 task: [included3:task1] echo "VAR_2 is included-default-var2" VAR_2 is included-default-var2 -task: [included4:task1] echo "VAR_1 is included4-var1" -VAR_1 is included4-var1 -task: [included4:task1] echo "VAR_2 is included-default-var2" -VAR_2 is included-default-var2 `) require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task1"})) t.Log(buff.String()) diff --git a/taskfile/reader.go b/taskfile/reader.go index edcf54e0d0..3230807639 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -249,7 +249,7 @@ func (r *Reader) include(ctx context.Context, node Node) error { Aliases: include.Aliases, AdvancedImport: include.AdvancedImport, Excludes: include.Excludes, - Vars: templater.ReplaceVars(include.Vars, cache), + Vars: include.Vars, } if err := cache.Err(); err != nil { return err diff --git a/testdata/include_with_vars/Taskfile.yml b/testdata/include_with_vars/Taskfile.yml index 0f5e0d88ba..5ded3e3349 100644 --- a/testdata/include_with_vars/Taskfile.yml +++ b/testdata/include_with_vars/Taskfile.yml @@ -1,23 +1,16 @@ version: "3" -vars: - VAR_1: included4-var1 - includes: included1: - taskfile: include/Taskfile.include.yml + taskfile: include/Taskfile.include1.yml vars: VAR_1: included1-var1 included2: - taskfile: include/Taskfile.include.yml + taskfile: include/Taskfile.include2.yml vars: VAR_1: included2-var1 included3: - taskfile: include/Taskfile.include.yml - included4: - taskfile: include/Taskfile.include.yml - vars: - VAR_1: "{{.VAR_1}}" + taskfile: include/Taskfile.include3.yml tasks: task1: @@ -25,4 +18,3 @@ tasks: - task: included1:task1 - task: included2:task1 - task: included3:task1 - - task: included4:task1 diff --git a/testdata/include_with_vars/include/Taskfile.include.yml b/testdata/include_with_vars/include/Taskfile.include1.yml similarity index 100% rename from testdata/include_with_vars/include/Taskfile.include.yml rename to testdata/include_with_vars/include/Taskfile.include1.yml diff --git a/testdata/include_with_vars/include/Taskfile.include2.yml b/testdata/include_with_vars/include/Taskfile.include2.yml new file mode 100644 index 0000000000..3bb7a68e05 --- /dev/null +++ b/testdata/include_with_vars/include/Taskfile.include2.yml @@ -0,0 +1,11 @@ +version: "3" + +vars: + VAR_1: '{{.VAR_1 | default "included-default-var1"}}' + VAR_2: '{{.VAR_2 | default "included-default-var2"}}' + +tasks: + task1: + cmds: + - echo "VAR_1 is {{.VAR_1}}" + - echo "VAR_2 is {{.VAR_2}}" diff --git a/testdata/include_with_vars/include/Taskfile.include3.yml b/testdata/include_with_vars/include/Taskfile.include3.yml new file mode 100644 index 0000000000..3bb7a68e05 --- /dev/null +++ b/testdata/include_with_vars/include/Taskfile.include3.yml @@ -0,0 +1,11 @@ +version: "3" + +vars: + VAR_1: '{{.VAR_1 | default "included-default-var1"}}' + VAR_2: '{{.VAR_2 | default "included-default-var2"}}' + +tasks: + task1: + cmds: + - echo "VAR_1 is {{.VAR_1}}" + - echo "VAR_2 is {{.VAR_2}}" From bd8ccb8d033461e67f2ca7567f4da3348f92011b Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 27 Apr 2025 22:26:29 +0000 Subject: [PATCH 16/30] chore: changelogs for reverts --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac571602b..8a04abf496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +Reverted the changes made in #2113 and #2186 that affected the +`USER_WORKING_DIR` and built-in variables. This fixes #2206, #2195, #2207 and +#2208. + ## v3.43.2 - 2025-04-21 - Fixed regresion of `CLI_ARGS` being exposed as the wrong type (#2190, #2191 by From 20c1ffe09856dedda4ccc4257d800cda74ed942b Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 27 Apr 2025 22:26:59 +0000 Subject: [PATCH 17/30] docs: update variables example so that it doesn't error --- website/docs/usage.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/website/docs/usage.mdx b/website/docs/usage.mdx index 1942d9475c..eda391a093 100644 --- a/website/docs/usage.mdx +++ b/website/docs/usage.mdx @@ -1116,14 +1116,14 @@ tasks: MAP: map: {A: 1, B: 2, C: 3} cmds: - - 'echo {{.STRING}}' # Hello, World! - - 'echo {{.BOOL}}' # true - - 'echo {{.INT}}' # 42 - - 'echo {{.FLOAT}}' # 3.14 - - 'echo {{.ARRAY}}' # [1 2 3] - - 'echo {{.ARRAY.0}}' # 1 - - 'echo {{.MAP}}' # map[A:1 B:2 C:3] - - 'echo {{.MAP.A}}' # 1 + - 'echo {{.STRING}}' # Hello, World! + - 'echo {{.BOOL}}' # true + - 'echo {{.INT}}' # 42 + - 'echo {{.FLOAT}}' # 3.14 + - 'echo {{.ARRAY}}' # [1 2 3] + - 'echo {{index .ARRAY 0}}' # 1 + - 'echo {{.MAP}}' # map[A:1 B:2 C:3] + - 'echo {{.MAP.A}}' # 1 ``` Variables can be set in many places in a Taskfile. When executing From 13daa6dc35b4bf723265f438e3c62e5ba11bf3a2 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 27 Apr 2025 20:38:13 +0000 Subject: [PATCH 18/30] feat: formatting with golangci-lint and gci --- .golangci.yml | 8 ++++++++ Taskfile.yml | 9 +++++++++ internal/templater/templater.go | 3 ++- task.go | 6 +++--- task_test.go | 1 + watch_test.go | 6 +++--- website/docs/contributing.mdx | 24 ++++++++++++++++-------- 7 files changed, 42 insertions(+), 15 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 7b82d8007a..3da13bd6fb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,8 +5,10 @@ formatters: - gofmt - gofumpt - goimports + - gci settings: gofmt: + simplify: true rewrite-rules: - pattern: interface{} replacement: any @@ -15,6 +17,12 @@ formatters: goimports: local-prefixes: - github.com/go-task + gci: + sections: + - standard + - default + - prefix(github.com/go-task) + - localmodule exclusions: generated: lax paths: diff --git a/Taskfile.yml b/Taskfile.yml index bb27299af8..95430d5db3 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -98,6 +98,15 @@ tasks: cmds: - golangci-lint run --fix + format: + desc: Runs golangci-lint and formats any Go files + aliases: [fmt, f] + sources: + - './**/*.go' + - .golangci.yml + cmds: + - golangci-lint fmt + sleepit:build: desc: Builds the sleepit test helper sources: diff --git a/internal/templater/templater.go b/internal/templater/templater.go index e5265b8117..896cba23c1 100644 --- a/internal/templater/templater.go +++ b/internal/templater/templater.go @@ -6,9 +6,10 @@ import ( "maps" "strings" + "github.com/go-task/template" + "github.com/go-task/task/v3/internal/deepcopy" "github.com/go-task/task/v3/taskfile/ast" - "github.com/go-task/template" ) // Cache is a help struct that allow us to call "replaceX" funcs multiple diff --git a/task.go b/task.go index 01dccc13f9..0b762c6140 100644 --- a/task.go +++ b/task.go @@ -8,6 +8,9 @@ import ( "slices" "sync/atomic" + "golang.org/x/sync/errgroup" + "mvdan.cc/sh/v3/interp" + "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/env" "github.com/go-task/task/v3/internal/execext" @@ -19,9 +22,6 @@ import ( "github.com/go-task/task/v3/internal/summary" "github.com/go-task/task/v3/internal/templater" "github.com/go-task/task/v3/taskfile/ast" - - "golang.org/x/sync/errgroup" - "mvdan.cc/sh/v3/interp" ) const ( diff --git a/task_test.go b/task_test.go index 092c23e718..711d672416 100644 --- a/task_test.go +++ b/task_test.go @@ -933,6 +933,7 @@ func TestIncludesHttp(t *testing.T) { for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { + t.Parallel() task, err := e.CompiledTask(&task.Call{Task: tc.name}) require.NoError(t, err) assert.Equal(t, tc.dir, task.Dir) diff --git a/watch_test.go b/watch_test.go index 447c910a16..16e23ecdf4 100644 --- a/watch_test.go +++ b/watch_test.go @@ -49,10 +49,10 @@ task: task "default" finished running dirPath := filepathext.SmartJoin(dir, "src") filePath := filepathext.SmartJoin(dirPath, "a") - err := os.MkdirAll(dirPath, 0755) + err := os.MkdirAll(dirPath, 0o755) require.NoError(t, err) - err = os.WriteFile(filePath, []byte("test"), 0644) + err = os.WriteFile(filePath, []byte("test"), 0o644) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) @@ -72,7 +72,7 @@ task: task "default" finished running }() time.Sleep(10 * time.Millisecond) - err = os.WriteFile(filePath, []byte("test updated"), 0644) + err = os.WriteFile(filePath, []byte("test updated"), 0o644) require.NoError(t, err) time.Sleep(150 * time.Millisecond) diff --git a/website/docs/contributing.mdx b/website/docs/contributing.mdx index 546129dee7..62f5f65932 100644 --- a/website/docs/contributing.mdx +++ b/website/docs/contributing.mdx @@ -43,12 +43,16 @@ Studio Code][vscode-task]. ## 2. Making changes - **Code style** - Try to maintain the existing code style where possible. Go - code should be formatted by [`gofumpt`][gofumpt] and linted using - [`golangci-lint`][golangci-lint]. Any Markdown or TypeScript files should be - formatted and linted by [Prettier][prettier]. This style is enforced by our CI - to ensure that we have a consistent style across the project. You can use the - `task lint` command to lint the code locally and the `task lint:fix` command - to automatically fix any issues that are found. + code should be formatted and linted by [`golangci-lint`][golangci-lint]. This + wraps the [`gofumpt`][gofumpt] and [`gci`][gci] formatters and a number of + linters. We recommend that you take a look at the [golangci-lint + docs][golangci-lint-docs] for a guide on how to setup your editor to + auto-format your code. Any Markdown or TypeScript files should be formatted + and linted by [Prettier][prettier]. This style is enforced by our CI to ensure + that we have a consistent style across the project. You can use the `task + lint` command to lint the code locally and the `task lint:fix` command to try + to automatically fix any issues that are found. You can also use the `task + fmt` command to auto-format the files if your editor doesn't do it for you. - **Documentation** - Ensure that you add/update any relevant documentation. See the [updating documentation](#updating-documentation) section below. - **Tests** - Ensure that you add/update any relevant tests and that all tests @@ -73,8 +77,9 @@ install the extension. Task uses [Docusaurus][docusaurus] to host a documentation server. The code for this is located in the core Task repository. This can be setup and run locally by using `task website` (requires `nodejs` & `yarn`). All content is written in -Markdown and is located in the `website/docs` directory. All Markdown documents -should have an 80 character line wrap limit (enforced by Prettier). +[MDX][mdx] (an extension of Markdown) and is located in the `website/docs` +directory. All Markdown documents should have an 80 character line wrap limit +(enforced by Prettier). When making a change, consider whether a change to the [Usage Guide](/usage) is necessary. This document contains descriptions and examples of how to use Task @@ -154,7 +159,9 @@ If you have questions, feel free to ask them in the `#help` forum channel on our [vscode-task]: https://github.com/go-task/vscode-task [go]: https://go.dev [gofumpt]: https://github.com/mvdan/gofumpt +[gci]: https://github.com/daixiang0/gci [golangci-lint]: https://golangci-lint.run +[golangci-lint-docs]: https://golangci-lint.run/welcome/integrations/ [prettier]: https://prettier.io [nodejs]: https://nodejs.org/en/ [yarn]: https://yarnpkg.com/ @@ -166,4 +173,5 @@ If you have questions, feel free to ask them in the `#help` forum channel on our [discord-server]: https://discord.gg/6TY36E39UK [discussion]: https://github.com/go-task/task/discussions [conventional-commits]: https://www.conventionalcommits.org +[mdx]: https://mdxjs.com/ {/* prettier-ignore-end */} From 1c35358fcca6ae3bdcee1ec5ebff6558439a8356 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 27 Apr 2025 22:29:34 +0000 Subject: [PATCH 19/30] v3.43.3 --- CHANGELOG.md | 2 +- internal/version/version.txt | 2 +- package-lock.json | 2 +- package.json | 2 +- website/docs/changelog.mdx | 6 +++++ .../version-latest/changelog.mdx | 6 +++++ .../version-latest/contributing.mdx | 24 ++++++++++++------- .../version-latest/reference/templating.mdx | 2 +- .../versioned_docs/version-latest/usage.mdx | 22 +++++++---------- 9 files changed, 41 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a04abf496..595dacc6c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## v3.43.3 - 2025-04-27 Reverted the changes made in #2113 and #2186 that affected the `USER_WORKING_DIR` and built-in variables. This fixes #2206, #2195, #2207 and diff --git a/internal/version/version.txt b/internal/version/version.txt index a140060640..0fe676c57a 100644 --- a/internal/version/version.txt +++ b/internal/version/version.txt @@ -1 +1 @@ -3.43.2 +3.43.3 diff --git a/package-lock.json b/package-lock.json index 5478c6b444..028abd6365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.43.2", + "version": "3.43.3", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index ad54f40208..ef114b8a16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.43.2", + "version": "3.43.3", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", diff --git a/website/docs/changelog.mdx b/website/docs/changelog.mdx index 980e59d02d..773ba2ba80 100644 --- a/website/docs/changelog.mdx +++ b/website/docs/changelog.mdx @@ -5,6 +5,12 @@ sidebar_position: 14 # Changelog +## v3.43.3 - 2025-04-27 + +Reverted the changes made in #2113 and #2186 that affected the +`USER_WORKING_DIR` and built-in variables. This fixes #2206, #2195, #2207 and +#2208. + ## v3.43.2 - 2025-04-21 - Fixed regresion of `CLI_ARGS` being exposed as the wrong type (#2190, #2191 by diff --git a/website/versioned_docs/version-latest/changelog.mdx b/website/versioned_docs/version-latest/changelog.mdx index 980e59d02d..773ba2ba80 100644 --- a/website/versioned_docs/version-latest/changelog.mdx +++ b/website/versioned_docs/version-latest/changelog.mdx @@ -5,6 +5,12 @@ sidebar_position: 14 # Changelog +## v3.43.3 - 2025-04-27 + +Reverted the changes made in #2113 and #2186 that affected the +`USER_WORKING_DIR` and built-in variables. This fixes #2206, #2195, #2207 and +#2208. + ## v3.43.2 - 2025-04-21 - Fixed regresion of `CLI_ARGS` being exposed as the wrong type (#2190, #2191 by diff --git a/website/versioned_docs/version-latest/contributing.mdx b/website/versioned_docs/version-latest/contributing.mdx index 546129dee7..62f5f65932 100644 --- a/website/versioned_docs/version-latest/contributing.mdx +++ b/website/versioned_docs/version-latest/contributing.mdx @@ -43,12 +43,16 @@ Studio Code][vscode-task]. ## 2. Making changes - **Code style** - Try to maintain the existing code style where possible. Go - code should be formatted by [`gofumpt`][gofumpt] and linted using - [`golangci-lint`][golangci-lint]. Any Markdown or TypeScript files should be - formatted and linted by [Prettier][prettier]. This style is enforced by our CI - to ensure that we have a consistent style across the project. You can use the - `task lint` command to lint the code locally and the `task lint:fix` command - to automatically fix any issues that are found. + code should be formatted and linted by [`golangci-lint`][golangci-lint]. This + wraps the [`gofumpt`][gofumpt] and [`gci`][gci] formatters and a number of + linters. We recommend that you take a look at the [golangci-lint + docs][golangci-lint-docs] for a guide on how to setup your editor to + auto-format your code. Any Markdown or TypeScript files should be formatted + and linted by [Prettier][prettier]. This style is enforced by our CI to ensure + that we have a consistent style across the project. You can use the `task + lint` command to lint the code locally and the `task lint:fix` command to try + to automatically fix any issues that are found. You can also use the `task + fmt` command to auto-format the files if your editor doesn't do it for you. - **Documentation** - Ensure that you add/update any relevant documentation. See the [updating documentation](#updating-documentation) section below. - **Tests** - Ensure that you add/update any relevant tests and that all tests @@ -73,8 +77,9 @@ install the extension. Task uses [Docusaurus][docusaurus] to host a documentation server. The code for this is located in the core Task repository. This can be setup and run locally by using `task website` (requires `nodejs` & `yarn`). All content is written in -Markdown and is located in the `website/docs` directory. All Markdown documents -should have an 80 character line wrap limit (enforced by Prettier). +[MDX][mdx] (an extension of Markdown) and is located in the `website/docs` +directory. All Markdown documents should have an 80 character line wrap limit +(enforced by Prettier). When making a change, consider whether a change to the [Usage Guide](/usage) is necessary. This document contains descriptions and examples of how to use Task @@ -154,7 +159,9 @@ If you have questions, feel free to ask them in the `#help` forum channel on our [vscode-task]: https://github.com/go-task/vscode-task [go]: https://go.dev [gofumpt]: https://github.com/mvdan/gofumpt +[gci]: https://github.com/daixiang0/gci [golangci-lint]: https://golangci-lint.run +[golangci-lint-docs]: https://golangci-lint.run/welcome/integrations/ [prettier]: https://prettier.io [nodejs]: https://nodejs.org/en/ [yarn]: https://yarnpkg.com/ @@ -166,4 +173,5 @@ If you have questions, feel free to ask them in the `#help` forum channel on our [discord-server]: https://discord.gg/6TY36E39UK [discussion]: https://github.com/go-task/task/discussions [conventional-commits]: https://www.conventionalcommits.org +[mdx]: https://mdxjs.com/ {/* prettier-ignore-end */} diff --git a/website/versioned_docs/version-latest/reference/templating.mdx b/website/versioned_docs/version-latest/reference/templating.mdx index fc5e4fb083..98d45bae00 100644 --- a/website/versioned_docs/version-latest/reference/templating.mdx +++ b/website/versioned_docs/version-latest/reference/templating.mdx @@ -115,7 +115,7 @@ special variable will be overridden. | `TASKFILE` | The absolute path of the included Taskfile. | | `TASKFILE_DIR` | The absolute path of the included Taskfile directory. | | `TASK_DIR` | The absolute path of the directory where the task is executed. | -| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from, or the value of `--dir` (`-d`) if given. | +| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from. | | `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. | | `TIMESTAMP` | The date object of the greatest timestamp of the files listed in `sources`. Only available within the `status` prop and if method is set to `timestamp`. | | `TASK_VERSION` | The current version of task. | diff --git a/website/versioned_docs/version-latest/usage.mdx b/website/versioned_docs/version-latest/usage.mdx index e672cf822d..eda391a093 100644 --- a/website/versioned_docs/version-latest/usage.mdx +++ b/website/versioned_docs/version-latest/usage.mdx @@ -61,12 +61,6 @@ In this example, we can run `cd ` and `task up` and as long as the `` directory contains a `docker-compose.yml`, the Docker composition will be brought up. -:::info - -`.USER_WORKING_DIR` will contain the value of the `--dir` (`-d`) flag, if given. - -::: - ### Running a global Taskfile If you call Task with the `--global` (alias `-g`) flag, it will look for your @@ -1122,14 +1116,14 @@ tasks: MAP: map: {A: 1, B: 2, C: 3} cmds: - - 'echo {{.STRING}}' # Hello, World! - - 'echo {{.BOOL}}' # true - - 'echo {{.INT}}' # 42 - - 'echo {{.FLOAT}}' # 3.14 - - 'echo {{.ARRAY}}' # [1 2 3] - - 'echo {{.ARRAY.0}}' # 1 - - 'echo {{.MAP}}' # map[A:1 B:2 C:3] - - 'echo {{.MAP.A}}' # 1 + - 'echo {{.STRING}}' # Hello, World! + - 'echo {{.BOOL}}' # true + - 'echo {{.INT}}' # 42 + - 'echo {{.FLOAT}}' # 3.14 + - 'echo {{.ARRAY}}' # [1 2 3] + - 'echo {{index .ARRAY 0}}' # 1 + - 'echo {{.MAP}}' # map[A:1 B:2 C:3] + - 'echo {{.MAP.A}}' # 1 ``` Variables can be set in many places in a Taskfile. When executing From a33544101a32fc6cd9dc23189077fa7aa76ae97f Mon Sep 17 00:00:00 2001 From: Valentin Maerten Date: Mon, 28 Apr 2025 01:00:54 +0200 Subject: [PATCH 20/30] fix: fuzzy model was not instanciated (#2200) * fix: fuzzy model was not instanciated * add test * add test --- executor_test.go | 21 +++++++++++++++++++ setup.go | 2 +- testdata/fuzzy/Taskfile.yml | 4 ++++ .../TestFuzzyModel-fuzzy-err-run.golden | 1 + .../testdata/TestFuzzyModel-fuzzy.golden | 1 + .../testdata/TestFuzzyModel-not-fuzzy.golden | 2 ++ 6 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 testdata/fuzzy/Taskfile.yml create mode 100644 testdata/fuzzy/testdata/TestFuzzyModel-fuzzy-err-run.golden create mode 100644 testdata/fuzzy/testdata/TestFuzzyModel-fuzzy.golden create mode 100644 testdata/fuzzy/testdata/TestFuzzyModel-not-fuzzy.golden diff --git a/executor_test.go b/executor_test.go index 8e61a9e960..8f3c794c9d 100644 --- a/executor_test.go +++ b/executor_test.go @@ -937,3 +937,24 @@ func TestVarInheritance(t *testing.T) { ) } } + +func TestFuzzyModel(t *testing.T) { + t.Parallel() + + NewExecutorTest(t, + WithName("fuzzy"), + WithExecutorOptions( + task.WithDir("testdata/fuzzy"), + ), + WithTask("instal"), + WithRunError(), + ) + + NewExecutorTest(t, + WithName("not-fuzzy"), + WithExecutorOptions( + task.WithDir("testdata/fuzzy"), + ), + WithTask("install"), + ) +} diff --git a/setup.go b/setup.go index 13f03dd40b..0e94f3e67a 100644 --- a/setup.go +++ b/setup.go @@ -95,7 +95,7 @@ func (e *Executor) readTaskfile(node taskfile.Node) error { } func (e *Executor) setupFuzzyModel() { - if e.Taskfile != nil { + if e.Taskfile == nil { return } diff --git a/testdata/fuzzy/Taskfile.yml b/testdata/fuzzy/Taskfile.yml new file mode 100644 index 0000000000..a05ed02e3c --- /dev/null +++ b/testdata/fuzzy/Taskfile.yml @@ -0,0 +1,4 @@ +version: 3 + +tasks: + install: echo 'install' diff --git a/testdata/fuzzy/testdata/TestFuzzyModel-fuzzy-err-run.golden b/testdata/fuzzy/testdata/TestFuzzyModel-fuzzy-err-run.golden new file mode 100644 index 0000000000..c4724624eb --- /dev/null +++ b/testdata/fuzzy/testdata/TestFuzzyModel-fuzzy-err-run.golden @@ -0,0 +1 @@ +task: Task "instal" does not exist. Did you mean "install"? \ No newline at end of file diff --git a/testdata/fuzzy/testdata/TestFuzzyModel-fuzzy.golden b/testdata/fuzzy/testdata/TestFuzzyModel-fuzzy.golden new file mode 100644 index 0000000000..56e8128e82 --- /dev/null +++ b/testdata/fuzzy/testdata/TestFuzzyModel-fuzzy.golden @@ -0,0 +1 @@ +task: No tasks with description available. Try --list-all to list all tasks diff --git a/testdata/fuzzy/testdata/TestFuzzyModel-not-fuzzy.golden b/testdata/fuzzy/testdata/TestFuzzyModel-not-fuzzy.golden new file mode 100644 index 0000000000..cc931bc399 --- /dev/null +++ b/testdata/fuzzy/testdata/TestFuzzyModel-not-fuzzy.golden @@ -0,0 +1,2 @@ +task: [install] echo 'install' +install From 9e8fd54be99ac1ea6be502844d73c9596f3418ec Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 27 Apr 2025 23:02:32 +0000 Subject: [PATCH 21/30] chore: changelog for #2200 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 595dacc6c5..b7cc159fa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +- Fixed fuzzy suggestions not working when misspelling a task name (#2192, #2200 + by @vmaerten). + ## v3.43.3 - 2025-04-27 Reverted the changes made in #2113 and #2186 that affected the From b3c4007756dff95574def1420777eff85799d89f Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Mon, 28 Apr 2025 13:02:46 +0100 Subject: [PATCH 22/30] fix: double escaped paths (#2216) --- internal/execext/exec.go | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/internal/execext/exec.go b/internal/execext/exec.go index 811d539ca4..49af5249ea 100644 --- a/internal/execext/exec.go +++ b/internal/execext/exec.go @@ -90,15 +90,6 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error { return r.Run(ctx, p) } -func escape(s string) string { - s = filepath.ToSlash(s) - s = strings.ReplaceAll(s, " ", `\ `) - s = strings.ReplaceAll(s, "&", `\&`) - s = strings.ReplaceAll(s, "(", `\(`) - s = strings.ReplaceAll(s, ")", `\)`) - return s -} - // ExpandLiteral is a wrapper around [expand.Literal]. It will escape the input // string, expand any shell symbols (such as '~') and resolve any environment // variables. @@ -106,25 +97,17 @@ func ExpandLiteral(s string) (string, error) { if s == "" { return "", nil } - s = escape(s) p := syntax.NewParser() - var words []*syntax.Word - err := p.Words(strings.NewReader(s), func(w *syntax.Word) bool { - words = append(words, w) - return true - }) + word, err := p.Document(strings.NewReader(s)) if err != nil { return "", err } - if len(words) == 0 { - return "", nil - } cfg := &expand.Config{ Env: expand.FuncEnviron(os.Getenv), ReadDir2: os.ReadDir, GlobStar: true, } - return expand.Literal(cfg, words[0]) + return expand.Literal(cfg, word) } // ExpandFields is a wrapper around [expand.Fields]. It will escape the input @@ -132,7 +115,6 @@ func ExpandLiteral(s string) (string, error) { // variables. It also expands brace expressions ({a.b}) and globs (*/**) and // returns the results as a list of strings. func ExpandFields(s string) ([]string, error) { - s = escape(s) p := syntax.NewParser() var words []*syntax.Word err := p.Words(strings.NewReader(s), func(w *syntax.Word) bool { From 0058f1867634208692f13dcc025a7b3be2ced2ee Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Mon, 28 Apr 2025 12:05:10 +0000 Subject: [PATCH 23/30] chore: changelog for #2216 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7cc159fa2..6f7696d13a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Fixed fuzzy suggestions not working when misspelling a task name (#2192, #2200 by @vmaerten). +- Fixed a bug where taskfiles in directories containing spaces created + directories in the wrong location (#2208, #2216 by @pd93). ## v3.43.3 - 2025-04-27 From d850d03c96b3d0a610c464cc4b30dc1ffbf9f3a6 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Mon, 28 Apr 2025 13:19:56 +0100 Subject: [PATCH 24/30] feat: add yaml templating functions (#2219) * feat: add yaml templating functions * docs: add yaml functions to templating reference * refactor: remove some unnecessary function wrappers --- internal/templater/funcs.go | 147 +++++++++++++++++--------- website/docs/reference/templating.mdx | 2 + 2 files changed, 97 insertions(+), 52 deletions(-) diff --git a/internal/templater/funcs.go b/internal/templater/funcs.go index 5daabf84cf..0fa1b2b429 100644 --- a/internal/templater/funcs.go +++ b/internal/templater/funcs.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/davecgh/go-spew/spew" + "gopkg.in/yaml.v3" "mvdan.cc/sh/v3/shell" "mvdan.cc/sh/v3/syntax" @@ -18,58 +19,25 @@ var templateFuncs template.FuncMap func init() { taskFuncs := template.FuncMap{ - "OS": func() string { return runtime.GOOS }, - "ARCH": func() string { return runtime.GOARCH }, - "numCPU": func() int { return runtime.NumCPU() }, - "catLines": func(s string) string { - s = strings.ReplaceAll(s, "\r\n", " ") - return strings.ReplaceAll(s, "\n", " ") - }, - "splitLines": func(s string) []string { - s = strings.ReplaceAll(s, "\r\n", "\n") - return strings.Split(s, "\n") - }, - "fromSlash": func(path string) string { - return filepath.FromSlash(path) - }, - "toSlash": func(path string) string { - return filepath.ToSlash(path) - }, - "exeExt": func() string { - if runtime.GOOS == "windows" { - return ".exe" - } - return "" - }, - "shellQuote": func(str string) (string, error) { - return syntax.Quote(str, syntax.LangBash) - }, - "splitArgs": func(s string) ([]string, error) { - return shell.Fields(s, nil) - }, - // IsSH is deprecated. - "IsSH": func() bool { return true }, - "joinPath": func(elem ...string) string { - return filepath.Join(elem...) - }, - "relPath": func(basePath, targetPath string) (string, error) { - return filepath.Rel(basePath, targetPath) - }, - "merge": func(base map[string]any, v ...map[string]any) map[string]any { - cap := len(v) - for _, m := range v { - cap += len(m) - } - result := make(map[string]any, cap) - maps.Copy(result, base) - for _, m := range v { - maps.Copy(result, m) - } - return result - }, - "spew": func(v any) string { - return spew.Sdump(v) - }, + "OS": os, + "ARCH": arch, + "numCPU": runtime.NumCPU, + "catLines": catLines, + "splitLines": splitLines, + "fromSlash": filepath.FromSlash, + "toSlash": filepath.ToSlash, + "exeExt": exeExt, + "shellQuote": shellQuote, + "splitArgs": splitArgs, + "IsSH": IsSH, // Deprecated + "joinPath": filepath.Join, + "relPath": filepath.Rel, + "merge": merge, + "spew": spew.Sdump, + "fromYaml": fromYaml, + "mustFromYaml": mustFromYaml, + "toYaml": toYaml, + "mustToYaml": mustToYaml, } // aliases @@ -83,3 +51,78 @@ func init() { templateFuncs = template.FuncMap(sprig.TxtFuncMap()) maps.Copy(templateFuncs, taskFuncs) } + +func os() string { + return runtime.GOOS +} + +func arch() string { + return runtime.GOARCH +} + +func catLines(s string) string { + s = strings.ReplaceAll(s, "\r\n", " ") + return strings.ReplaceAll(s, "\n", " ") +} + +func splitLines(s string) []string { + s = strings.ReplaceAll(s, "\r\n", "\n") + return strings.Split(s, "\n") +} + +func exeExt() string { + if runtime.GOOS == "windows" { + return ".exe" + } + return "" +} + +func shellQuote(str string) (string, error) { + return syntax.Quote(str, syntax.LangBash) +} + +func splitArgs(s string) ([]string, error) { + return shell.Fields(s, nil) +} + +// Deprecated: now always returns true +func IsSH() bool { + return true +} + +func merge(base map[string]any, v ...map[string]any) map[string]any { + cap := len(v) + for _, m := range v { + cap += len(m) + } + result := make(map[string]any, cap) + maps.Copy(result, base) + for _, m := range v { + maps.Copy(result, m) + } + return result +} + +func fromYaml(v string) any { + output, _ := mustFromYaml(v) + return output +} + +func mustFromYaml(v string) (any, error) { + var output any + err := yaml.Unmarshal([]byte(v), &output) + return output, err +} + +func toYaml(v any) string { + output, _ := yaml.Marshal(v) + return string(output) +} + +func mustToYaml(v any) (string, error) { + output, err := yaml.Marshal(v) + if err != nil { + return "", err + } + return string(output), nil +} diff --git a/website/docs/reference/templating.mdx b/website/docs/reference/templating.mdx index 98d45bae00..b471b134ca 100644 --- a/website/docs/reference/templating.mdx +++ b/website/docs/reference/templating.mdx @@ -264,6 +264,8 @@ description here for completeness. For detailed usage, please refer to the | `toJson`\* | Encodes an object as a JSON string. | | `toPrettyJson`\* | Encodes an object as a JSON string with new lines and indentation. | | `toRawJson`\* | Encodes an object as a JSON string with HTML characters unescaped. | +| `fromYaml`\* | Decodes a YAML string into an object. | +| `toYaml`\* | Encodes an object as a YAML string. | | `b64enc` | Encodes a string into base 64. | | `b64dec` | Decodes a string from base 64. | | `b32enc` | Encodes a string into base 32. | From d3e9be152054084205c8d8a8aa8cb817d7329a6b Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Mon, 28 Apr 2025 12:21:26 +0000 Subject: [PATCH 25/30] chore: changelog for #2219 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f7696d13a..4587d56ed6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Added `toYaml` and `fromYaml` templating functions (#2217, #2219 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 From fc17343fccb9c63c522e1f7e8a1ef083f345c592 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 14:37:44 +0200 Subject: [PATCH 26/30] chore(deps): update all non-major dependencies (#2214) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .nvmrc | 2 +- go.mod | 2 +- go.sum | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.nvmrc b/.nvmrc index 7d41c735d7..b8ffd70759 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.14.0 +22.15.0 diff --git a/go.mod b/go.mod index 142c675c7f..1cb8ba6e80 100644 --- a/go.mod +++ b/go.mod @@ -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.16.0 + github.com/alecthomas/chroma/v2 v2.17.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 diff --git a/go.sum b/go.sum index 3095a5984b..0f29254711 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpST github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= github.com/alecthomas/chroma/v2 v2.16.0 h1:QC5ZMizk67+HzxFDjQ4ASjni5kWBTGiigRG1u23IGvA= github.com/alecthomas/chroma/v2 v2.16.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +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/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= From d1bfd3e9f731ec7128c384e5dfe334f71c5b557d Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Mon, 28 Apr 2025 20:57:18 +0000 Subject: [PATCH 27/30] docs: move yaml templating functions to the correct section --- website/docs/reference/templating.mdx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/website/docs/reference/templating.mdx b/website/docs/reference/templating.mdx index b471b134ca..47ad7d9a47 100644 --- a/website/docs/reference/templating.mdx +++ b/website/docs/reference/templating.mdx @@ -264,13 +264,15 @@ description here for completeness. For detailed usage, please refer to the | `toJson`\* | Encodes an object as a JSON string. | | `toPrettyJson`\* | Encodes an object as a JSON string with new lines and indentation. | | `toRawJson`\* | Encodes an object as a JSON string with HTML characters unescaped. | -| `fromYaml`\* | Decodes a YAML string into an object. | -| `toYaml`\* | Encodes an object as a YAML string. | | `b64enc` | Encodes a string into base 64. | | `b64dec` | Decodes a string from base 64. | | `b32enc` | Encodes a string into base 32. | | `b32dec` | Decodes a string from base 32. | +:::note +YAML encoding functions are [provided directly by Task](#task-functions). +::: + #### [List Functions][list-functions] | Function | Description | @@ -338,6 +340,10 @@ description here for completeness. For detailed usage, please refer to the | `osExt` | Returns the file extension of a filepath. | | `osIsAbs` | Checks if a filepath is absolute. | +:::note +More filepath encoding functions are [provided directly by Task](#task-functions). +::: + #### [Flow Control Functions][flow-control-functions] | Function | Description | @@ -377,7 +383,7 @@ Lastly, Task itself provides a few functions: | Function | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `OS` | Returns the operating system. Possible values are `windows`, `linux`, `darwin` (macOS) and `freebsd`. | -| `ARCH` | Returns the architecture Task was compiled to: `386`, `amd64`, `arm` or `s390x`. | +| `ARCH` | Returns the architecture Task was compiled to: `386`, `amd64`, `arm` or `s390x`. | | `numCPU` | Returns the number of logical CPU's usable by the current process. | | `splitLines` | Splits Unix (`\n`) and Windows (`\r\n`) styled newlines. | | `catLines` | Replaces Unix (`\n`) and Windows (`\r\n`) styled newlines with a space. | @@ -390,6 +396,8 @@ Lastly, Task itself provides a few functions: | `relPath` | Converts an absolute path (second argument) into a relative path, based on a base path (first argument). The same as Go's [filepath.Rel](https://pkg.go.dev/path/filepath#Rel). | | `merge` | Creates a new map that is a copy of the first map with the keys of each subsequent map merged into it. If there is a duplicate key, the value of the last map with that key is used. | | `spew` | Returns the Go representation of a specific variable. Useful for debugging. Uses the [davecgh/go-spew](https://github.com/davecgh/go-spew) package. | +| `fromYaml`\* | Decodes a YAML string into an object. | +| `toYaml`\* | Encodes an object as a YAML string. | {/* prettier-ignore-start */} [text/template]: https://pkg.go.dev/text/template From c12ed49acb33000241f8f946653c5a81048b30b4 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Mon, 28 Apr 2025 21:04:24 +0000 Subject: [PATCH 28/30] chore: remove unused any2 testdata --- testdata/vars/any/Taskfile.yml | 158 ++++++++++++----------- testdata/vars/{any2 => any}/example.json | 0 testdata/vars/{any2 => any}/example.yaml | 0 testdata/vars/any2/Taskfile.yml | 115 ----------------- 4 files changed, 82 insertions(+), 191 deletions(-) rename testdata/vars/{any2 => any}/example.json (100%) rename testdata/vars/{any2 => any}/example.yaml (100%) delete mode 100644 testdata/vars/any2/Taskfile.yml diff --git a/testdata/vars/any/Taskfile.yml b/testdata/vars/any/Taskfile.yml index a2eb2767dd..46f5b066ec 100644 --- a/testdata/vars/any/Taskfile.yml +++ b/testdata/vars/any/Taskfile.yml @@ -2,107 +2,113 @@ version: '3' tasks: default: - - task: dynamic - - task: string - - task: bool - - task: int - - task: string-array - task: map - - task: for-string - - task: for-int - - task: for-map - - task: for-multi-layer-map + - task: nested-map + - task: slice + - task: ref + - task: ref-sh + - task: ref-dep + - task: ref-resolver + - task: json - dynamic: + map: vars: - STRING_A: '$echo "A"' - STRING_B: '$echo {{.STRING_A}}B' - STRING_C: '$echo {{.STRING_B}}C' + MAP: + map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} cmds: - - echo '{{.STRING_C}}' + - task: print-story + vars: + VAR: + ref: .MAP - string: + nested-map: vars: - STRING_A: 'A' - STRING_B: '{{.STRING_A}}B' - STRING_C: '{{.STRING_B}}C' + FOO: "foo" + nested: + map: + variables: + work: "{{.FOO}}" cmds: - - echo '{{.STRING_C}}' + - echo {{.nested.variables.work}} - bool: + slice: vars: - BOOL_TRUE: true - BOOL_FALSE: false - BOOL_A: '{{and .BOOL_TRUE .BOOL_FALSE}}' - BOOL_B: '{{or .BOOL_TRUE .BOOL_FALSE}}' - BOOL_C: '{{not .BOOL_TRUE}}' + FOO: "foo" + BAR: "bar" + slice_variables_work: ["{{.FOO}}","{{.BAR}}"] cmds: - - echo '{{if .BOOL_TRUE}}A:{{.BOOL_A}} B:{{.BOOL_B}} C:{{.BOOL_C}}{{end}}' + - echo {{index .slice_variables_work 0}} {{index .slice_variables_work 1}} - int: + ref: vars: - INT_100: 100 - INT_10: 10 + MAP: + map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} + MAP_REF: + ref: .MAP cmds: - - echo '100 + 10 = {{add .INT_100 .INT_10}}' - - echo '100 - 10 = {{sub .INT_100 .INT_10}}' - - echo '100 * 10 = {{mul .INT_100 .INT_10}}' - - echo '100 / 10 = {{div .INT_100 .INT_10}}' + - task: print-story + vars: + VAR: + ref: .MAP_REF - string-array: + ref-sh: vars: - ARRAY_1: ['A', 'B', 'C'] - ARRAY_2: ['D', 'E', 'F'] + JSON_STRING: + sh: echo '{"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]}' + JSON: "fromJson {{.JSON_STRING}}" + MAP_REF: + ref: .JSON cmds: - - echo '{{append .ARRAY_1 "D"}}' - - echo '{{concat .ARRAY_1 .ARRAY_2}}' - - echo '{{join " " .ARRAY_1}}' + - task: print-story + vars: + VAR: + ref: .MAP_REF - map: + ref-dep: vars: - MAP_1: {A: 1, B: 2, C: 3} - MAP_2: {B: 4, C: 5, D: 6} - MAP_3: {C: 7, D: 8, E: 9} - cmds: - - echo '{{merge .MAP_1 .MAP_2 .MAP_3}}' + MAP: + map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} + deps: + - task: print-story + vars: + VAR: + ref: .MAP - for-string: + ref-resolver: vars: - LIST: [foo, bar, baz] + MAP: + map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} + MAP_REF: + ref: .MAP cmds: - - for: - var: LIST - cmd: echo {{.ITEM}} + - task: print-var + vars: + VAR: + ref: (index .MAP_REF.children 0).name - for-int: + json: vars: - LIST: [1, 2, 3] + JSON_STRING: + sh: cat example.json + JSON: + ref: "fromJson .JSON_STRING" cmds: - - for: - var: LIST - cmd: echo {{add .ITEM 100}} + - task: print-story + vars: + VAR: + ref: .JSON - for-map: - vars: - MAP: - KEY_1: value_1 - KEY_2: value_2 - KEY_3: value_3 + print-var: cmds: - - for: - var: MAP - cmd: echo {{.KEY}} {{.ITEM}} + - echo "{{.VAR}}" - for-multi-layer-map: - vars: - MAP: - KEY_1: - SUBKEY: sub_value_1 - KEY_2: - SUBKEY: sub_value_2 - KEY_3: - SUBKEY: sub_value_3 + print-story: cmds: - - for: - var: MAP - cmd: echo {{.KEY}} {{.ITEM.SUBKEY}} + - >- + echo "{{.VAR.name}} has {{len .VAR.children}} children called + {{- $children := .VAR.children -}} + {{- range $i, $child := $children -}} + {{- if lt $i (sub (len $children) 1)}} {{$child.name -}}, + {{- else}} and {{$child.name -}} + {{- end -}} + {{- end -}}" diff --git a/testdata/vars/any2/example.json b/testdata/vars/any/example.json similarity index 100% rename from testdata/vars/any2/example.json rename to testdata/vars/any/example.json diff --git a/testdata/vars/any2/example.yaml b/testdata/vars/any/example.yaml similarity index 100% rename from testdata/vars/any2/example.yaml rename to testdata/vars/any/example.yaml diff --git a/testdata/vars/any2/Taskfile.yml b/testdata/vars/any2/Taskfile.yml deleted file mode 100644 index 37b0206ecf..0000000000 --- a/testdata/vars/any2/Taskfile.yml +++ /dev/null @@ -1,115 +0,0 @@ -version: '3' - -tasks: - default: - - task: map - - task: nested-map - - task: slice - - task: ref - - task: ref-sh - - task: ref-dep - - task: ref-resolver - - task: json - - map: - vars: - MAP: - map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} - cmds: - - task: print-story - vars: - VAR: - ref: .MAP - - nested-map: - vars: - FOO: "foo" - nested: - map: - variables: - work: "{{.FOO}}" - cmds: - - echo {{.nested.variables.work}} - - slice: - vars: - FOO: "foo" - BAR: "bar" - slice_variables_work: ["{{.FOO}}","{{.BAR}}"] - cmds: - - echo {{index .slice_variables_work 0}} {{index .slice_variables_work 1}} - - ref: - vars: - MAP: - map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} - MAP_REF: - ref: .MAP - cmds: - - task: print-story - vars: - VAR: - ref: .MAP_REF - - ref-sh: - vars: - JSON_STRING: - sh: echo '{"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]}' - JSON: - json: "{{.JSON_STRING}}" - MAP_REF: - ref: .JSON - cmds: - - task: print-story - vars: - VAR: - ref: .MAP_REF - - ref-dep: - vars: - MAP: - map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} - deps: - - task: print-story - vars: - VAR: - ref: .MAP - - ref-resolver: - vars: - MAP: - map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]} - MAP_REF: - ref: .MAP - cmds: - - task: print-var - vars: - VAR: - ref: (index .MAP_REF.children 0).name - - json: - vars: - JSON_STRING: - sh: cat example.json - JSON: - ref: "fromJson .JSON_STRING" - cmds: - - task: print-story - vars: - VAR: - ref: .JSON - - print-var: - cmds: - - echo "{{.VAR}}" - - print-story: - cmds: - - >- - echo "{{.VAR.name}} has {{len .VAR.children}} children called - {{- $children := .VAR.children -}} - {{- range $i, $child := $children -}} - {{- if lt $i (sub (len $children) 1)}} {{$child.name -}}, - {{- else}} and {{$child.name -}} - {{- end -}} - {{- end -}}" From 6896accf8632452bee35dd2e9488ada779370131 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Thu, 1 May 2025 18:43:43 +0100 Subject: [PATCH 29/30] feat: cli args list (#2140) --- args/args.go | 28 ++++++++++++++------------- cmd/task/task.go | 11 ++++++++--- website/docs/reference/templating.mdx | 3 ++- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/args/args.go b/args/args.go index 8f6b5052ce..3376ee9d05 100644 --- a/args/args.go +++ b/args/args.go @@ -13,24 +13,14 @@ import ( // Get fetches the remaining arguments after CLI parsing and splits them into // two groups: the arguments before the double dash (--) and the arguments after // the double dash. -func Get() ([]string, string, error) { +func Get() ([]string, []string, error) { args := pflag.Args() doubleDashPos := pflag.CommandLine.ArgsLenAtDash() if doubleDashPos == -1 { - return args, "", nil + return args, nil, nil } - - var quotedCliArgs []string - for _, arg := range args[doubleDashPos:] { - quotedCliArg, err := syntax.Quote(arg, syntax.LangBash) - if err != nil { - return nil, "", err - } - quotedCliArgs = append(quotedCliArgs, quotedCliArg) - } - - return args[:doubleDashPos], strings.Join(quotedCliArgs, " "), nil + return args[:doubleDashPos], args[doubleDashPos:], nil } // Parse parses command line argument: tasks and global variables @@ -51,6 +41,18 @@ func Parse(args ...string) ([]*task.Call, *ast.Vars) { return calls, globals } +func ToQuotedString(args []string) (string, error) { + var quotedCliArgs []string + for _, arg := range args { + quotedCliArg, err := syntax.Quote(arg, syntax.LangBash) + if err != nil { + return "", err + } + quotedCliArgs = append(quotedCliArgs, quotedCliArg) + } + return strings.Join(quotedCliArgs, " "), nil +} + func splitVar(s string) (string, string) { pair := strings.SplitN(s, "=", 2) return pair[0], pair[1] diff --git a/cmd/task/task.go b/cmd/task/task.go index 31538afc57..d5f2b7baf0 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -144,18 +144,23 @@ func run() error { } // Parse the remaining arguments - argv, cliArgs, err := args.Get() + cliArgsPreDash, cliArgsPostDash, err := args.Get() if err != nil { return err } - calls, globals := args.Parse(argv...) + calls, globals := args.Parse(cliArgsPreDash...) // If there are no calls, run the default task instead if len(calls) == 0 { calls = append(calls, &task.Call{Task: "default"}) } - globals.Set("CLI_ARGS", ast.Var{Value: cliArgs}) + cliArgsPostDashQuoted, err := args.ToQuotedString(cliArgsPostDash) + if err != nil { + return err + } + globals.Set("CLI_ARGS", ast.Var{Value: cliArgsPostDashQuoted}) + globals.Set("CLI_ARGS_LIST", ast.Var{Value: cliArgsPostDash}) globals.Set("CLI_FORCE", ast.Var{Value: flags.Force || flags.ForceAll}) globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent}) globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose}) diff --git a/website/docs/reference/templating.mdx b/website/docs/reference/templating.mdx index 47ad7d9a47..60413d57af 100644 --- a/website/docs/reference/templating.mdx +++ b/website/docs/reference/templating.mdx @@ -102,7 +102,8 @@ special variable will be overridden. | Var | Description | |--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI. | +| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI as a string. | +| `CLI_ARGS_LIST` | Contain all extra arguments passed after `--` when calling Task through the CLI as a shell parsed list. | | `CLI_FORCE` | A boolean containing whether the `--force` or `--force-all` flags were set. | | `CLI_SILENT` | A boolean containing whether the `--silent` flag was set. | | `CLI_VERBOSE` | A boolean containing whether the `--verbose` flag was set. | From f8736c5f770d9f351afd4e8504dddfeec3ec7ad8 Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Thu, 1 May 2025 17:51:47 +0000 Subject: [PATCH 30/30] chore: changelog for #2140 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4587d56ed6..8bbcbd7431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Added new `CLI_ARGS_LIST` array variable which contains the arguments passed + 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). - Fixed fuzzy suggestions not working when misspelling a task name (#2192, #2200 by @vmaerten).