diff --git a/CHANGELOG.md b/CHANGELOG.md index abb1322765..317c733196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ gets `{{.TIMESTAMP}}` and no longer a `{{.CHECKSUM}}` (which now renders as an empty string), and neither variable is injected when the effective method is `none` (#2924 by @vmaerten). +- Fixed `{{.TIMESTAMP}}` rendering as the Unix epoch (e.g. `1970-01-01 01:00:00 + +0100 CET`) when a task's `sources` matched no file. It now renders as an + empty string, instead of a date that looks genuine and carries the machine's + time zone (#2960 by @vmaerten). ## v3.52.0 - 2026-07-02 diff --git a/internal/fingerprint/sources_timestamp.go b/internal/fingerprint/sources_timestamp.go index 929b044bca..3bb7a07cd3 100644 --- a/internal/fingerprint/sources_timestamp.go +++ b/internal/fingerprint/sources_timestamp.go @@ -122,8 +122,12 @@ func (checker *TimestampChecker) Value(t *ast.Task) (any, error) { return time.Now(), err } + // No source matched, so there is no modification time to report. Returning + // the Unix epoch here would render as a real date in templates, e.g. + // "1970-01-01 01:00:00 +0100 CET", which reads as a genuine timestamp and + // carries a machine-dependent zone. if sourcesMaxTime.IsZero() { - return time.Unix(0, 0), nil + return "", nil } return sourcesMaxTime, nil diff --git a/internal/fingerprint/sources_timestamp_test.go b/internal/fingerprint/sources_timestamp_test.go new file mode 100644 index 0000000000..665dc7cc89 --- /dev/null +++ b/internal/fingerprint/sources_timestamp_test.go @@ -0,0 +1,44 @@ +package fingerprint + +import ( + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/go-task/task/v3/taskfile/ast" +) + +func TestTimestampCheckerValue(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(dir, "source.txt"), []byte("content"), 0o644)) + checker := NewTimestampChecker(t.TempDir(), true) + + t.Run("reports the newest source modification time", func(t *testing.T) { + t.Parallel() + + value, err := checker.Value(&ast.Task{ + Dir: dir, + Sources: []*ast.Glob{{Glob: "source.txt"}}, + }) + require.NoError(t, err) + assert.IsType(t, time.Time{}, value) + assert.False(t, value.(time.Time).IsZero()) + }) + + t.Run("reports no value when no source matches", func(t *testing.T) { + t.Parallel() + + value, err := checker.Value(&ast.Task{ + Dir: dir, + Sources: []*ast.Glob{{Glob: "gen/**/*.go"}}, + }) + require.NoError(t, err) + assert.Equal(t, "", value) + }) +}