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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/fingerprint/sources_checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func (checker *ChecksumChecker) IsUpToDate(t *ast.Task) (bool, error) {
if len(t.Generates) > 0 {
// For each specified 'generates' field, check whether the files actually exist
for _, g := range t.Generates {
// Exclusion patterns don't represent output files; skip them.
if g.Negate {
continue
}
Expand Down
22 changes: 22 additions & 0 deletions internal/fingerprint/sources_timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@ func (checker *TimestampChecker) IsUpToDate(t *ast.Task) (bool, error) {
if err != nil {
return false, nil
}

// If generates are declared, ensure they all exist. A missing generated
// file means the task must run regardless of timestamps.
if len(t.Generates) > 0 {
for _, g := range t.Generates {
// Exclusion patterns don't represent output files; skip them.
if g.Negate {
continue
}
files, err := glob(t.Dir, g.Glob)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}
if len(files) == 0 {
return false, nil
}
}
}

generates, err := Globs(t.Dir, t.Generates)
if err != nil {
return false, nil
Expand Down
98 changes: 98 additions & 0 deletions task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,104 @@ func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in
}
}

// TestStatusTimestamp is a regression test for https://github.com/go-task/task/issues/1230.
// When using method: timestamp, deleting a generated file should cause the task to re-run,
// not be skipped because the timestamp file is still present.
func TestStatusTimestamp(t *testing.T) { // nolint:paralleltest // cannot run in parallel
const dir = "testdata/timestamp"

generatedFile := filepathext.SmartJoin(dir, "generated.txt")
tempDir := task.TempDir{
Remote: filepathext.SmartJoin(dir, ".task"),
Fingerprint: filepathext.SmartJoin(dir, ".task"),
}

// Clean up any state from previous runs.
_ = os.Remove(generatedFile)
_ = os.RemoveAll(filepathext.SmartJoin(dir, ".task"))

var buff bytes.Buffer
e := task.NewExecutor(
task.WithDir(dir),
task.WithStdout(&buff),
task.WithStderr(&buff),
task.WithTempDir(tempDir),
)
require.NoError(t, e.Setup())

// First run: task should execute and create generated.txt.
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
_, err := os.Stat(generatedFile)
require.NoError(t, err, "generated.txt should exist after first run")
buff.Reset()

// Second run: task should be up to date.
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
buff.Reset()

// Delete the generated file (simulate a clean), but leave the timestamp file.
require.NoError(t, os.Remove(generatedFile))
_, err = os.Stat(generatedFile)
require.Error(t, err, "generated.txt should be gone")

// Third run: task MUST re-run because generated.txt is missing.
// This is the regression: previously the task was incorrectly skipped.
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
assert.NotContains(t, buff.String(), "is up to date", "task should re-run when generated file is missing")
_, err = os.Stat(generatedFile)
require.NoError(t, err, "generated.txt should be recreated after third run")
}

// TestStatusChecksumMissingGenerated is a regression test for https://github.com/go-task/task/issues/1230.
// When using method: checksum, deleting a generated file should cause the task to re-run,
// not be skipped because the checksum file still matches.
func TestStatusChecksumMissingGenerated(t *testing.T) { // nolint:paralleltest // cannot run in parallel
const dir = "testdata/checksum"

generatedFile := filepathext.SmartJoin(dir, "generated.txt")
tempDir := task.TempDir{
Remote: filepathext.SmartJoin(dir, ".task"),
Fingerprint: filepathext.SmartJoin(dir, ".task"),
}

// Clean up any state from previous runs.
_ = os.Remove(generatedFile)
_ = os.RemoveAll(filepathext.SmartJoin(dir, ".task"))

var buff bytes.Buffer
e := task.NewExecutor(
task.WithDir(dir),
task.WithStdout(&buff),
task.WithStderr(&buff),
task.WithTempDir(tempDir),
)
require.NoError(t, e.Setup())

// First run: task should execute and create generated.txt.
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
_, err := os.Stat(generatedFile)
require.NoError(t, err, "generated.txt should exist after first run")
buff.Reset()

// Second run: task should be up to date.
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
buff.Reset()

// Delete the generated file (simulate a clean), but leave the checksum file.
require.NoError(t, os.Remove(generatedFile))
_, err = os.Stat(generatedFile)
require.Error(t, err, "generated.txt should be gone")

// Third run: task MUST re-run because generated.txt is missing.
// This is the regression: previously the task was incorrectly skipped.
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
assert.NotContains(t, buff.String(), "is up to date", "task should re-run when generated file is missing")
_, err = os.Stat(generatedFile)
require.NoError(t, err, "generated.txt should be recreated after third run")
}

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

Expand Down
2 changes: 2 additions & 0 deletions testdata/timestamp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.task
generated.txt
11 changes: 11 additions & 0 deletions testdata/timestamp/Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: '3'

tasks:
build:
cmds:
- cp ./source.txt ./generated.txt
sources:
- ./source.txt
generates:
- ./generated.txt
method: timestamp
1 change: 1 addition & 0 deletions testdata/timestamp/source.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello from source