mirror of
https://github.com/go-task/task.git
synced 2025-02-13 13:59:32 +02:00
fix: avoid reruns when the timestamp method is used (#977)
This commit is contained in:
parent
fce7575b03
commit
347fcf9f67
@ -667,9 +667,9 @@ The method `none` skips any validation and always run the task.
|
||||
|
||||
:::info
|
||||
|
||||
For the `checksum` (default) method to work, it is only necessary to
|
||||
inform the source files, but if you want to use the `timestamp` method, you
|
||||
also need to inform the generated files with `generates`.
|
||||
For the `checksum` (default) or `timestamp` method to work, it is only necessary to
|
||||
inform the source files.
|
||||
When the `timestamp` method is used, the last time of the running the task is considered as a generate.
|
||||
|
||||
:::
|
||||
|
||||
|
@ -109,12 +109,12 @@ func (*Checksum) Kind() string {
|
||||
}
|
||||
|
||||
func (c *Checksum) checksumFilePath() string {
|
||||
return filepath.Join(c.TempDir, "checksum", c.normalizeFilename(c.Task))
|
||||
return filepath.Join(c.TempDir, "checksum", NormalizeFilename(c.Task))
|
||||
}
|
||||
|
||||
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
||||
|
||||
// replaces invalid caracters on filenames with "-"
|
||||
func (*Checksum) normalizeFilename(f string) string {
|
||||
func NormalizeFilename(f string) string {
|
||||
return checksumFilenameRegexp.ReplaceAllString(f, "-")
|
||||
}
|
||||
|
@ -16,6 +16,6 @@ func TestNormalizeFilename(t *testing.T) {
|
||||
{"foo1bar2baz3", "foo1bar2baz3"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
assert.Equal(t, test.Out, (&Checksum{}).normalizeFilename(test.In))
|
||||
assert.Equal(t, test.Out, NormalizeFilename(test.In))
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,24 @@ package status
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Timestamp checks if any source change compared with the generated files,
|
||||
// using file modifications timestamps.
|
||||
type Timestamp struct {
|
||||
TempDir string
|
||||
Task string
|
||||
Dir string
|
||||
Sources []string
|
||||
Generates []string
|
||||
Dry bool
|
||||
}
|
||||
|
||||
// IsUpToDate implements the Checker interface
|
||||
func (t *Timestamp) IsUpToDate() (bool, error) {
|
||||
if len(t.Sources) == 0 || len(t.Generates) == 0 {
|
||||
if len(t.Sources) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@ -28,17 +32,43 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
sourcesMaxTime, err := getMaxTime(sources...)
|
||||
if err != nil || sourcesMaxTime.IsZero() {
|
||||
timestampFile := t.timestampFilePath()
|
||||
|
||||
// if the file exists, add the file path to the generates
|
||||
// if the generate file is old, the task will be executed
|
||||
_, err = os.Stat(timestampFile)
|
||||
if err == nil {
|
||||
generates = append(generates, timestampFile)
|
||||
} else {
|
||||
// create the timestamp file for the next execution when the file does not exist
|
||||
if !t.Dry {
|
||||
_ = os.MkdirAll(filepath.Dir(timestampFile), 0o755)
|
||||
_, _ = os.Create(timestampFile)
|
||||
}
|
||||
}
|
||||
|
||||
taskTime := time.Now()
|
||||
|
||||
// compare the time of the generates and sources. If the generates are old, the task will be executed
|
||||
|
||||
// get the max time of the generates
|
||||
generateMaxTime, err := getMaxTime(generates...)
|
||||
if err != nil || generateMaxTime.IsZero() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
generatesMinTime, err := getMinTime(generates...)
|
||||
if err != nil || generatesMinTime.IsZero() {
|
||||
// check if any of the source files is newer than the max time of the generates
|
||||
shouldUpdate, err := anyFileNewerThan(sources, generateMaxTime)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return !generatesMinTime.Before(sourcesMaxTime), nil
|
||||
// modify the metadata of the file to the the current time
|
||||
if !t.Dry {
|
||||
_ = os.Chtimes(timestampFile, taskTime, taskTime)
|
||||
}
|
||||
|
||||
return !shouldUpdate, nil
|
||||
}
|
||||
|
||||
func (t *Timestamp) Kind() string {
|
||||
@ -64,18 +94,6 @@ func (t *Timestamp) Value() (interface{}, error) {
|
||||
return sourcesMaxTime, nil
|
||||
}
|
||||
|
||||
func getMinTime(files ...string) (time.Time, error) {
|
||||
var t time.Time
|
||||
for _, f := range files {
|
||||
info, err := os.Stat(f)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
t = minTime(t, info.ModTime())
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func getMaxTime(files ...string) (time.Time, error) {
|
||||
var t time.Time
|
||||
for _, f := range files {
|
||||
@ -88,11 +106,19 @@ func getMaxTime(files ...string) (time.Time, error) {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func minTime(a, b time.Time) time.Time {
|
||||
if !a.IsZero() && a.Before(b) {
|
||||
return a
|
||||
// if the modification time of any of the files is newer than the the given time, returns true
|
||||
// This function is lazy, as it stops when it finds a file newer than the given time
|
||||
func anyFileNewerThan(files []string, givenTime time.Time) (bool, error) {
|
||||
for _, f := range files {
|
||||
info, err := os.Stat(f)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if info.ModTime().After(givenTime) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return b
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func maxTime(a, b time.Time) time.Time {
|
||||
@ -106,3 +132,7 @@ func maxTime(a, b time.Time) time.Time {
|
||||
func (*Timestamp) OnError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Timestamp) timestampFilePath() string {
|
||||
return filepath.Join(t.TempDir, "timestamp", NormalizeFilename(t.Task))
|
||||
}
|
||||
|
@ -87,9 +87,12 @@ func (e *Executor) getStatusChecker(t *taskfile.Task) (status.Checker, error) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user