mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-08 22:13:26 +09:00
fix(packages): ignore nested Package.swift (#38788)
Nested `Package.swift` files overwrote the real package manifest. The parser matched on the base name and kept the last entry in ZIP order. `apple/swift-collections` ships `Benchmarks/Package.swift` and `Utils/Debugger/FormatterFixtures/Package.swift`. The latter sorts after the root `Package.swift`, so the registry stored a fixture manifest with the wrong `swift-tools-version`, causing a toolchain mismatch and a failed build. GRDB, swift-markdown, swift-syntax, sentry-cocoa and SDWebImage share this layout. The parser now keeps only manifests from the shallowest directory holding one. That covers both a package at the archive root and the single top level directory `swift package archive-source` produces. At equal depth the first directory by name wins, so an archive always yields the same metadata. A nested manifest above the size limit no longer rejects the upload. Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
co-authored by
silverwind
parent
25350d0c79
commit
8163139ec0
@@ -123,11 +123,26 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nested packages (test fixtures, examples, benchmarks) ship their own manifests, which must not
|
||||||
|
// replace the package manifest. The package sits at the archive root or in a single top level
|
||||||
|
// directory, so keep only the shallowest manifest directory, breaking ties by name for stability.
|
||||||
|
var manifestFiles []*zip.File
|
||||||
|
manifestDir, manifestDepth := "", 0
|
||||||
for _, file := range zr.File {
|
for _, file := range zr.File {
|
||||||
manifestMatch := manifestPattern.FindStringSubmatch(path.Base(file.Name))
|
if strings.HasSuffix(file.Name, "/") || !manifestPattern.MatchString(path.Base(file.Name)) {
|
||||||
if len(manifestMatch) == 0 {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
dir, depth := path.Dir(file.Name), strings.Count(file.Name, "/")
|
||||||
|
switch {
|
||||||
|
case manifestFiles == nil || depth < manifestDepth || (depth == manifestDepth && dir < manifestDir):
|
||||||
|
manifestDir, manifestDepth, manifestFiles = dir, depth, []*zip.File{file}
|
||||||
|
case dir == manifestDir:
|
||||||
|
manifestFiles = append(manifestFiles, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range manifestFiles {
|
||||||
|
manifestMatch := manifestPattern.FindStringSubmatch(path.Base(file.Name))
|
||||||
|
|
||||||
if file.UncompressedSize64 > maxManifestFileSize {
|
if file.UncompressedSize64 > maxManifestFileSize {
|
||||||
return nil, ErrManifestFileTooLarge
|
return nil, ErrManifestFileTooLarge
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package swift
|
package swift
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -24,6 +25,18 @@ const (
|
|||||||
packageLicense = "MIT"
|
packageLicense = "MIT"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// writeOrderedZipArchive writes name/content pairs in the given order, which map based test.WriteZipArchive cannot do
|
||||||
|
func writeOrderedZipArchive(entries [][2]string) *bytes.Buffer {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
zw := zip.NewWriter(buf)
|
||||||
|
for _, entry := range entries {
|
||||||
|
w, _ := zw.Create(entry[0])
|
||||||
|
_, _ = w.Write([]byte(entry[1]))
|
||||||
|
}
|
||||||
|
_ = zw.Close()
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
func TestParsePackage(t *testing.T) {
|
func TestParsePackage(t *testing.T) {
|
||||||
t.Run("MissingManifestFile", func(t *testing.T) {
|
t.Run("MissingManifestFile", func(t *testing.T) {
|
||||||
data := test.WriteZipArchive(map[string]string{"dummy.txt": ""})
|
data := test.WriteZipArchive(map[string]string{"dummy.txt": ""})
|
||||||
@@ -65,6 +78,77 @@ func TestParsePackage(t *testing.T) {
|
|||||||
assert.Equal(t, content2, m.Content)
|
assert.Equal(t, content2, m.Content)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("IgnoresNestedManifests", func(t *testing.T) {
|
||||||
|
rootManifest := "// swift-tools-version:5.7\n//\n// Package.swift"
|
||||||
|
rootAltManifest := "// swift-tools-version:5.5\n//\n// Package@swift-5.5.swift"
|
||||||
|
rootPatchAltManifest := "// swift-tools-version:5.7.1\n//\n// Package@swift-5.7.1.swift"
|
||||||
|
nestedManifest := "// swift-tools-version:6.3\n//\n// nested fixture package"
|
||||||
|
|
||||||
|
data := writeOrderedZipArchive([][2]string{
|
||||||
|
{"Package.swift", rootManifest},
|
||||||
|
{"Package@swift-5.5.swift", rootAltManifest},
|
||||||
|
{"Package@swift-5.7.1.swift", rootPatchAltManifest},
|
||||||
|
{"Benchmarks/Package.swift", nestedManifest},
|
||||||
|
{"Utils/Fixtures/PlainPackage/Package.swift", nestedManifest},
|
||||||
|
})
|
||||||
|
|
||||||
|
p, err := ParsePackage(bytes.NewReader(data.Bytes()), int64(data.Len()), nil)
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Len(t, p.Metadata.Manifests, 3)
|
||||||
|
assert.Equal(t, rootManifest, p.Metadata.Manifests[""].Content)
|
||||||
|
assert.Equal(t, "5.7", p.Metadata.Manifests[""].ToolsVersion)
|
||||||
|
assert.Equal(t, rootAltManifest, p.Metadata.Manifests["5.5"].Content)
|
||||||
|
assert.Equal(t, rootPatchAltManifest, p.Metadata.Manifests["5.7.1"].Content)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("IgnoresNestedManifestsInPrefixedArchive", func(t *testing.T) {
|
||||||
|
rootManifest := "// swift-tools-version:5.7\n//\n// Package.swift"
|
||||||
|
|
||||||
|
// `swift package archive-source` produces archives with a single top level directory
|
||||||
|
data := writeOrderedZipArchive([][2]string{
|
||||||
|
{"gitea-1.0.1/Package.swift", rootManifest},
|
||||||
|
{"gitea-1.0.1/Tests/Fixtures/Package.swift", "// swift-tools-version:6.3"},
|
||||||
|
})
|
||||||
|
|
||||||
|
p, err := ParsePackage(bytes.NewReader(data.Bytes()), int64(data.Len()), nil)
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Len(t, p.Metadata.Manifests, 1)
|
||||||
|
assert.Equal(t, rootManifest, p.Metadata.Manifests[""].Content)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("AltManifestOnlyInRootDirectory", func(t *testing.T) {
|
||||||
|
// a deeper Package.swift belongs to a nested package and must not stand in for the missing root manifest
|
||||||
|
data := test.WriteZipArchive(map[string]string{
|
||||||
|
"Package@swift-5.5.swift": "// swift-tools-version:5.5",
|
||||||
|
"Sub/Package.swift": "// swift-tools-version:5.7",
|
||||||
|
})
|
||||||
|
|
||||||
|
p, err := ParsePackage(bytes.NewReader(data.Bytes()), int64(data.Len()), nil)
|
||||||
|
assert.Nil(t, p)
|
||||||
|
assert.ErrorIs(t, err, ErrMissingManifestFile)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ManifestDirectoryTieBreak", func(t *testing.T) {
|
||||||
|
contentA := "// swift-tools-version:5.7\n// A"
|
||||||
|
contentB := "// swift-tools-version:5.7\n// B"
|
||||||
|
|
||||||
|
// at equal depth the name decides, never the archive order
|
||||||
|
data := writeOrderedZipArchive([][2]string{
|
||||||
|
{"a/Package.swift", contentA},
|
||||||
|
{"b/Package.swift", contentB},
|
||||||
|
})
|
||||||
|
|
||||||
|
p, err := ParsePackage(bytes.NewReader(data.Bytes()), int64(data.Len()), nil)
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, p.Metadata.Manifests, 1)
|
||||||
|
assert.Equal(t, contentA, p.Metadata.Manifests[""].Content)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("WithMetadata", func(t *testing.T) {
|
t.Run("WithMetadata", func(t *testing.T) {
|
||||||
data := test.WriteZipArchive(map[string]string{
|
data := test.WriteZipArchive(map[string]string{
|
||||||
"Package.swift": "// swift-tools-version:5.7\n//\n// Package.swift",
|
"Package.swift": "// swift-tools-version:5.7\n//\n// Package.swift",
|
||||||
|
|||||||
Reference in New Issue
Block a user