fix(packages): ignore nested Package.swift (#38788) (#38836)

Backport #38788 by @terriblegoodday

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.

Assisted by Claude Opus 5 and Claude Fable.

Co-authored-by: Eduard Dzhumagaliev <ed_dzhumagaliev@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
Giteabot
2026-08-08 08:43:21 -07:00
committed by GitHub
parent 92044649a0
commit e2a0a87ae0
2 changed files with 101 additions and 2 deletions
+17 -2
View File
@@ -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 {
manifestMatch := manifestPattern.FindStringSubmatch(path.Base(file.Name))
if len(manifestMatch) == 0 {
if strings.HasSuffix(file.Name, "/") || !manifestPattern.MatchString(path.Base(file.Name)) {
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 {
return nil, ErrManifestFileTooLarge