feat: vendor gitea 1.16.2
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
@@ -53,8 +54,11 @@ func (l PackagePropertyList) GetByName(name string) string {
|
||||
|
||||
// PackageDescriptor describes a package
|
||||
type PackageDescriptor struct {
|
||||
Package *Package
|
||||
Owner *user_model.User
|
||||
// basic package info
|
||||
Package *Package
|
||||
Owner *user_model.User
|
||||
|
||||
// package version info
|
||||
Repository *repo_model.Repository
|
||||
Version *PackageVersion
|
||||
SemVer *version.Version
|
||||
@@ -77,6 +81,11 @@ func (pd *PackageDescriptor) PackageWebLink() string {
|
||||
return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HomeLink(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
|
||||
}
|
||||
|
||||
// PackageSettingsLink returns the relative package settings link
|
||||
func (pd *PackageDescriptor) PackageSettingsLink() string {
|
||||
return fmt.Sprintf("%s/-/packages/settings/%s/%s", pd.Owner.HomeLink(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
|
||||
}
|
||||
|
||||
// VersionWebLink returns the relative package version web link
|
||||
func (pd *PackageDescriptor) VersionWebLink() string {
|
||||
return fmt.Sprintf("%s/%s", pd.PackageWebLink(), url.PathEscape(pd.Version.LowerVersion))
|
||||
@@ -203,6 +212,8 @@ func GetPackageDescriptorWithCache(ctx context.Context, pv *PackageVersion, c *c
|
||||
metadata = &rubygems.Metadata{}
|
||||
case TypeSwift:
|
||||
metadata = &swift.Metadata{}
|
||||
case TypeTerraformState:
|
||||
// terraform packages have no metadata
|
||||
case TypeVagrant:
|
||||
metadata = &vagrant.Metadata{}
|
||||
default:
|
||||
@@ -267,6 +278,15 @@ func GetPackageDescriptors(ctx context.Context, pvs []*PackageVersion) ([]*Packa
|
||||
return getPackageDescriptors(ctx, pvs, cache.NewEphemeralCache())
|
||||
}
|
||||
|
||||
// GetAllPackageDescriptors gets all package descriptors for a package
|
||||
func GetAllPackageDescriptors(ctx context.Context, p *Package) ([]*PackageDescriptor, error) {
|
||||
pvs := make([]*PackageVersion, 0, 10)
|
||||
if err := db.GetEngine(ctx).Where("package_id = ?", p.ID).Find(&pvs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getPackageDescriptors(ctx, pvs, cache.NewEphemeralCache())
|
||||
}
|
||||
|
||||
func getPackageDescriptors(ctx context.Context, pvs []*PackageVersion, c *cache.EphemeralCache) ([]*PackageDescriptor, error) {
|
||||
pds := make([]*PackageDescriptor, 0, len(pvs))
|
||||
for _, pv := range pvs {
|
||||
|
||||
@@ -30,28 +30,29 @@ type Type string
|
||||
|
||||
// List of supported packages
|
||||
const (
|
||||
TypeAlpine Type = "alpine"
|
||||
TypeArch Type = "arch"
|
||||
TypeCargo Type = "cargo"
|
||||
TypeChef Type = "chef"
|
||||
TypeComposer Type = "composer"
|
||||
TypeConan Type = "conan"
|
||||
TypeConda Type = "conda"
|
||||
TypeContainer Type = "container"
|
||||
TypeCran Type = "cran"
|
||||
TypeDebian Type = "debian"
|
||||
TypeGeneric Type = "generic"
|
||||
TypeGo Type = "go"
|
||||
TypeHelm Type = "helm"
|
||||
TypeMaven Type = "maven"
|
||||
TypeNpm Type = "npm"
|
||||
TypeNuGet Type = "nuget"
|
||||
TypePub Type = "pub"
|
||||
TypePyPI Type = "pypi"
|
||||
TypeRpm Type = "rpm"
|
||||
TypeRubyGems Type = "rubygems"
|
||||
TypeSwift Type = "swift"
|
||||
TypeVagrant Type = "vagrant"
|
||||
TypeAlpine Type = "alpine"
|
||||
TypeArch Type = "arch"
|
||||
TypeCargo Type = "cargo"
|
||||
TypeChef Type = "chef"
|
||||
TypeComposer Type = "composer"
|
||||
TypeConan Type = "conan"
|
||||
TypeConda Type = "conda"
|
||||
TypeContainer Type = "container"
|
||||
TypeCran Type = "cran"
|
||||
TypeDebian Type = "debian"
|
||||
TypeGeneric Type = "generic"
|
||||
TypeGo Type = "go"
|
||||
TypeHelm Type = "helm"
|
||||
TypeMaven Type = "maven"
|
||||
TypeNpm Type = "npm"
|
||||
TypeNuGet Type = "nuget"
|
||||
TypePub Type = "pub"
|
||||
TypePyPI Type = "pypi"
|
||||
TypeRpm Type = "rpm"
|
||||
TypeRubyGems Type = "rubygems"
|
||||
TypeSwift Type = "swift"
|
||||
TypeTerraformState Type = "terraform"
|
||||
TypeVagrant Type = "vagrant"
|
||||
)
|
||||
|
||||
var TypeList = []Type{
|
||||
@@ -76,6 +77,7 @@ var TypeList = []Type{
|
||||
TypeRpm,
|
||||
TypeRubyGems,
|
||||
TypeSwift,
|
||||
TypeTerraformState,
|
||||
TypeVagrant,
|
||||
}
|
||||
|
||||
@@ -124,6 +126,8 @@ func (pt Type) Name() string {
|
||||
return "RubyGems"
|
||||
case TypeSwift:
|
||||
return "Swift"
|
||||
case TypeTerraformState:
|
||||
return "Terraform State"
|
||||
case TypeVagrant:
|
||||
return "Vagrant"
|
||||
}
|
||||
@@ -175,6 +179,8 @@ func (pt Type) SVGName() string {
|
||||
return "gitea-rubygems"
|
||||
case TypeSwift:
|
||||
return "gitea-swift"
|
||||
case TypeTerraformState:
|
||||
return "gitea-terraform"
|
||||
case TypeVagrant:
|
||||
return "gitea-vagrant"
|
||||
}
|
||||
|
||||
@@ -43,13 +43,15 @@ func GetOrInsertBlob(ctx context.Context, pb *PackageBlob) (*PackageBlob, bool,
|
||||
|
||||
existing := &PackageBlob{}
|
||||
|
||||
has, err := e.Where(builder.Eq{
|
||||
hashCond := builder.Eq{
|
||||
"size": pb.Size,
|
||||
"hash_md5": pb.HashMD5,
|
||||
"hash_sha1": pb.HashSHA1,
|
||||
"hash_sha256": pb.HashSHA256,
|
||||
"hash_sha512": pb.HashSHA512,
|
||||
}).Get(existing)
|
||||
}
|
||||
|
||||
has, err := e.Where(hashCond).Get(existing)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -57,6 +59,11 @@ func GetOrInsertBlob(ctx context.Context, pb *PackageBlob) (*PackageBlob, bool,
|
||||
return existing, true, nil
|
||||
}
|
||||
if _, err = e.Insert(pb); err != nil {
|
||||
// Handle race condition: another request may have inserted the same blob
|
||||
// between our SELECT and INSERT. Retry the SELECT to get the existing blob.
|
||||
if has, _ = e.Where(hashCond).Get(existing); has {
|
||||
return existing, true, nil
|
||||
}
|
||||
return nil, false, err
|
||||
}
|
||||
return pb, false, nil
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func TestGetOrInsertBlobConcurrent(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
testBlob := PackageBlob{
|
||||
Size: 123,
|
||||
HashMD5: "md5",
|
||||
HashSHA1: "sha1",
|
||||
HashSHA256: "sha256",
|
||||
HashSHA512: "sha512",
|
||||
}
|
||||
|
||||
const numGoroutines = 3
|
||||
var wg errgroup.Group
|
||||
results := make([]*PackageBlob, numGoroutines)
|
||||
existed := make([]bool, numGoroutines)
|
||||
for idx := range numGoroutines {
|
||||
wg.Go(func() error {
|
||||
blob := testBlob // Create a copy of the test blob for each goroutine
|
||||
var err error
|
||||
results[idx], existed[idx], err = GetOrInsertBlob(t.Context(), &blob)
|
||||
return err
|
||||
})
|
||||
}
|
||||
require.NoError(t, wg.Wait())
|
||||
|
||||
// then: all GetOrInsertBlob succeeds with the same blob ID, and only one indicates it did not exist before
|
||||
existedCount := 0
|
||||
assert.NotNil(t, results[0])
|
||||
for i := range numGoroutines {
|
||||
assert.Equal(t, results[0].ID, results[i].ID)
|
||||
if existed[i] {
|
||||
existedCount++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, numGoroutines-1, existedCount)
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func TryInsertFile(ctx context.Context, pf *PackageFile) (*PackageFile, error) {
|
||||
// GetFilesByVersionID gets all files of a version
|
||||
func GetFilesByVersionID(ctx context.Context, versionID int64) ([]*PackageFile, error) {
|
||||
pfs := make([]*PackageFile, 0, 10)
|
||||
return pfs, db.GetEngine(ctx).Where("version_id = ?", versionID).Find(&pfs)
|
||||
return pfs, db.GetEngine(ctx).Where("version_id = ?", versionID).OrderBy("lower_name, created_unix, id").Find(&pfs)
|
||||
}
|
||||
|
||||
// GetFileForVersionByID gets a file of a version by id
|
||||
@@ -115,6 +115,20 @@ func DeleteFileByID(ctx context.Context, fileID int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFilesByPackageID deletes all files of a specific package
|
||||
// Versions must not be deleted prior to this call
|
||||
func DeleteFilesByPackageID(ctx context.Context, packageID int64) error {
|
||||
deleteStmt := builder.Delete(builder.In("version_id", builder.Select("package_version.id").From("package_version").Where(builder.Eq{"package_id": packageID}))).From("package_file")
|
||||
_, err := db.GetEngine(ctx).Exec(deleteStmt)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFilesByVersionID deletes all files of a specific version
|
||||
func DeleteFilesByVersionID(ctx context.Context, versionID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where("version_id = ?", versionID).Delete(&PackageFile{})
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateFile(ctx context.Context, pf *PackageFile, cols []string) error {
|
||||
_, err := db.GetEngine(ctx).ID(pf.ID).Cols(cols...).Update(pf)
|
||||
return err
|
||||
|
||||
@@ -5,6 +5,7 @@ package packages
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
||||
@@ -51,13 +52,13 @@ func InsertProperty(ctx context.Context, refType PropertyType, refID int64, name
|
||||
// GetProperties gets all properties
|
||||
func GetProperties(ctx context.Context, refType PropertyType, refID int64) ([]*PackageProperty, error) {
|
||||
pps := make([]*PackageProperty, 0, 10)
|
||||
return pps, db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ?", refType, refID).Find(&pps)
|
||||
return pps, db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ?", refType, refID).OrderBy("id").Find(&pps)
|
||||
}
|
||||
|
||||
// GetPropertiesByName gets all properties with a specific name
|
||||
func GetPropertiesByName(ctx context.Context, refType PropertyType, refID int64, name string) ([]*PackageProperty, error) {
|
||||
pps := make([]*PackageProperty, 0, 10)
|
||||
return pps, db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Find(&pps)
|
||||
return pps, db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).OrderBy("id").Find(&pps)
|
||||
}
|
||||
|
||||
// UpdateProperty updates a property
|
||||
@@ -86,6 +87,46 @@ func DeleteAllProperties(ctx context.Context, refType PropertyType, refID int64)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeletePropertiesByPackageID deletes properties of a typed linked to the package
|
||||
// Use to avoid for loops in mass deletion of properties
|
||||
func DeletePropertiesByPackageID(ctx context.Context, refType PropertyType, packageID int64) error {
|
||||
var deleteStmt *builder.Builder
|
||||
|
||||
switch refType {
|
||||
case PropertyTypeFile:
|
||||
deleteStmt = builder.Delete(
|
||||
// Delete all properties that are attached to a file and are in ids from a subquery
|
||||
// which returns ids from the package_file table joined on package_version to link it with package id
|
||||
builder.Eq{"ref_type": PropertyTypeFile}, builder.In("ref_id",
|
||||
builder.Select("package_file.id").From("package_file").
|
||||
LeftJoin("package_version", "package_file.version_id = package_version.id").
|
||||
Where(builder.Eq{"package_version.package_id": packageID}))).From("package_property")
|
||||
case PropertyTypeVersion:
|
||||
// Delete all properties that are attached to a version and are in ids from subquery to the package_version filtered by package id
|
||||
deleteStmt = builder.Delete(
|
||||
builder.Eq{"ref_type": PropertyTypeVersion}, builder.In("ref_id",
|
||||
builder.Select("package_version.id").From("package_version").
|
||||
Where(builder.Eq{"package_version.package_id": packageID}))).From("package_property")
|
||||
case PropertyTypePackage:
|
||||
// Delete all properties that are attached to a package and their reference links to the given package ID
|
||||
deleteStmt = builder.Delete(
|
||||
builder.Eq{"ref_type": PropertyTypePackage}, builder.Eq{"ref_id": packageID}).
|
||||
From("package_property")
|
||||
default:
|
||||
return errors.New("invalid ref type")
|
||||
}
|
||||
|
||||
_, err := db.GetEngine(ctx).Exec(deleteStmt)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFilePropertiesByVersionID deletes all file properties linked to specific version
|
||||
func DeleteFilePropertiesByVersionID(ctx context.Context, versionID int64) error {
|
||||
deleteStmt := builder.Delete(builder.Eq{"ref_type": PropertyTypeFile}, builder.In("ref_id", builder.Select("id").From("package_file").Where(builder.Eq{"version_id": versionID}))).From("package_property")
|
||||
_, err := db.GetEngine(ctx).Exec(deleteStmt)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeletePropertyByID deletes a property
|
||||
func DeletePropertyByID(ctx context.Context, propertyID int64) error {
|
||||
_, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{})
|
||||
|
||||
@@ -157,6 +157,12 @@ func DeleteVersionByID(ctx context.Context, versionID int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteVersionsByPackageID deletes all versions of a specific package
|
||||
func DeleteVersionsByPackageID(ctx context.Context, packageID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where(builder.Eq{"package_id": packageID}).Delete(&PackageVersion{})
|
||||
return err
|
||||
}
|
||||
|
||||
// HasVersionFileReferences checks if there are associated files
|
||||
func HasVersionFileReferences(ctx context.Context, versionID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).Get(&PackageFile{
|
||||
|
||||
Reference in New Issue
Block a user