Skip to content
Merged
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
140 changes: 140 additions & 0 deletions archives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"bytes"
"compress/gzip"
"io"
"os"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -382,6 +383,145 @@ func TestOpenWithPrefix(t *testing.T) {
}
}

// createTestZipWithDirEntries creates a zip with explicit directory entries,
// like GitHub zipball downloads produce.
func createTestZipWithDirEntries() []byte {
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)

// Add explicit directory entry (GitHub zipballs do this)
header := &zip.FileHeader{
Name: "project-abc123/",
Method: zip.Store,
Modified: time.Date(2026, 3, 30, 8, 14, 47, 0, time.UTC),
}
header.SetMode(0755 | os.ModeDir)
_, _ = w.CreateHeader(header)

// Add explicit src/ subdirectory entry
srcHeader := &zip.FileHeader{
Name: "project-abc123/src/",
Method: zip.Store,
Modified: time.Date(2026, 3, 30, 8, 14, 47, 0, time.UTC),
}
srcHeader.SetMode(0755 | os.ModeDir)
_, _ = w.CreateHeader(srcHeader)

// Add files inside that directory
files := []struct {
name string
content string
}{
{"project-abc123/README.md", "# Test"},
{"project-abc123/src/main.go", "package main"},
{"project-abc123/src/util.go", "package main"},
}

for _, file := range files {
f, _ := w.Create(file.name)
_, _ = f.Write([]byte(file.content))
}

_ = w.Close()
return buf.Bytes()
}

// createTestTarGzWithDirEntries creates a tar.gz with explicit directory entries,
// like GitHub tarball downloads produce.
func createTestTarGzWithDirEntries() []byte {
buf := new(bytes.Buffer)
gw := gzip.NewWriter(buf)
tw := tar.NewWriter(gw)

// Add explicit directory entries
for _, dir := range []string{"project-abc123/", "project-abc123/src/"} {
_ = tw.WriteHeader(&tar.Header{
Typeflag: tar.TypeDir,
Name: dir,
Mode: 0755,
ModTime: time.Date(2026, 3, 30, 8, 14, 47, 0, time.UTC),
})
}

files := []struct {
name string
content string
}{
{"project-abc123/README.md", "# Test"},
{"project-abc123/src/main.go", "package main"},
{"project-abc123/src/util.go", "package main"},
}

for _, file := range files {
_ = tw.WriteHeader(&tar.Header{
Name: file.name,
Size: int64(len(file.content)),
Mode: 0644,
ModTime: time.Date(2026, 3, 30, 8, 14, 47, 0, time.UTC),
})
_, _ = tw.Write([]byte(file.content))
}

_ = tw.Close()
_ = gw.Close()
return buf.Bytes()
}

func assertNoDuplicates(t *testing.T, label string, files []FileInfo) {
t.Helper()
seen := map[string]int{}
for _, f := range files {
seen[f.Path]++
}
for path, count := range seen {
if count > 1 {
t.Errorf("%s: %d entries for %q, want 1", label, count, path)
}
}
}

func TestZipListDirNoDuplicatesWithExplicitDirEntries(t *testing.T) {
data := createTestZipWithDirEntries()
reader, err := openZip(bytes.NewReader(data))
if err != nil {
t.Fatalf("openZip failed: %v", err)
}
defer func() { _ = reader.Close() }()

files, err := reader.ListDir("")
if err != nil {
t.Fatalf("ListDir root failed: %v", err)
}
assertNoDuplicates(t, "ListDir root", files)

files, err = reader.ListDir("project-abc123/")
if err != nil {
t.Fatalf("ListDir subdir failed: %v", err)
}
assertNoDuplicates(t, "ListDir project-abc123/", files)
}

func TestTarListDirNoDuplicatesWithExplicitDirEntries(t *testing.T) {
data := createTestTarGzWithDirEntries()
reader, err := openTar(bytes.NewReader(data), "gzip")
if err != nil {
t.Fatalf("openTar failed: %v", err)
}
defer func() { _ = reader.Close() }()

files, err := reader.ListDir("")
if err != nil {
t.Fatalf("ListDir root failed: %v", err)
}
assertNoDuplicates(t, "ListDir root", files)

files, err = reader.ListDir("project-abc123/")
if err != nil {
t.Fatalf("ListDir subdir failed: %v", err)
}
assertNoDuplicates(t, "ListDir project-abc123/", files)
}

func TestGetStripPrefixNpm(t *testing.T) {
// Create npm-style archive
buf := new(bytes.Buffer)
Expand Down
3 changes: 3 additions & 0 deletions tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ func (t *tarReader) ListDir(dirPath string) ([]FileInfo, error) {

// Check if this file/dir is directly in the requested directory
if isInDir(path, dirPath) {
if f.info.IsDir {
seenDirs[path] = true
}
files = append(files, f.info)
continue
}
Expand Down
3 changes: 3 additions & 0 deletions zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ func (z *zipReader) ListDir(dirPath string) ([]FileInfo, error) {

// Check if this file/dir is directly in the requested directory
if isInDir(path, dirPath) {
if f.FileInfo().IsDir() {
seenDirs[path] = true
}
files = append(files, fileInfoFromZip(f))
continue
}
Expand Down