Skip to content

Commit 84df43c

Browse files
committed
fix: preserve zip headers for updated UTF-8 files
1 parent 5b118a4 commit 84df43c

2 files changed

Lines changed: 51 additions & 4 deletions

File tree

internal/aggregated/storage/storage_test.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,18 @@ func TestTemplateStorageUpdatePreservesNonUTF8Files(t *testing.T) {
503503
t.Fatal("expected non-UTF8 binary.dat to be omitted from spec.files")
504504
}
505505

506+
originalSourceZip, ok := state.templateActiveSourceZip("acme", "starter-template")
507+
if !ok {
508+
t.Fatal("expected active source zip for starter-template before update")
509+
}
510+
mainModeBefore, ok := zipEntryMode(t, originalSourceZip, "main.tf")
511+
if !ok {
512+
t.Fatal("expected seeded source zip mode metadata for main.tf")
513+
}
514+
if mainModeBefore.Perm() != fs.FileMode(0o755) {
515+
t.Fatalf("expected seeded main.tf mode 0755, got %v", mainModeBefore)
516+
}
517+
506518
updatedMainTF := "resource \"null_resource\" \"updated_binary_preserve\" {}"
507519
fileCountBefore := state.fileCount()
508520
templateVersionCountBefore := state.templateVersionCount()
@@ -568,6 +580,14 @@ func TestTemplateStorageUpdatePreservesNonUTF8Files(t *testing.T) {
568580
t.Fatalf("expected merged main.tf %q, got %q", updatedMainTF, string(updatedMainBytes))
569581
}
570582

583+
updatedMainMode, ok := zipEntryMode(t, activeSourceZip, "main.tf")
584+
if !ok {
585+
t.Fatal("expected merged source zip mode metadata for main.tf")
586+
}
587+
if updatedMainMode.Perm() != fs.FileMode(0o755) {
588+
t.Fatalf("expected preserved main.tf mode 0755, got %v", updatedMainMode)
589+
}
590+
571591
expectedBinary := []byte{0x80, 0x81, 0x82}
572592
binaryBytes, ok := zipEntries["binary.dat"]
573593
if !ok {
@@ -2926,7 +2946,9 @@ func buildSeededTemplateSourceZip() ([]byte, error) {
29262946
var sourceZip bytes.Buffer
29272947
zipWriter := zip.NewWriter(&sourceZip)
29282948

2929-
mainTFWriter, err := zipWriter.Create("main.tf")
2949+
mainTFHeader := zip.FileHeader{Name: "main.tf", Method: zip.Deflate}
2950+
mainTFHeader.SetMode(fs.FileMode(0o755))
2951+
mainTFWriter, err := zipWriter.CreateHeader(&mainTFHeader)
29302952
if err != nil {
29312953
return nil, fmt.Errorf("create seeded main.tf zip entry: %w", err)
29322954
}

internal/aggregated/storage/template_files.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ func buildMergedSourceZip(originalZipBytes []byte, desiredFiles map[string]strin
212212
header zip.FileHeader
213213
}
214214
preservedBinaryFiles := make(map[string]preservedBinaryEntry)
215+
originalHeaders := make(map[string]zip.FileHeader, len(archiveReader.File))
215216
totalUncompressedBytes := int64(0)
216217

217218
for _, archiveFile := range archiveReader.File {
@@ -226,6 +227,12 @@ func buildMergedSourceZip(originalZipBytes []byte, desiredFiles map[string]strin
226227
if err != nil {
227228
return nil, fmt.Errorf("validate template source path %q: %w", archiveFile.Name, err)
228229
}
230+
if _, exists := originalHeaders[relativePath]; exists {
231+
return nil, fmt.Errorf("duplicate normalized template source path %q", relativePath)
232+
}
233+
header := archiveFile.FileHeader
234+
header.Name = relativePath
235+
originalHeaders[relativePath] = header
229236
if _, exists := normalizedDesiredFiles[relativePath]; exists {
230237
continue
231238
}
@@ -271,11 +278,9 @@ func buildMergedSourceZip(originalZipBytes []byte, desiredFiles map[string]strin
271278
return nil, fmt.Errorf("duplicate normalized template source path %q", relativePath)
272279
}
273280

274-
header := archiveFile.FileHeader
275-
header.Name = relativePath
276281
preservedBinaryFiles[relativePath] = preservedBinaryEntry{
277282
contents: contents,
278-
header: header,
283+
header: originalHeaders[relativePath],
279284
}
280285
}
281286

@@ -333,6 +338,26 @@ func buildMergedSourceZip(originalZipBytes []byte, desiredFiles map[string]strin
333338
}
334339

335340
if hasDesired {
341+
if originalHeader, exists := originalHeaders[sourcePath]; exists {
342+
header := originalHeader
343+
header.Name = sourcePath
344+
// Header sizes are specific to the original contents. Reset them so
345+
// the zip writer recomputes values for the updated file bytes.
346+
header.CompressedSize64 = 0
347+
header.UncompressedSize64 = 0
348+
//nolint:staticcheck // archive/zip still consumes legacy 32-bit size fields.
349+
header.CompressedSize = 0
350+
//nolint:staticcheck // archive/zip still consumes legacy 32-bit size fields.
351+
header.UncompressedSize = 0
352+
fileWriter, err := zipWriter.CreateHeader(&header)
353+
if err != nil {
354+
return nil, fmt.Errorf("create zip entry %q from original header: %w", sourcePath, err)
355+
}
356+
if _, err := fileWriter.Write([]byte(desiredContent)); err != nil {
357+
return nil, fmt.Errorf("write zip entry %q: %w", sourcePath, err)
358+
}
359+
continue
360+
}
336361
fileWriter, err := zipWriter.Create(sourcePath)
337362
if err != nil {
338363
return nil, fmt.Errorf("create zip entry %q: %w", sourcePath, err)

0 commit comments

Comments
 (0)