diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index 947144ec54..c4b8a7218d 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -197,7 +197,7 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc case TypeRubyGems: metadata = &rubygems.Metadata{} case TypeSwift: - metadata = &swift.Metadata{} + metadata = &swift.Package{} case TypeVagrant: metadata = &vagrant.Metadata{} default: diff --git a/modules/packages/swift/metadata.go b/modules/packages/swift/metadata.go index 094fa0c7a4..e8b722d3ee 100644 --- a/modules/packages/swift/metadata.go +++ b/modules/packages/swift/metadata.go @@ -13,7 +13,6 @@ import ( "forgejo.org/modules/json" "forgejo.org/modules/util" - "forgejo.org/modules/validation" "github.com/hashicorp/go-version" ) @@ -37,18 +36,8 @@ const ( // Package represents a Swift package type Package struct { - RepositoryURLs []string - Metadata *Metadata -} - -// Metadata represents the metadata of a Swift package -type Metadata struct { - Description string `json:"description,omitempty"` - Keywords []string `json:"keywords,omitempty"` - RepositoryURL string `json:"repository_url,omitempty"` - License string `json:"license,omitempty"` - Author Person `json:"author"` - Manifests map[string]*Manifest `json:"manifests,omitempty"` + Manifests map[string]*Manifest + Metadata *PackageRelease } // Manifest represents a Package.swift file @@ -57,54 +46,31 @@ type Manifest struct { ToolsVersion string `json:"tools_version,omitempty"` } -// https://schema.org/SoftwareSourceCode -type SoftwareSourceCode struct { - Context []string `json:"@context"` - Type string `json:"@type"` - Name string `json:"name"` - Version string `json:"version"` - Description string `json:"description,omitempty"` - Keywords []string `json:"keywords,omitempty"` - CodeRepository string `json:"codeRepository,omitempty"` - License string `json:"license,omitempty"` - Author Person `json:"author"` - ProgrammingLanguage ProgrammingLanguage `json:"programmingLanguage"` - RepositoryURLs []string `json:"repositoryURLs,omitempty"` +// https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/registryserverspecification/#PackageRelease-type +type PackageRelease struct { + Author *Author `json:"author,omitempty"` + Description string `json:"description,omitempty"` + LicenseURL string `json:"licenseURL,omitempty"` + OriginalPublicationTime string `json:"originalPublicationTime,omitempty"` + ReadmeURL string `json:"readmeURL,omitempty"` + RepositoryURLs []string `json:"repositoryURLs,omitempty"` } -// https://schema.org/ProgrammingLanguage -type ProgrammingLanguage struct { - Type string `json:"@type"` - Name string `json:"name"` - URL string `json:"url"` +// https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/registryserverspecification/#Author-type +type Author struct { + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` + Description string `json:"description,omitempty"` + Organization *Organization `json:"organization,omitempty"` + URL string `json:"url,omitempty"` } -// https://schema.org/Person -type Person struct { - Type string `json:"@type,omitempty"` - GivenName string `json:"givenName,omitempty"` - MiddleName string `json:"middleName,omitempty"` - FamilyName string `json:"familyName,omitempty"` -} - -func (p Person) String() string { - var sb strings.Builder - if p.GivenName != "" { - sb.WriteString(p.GivenName) - } - if p.MiddleName != "" { - if sb.Len() > 0 { - sb.WriteRune(' ') - } - sb.WriteString(p.MiddleName) - } - if p.FamilyName != "" { - if sb.Len() > 0 { - sb.WriteRune(' ') - } - sb.WriteString(p.FamilyName) - } - return sb.String() +// https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/registryserverspecification/#Organization-type +type Organization struct { + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` + Description string `json:"description,omitempty"` + URL string `json:"url,omitempty"` } // ParsePackage parses the Swift package upload @@ -115,9 +81,8 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) { } p := &Package{ - Metadata: &Metadata{ - Manifests: make(map[string]*Manifest), - }, + Manifests: make(map[string]*Manifest), + Metadata: &PackageRelease{}, } for _, file := range zr.File { @@ -168,34 +133,27 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) { manifest.ToolsVersion = TrimmedVersionString(v) } - p.Metadata.Manifests[swiftVersion] = manifest + p.Manifests[swiftVersion] = manifest } - if _, found := p.Metadata.Manifests[""]; !found { + if _, found := p.Manifests[""]; !found { return nil, ErrMissingManifestFile } if mr != nil { - var ssc *SoftwareSourceCode - if err := json.NewDecoder(mr).Decode(&ssc); err != nil { + var pr *PackageRelease + if err := json.NewDecoder(mr).Decode(&pr); err != nil { return nil, err } - - p.Metadata.Description = ssc.Description - p.Metadata.Keywords = ssc.Keywords - p.Metadata.License = ssc.License - p.Metadata.Author = Person{ - GivenName: ssc.Author.GivenName, - MiddleName: ssc.Author.MiddleName, - FamilyName: ssc.Author.FamilyName, + if pr.Author != nil { + if pr.Author.Name == "" { + return nil, util.NewInvalidArgumentErrorf("if metadata.author exists, its name can't be empty") + } + if pr.Author.Organization != nil && pr.Author.Organization.Name == "" { + return nil, util.NewInvalidArgumentErrorf("if metadata.author.organization exists, its name can't be empty") + } } - - p.Metadata.RepositoryURL = ssc.CodeRepository - if !validation.IsValidURL(p.Metadata.RepositoryURL) { - p.Metadata.RepositoryURL = "" - } - - p.RepositoryURLs = ssc.RepositoryURLs + p.Metadata = pr } return p, nil diff --git a/modules/packages/swift/metadata_test.go b/modules/packages/swift/metadata_test.go index b223d8c15f..e5074f4ddd 100644 --- a/modules/packages/swift/metadata_test.go +++ b/modules/packages/swift/metadata_test.go @@ -9,18 +9,19 @@ import ( "strings" "testing" + "forgejo.org/modules/util" + "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const ( - packageName = "gitea" - packageVersion = "1.0.1" packageDescription = "Package Description" packageRepositoryURL = "https://gitea.io/gitea/gitea" packageAuthor = "KN4CK3R" - packageLicense = "MIT" + packageEmail = "example@example.com" + packageLicense = "https://opensource.org/license/mit" ) func TestParsePackage(t *testing.T) { @@ -67,12 +68,12 @@ func TestParsePackage(t *testing.T) { require.NoError(t, err) assert.NotNil(t, p.Metadata) - assert.Empty(t, p.RepositoryURLs) - assert.Len(t, p.Metadata.Manifests, 2) - m := p.Metadata.Manifests[""] + assert.Empty(t, p.Metadata.RepositoryURLs) + assert.Len(t, p.Manifests, 2) + m := p.Manifests[""] assert.Equal(t, "5.7", m.ToolsVersion) assert.Equal(t, content1, m.Content) - m = p.Metadata.Manifests["5.5"] + m = p.Manifests["5.5"] assert.Equal(t, "5.6", m.ToolsVersion) assert.Equal(t, content2, m.Content) }) @@ -85,22 +86,34 @@ func TestParsePackage(t *testing.T) { p, err := ParsePackage( data, data.Size(), - strings.NewReader(`{"name":"`+packageName+`","version":"`+packageVersion+`","description":"`+packageDescription+`","keywords":["swift","package"],"license":"`+packageLicense+`","codeRepository":"`+packageRepositoryURL+`","author":{"givenName":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`), + strings.NewReader(`{"description":"`+packageDescription+`","licenseURL":"`+packageLicense+`","author":{"name":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`), ) assert.NotNil(t, p) require.NoError(t, err) assert.NotNil(t, p.Metadata) - assert.Len(t, p.Metadata.Manifests, 1) - m := p.Metadata.Manifests[""] + assert.Len(t, p.Manifests, 1) + m := p.Manifests[""] assert.Equal(t, "5.7", m.ToolsVersion) 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.GivenName) - assert.Equal(t, packageRepositoryURL, p.Metadata.RepositoryURL) - assert.ElementsMatch(t, []string{packageRepositoryURL}, p.RepositoryURLs) + assert.Equal(t, packageLicense, p.Metadata.LicenseURL) + assert.Equal(t, packageAuthor, p.Metadata.Author.Name) + assert.ElementsMatch(t, []string{packageRepositoryURL}, p.Metadata.RepositoryURLs) + }) + + t.Run("WithInvalidMetadata", func(t *testing.T) { + data := createArchive(map[string][]byte{ + "Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"), + }) + + p, err := ParsePackage( + data, + data.Size(), + strings.NewReader(`{"description":"`+packageDescription+`","licenseURL":"`+packageLicense+`","author":{"email":"`+packageEmail+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`), + ) + assert.Nil(t, p) + require.ErrorIs(t, err, util.ErrInvalidArgument) }) } diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index a65bd31cd9..c8b54f3f30 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -174,10 +174,10 @@ type Resource struct { } type PackageVersionMetadataResponse struct { - ID string `json:"id"` - Version string `json:"version"` - Resources []Resource `json:"resources"` - Metadata *swift_module.SoftwareSourceCode `json:"metadata"` + ID string `json:"id"` + Version string `json:"version"` + Resources []Resource `json:"resources"` + Metadata *swift_module.PackageRelease `json:"metadata"` } // https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-2 @@ -200,7 +200,7 @@ func PackageVersionMetadata(ctx *context.Context) { return } - metadata := pd.Metadata.(*swift_module.Metadata) + metadata := pd.Metadata.(*swift_module.Package) setResponseHeaders(ctx.Resp, &headers{}) @@ -214,27 +214,7 @@ func PackageVersionMetadata(ctx *context.Context) { Checksum: pd.Files[0].Blob.HashSHA256, }, }, - Metadata: &swift_module.SoftwareSourceCode{ - Context: []string{"http://schema.org/"}, - Type: "SoftwareSourceCode", - Name: pd.PackageProperties.GetByName(swift_module.PropertyName), - Version: pd.Version.Version, - Description: metadata.Description, - Keywords: metadata.Keywords, - CodeRepository: metadata.RepositoryURL, - License: metadata.License, - ProgrammingLanguage: swift_module.ProgrammingLanguage{ - Type: "ComputerLanguage", - Name: "Swift", - URL: "https://swift.org", - }, - Author: swift_module.Person{ - Type: "Person", - GivenName: metadata.Author.GivenName, - MiddleName: metadata.Author.MiddleName, - FamilyName: metadata.Author.FamilyName, - }, - }, + Metadata: metadata.Metadata, }) } @@ -267,7 +247,7 @@ func DownloadManifest(ctx *context.Context) { swiftVersion = swift_module.TrimmedVersionString(v) } } - m, ok := pd.Metadata.(*swift_module.Metadata).Manifests[swiftVersion] + m, ok := pd.Metadata.(*swift_module.Package).Manifests[swiftVersion] if !ok { setResponseHeaders(ctx.Resp, &headers{ Status: http.StatusSeeOther, @@ -370,7 +350,7 @@ func UploadPackageFile(ctx *context.Context) { }, SemverCompatible: true, Creator: ctx.Doer, - Metadata: pck.Metadata, + Metadata: pck, PackageProperties: map[string]string{ swift_module.PropertyScope: packageScope, swift_module.PropertyName: packageName, @@ -397,7 +377,7 @@ func UploadPackageFile(ctx *context.Context) { return } - for _, url := range pck.RepositoryURLs { + for _, url := range pck.Metadata.RepositoryURLs { _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, swift_module.PropertyRepositoryURL, url) if err != nil { log.Error("InsertProperty failed: %v", err) diff --git a/templates/package/content/swift.tmpl b/templates/package/content/swift.tmpl index 25c558dd2f..834d2398b9 100644 --- a/templates/package/content/swift.tmpl +++ b/templates/package/content/swift.tmpl @@ -22,19 +22,10 @@ - {{if .PackageDescriptor.Metadata.Description}} + {{if .PackageDescriptor.Metadata.Metadata.Description}}

{{ctx.Locale.Tr "packages.about"}}

- {{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{end}} -
- {{end}} - - {{if .PackageDescriptor.Metadata.Keywords}} -

{{ctx.Locale.Tr "packages.keywords"}}

-
- {{range .PackageDescriptor.Metadata.Keywords}} - {{.}} - {{end}} + {{.PackageDescriptor.Metadata.Metadata.Description}}
{{end}} {{end}} diff --git a/templates/package/metadata/swift.tmpl b/templates/package/metadata/swift.tmpl index fdffb6dede..8c621480dd 100644 --- a/templates/package/metadata/swift.tmpl +++ b/templates/package/metadata/swift.tmpl @@ -1,4 +1,3 @@ {{if eq .PackageDescriptor.Package.Type "swift"}} - {{if .PackageDescriptor.Metadata.Author.String}}
{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.Metadata.Author}}
{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Metadata.Author.Name}}
{{end}} {{end}} diff --git a/tests/integration/api_packages_swift_test.go b/tests/integration/api_packages_swift_test.go index 080f2400ff..1cefee770a 100644 --- a/tests/integration/api_packages_swift_test.go +++ b/tests/integration/api_packages_swift_test.go @@ -147,7 +147,7 @@ func TestPackageSwift(t *testing.T) { "Package.swift": contentManifest1, "Package@swift-5.6.swift": contentManifest2, }), - `{"name":"`+packageName+`","version":"`+packageVersion+`","description":"`+packageDescription+`","codeRepository":"`+packageRepositoryURL+`","author":{"givenName":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`, + `{"description":"`+packageDescription+`","author":{"name":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`, ) pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeSwift) @@ -159,9 +159,9 @@ func TestPackageSwift(t *testing.T) { assert.NotNil(t, pd.SemVer) assert.Equal(t, packageID, pd.Package.Name) assert.Equal(t, packageVersion, pd.Version.Version) - assert.IsType(t, &swift_module.Metadata{}, pd.Metadata) - metadata := pd.Metadata.(*swift_module.Metadata) - assert.Equal(t, packageDescription, metadata.Description) + assert.IsType(t, &swift_module.Package{}, pd.Metadata) + metadata := pd.Metadata.(*swift_module.Package) + assert.Equal(t, packageDescription, metadata.Metadata.Description) assert.Len(t, metadata.Manifests, 2) assert.Equal(t, contentManifest1, metadata.Manifests[""].Content) assert.Equal(t, contentManifest2, metadata.Manifests["5.6"].Content) @@ -235,7 +235,7 @@ func TestPackageSwift(t *testing.T) { "Package.swift": contentManifest1, "Package@swift-5.6.swift": contentManifest2, }), - `{"name":"`+packageName+`","version":"`+packageVersion2+`","description":"`+packageDescription+`","codeRepository":"`+packageRepositoryURL+`","author":{"givenName":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`, + `{"description":"`+packageDescription+`","author":{"name":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`, ) pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeSwift) @@ -247,9 +247,9 @@ func TestPackageSwift(t *testing.T) { assert.NotNil(t, pd.SemVer) assert.Equal(t, packageID, pd.Package.Name) assert.Equal(t, packageVersion2, pd.Version.Version) - assert.IsType(t, &swift_module.Metadata{}, pd.Metadata) - metadata := pd.Metadata.(*swift_module.Metadata) - assert.Equal(t, packageDescription, metadata.Description) + assert.IsType(t, &swift_module.Package{}, pd.Metadata) + metadata := pd.Metadata.(*swift_module.Package) + assert.Equal(t, packageDescription, metadata.Metadata.Description) assert.Len(t, metadata.Manifests, 2) assert.Equal(t, contentManifest1, metadata.Manifests[""].Content) assert.Equal(t, contentManifest2, metadata.Manifests["5.6"].Content) @@ -350,12 +350,8 @@ func TestPackageSwift(t *testing.T) { assert.Equal(t, "source-archive", result.Resources[0].Name) assert.Equal(t, "application/zip", result.Resources[0].Type) assert.Equal(t, pd.Files[0].Blob.HashSHA256, result.Resources[0].Checksum) - assert.Equal(t, "SoftwareSourceCode", result.Metadata.Type) - assert.Equal(t, packageName, result.Metadata.Name) - 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.GivenName) + assert.Equal(t, packageAuthor, result.Metadata.Author.Name) req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s.json", url, packageScope, packageName, packageVersion)). AddBasicAuth(user.Name)