fix: normalize epoch to UTC/truncate before stamping tar modtimes

Tar headers have second-level granularity and store timezone-independent
Unix timestamps. Normalize SourceDateEpoch with .UTC().Truncate(time.Second)
so callers passing sub-second or non-UTC values still get deterministic output.

Also verify in the test that gzip header ModTime is zero (helm design) and
compare tar entry ModTimes with time.Equal() since tar.Reader returns local time.

Signed-off-by: Ilya Kiselev <kis-ilya-a@yandex.ru>
pull/32173/head
Ilya Kiselev 2 weeks ago
parent 1df209e084
commit 9b457ea032

@ -267,8 +267,11 @@ func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error)
}
// stampModTimes recursively sets all file modification times in a chart to t.
// t is normalized to UTC and truncated to whole seconds before use because tar
// headers have second-level granularity and timezone-independent storage.
// This is used to produce reproducible archives when SourceDateEpoch is set.
func stampModTimes(c *chart.Chart, t time.Time) {
t = t.UTC().Truncate(time.Second)
c.ModTime = t
c.SchemaModTime = t
for _, f := range c.Raw {

@ -177,7 +177,8 @@ func TestRun(t *testing.T) {
func TestRunWithSourceDateEpoch(t *testing.T) {
chartPath := "testdata/charts/chart-with-schema"
epoch := time.Unix(1700000000, 0)
// Use UTC so the comparison with tar header ModTime (always UTC) is straightforward.
epoch := time.Unix(1700000000, 0).UTC()
client := NewPackage()
client.SourceDateEpoch = &epoch
@ -186,15 +187,18 @@ func TestRunWithSourceDateEpoch(t *testing.T) {
require.NoError(t, err)
t.Cleanup(func() { os.Remove(filename) })
// All tar entry ModTimes must equal the epoch.
f, err := os.Open(filename)
require.NoError(t, err)
defer f.Close()
// Check gzip header ModTime: helm leaves it at zero (deterministic by design).
gr, err := gzip.NewReader(f)
require.NoError(t, err)
require.True(t, gr.Header.ModTime.IsZero(), "gzip header ModTime should be zero")
defer gr.Close()
// All tar entry ModTimes must represent the same instant as epoch.
// tar.Reader returns ModTime in local timezone, so use Equal() not require.Equal.
tr := tar.NewReader(gr)
for {
hdr, err := tr.Next()
@ -202,7 +206,7 @@ func TestRunWithSourceDateEpoch(t *testing.T) {
break
}
require.NoError(t, err)
require.Equal(t, epoch, hdr.ModTime, "expected epoch ModTime for entry %s", hdr.Name)
require.True(t, epoch.Equal(hdr.ModTime), "entry %s: got ModTime %v, want %v", hdr.Name, hdr.ModTime, epoch)
}
}

Loading…
Cancel
Save