From 89c99a4dcb9cef04922bec53a446943aa4411c9c Mon Sep 17 00:00:00 2001 From: Giteabot Date: Mon, 8 Sep 2025 03:37:53 +0800 Subject: [PATCH] fix: add author.name field to Swift Package Registry API response (#35410) (#35431) Backport #35410 by ahanoff Fixes #35159 Swift Package Manager expects an 'author.name' field in package metadata, but Gitea was only providing schema.org format fields (givenName, middleName, familyName). This caused SPM to fail with keyNotFound error when fetching package metadata. Changes: - Add 'name' field to Person struct (inherited from https://schema.org/Thing) - Populate 'name' field in API response using existing String() method - Maintains backward compatibility with existing schema.org fields - Provides both formats for maximum compatibility The fix ensures Swift Package Manager can successfully resolve packages while preserving full schema.org compliance. Co-authored-by: Akhan Zhakiyanov --- modules/packages/swift/metadata.go | 9 ++- modules/packages/swift/metadata_test.go | 79 ++++++++++++++++++++ routers/api/packages/swift/swift.go | 1 + tests/integration/api_packages_swift_test.go | 1 + 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/modules/packages/swift/metadata.go b/modules/packages/swift/metadata.go index 24c4262ab7..959c5292e4 100644 --- a/modules/packages/swift/metadata.go +++ b/modules/packages/swift/metadata.go @@ -82,6 +82,7 @@ type ProgrammingLanguage struct { // https://schema.org/Person type Person struct { Type string `json:"@type,omitempty"` + Name string `json:"name,omitempty"` // inherited from https://schema.org/Thing GivenName string `json:"givenName,omitempty"` MiddleName string `json:"middleName,omitempty"` FamilyName string `json:"familyName,omitempty"` @@ -184,11 +185,17 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) { p.Metadata.Description = ssc.Description p.Metadata.Keywords = ssc.Keywords p.Metadata.License = ssc.License - p.Metadata.Author = Person{ + author := Person{ + Name: ssc.Author.Name, GivenName: ssc.Author.GivenName, MiddleName: ssc.Author.MiddleName, FamilyName: ssc.Author.FamilyName, } + // If Name is not provided, generate it from individual name components + if author.Name == "" { + author.Name = author.String() + } + p.Metadata.Author = author p.Metadata.RepositoryURL = ssc.CodeRepository if !validation.IsValidURL(p.Metadata.RepositoryURL) { diff --git a/modules/packages/swift/metadata_test.go b/modules/packages/swift/metadata_test.go index 3913c2355b..461773cbfc 100644 --- a/modules/packages/swift/metadata_test.go +++ b/modules/packages/swift/metadata_test.go @@ -97,10 +97,49 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, packageDescription, p.Metadata.Description) assert.ElementsMatch(t, []string{"swift", "package"}, p.Metadata.Keywords) assert.Equal(t, packageLicense, p.Metadata.License) + assert.Equal(t, packageAuthor, p.Metadata.Author.Name) assert.Equal(t, packageAuthor, p.Metadata.Author.GivenName) assert.Equal(t, packageRepositoryURL, p.Metadata.RepositoryURL) assert.ElementsMatch(t, []string{packageRepositoryURL}, p.RepositoryURLs) }) + + t.Run("WithExplicitNameField", func(t *testing.T) { + data := createArchive(map[string][]byte{ + "Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"), + }) + + authorName := "John Doe" + p, err := ParsePackage( + data, + data.Size(), + strings.NewReader(`{"name":"`+packageName+`","version":"`+packageVersion+`","description":"`+packageDescription+`","author":{"name":"`+authorName+`","givenName":"John","familyName":"Doe"}}`), + ) + assert.NotNil(t, p) + assert.NoError(t, err) + + assert.Equal(t, authorName, p.Metadata.Author.Name) + assert.Equal(t, "John", p.Metadata.Author.GivenName) + assert.Equal(t, "Doe", p.Metadata.Author.FamilyName) + }) + + t.Run("NameFieldGeneration", func(t *testing.T) { + data := createArchive(map[string][]byte{ + "Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"), + }) + + // Test with only individual name components - Name should be auto-generated + p, err := ParsePackage( + data, + data.Size(), + strings.NewReader(`{"author":{"givenName":"John","middleName":"Q","familyName":"Doe"}}`), + ) + assert.NotNil(t, p) + assert.NoError(t, err) + assert.Equal(t, "John Q Doe", p.Metadata.Author.Name) + assert.Equal(t, "John", p.Metadata.Author.GivenName) + assert.Equal(t, "Q", p.Metadata.Author.MiddleName) + assert.Equal(t, "Doe", p.Metadata.Author.FamilyName) + }) } func TestTrimmedVersionString(t *testing.T) { @@ -142,3 +181,43 @@ func TestTrimmedVersionString(t *testing.T) { assert.Equal(t, c.Expected, TrimmedVersionString(c.Version)) } } + +func TestPersonNameString(t *testing.T) { + cases := []struct { + Name string + Person Person + Expected string + }{ + { + Name: "GivenNameOnly", + Person: Person{GivenName: "John"}, + Expected: "John", + }, + { + Name: "GivenAndFamily", + Person: Person{GivenName: "John", FamilyName: "Doe"}, + Expected: "John Doe", + }, + { + Name: "FullName", + Person: Person{GivenName: "John", MiddleName: "Q", FamilyName: "Doe"}, + Expected: "John Q Doe", + }, + { + Name: "MiddleAndFamily", + Person: Person{MiddleName: "Q", FamilyName: "Doe"}, + Expected: "Q Doe", + }, + { + Name: "Empty", + Person: Person{}, + Expected: "", + }, + } + + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + assert.Equal(t, c.Expected, c.Person.String()) + }) + } +} diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index 47439c4c3b..af7bf4e5a6 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -230,6 +230,7 @@ func PackageVersionMetadata(ctx *context.Context) { }, Author: swift_module.Person{ Type: "Person", + Name: metadata.Author.String(), GivenName: metadata.Author.GivenName, MiddleName: metadata.Author.MiddleName, FamilyName: metadata.Author.FamilyName, diff --git a/tests/integration/api_packages_swift_test.go b/tests/integration/api_packages_swift_test.go index b29e8459ff..8cb2d757e4 100644 --- a/tests/integration/api_packages_swift_test.go +++ b/tests/integration/api_packages_swift_test.go @@ -355,6 +355,7 @@ func TestPackageSwift(t *testing.T) { assert.Equal(t, packageVersion, result.Metadata.Version) assert.Equal(t, packageDescription, result.Metadata.Description) assert.Equal(t, "Swift", result.Metadata.ProgrammingLanguage.Name) + assert.Equal(t, packageAuthor, result.Metadata.Author.Name) assert.Equal(t, packageAuthor, result.Metadata.Author.GivenName) req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s.json", url, packageScope, packageName, packageVersion)).