From 87362b4dc17f4537be56366e68dd53eaa3442334 Mon Sep 17 00:00:00 2001 From: Akhan Zhakiyanov Date: Mon, 8 Sep 2025 02:24:25 +0800 Subject: [PATCH] fix: add author.name field to Swift Package Registry API response (#35410) 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. --- 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 85beb57607..78925c6e6d 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 e1c3b36834..d84f79a0a8 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -227,6 +227,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 59e1470632..ddc3cb63f3 100644 --- a/tests/integration/api_packages_swift_test.go +++ b/tests/integration/api_packages_swift_test.go @@ -354,6 +354,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)).