mirror of
https://github.com/go-task/task.git
synced 2025-09-16 09:26:16 +02:00
refactor: decouple fingerprinting from executor (#1039)
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
- Fixed bug where `.task/checksum` file was sometimes not being created when
|
- Fixed bug where `.task/checksum` file was sometimes not being created when
|
||||||
task also declares a `status:`
|
task also declares a `status:`
|
||||||
([#840](https://github.com/go-task/task/issues/840), [#1035](https://github.com/go-task/task/pull/1035) by @harelwa, [#1037](https://github.com/go-task/task/pull/1037) by @pd93).
|
([#840](https://github.com/go-task/task/issues/840), [#1035](https://github.com/go-task/task/pull/1035) by @harelwa, [#1037](https://github.com/go-task/task/pull/1037) by @pd93).
|
||||||
|
- Refactored and decoupled fingerprinting from the main Task executor ([#1039](https://github.com/go-task/task/issues/1039) by @pd93).
|
||||||
- Fixed deadlock issue when using `run: once`
|
- Fixed deadlock issue when using `run: once`
|
||||||
([#715](https://github.com/go-task/task/issues/715), [#1025](https://github.com/go-task/task/pull/1025) by @theunrepentantgeek).
|
([#715](https://github.com/go-task/task/issues/715), [#1025](https://github.com/go-task/task/pull/1025) by @theunrepentantgeek).
|
||||||
|
|
||||||
|
18
Taskfile.yml
18
Taskfile.yml
@@ -27,6 +27,24 @@ tasks:
|
|||||||
GIT_COMMIT:
|
GIT_COMMIT:
|
||||||
sh: git log -n 1 --format=%h
|
sh: git log -n 1 --format=%h
|
||||||
|
|
||||||
|
generate:
|
||||||
|
desc: Runs Go generate to create mocks
|
||||||
|
aliases: [gen, g]
|
||||||
|
deps: [install:mockgen]
|
||||||
|
sources:
|
||||||
|
- "internal/fingerprint/checker.go"
|
||||||
|
generates:
|
||||||
|
- "internal/fingerprint/checker_mock.go"
|
||||||
|
cmds:
|
||||||
|
- mockgen -source=internal/fingerprint/checker.go -destination=internal/fingerprint/checker_mock.go -package=fingerprint
|
||||||
|
|
||||||
|
install:mockgen:
|
||||||
|
desc: Installs mockgen; a tool to generate mock files
|
||||||
|
status:
|
||||||
|
- command -v mockgen &>/dev/null
|
||||||
|
cmds:
|
||||||
|
- go install github.com/golang/mock/mockgen@latest
|
||||||
|
|
||||||
mod:
|
mod:
|
||||||
desc: Downloads and tidy Go modules
|
desc: Downloads and tidy Go modules
|
||||||
cmds:
|
cmds:
|
||||||
|
1
go.mod
1
go.mod
@@ -6,6 +6,7 @@ require (
|
|||||||
github.com/Masterminds/semver/v3 v3.2.0
|
github.com/Masterminds/semver/v3 v3.2.0
|
||||||
github.com/fatih/color v1.14.1
|
github.com/fatih/color v1.14.1
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
||||||
|
github.com/golang/mock v1.6.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/mattn/go-zglob v0.0.4
|
github.com/mattn/go-zglob v0.0.4
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
|
27
go.sum
27
go.sum
@@ -9,6 +9,8 @@ github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8Wlg
|
|||||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
@@ -40,17 +42,38 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6 h1:Ic9KukPQ7PegFzHckNiMTQXGgEszA7mY2Fn4ZMtnMbw=
|
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6 h1:Ic9KukPQ7PegFzHckNiMTQXGgEszA7mY2Fn4ZMtnMbw=
|
||||||
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
13
help.go
13
help.go
@@ -12,6 +12,7 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/editors"
|
"github.com/go-task/task/v3/internal/editors"
|
||||||
|
"github.com/go-task/task/v3/internal/fingerprint"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
@@ -148,7 +149,17 @@ func (e *Executor) ToEditorOutput(tasks []*taskfile.Task) (*editors.Output, erro
|
|||||||
Tasks: make([]editors.Task, len(tasks)),
|
Tasks: make([]editors.Task, len(tasks)),
|
||||||
}
|
}
|
||||||
for i, t := range tasks {
|
for i, t := range tasks {
|
||||||
upToDate, err := e.isTaskUpToDate(context.Background(), t)
|
// Get the fingerprinting method to use
|
||||||
|
method := e.Taskfile.Method
|
||||||
|
if t.Method != "" {
|
||||||
|
method = t.Method
|
||||||
|
}
|
||||||
|
upToDate, err := fingerprint.IsTaskUpToDate(context.Background(), t,
|
||||||
|
fingerprint.WithMethod(method),
|
||||||
|
fingerprint.WithTempDir(e.TempDir),
|
||||||
|
fingerprint.WithDry(e.Dry),
|
||||||
|
fingerprint.WithLogger(e.Logger),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
31
internal/env/env.go
vendored
Normal file
31
internal/env/env.go
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Get(t *taskfile.Task) []string {
|
||||||
|
if t.Env == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
environ := os.Environ()
|
||||||
|
|
||||||
|
for k, v := range t.Env.ToCacheMap() {
|
||||||
|
str, isString := v.(string)
|
||||||
|
if !isString {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, alreadySet := os.LookupEnv(k); alreadySet {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
environ = append(environ, fmt.Sprintf("%s=%s", k, str))
|
||||||
|
}
|
||||||
|
|
||||||
|
return environ
|
||||||
|
}
|
20
internal/fingerprint/checker.go
Normal file
20
internal/fingerprint/checker.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusCheckable defines any type that can check if the status of a task is up-to-date.
|
||||||
|
type StatusCheckable interface {
|
||||||
|
IsUpToDate(ctx context.Context, t *taskfile.Task) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourcesCheckable defines any type that can check if the sources of a task are up-to-date.
|
||||||
|
type SourcesCheckable interface {
|
||||||
|
IsUpToDate(t *taskfile.Task) (bool, error)
|
||||||
|
Value(t *taskfile.Task) (interface{}, error)
|
||||||
|
OnError(t *taskfile.Task) error
|
||||||
|
Kind() string
|
||||||
|
}
|
132
internal/fingerprint/checker_mock.go
Normal file
132
internal/fingerprint/checker_mock.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: checker.go
|
||||||
|
|
||||||
|
// Package fingerprint is a generated GoMock package.
|
||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
taskfile "github.com/go-task/task/v3/taskfile"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockStatusCheckable is a mock of StatusCheckable interface.
|
||||||
|
type MockStatusCheckable struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockStatusCheckableMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockStatusCheckableMockRecorder is the mock recorder for MockStatusCheckable.
|
||||||
|
type MockStatusCheckableMockRecorder struct {
|
||||||
|
mock *MockStatusCheckable
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockStatusCheckable creates a new mock instance.
|
||||||
|
func NewMockStatusCheckable(ctrl *gomock.Controller) *MockStatusCheckable {
|
||||||
|
mock := &MockStatusCheckable{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockStatusCheckableMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockStatusCheckable) EXPECT() *MockStatusCheckableMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpToDate mocks base method.
|
||||||
|
func (m *MockStatusCheckable) IsUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "IsUpToDate", ctx, t)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpToDate indicates an expected call of IsUpToDate.
|
||||||
|
func (mr *MockStatusCheckableMockRecorder) IsUpToDate(ctx, t interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsUpToDate", reflect.TypeOf((*MockStatusCheckable)(nil).IsUpToDate), ctx, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockSourcesCheckable is a mock of SourcesCheckable interface.
|
||||||
|
type MockSourcesCheckable struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockSourcesCheckableMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockSourcesCheckableMockRecorder is the mock recorder for MockSourcesCheckable.
|
||||||
|
type MockSourcesCheckableMockRecorder struct {
|
||||||
|
mock *MockSourcesCheckable
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockSourcesCheckable creates a new mock instance.
|
||||||
|
func NewMockSourcesCheckable(ctrl *gomock.Controller) *MockSourcesCheckable {
|
||||||
|
mock := &MockSourcesCheckable{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockSourcesCheckableMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockSourcesCheckable) EXPECT() *MockSourcesCheckableMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpToDate mocks base method.
|
||||||
|
func (m *MockSourcesCheckable) IsUpToDate(t *taskfile.Task) (bool, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "IsUpToDate", t)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpToDate indicates an expected call of IsUpToDate.
|
||||||
|
func (mr *MockSourcesCheckableMockRecorder) IsUpToDate(t interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsUpToDate", reflect.TypeOf((*MockSourcesCheckable)(nil).IsUpToDate), t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind mocks base method.
|
||||||
|
func (m *MockSourcesCheckable) Kind() string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Kind")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind indicates an expected call of Kind.
|
||||||
|
func (mr *MockSourcesCheckableMockRecorder) Kind() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Kind", reflect.TypeOf((*MockSourcesCheckable)(nil).Kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnError mocks base method.
|
||||||
|
func (m *MockSourcesCheckable) OnError(t *taskfile.Task) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "OnError", t)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnError indicates an expected call of OnError.
|
||||||
|
func (mr *MockSourcesCheckableMockRecorder) OnError(t interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnError", reflect.TypeOf((*MockSourcesCheckable)(nil).OnError), t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value mocks base method.
|
||||||
|
func (m *MockSourcesCheckable) Value(t *taskfile.Task) (interface{}, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Value", t)
|
||||||
|
ret0, _ := ret[0].(interface{})
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value indicates an expected call of Value.
|
||||||
|
func (mr *MockSourcesCheckableMockRecorder) Value(t interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Value", reflect.TypeOf((*MockSourcesCheckable)(nil).Value), t)
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package status
|
package fingerprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
16
internal/fingerprint/sources.go
Normal file
16
internal/fingerprint/sources.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func NewSourcesChecker(method, tempDir string, dry bool) (SourcesCheckable, error) {
|
||||||
|
switch method {
|
||||||
|
case "timestamp":
|
||||||
|
return NewTimestampChecker(tempDir, dry), nil
|
||||||
|
case "checksum":
|
||||||
|
return NewChecksumChecker(tempDir, dry), nil
|
||||||
|
case "none":
|
||||||
|
return NoneChecker{}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(`task: invalid method "%s"`, method)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package status
|
package fingerprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
@@ -10,51 +10,54 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checksum validades if a task is up to date by calculating its source
|
// ChecksumChecker validates if a task is up to date by calculating its source
|
||||||
// files checksum
|
// files checksum
|
||||||
type Checksum struct {
|
type ChecksumChecker struct {
|
||||||
TempDir string
|
tempDir string
|
||||||
TaskDir string
|
dry bool
|
||||||
Task string
|
|
||||||
Sources []string
|
|
||||||
Generates []string
|
|
||||||
Dry bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUpToDate implements the Checker interface
|
func NewChecksumChecker(tempDir string, dry bool) *ChecksumChecker {
|
||||||
func (c *Checksum) IsUpToDate() (bool, error) {
|
return &ChecksumChecker{
|
||||||
if len(c.Sources) == 0 {
|
tempDir: tempDir,
|
||||||
|
dry: dry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *ChecksumChecker) IsUpToDate(t *taskfile.Task) (bool, error) {
|
||||||
|
if len(t.Sources) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
checksumFile := c.checksumFilePath()
|
checksumFile := checker.checksumFilePath(t)
|
||||||
|
|
||||||
data, _ := os.ReadFile(checksumFile)
|
data, _ := os.ReadFile(checksumFile)
|
||||||
oldMd5 := strings.TrimSpace(string(data))
|
oldMd5 := strings.TrimSpace(string(data))
|
||||||
|
|
||||||
sources, err := globs(c.TaskDir, c.Sources)
|
sources, err := globs(t.Dir, t.Sources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMd5, err := c.checksum(sources...)
|
newMd5, err := checker.checksum(sources...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.Dry {
|
if !checker.dry {
|
||||||
_ = os.MkdirAll(filepathext.SmartJoin(c.TempDir, "checksum"), 0o755)
|
_ = os.MkdirAll(filepathext.SmartJoin(checker.tempDir, "checksum"), 0o755)
|
||||||
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0o644); err != nil {
|
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0o644); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Generates) > 0 {
|
if len(t.Generates) > 0 {
|
||||||
// For each specified 'generates' field, check whether the files actually exist
|
// For each specified 'generates' field, check whether the files actually exist
|
||||||
for _, g := range c.Generates {
|
for _, g := range t.Generates {
|
||||||
generates, err := Glob(c.TaskDir, g)
|
generates, err := Glob(t.Dir, g)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -70,7 +73,22 @@ func (c *Checksum) IsUpToDate() (bool, error) {
|
|||||||
return oldMd5 == newMd5, nil
|
return oldMd5 == newMd5, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checksum) checksum(files ...string) (string, error) {
|
func (checker *ChecksumChecker) Value(t *taskfile.Task) (interface{}, error) {
|
||||||
|
return checker.checksum()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *ChecksumChecker) OnError(t *taskfile.Task) error {
|
||||||
|
if len(t.Sources) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return os.Remove(checker.checksumFilePath(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ChecksumChecker) Kind() string {
|
||||||
|
return "checksum"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChecksumChecker) checksum(files ...string) (string, error) {
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
@@ -91,31 +109,13 @@ func (c *Checksum) checksum(files ...string) (string, error) {
|
|||||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value implements the Checker Interface
|
func (checker *ChecksumChecker) checksumFilePath(t *taskfile.Task) string {
|
||||||
func (c *Checksum) Value() (interface{}, error) {
|
return filepath.Join(checker.tempDir, "checksum", normalizeFilename(t.Name()))
|
||||||
return c.checksum()
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnError implements the Checker interface
|
|
||||||
func (c *Checksum) OnError() error {
|
|
||||||
if len(c.Sources) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return os.Remove(c.checksumFilePath())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kind implements the Checker Interface
|
|
||||||
func (*Checksum) Kind() string {
|
|
||||||
return "checksum"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Checksum) checksumFilePath() string {
|
|
||||||
return filepath.Join(c.TempDir, "checksum", normalizeFilename(c.Task))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
||||||
|
|
||||||
// replaces invalid caracters on filenames with "-"
|
// replaces invalid characters on filenames with "-"
|
||||||
func normalizeFilename(f string) string {
|
func normalizeFilename(f string) string {
|
||||||
return checksumFilenameRegexp.ReplaceAllString(f, "-")
|
return checksumFilenameRegexp.ReplaceAllString(f, "-")
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package status
|
package fingerprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
23
internal/fingerprint/sources_none.go
Normal file
23
internal/fingerprint/sources_none.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import "github.com/go-task/task/v3/taskfile"
|
||||||
|
|
||||||
|
// NoneChecker is a no-op Checker.
|
||||||
|
// It will always report that the task is not up-to-date.
|
||||||
|
type NoneChecker struct{}
|
||||||
|
|
||||||
|
func (NoneChecker) IsUpToDate(t *taskfile.Task) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NoneChecker) Value(t *taskfile.Task) (interface{}, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NoneChecker) OnError(t *taskfile.Task) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NoneChecker) Kind() string {
|
||||||
|
return "none"
|
||||||
|
}
|
@@ -1,24 +1,29 @@
|
|||||||
package status
|
package fingerprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Timestamp checks if any source change compared with the generated files,
|
// TimestampChecker checks if any source change compared with the generated files,
|
||||||
// using file modifications timestamps.
|
// using file modifications timestamps.
|
||||||
type Timestamp struct {
|
type TimestampChecker struct {
|
||||||
TempDir string
|
tempDir string
|
||||||
Task string
|
dry bool
|
||||||
Dir string
|
}
|
||||||
Sources []string
|
|
||||||
Generates []string
|
func NewTimestampChecker(tempDir string, dry bool) *TimestampChecker {
|
||||||
Dry bool
|
return &TimestampChecker{
|
||||||
|
tempDir: tempDir,
|
||||||
|
dry: dry,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUpToDate implements the Checker interface
|
// IsUpToDate implements the Checker interface
|
||||||
func (t *Timestamp) IsUpToDate() (bool, error) {
|
func (checker *TimestampChecker) IsUpToDate(t *taskfile.Task) (bool, error) {
|
||||||
if len(t.Sources) == 0 {
|
if len(t.Sources) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -32,7 +37,7 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
timestampFile := t.timestampFilePath()
|
timestampFile := checker.timestampFilePath(t)
|
||||||
|
|
||||||
// If the file exists, add the file path to the generates.
|
// If the file exists, add the file path to the generates.
|
||||||
// If the generate file is old, the task will be executed.
|
// If the generate file is old, the task will be executed.
|
||||||
@@ -41,7 +46,7 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
|
|||||||
generates = append(generates, timestampFile)
|
generates = append(generates, timestampFile)
|
||||||
} else {
|
} else {
|
||||||
// Create the timestamp file for the next execution when the file does not exist.
|
// Create the timestamp file for the next execution when the file does not exist.
|
||||||
if !t.Dry {
|
if !checker.dry {
|
||||||
if err := os.MkdirAll(filepath.Dir(timestampFile), 0o755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(timestampFile), 0o755); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -70,7 +75,7 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Modify the metadata of the file to the the current time.
|
// Modify the metadata of the file to the the current time.
|
||||||
if !t.Dry {
|
if !checker.dry {
|
||||||
if err := os.Chtimes(timestampFile, taskTime, taskTime); err != nil {
|
if err := os.Chtimes(timestampFile, taskTime, taskTime); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -79,12 +84,12 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
|
|||||||
return !shouldUpdate, nil
|
return !shouldUpdate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Timestamp) Kind() string {
|
func (checker *TimestampChecker) Kind() string {
|
||||||
return "timestamp"
|
return "timestamp"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value implements the Checker Interface
|
// Value implements the Checker Interface
|
||||||
func (t *Timestamp) Value() (interface{}, error) {
|
func (checker *TimestampChecker) Value(t *taskfile.Task) (interface{}, error) {
|
||||||
sources, err := globs(t.Dir, t.Sources)
|
sources, err := globs(t.Dir, t.Sources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Now(), err
|
return time.Now(), err
|
||||||
@@ -137,10 +142,10 @@ func anyFileNewerThan(files []string, givenTime time.Time) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OnError implements the Checker interface
|
// OnError implements the Checker interface
|
||||||
func (*Timestamp) OnError() error {
|
func (*TimestampChecker) OnError(t *taskfile.Task) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Timestamp) timestampFilePath() string {
|
func (checker *TimestampChecker) timestampFilePath(t *taskfile.Task) string {
|
||||||
return filepath.Join(t.TempDir, "timestamp", normalizeFilename(t.Task))
|
return filepath.Join(checker.tempDir, "timestamp", normalizeFilename(t.Task))
|
||||||
}
|
}
|
36
internal/fingerprint/status.go
Normal file
36
internal/fingerprint/status.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/env"
|
||||||
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StatusChecker struct {
|
||||||
|
logger *logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatusChecker(logger *logger.Logger) StatusCheckable {
|
||||||
|
return &StatusChecker{
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *StatusChecker) IsUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
|
||||||
|
for _, s := range t.Status {
|
||||||
|
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
|
||||||
|
Command: s,
|
||||||
|
Dir: t.Dir,
|
||||||
|
Env: env.Get(t),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
checker.logger.VerboseOutf(logger.Yellow, "task: status command %s exited non-zero: %s", s, err)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
checker.logger.VerboseOutf(logger.Yellow, "task: status command %s exited zero", s)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
132
internal/fingerprint/task.go
Normal file
132
internal/fingerprint/task.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
CheckerOption func(*CheckerConfig)
|
||||||
|
CheckerConfig struct {
|
||||||
|
method string
|
||||||
|
dry bool
|
||||||
|
tempDir string
|
||||||
|
logger *logger.Logger
|
||||||
|
statusChecker StatusCheckable
|
||||||
|
sourcesChecker SourcesCheckable
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func WithMethod(method string) CheckerOption {
|
||||||
|
return func(config *CheckerConfig) {
|
||||||
|
config.method = method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDry(dry bool) CheckerOption {
|
||||||
|
return func(config *CheckerConfig) {
|
||||||
|
config.dry = dry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTempDir(tempDir string) CheckerOption {
|
||||||
|
return func(config *CheckerConfig) {
|
||||||
|
config.tempDir = tempDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithLogger(logger *logger.Logger) CheckerOption {
|
||||||
|
return func(config *CheckerConfig) {
|
||||||
|
config.logger = logger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithStatusChecker(checker StatusCheckable) CheckerOption {
|
||||||
|
return func(config *CheckerConfig) {
|
||||||
|
config.statusChecker = checker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSourcesChecker(checker SourcesCheckable) CheckerOption {
|
||||||
|
return func(config *CheckerConfig) {
|
||||||
|
config.sourcesChecker = checker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTaskUpToDate(
|
||||||
|
ctx context.Context,
|
||||||
|
t *taskfile.Task,
|
||||||
|
opts ...CheckerOption,
|
||||||
|
) (bool, error) {
|
||||||
|
var statusUpToDate bool
|
||||||
|
var sourcesUpToDate bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Default config
|
||||||
|
config := &CheckerConfig{
|
||||||
|
method: "none",
|
||||||
|
tempDir: "",
|
||||||
|
dry: false,
|
||||||
|
logger: nil,
|
||||||
|
statusChecker: nil,
|
||||||
|
sourcesChecker: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply functional options
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no status checker was given, set up the default one
|
||||||
|
if config.statusChecker == nil {
|
||||||
|
config.statusChecker = NewStatusChecker(config.logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no sources checker was given, set up the default one
|
||||||
|
if config.sourcesChecker == nil {
|
||||||
|
config.sourcesChecker, err = NewSourcesChecker(config.method, config.tempDir, config.dry)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusIsSet := len(t.Status) != 0
|
||||||
|
sourcesIsSet := len(t.Sources) != 0
|
||||||
|
|
||||||
|
// If status is set, check if it is up-to-date
|
||||||
|
if statusIsSet {
|
||||||
|
statusUpToDate, err = config.statusChecker.IsUpToDate(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If sources is set, check if they are up-to-date
|
||||||
|
if sourcesIsSet {
|
||||||
|
sourcesUpToDate, err = config.sourcesChecker.IsUpToDate(t)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both status and sources are set, the task is up-to-date if both are up-to-date
|
||||||
|
if statusIsSet && sourcesIsSet {
|
||||||
|
return statusUpToDate && sourcesUpToDate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If only status is set, the task is up-to-date if the status is up-to-date
|
||||||
|
if statusIsSet {
|
||||||
|
return statusUpToDate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If only sources is set, the task is up-to-date if the sources are up-to-date
|
||||||
|
if sourcesIsSet {
|
||||||
|
return sourcesUpToDate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no status or sources are set, the task should always run
|
||||||
|
// i.e. it is never considered "up-to-date"
|
||||||
|
return false, nil
|
||||||
|
}
|
174
internal/fingerprint/task_test.go
Normal file
174
internal/fingerprint/task_test.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TruthTable
|
||||||
|
//
|
||||||
|
// | Status up-to-date | Sources up-to-date | Task is up-to-date |
|
||||||
|
// | ----------------- | ------------------ | ------------------ |
|
||||||
|
// | not set | not set | false |
|
||||||
|
// | not set | true | true |
|
||||||
|
// | not set | false | false |
|
||||||
|
// | true | not set | true |
|
||||||
|
// | true | true | true |
|
||||||
|
// | true | false | false |
|
||||||
|
// | false | not set | false |
|
||||||
|
// | false | true | false |
|
||||||
|
// | false | false | false |
|
||||||
|
func TestIsTaskUpToDate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
task *taskfile.Task
|
||||||
|
setupMockStatusChecker func(m *MockStatusCheckable)
|
||||||
|
setupMockSourcesChecker func(m *MockSourcesCheckable)
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "expect FALSE when no status or sources are defined",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: nil,
|
||||||
|
Sources: nil,
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: nil,
|
||||||
|
setupMockSourcesChecker: nil,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect TRUE when no status is defined and sources are up-to-date",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: nil,
|
||||||
|
Sources: []string{"sources"},
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: nil,
|
||||||
|
setupMockSourcesChecker: func(m *MockSourcesCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any()).Return(true, nil)
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect FALSE when no status is defined and sources are NOT up-to-date",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: nil,
|
||||||
|
Sources: []string{"sources"},
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: nil,
|
||||||
|
setupMockSourcesChecker: func(m *MockSourcesCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any()).Return(false, nil)
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect TRUE when status is up-to-date and sources are not defined",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: []string{"status"},
|
||||||
|
Sources: nil,
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: func(m *MockStatusCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any(), gomock.Any()).Return(true, nil)
|
||||||
|
},
|
||||||
|
setupMockSourcesChecker: nil,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect TRUE when status and sources are up-to-date",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: []string{"status"},
|
||||||
|
Sources: []string{"sources"},
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: func(m *MockStatusCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any(), gomock.Any()).Return(true, nil)
|
||||||
|
},
|
||||||
|
setupMockSourcesChecker: func(m *MockSourcesCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any()).Return(true, nil)
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect FALSE when status is up-to-date, but sources are NOT up-to-date",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: []string{"status"},
|
||||||
|
Sources: []string{"sources"},
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: func(m *MockStatusCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any(), gomock.Any()).Return(true, nil)
|
||||||
|
},
|
||||||
|
setupMockSourcesChecker: func(m *MockSourcesCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any()).Return(false, nil)
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect FALSE when status is NOT up-to-date and sources are not defined",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: []string{"status"},
|
||||||
|
Sources: nil,
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: func(m *MockStatusCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any(), gomock.Any()).Return(false, nil)
|
||||||
|
},
|
||||||
|
setupMockSourcesChecker: nil,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect FALSE when status is NOT up-to-date, but sources are up-to-date",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: []string{"status"},
|
||||||
|
Sources: []string{"sources"},
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: func(m *MockStatusCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any(), gomock.Any()).Return(false, nil)
|
||||||
|
},
|
||||||
|
setupMockSourcesChecker: func(m *MockSourcesCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any()).Return(true, nil)
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expect FALSE when status and sources are NOT up-to-date",
|
||||||
|
task: &taskfile.Task{
|
||||||
|
Status: []string{"status"},
|
||||||
|
Sources: []string{"sources"},
|
||||||
|
},
|
||||||
|
setupMockStatusChecker: func(m *MockStatusCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any(), gomock.Any()).Return(false, nil)
|
||||||
|
},
|
||||||
|
setupMockSourcesChecker: func(m *MockSourcesCheckable) {
|
||||||
|
m.EXPECT().IsUpToDate(gomock.Any()).Return(false, nil)
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
mockStatusChecker := NewMockStatusCheckable(ctrl)
|
||||||
|
if tt.setupMockStatusChecker != nil {
|
||||||
|
tt.setupMockStatusChecker(mockStatusChecker)
|
||||||
|
}
|
||||||
|
|
||||||
|
mockSourcesChecker := NewMockSourcesCheckable(ctrl)
|
||||||
|
if tt.setupMockSourcesChecker != nil {
|
||||||
|
tt.setupMockSourcesChecker(mockSourcesChecker)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := IsTaskUpToDate(
|
||||||
|
context.Background(),
|
||||||
|
tt.task,
|
||||||
|
WithStatusChecker(mockStatusChecker),
|
||||||
|
WithSourcesChecker(mockSourcesChecker),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -1,23 +0,0 @@
|
|||||||
package status
|
|
||||||
|
|
||||||
// None is a no-op Checker
|
|
||||||
type None struct{}
|
|
||||||
|
|
||||||
// IsUpToDate implements the Checker interface
|
|
||||||
func (None) IsUpToDate() (bool, error) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the Checker interface
|
|
||||||
func (None) Value() (interface{}, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (None) Kind() string {
|
|
||||||
return "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnError implements the Checker interface
|
|
||||||
func (None) OnError() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
package status
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ Checker = &Timestamp{}
|
|
||||||
_ Checker = &Checksum{}
|
|
||||||
_ Checker = None{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Checker is an interface that checks if the status is up-to-date
|
|
||||||
type Checker interface {
|
|
||||||
IsUpToDate() (bool, error)
|
|
||||||
Value() (interface{}, error)
|
|
||||||
OnError() error
|
|
||||||
Kind() string
|
|
||||||
}
|
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/env"
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
@@ -19,7 +20,7 @@ func (e *Executor) areTaskPreconditionsMet(ctx context.Context, t *taskfile.Task
|
|||||||
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
|
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
|
||||||
Command: p.Sh,
|
Command: p.Sh,
|
||||||
Dir: t.Dir,
|
Dir: t.Dir,
|
||||||
Env: getEnviron(t),
|
Env: env.Get(t),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
128
status.go
128
status.go
@@ -4,20 +4,33 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/fingerprint"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
|
||||||
"github.com/go-task/task/v3/internal/status"
|
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Status returns an error if any the of given tasks is not up-to-date
|
// Status returns an error if any the of given tasks is not up-to-date
|
||||||
func (e *Executor) Status(ctx context.Context, calls ...taskfile.Call) error {
|
func (e *Executor) Status(ctx context.Context, calls ...taskfile.Call) error {
|
||||||
for _, call := range calls {
|
for _, call := range calls {
|
||||||
|
|
||||||
|
// Compile the task
|
||||||
t, err := e.CompiledTask(call)
|
t, err := e.CompiledTask(call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
isUpToDate, err := e.isTaskUpToDate(ctx, t)
|
|
||||||
|
// Get the fingerprinting method to use
|
||||||
|
method := e.Taskfile.Method
|
||||||
|
if t.Method != "" {
|
||||||
|
method = t.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the task is up-to-date
|
||||||
|
isUpToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
||||||
|
fingerprint.WithMethod(method),
|
||||||
|
fingerprint.WithTempDir(e.TempDir),
|
||||||
|
fingerprint.WithDry(e.Dry),
|
||||||
|
fingerprint.WithLogger(e.Logger),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -28,113 +41,14 @@ func (e *Executor) Status(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
|
|
||||||
var statusUpToDate bool
|
|
||||||
var sourcesUpToDate bool
|
|
||||||
var err error
|
|
||||||
|
|
||||||
statusIsSet := len(t.Status) != 0
|
|
||||||
sourcesIsSet := len(t.Sources) != 0
|
|
||||||
|
|
||||||
// If status is set, check if it is up-to-date
|
|
||||||
if statusIsSet {
|
|
||||||
statusUpToDate, err = e.isTaskUpToDateStatus(ctx, t)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If sources is set, check if they are up-to-date
|
|
||||||
if sourcesIsSet {
|
|
||||||
checker, err := e.getStatusChecker(t)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
sourcesUpToDate, err = checker.IsUpToDate()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both status and sources are set, the task is up-to-date if both are up-to-date
|
|
||||||
if statusIsSet && sourcesIsSet {
|
|
||||||
return statusUpToDate && sourcesUpToDate, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If only status is set, the task is up-to-date if the status is up-to-date
|
|
||||||
if statusIsSet {
|
|
||||||
return statusUpToDate, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If only sources is set, the task is up-to-date if the sources are up-to-date
|
|
||||||
if sourcesIsSet {
|
|
||||||
return sourcesUpToDate, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no status or sources are set, the task should always run
|
|
||||||
// i.e. it is never considered "up-to-date"
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) statusOnError(t *taskfile.Task) error {
|
func (e *Executor) statusOnError(t *taskfile.Task) error {
|
||||||
checker, err := e.getStatusChecker(t)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return checker.OnError()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) getStatusChecker(t *taskfile.Task) (status.Checker, error) {
|
|
||||||
method := t.Method
|
method := t.Method
|
||||||
if method == "" {
|
if method == "" {
|
||||||
method = e.Taskfile.Method
|
method = e.Taskfile.Method
|
||||||
}
|
}
|
||||||
switch method {
|
checker, err := fingerprint.NewSourcesChecker(method, e.TempDir, e.Dry)
|
||||||
case "timestamp":
|
if err != nil {
|
||||||
return e.timestampChecker(t), nil
|
return err
|
||||||
case "checksum":
|
|
||||||
return e.checksumChecker(t), nil
|
|
||||||
case "none":
|
|
||||||
return status.None{}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf(`task: invalid method "%s"`, method)
|
|
||||||
}
|
}
|
||||||
}
|
return checker.OnError(t)
|
||||||
|
|
||||||
func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
|
|
||||||
return &status.Timestamp{
|
|
||||||
TempDir: e.TempDir,
|
|
||||||
Task: t.Name(),
|
|
||||||
Dir: t.Dir,
|
|
||||||
Sources: t.Sources,
|
|
||||||
Generates: t.Generates,
|
|
||||||
Dry: e.Dry,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) checksumChecker(t *taskfile.Task) status.Checker {
|
|
||||||
return &status.Checksum{
|
|
||||||
TempDir: e.TempDir,
|
|
||||||
TaskDir: t.Dir,
|
|
||||||
Task: t.Name(),
|
|
||||||
Sources: t.Sources,
|
|
||||||
Generates: t.Generates,
|
|
||||||
Dry: e.Dry,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) isTaskUpToDateStatus(ctx context.Context, t *taskfile.Task) (bool, error) {
|
|
||||||
for _, s := range t.Status {
|
|
||||||
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
|
|
||||||
Command: s,
|
|
||||||
Dir: t.Dir,
|
|
||||||
Env: getEnviron(t),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
e.Logger.VerboseOutf(logger.Yellow, "task: status command %s exited non-zero: %s", s, err)
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
e.Logger.VerboseOutf(logger.Yellow, "task: status command %s exited zero", s)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
}
|
||||||
|
40
task.go
40
task.go
@@ -13,7 +13,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/compiler"
|
"github.com/go-task/task/v3/internal/compiler"
|
||||||
|
"github.com/go-task/task/v3/internal/env"
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/fingerprint"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/output"
|
"github.com/go-task/task/v3/internal/output"
|
||||||
"github.com/go-task/task/v3/internal/slicesext"
|
"github.com/go-task/task/v3/internal/slicesext"
|
||||||
@@ -157,7 +159,18 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
upToDate, err := e.isTaskUpToDate(ctx, t)
|
// Get the fingerprinting method to use
|
||||||
|
method := e.Taskfile.Method
|
||||||
|
if t.Method != "" {
|
||||||
|
method = t.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
upToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
||||||
|
fingerprint.WithMethod(method),
|
||||||
|
fingerprint.WithTempDir(e.TempDir),
|
||||||
|
fingerprint.WithDry(e.Dry),
|
||||||
|
fingerprint.WithLogger(e.Logger),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -286,7 +299,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
|
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
|
||||||
Command: cmd.Cmd,
|
Command: cmd.Cmd,
|
||||||
Dir: t.Dir,
|
Dir: t.Dir,
|
||||||
Env: getEnviron(t),
|
Env: env.Get(t),
|
||||||
PosixOpts: slicesext.UniqueJoin(e.Taskfile.Set, t.Set, cmd.Set),
|
PosixOpts: slicesext.UniqueJoin(e.Taskfile.Set, t.Set, cmd.Set),
|
||||||
BashOpts: slicesext.UniqueJoin(e.Taskfile.Shopt, t.Shopt, cmd.Shopt),
|
BashOpts: slicesext.UniqueJoin(e.Taskfile.Shopt, t.Shopt, cmd.Shopt),
|
||||||
Stdin: e.Stdin,
|
Stdin: e.Stdin,
|
||||||
@@ -306,29 +319,6 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnviron(t *taskfile.Task) []string {
|
|
||||||
if t.Env == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
environ := os.Environ()
|
|
||||||
|
|
||||||
for k, v := range t.Env.ToCacheMap() {
|
|
||||||
str, isString := v.(string)
|
|
||||||
if !isString {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, alreadySet := os.LookupEnv(k); alreadySet {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
environ = append(environ, fmt.Sprintf("%s=%s", k, str))
|
|
||||||
}
|
|
||||||
|
|
||||||
return environ
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute func(ctx context.Context) error) error {
|
func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute func(ctx context.Context) error) error {
|
||||||
h, err := e.GetHash(t)
|
h, err := e.GetHash(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/status"
|
"github.com/go-task/task/v3/internal/fingerprint"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
@@ -161,8 +161,11 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(origTask.Status) > 0 {
|
if len(origTask.Status) > 0 {
|
||||||
for _, checker := range []status.Checker{e.timestampChecker(&new), e.checksumChecker(&new)} {
|
timestampChecker := fingerprint.NewTimestampChecker(e.TempDir, e.Dry)
|
||||||
value, err := checker.Value()
|
checksumChecker := fingerprint.NewChecksumChecker(e.TempDir, e.Dry)
|
||||||
|
|
||||||
|
for _, checker := range []fingerprint.SourcesCheckable{timestampChecker, checksumChecker} {
|
||||||
|
value, err := checker.Value(&new)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
4
watch.go
4
watch.go
@@ -12,8 +12,8 @@ import (
|
|||||||
|
|
||||||
"github.com/radovskyb/watcher"
|
"github.com/radovskyb/watcher"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/fingerprint"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/status"
|
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Ca
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range task.Sources {
|
for _, s := range task.Sources {
|
||||||
files, err := status.Glob(task.Dir, s)
|
files, err := fingerprint.Glob(task.Dir, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("task: %s: %w", s, err)
|
return fmt.Errorf("task: %s: %w", s, err)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user