From 00a56ef53210bf35e8583c7f37172e0bb792e14f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 4 Feb 2025 21:51:14 -0800 Subject: [PATCH 1/4] Add composor source field --- models/packages/descriptor.go | 9 +++++--- routers/api/packages/composer/api.go | 22 +++++++++++++++++-- .../integration/api_packages_composer_test.go | 1 + 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index 803b73c968995..d251fcc4a90df 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -110,9 +110,12 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc if err != nil { return nil, err } - repository, err := repo_model.GetRepositoryByID(ctx, p.RepoID) - if err != nil && !repo_model.IsErrRepoNotExist(err) { - return nil, err + var repository *repo_model.Repository + if p.RepoID > 0 { + repository, err = repo_model.GetRepositoryByID(ctx, p.RepoID) + if err != nil && !repo_model.IsErrRepoNotExist(err) { + return nil, err + } } creator, err := user_model.GetUserByID(ctx, pv.CreatorID) if err != nil { diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go index a3bcf80417c77..a3ea2c2f9a633 100644 --- a/routers/api/packages/composer/api.go +++ b/routers/api/packages/composer/api.go @@ -66,6 +66,7 @@ type PackageMetadataResponse struct { } // PackageVersionMetadata contains package metadata +// https://getcomposer.org/doc/05-repositories.md#package type PackageVersionMetadata struct { *composer_module.Metadata Name string `json:"name"` @@ -73,6 +74,7 @@ type PackageVersionMetadata struct { Type string `json:"type"` Created time.Time `json:"time"` Dist Dist `json:"dist"` + Source Source `json:"source"` } // Dist contains package download information @@ -82,6 +84,13 @@ type Dist struct { Checksum string `json:"shasum"` } +// Source contains package source information +type Source struct { + URL string `json:"url"` + Type string `json:"type"` + Reference string `json:"reference"` +} + func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *PackageMetadataResponse { versions := make([]*PackageVersionMetadata, 0, len(pds)) @@ -94,7 +103,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac } } - versions = append(versions, &PackageVersionMetadata{ + pkg := PackageVersionMetadata{ Name: pd.Package.Name, Version: pd.Version.Version, Type: packageType, @@ -105,7 +114,16 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac URL: fmt.Sprintf("%s/files/%s/%s/%s", registryURL, url.PathEscape(pd.Package.LowerName), url.PathEscape(pd.Version.LowerVersion), url.PathEscape(pd.Files[0].File.LowerName)), Checksum: pd.Files[0].Blob.HashSHA1, }, - }) + } + if pd.Repository != nil { + pkg.Source = Source{ + URL: pd.Repository.HTMLURL(), + Type: "git", + Reference: pd.Version.Version, + } + } + + versions = append(versions, &pkg) } return &PackageMetadataResponse{ diff --git a/tests/integration/api_packages_composer_test.go b/tests/integration/api_packages_composer_test.go index 51b047ab41ad4..c5776d6b7ee29 100644 --- a/tests/integration/api_packages_composer_test.go +++ b/tests/integration/api_packages_composer_test.go @@ -215,6 +215,7 @@ func TestPackageComposer(t *testing.T) { assert.Equal(t, packageAuthor, pkgs[0].Authors[0].Name) assert.Equal(t, "zip", pkgs[0].Dist.Type) assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum) + // TODO: add a test for repository field after link package to a repository via API is implemented assert.Len(t, pkgs[0].Bin, 1) assert.Equal(t, packageBin, pkgs[0].Bin[0]) }) From d192e8cb6fc9f20a24eb89a0a7f2b9cba262c24a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 16 Feb 2025 21:17:08 -0800 Subject: [PATCH 2/4] Add test --- .../integration/api_packages_composer_test.go | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/integration/api_packages_composer_test.go b/tests/integration/api_packages_composer_test.go index c5776d6b7ee29..a1d8d4a9b3371 100644 --- a/tests/integration/api_packages_composer_test.go +++ b/tests/integration/api_packages_composer_test.go @@ -13,6 +13,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/packages" + packages_model "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" composer_module "code.gitea.io/gitea/modules/packages/composer" @@ -215,8 +217,40 @@ func TestPackageComposer(t *testing.T) { assert.Equal(t, packageAuthor, pkgs[0].Authors[0].Name) assert.Equal(t, "zip", pkgs[0].Dist.Type) assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum) - // TODO: add a test for repository field after link package to a repository via API is implemented assert.Len(t, pkgs[0].Bin, 1) assert.Equal(t, packageBin, pkgs[0].Bin[0]) + + // Test package linked to repository + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeComposer) + assert.NoError(t, err) + assert.Len(t, pvs, 1) + + err = packages_model.SetRepositoryLink(db.DefaultContext, pvs[0].ID, repo1.ID) + assert.NoError(t, err) + + req = NewRequest(t, "GET", fmt.Sprintf("%s/p2/%s/%s.json", url, vendorName, projectName)). + AddBasicAuth(user.Name) + resp = MakeRequest(t, req, http.StatusOK) + + result = composer.PackageMetadataResponse{} + DecodeJSON(t, resp, &result) + + assert.Contains(t, result.Packages, packageName) + pkgs = result.Packages[packageName] + assert.Len(t, pkgs, 1) + assert.Equal(t, packageName, pkgs[0].Name) + assert.Equal(t, packageVersion, pkgs[0].Version) + assert.Equal(t, packageType, pkgs[0].Type) + assert.Equal(t, packageDescription, pkgs[0].Description) + assert.Len(t, pkgs[0].Authors, 1) + assert.Equal(t, packageAuthor, pkgs[0].Authors[0].Name) + assert.Equal(t, "zip", pkgs[0].Dist.Type) + assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum) + assert.Len(t, pkgs[0].Bin, 1) + assert.Equal(t, packageBin, pkgs[0].Bin[0]) + assert.Equal(t, repo1.HTMLURL(), pkgs[0].Source.URL) + assert.Equal(t, "git", pkgs[0].Source.Type) + assert.Equal(t, packageVersion, pkgs[0].Source.Reference) }) } From 357080bf671a37f70dc4579c40ee5ba77a800041 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 16 Feb 2025 23:09:12 -0800 Subject: [PATCH 3/4] Fix lint --- tests/integration/api_packages_composer_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration/api_packages_composer_test.go b/tests/integration/api_packages_composer_test.go index a1d8d4a9b3371..137e016aee607 100644 --- a/tests/integration/api_packages_composer_test.go +++ b/tests/integration/api_packages_composer_test.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/packages" - packages_model "code.gitea.io/gitea/models/packages" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -226,7 +225,7 @@ func TestPackageComposer(t *testing.T) { assert.NoError(t, err) assert.Len(t, pvs, 1) - err = packages_model.SetRepositoryLink(db.DefaultContext, pvs[0].ID, repo1.ID) + err = packages.SetRepositoryLink(db.DefaultContext, pvs[0].ID, repo1.ID) assert.NoError(t, err) req = NewRequest(t, "GET", fmt.Sprintf("%s/p2/%s/%s.json", url, vendorName, projectName)). From 8115cf119571f89ca38af3ce49910cf7894f06be Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 20 Feb 2025 12:31:58 -0800 Subject: [PATCH 4/4] Allow ignoring broken package repository link --- services/packages/package_update.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/services/packages/package_update.go b/services/packages/package_update.go index 8d851fac5311b..4a22ee7a62995 100644 --- a/services/packages/package_update.go +++ b/services/packages/package_update.go @@ -44,16 +44,17 @@ func UnlinkFromRepository(ctx context.Context, pkg *packages_model.Package, doer } repo, err := repo_model.GetRepositoryByID(ctx, pkg.RepoID) - if err != nil { + if err != nil && !repo_model.IsErrRepoNotExist(err) { return fmt.Errorf("error getting repository %d: %w", pkg.RepoID, err) } - - perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) - if err != nil { - return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) - } - if !perms.CanWrite(unit.TypePackages) { - return util.ErrPermissionDenied + if err == nil { + perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) + if err != nil { + return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) + } + if !perms.CanWrite(unit.TypePackages) { + return util.ErrPermissionDenied + } } user, err := user_model.GetUserByID(ctx, pkg.OwnerID)