aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorKN4CK3R <admin@oldschoolhack.me>2023-05-02 18:31:35 +0200
committerGitHub <noreply@github.com>2023-05-02 12:31:35 -0400
commitbf999e406994ab34420fb62e0de7948c8c2116c1 (patch)
tree016bd5a5469fe5914ebbf60949e0b1192d50c30e /models
parent1f52560ca466a780638543d4cd3513f892bf7522 (diff)
downloadgitea-bf999e406994ab34420fb62e0de7948c8c2116c1.tar.gz
gitea-bf999e406994ab34420fb62e0de7948c8c2116c1.zip
Add Debian package registry (#24426)
Co-authored-by: @awkwardbunny This PR adds a Debian package registry. You can follow [this tutorial](https://www.baeldung.com/linux/create-debian-package) to build a *.deb package for testing. Source packages are not supported at the moment and I did not find documentation of the architecture "all" and how these packages should be treated. ![grafik](https://user-images.githubusercontent.com/1666336/218126879-eb80a866-775c-4c8e-8529-5797203a64e6.png) Part of #20751. Revised copy of #22854. --------- Co-authored-by: Brian Hong <brian@hongs.me> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Giteabot <teabot@gitea.io>
Diffstat (limited to 'models')
-rw-r--r--models/migrations/migrations.go2
-rw-r--r--models/migrations/v1_20/v256.go23
-rw-r--r--models/packages/container/search.go11
-rw-r--r--models/packages/debian/search.go131
-rw-r--r--models/packages/descriptor.go26
-rw-r--r--models/packages/package.go19
-rw-r--r--models/packages/package_file.go36
-rw-r--r--models/packages/package_version.go6
-rw-r--r--models/user/setting.go5
9 files changed, 228 insertions, 31 deletions
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 1f1f43796c..0e84ae9f0e 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -489,6 +489,8 @@ var migrations = []Migration{
NewMigration("Add ActionTaskOutput table", v1_20.AddActionTaskOutputTable),
// v255 -> v256
NewMigration("Add ArchivedUnix Column", v1_20.AddArchivedUnixToRepository),
+ // v256 -> v257
+ NewMigration("Add is_internal column to package", v1_20.AddIsInternalColumnToPackage),
}
// GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go
new file mode 100644
index 0000000000..822153b93e
--- /dev/null
+++ b/models/migrations/v1_20/v256.go
@@ -0,0 +1,23 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_20 //nolint
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddIsInternalColumnToPackage(x *xorm.Engine) error {
+ type Package struct {
+ ID int64 `xorm:"pk autoincr"`
+ OwnerID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
+ RepoID int64 `xorm:"INDEX"`
+ Type string `xorm:"UNIQUE(s) INDEX NOT NULL"`
+ Name string `xorm:"NOT NULL"`
+ LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
+ SemverCompatible bool `xorm:"NOT NULL DEFAULT false"`
+ IsInternal bool `xorm:"NOT NULL DEFAULT false"`
+ }
+
+ return x.Sync(new(Package))
+}
diff --git a/models/packages/container/search.go b/models/packages/container/search.go
index b65c8634d6..0d3664d384 100644
--- a/models/packages/container/search.go
+++ b/models/packages/container/search.go
@@ -101,16 +101,7 @@ func getContainerBlobsLimit(ctx context.Context, opts *BlobSearchOptions, limit
return nil, err
}
- pfds := make([]*packages.PackageFileDescriptor, 0, len(pfs))
- for _, pf := range pfs {
- pfd, err := packages.GetPackageFileDescriptor(ctx, pf)
- if err != nil {
- return nil, err
- }
- pfds = append(pfds, pfd)
- }
-
- return pfds, nil
+ return packages.GetPackageFileDescriptors(ctx, pfs)
}
// GetManifestVersions gets all package versions representing the matching manifest
diff --git a/models/packages/debian/search.go b/models/packages/debian/search.go
new file mode 100644
index 0000000000..332a4f7040
--- /dev/null
+++ b/models/packages/debian/search.go
@@ -0,0 +1,131 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package debian
+
+import (
+ "context"
+ "strconv"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/packages"
+ debian_module "code.gitea.io/gitea/modules/packages/debian"
+
+ "xorm.io/builder"
+)
+
+type PackageSearchOptions struct {
+ OwnerID int64
+ Distribution string
+ Component string
+ Architecture string
+}
+
+// SearchLatestPackages gets the latest packages matching the search options
+func SearchLatestPackages(ctx context.Context, opts *PackageSearchOptions) ([]*packages.PackageFileDescriptor, error) {
+ var cond builder.Cond = builder.Eq{
+ "package_file.is_lead": true,
+ "package.type": packages.TypeDebian,
+ "package.owner_id": opts.OwnerID,
+ "package.is_internal": false,
+ "package_version.is_internal": false,
+ }
+
+ props := make(map[string]string)
+ if opts.Distribution != "" {
+ props[debian_module.PropertyDistribution] = opts.Distribution
+ }
+ if opts.Component != "" {
+ props[debian_module.PropertyComponent] = opts.Component
+ }
+ if opts.Architecture != "" {
+ props[debian_module.PropertyArchitecture] = opts.Architecture
+ }
+
+ if len(props) > 0 {
+ var propsCond builder.Cond = builder.Eq{
+ "package_property.ref_type": packages.PropertyTypeFile,
+ }
+ propsCond = propsCond.And(builder.Expr("package_property.ref_id = package_file.id"))
+
+ propsCondBlock := builder.NewCond()
+ for name, value := range props {
+ propsCondBlock = propsCondBlock.Or(builder.Eq{
+ "package_property.name": name,
+ "package_property.value": value,
+ })
+ }
+ propsCond = propsCond.And(propsCondBlock)
+
+ cond = cond.And(builder.Eq{
+ strconv.Itoa(len(props)): builder.Select("COUNT(*)").Where(propsCond).From("package_property"),
+ })
+ }
+
+ cond = cond.
+ And(builder.Expr("pv2.id IS NULL"))
+
+ joinCond := builder.
+ Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))").
+ And(builder.Eq{"pv2.is_internal": false})
+
+ pfs := make([]*packages.PackageFile, 0, 10)
+ err := db.GetEngine(ctx).
+ Table("package_file").
+ Select("package_file.*").
+ Join("INNER", "package_version", "package_version.id = package_file.version_id").
+ Join("LEFT", "package_version pv2", joinCond).
+ Join("INNER", "package", "package.id = package_version.package_id").
+ Where(cond).
+ Desc("package_version.created_unix").
+ Find(&pfs)
+ if err != nil {
+ return nil, err
+ }
+
+ return packages.GetPackageFileDescriptors(ctx, pfs)
+}
+
+// GetDistributions gets all available distributions
+func GetDistributions(ctx context.Context, ownerID int64) ([]string, error) {
+ return getDistinctPropertyValues(ctx, ownerID, "", debian_module.PropertyDistribution)
+}
+
+// GetComponents gets all available components for the given distribution
+func GetComponents(ctx context.Context, ownerID int64, distribution string) ([]string, error) {
+ return getDistinctPropertyValues(ctx, ownerID, distribution, debian_module.PropertyComponent)
+}
+
+// GetArchitectures gets all available architectures for the given distribution
+func GetArchitectures(ctx context.Context, ownerID int64, distribution string) ([]string, error) {
+ return getDistinctPropertyValues(ctx, ownerID, distribution, debian_module.PropertyArchitecture)
+}
+
+func getDistinctPropertyValues(ctx context.Context, ownerID int64, distribution, propName string) ([]string, error) {
+ var cond builder.Cond = builder.Eq{
+ "package_property.ref_type": packages.PropertyTypeFile,
+ "package_property.name": propName,
+ "package.type": packages.TypeDebian,
+ "package.owner_id": ownerID,
+ }
+ if distribution != "" {
+ innerCond := builder.
+ Expr("pp.ref_id = package_property.ref_id").
+ And(builder.Eq{
+ "pp.ref_type": packages.PropertyTypeFile,
+ "pp.name": debian_module.PropertyDistribution,
+ "pp.value": distribution,
+ })
+ cond = cond.And(builder.Exists(builder.Select("pp.ref_id").From("package_property pp").Where(innerCond)))
+ }
+
+ values := make([]string, 0, 5)
+ return values, db.GetEngine(ctx).
+ Table("package_property").
+ Distinct("package_property.value").
+ Join("INNER", "package_file", "package_file.id = package_property.ref_id").
+ Join("INNER", "package_version", "package_version.id = package_file.version_id").
+ Join("INNER", "package", "package.id = package_version.package_id").
+ Where(cond).
+ Find(&values)
+}
diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go
index 974c5b2c36..9256dd5630 100644
--- a/models/packages/descriptor.go
+++ b/models/packages/descriptor.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/packages/conan"
"code.gitea.io/gitea/modules/packages/conda"
"code.gitea.io/gitea/modules/packages/container"
+ "code.gitea.io/gitea/modules/packages/debian"
"code.gitea.io/gitea/modules/packages/helm"
"code.gitea.io/gitea/modules/packages/maven"
"code.gitea.io/gitea/modules/packages/npm"
@@ -127,13 +128,9 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
return nil, err
}
- pfds := make([]*PackageFileDescriptor, 0, len(pfs))
- for _, pf := range pfs {
- pfd, err := GetPackageFileDescriptor(ctx, pf)
- if err != nil {
- return nil, err
- }
- pfds = append(pfds, pfd)
+ pfds, err := GetPackageFileDescriptors(ctx, pfs)
+ if err != nil {
+ return nil, err
}
var metadata interface{}
@@ -150,6 +147,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
metadata = &conda.VersionMetadata{}
case TypeContainer:
metadata = &container.Metadata{}
+ case TypeDebian:
+ metadata = &debian.Metadata{}
case TypeGeneric:
// generic packages have no metadata
case TypeHelm:
@@ -210,6 +209,19 @@ func GetPackageFileDescriptor(ctx context.Context, pf *PackageFile) (*PackageFil
}, nil
}
+// GetPackageFileDescriptors gets the package file descriptors for the package files
+func GetPackageFileDescriptors(ctx context.Context, pfs []*PackageFile) ([]*PackageFileDescriptor, error) {
+ pfds := make([]*PackageFileDescriptor, 0, len(pfs))
+ for _, pf := range pfs {
+ pfd, err := GetPackageFileDescriptor(ctx, pf)
+ if err != nil {
+ return nil, err
+ }
+ pfds = append(pfds, pfd)
+ }
+ return pfds, nil
+}
+
// GetPackageDescriptors gets the package descriptions for the versions
func GetPackageDescriptors(ctx context.Context, pvs []*PackageVersion) ([]*PackageDescriptor, error) {
pds := make([]*PackageDescriptor, 0, len(pvs))
diff --git a/models/packages/package.go b/models/packages/package.go
index ccc9257c31..cc860b9a90 100644
--- a/models/packages/package.go
+++ b/models/packages/package.go
@@ -36,6 +36,7 @@ const (
TypeConan Type = "conan"
TypeConda Type = "conda"
TypeContainer Type = "container"
+ TypeDebian Type = "debian"
TypeGeneric Type = "generic"
TypeHelm Type = "helm"
TypeMaven Type = "maven"
@@ -55,6 +56,7 @@ var TypeList = []Type{
TypeConan,
TypeConda,
TypeContainer,
+ TypeDebian,
TypeGeneric,
TypeHelm,
TypeMaven,
@@ -82,6 +84,8 @@ func (pt Type) Name() string {
return "Conda"
case TypeContainer:
return "Container"
+ case TypeDebian:
+ return "Debian"
case TypeGeneric:
return "Generic"
case TypeHelm:
@@ -121,6 +125,8 @@ func (pt Type) SVGName() string {
return "gitea-conda"
case TypeContainer:
return "octicon-container"
+ case TypeDebian:
+ return "gitea-debian"
case TypeGeneric:
return "octicon-package"
case TypeHelm:
@@ -154,6 +160,7 @@ type Package struct {
Name string `xorm:"NOT NULL"`
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
SemverCompatible bool `xorm:"NOT NULL DEFAULT false"`
+ IsInternal bool `xorm:"NOT NULL DEFAULT false"`
}
// TryInsertPackage inserts a package. If a package exists already, ErrDuplicatePackage is returned
@@ -214,9 +221,10 @@ func GetPackageByID(ctx context.Context, packageID int64) (*Package, error) {
// GetPackageByName gets a package by name
func GetPackageByName(ctx context.Context, ownerID int64, packageType Type, name string) (*Package, error) {
var cond builder.Cond = builder.Eq{
- "package.owner_id": ownerID,
- "package.type": packageType,
- "package.lower_name": strings.ToLower(name),
+ "package.owner_id": ownerID,
+ "package.type": packageType,
+ "package.lower_name": strings.ToLower(name),
+ "package.is_internal": false,
}
p := &Package{}
@@ -236,8 +244,9 @@ func GetPackageByName(ctx context.Context, ownerID int64, packageType Type, name
// GetPackagesByType gets all packages of a specific type
func GetPackagesByType(ctx context.Context, ownerID int64, packageType Type) ([]*Package, error) {
var cond builder.Cond = builder.Eq{
- "package.owner_id": ownerID,
- "package.type": packageType,
+ "package.owner_id": ownerID,
+ "package.type": packageType,
+ "package.is_internal": false,
}
ps := make([]*Package, 0, 10)
diff --git a/models/packages/package_file.go b/models/packages/package_file.go
index 97e7a0d407..28e2a0111a 100644
--- a/models/packages/package_file.go
+++ b/models/packages/package_file.go
@@ -117,13 +117,15 @@ func DeleteFileByID(ctx context.Context, fileID int64) error {
// PackageFileSearchOptions are options for SearchXXX methods
type PackageFileSearchOptions struct {
- OwnerID int64
- PackageType string
- VersionID int64
- Query string
- CompositeKey string
- Properties map[string]string
- OlderThan time.Duration
+ OwnerID int64
+ PackageType string
+ VersionID int64
+ Query string
+ CompositeKey string
+ Properties map[string]string
+ OlderThan time.Duration
+ HashAlgorithm string
+ Hash string
db.Paginator
}
@@ -182,6 +184,26 @@ func (opts *PackageFileSearchOptions) toConds() builder.Cond {
cond = cond.And(builder.Lt{"package_file.created_unix": time.Now().Add(-opts.OlderThan).Unix()})
}
+ if opts.Hash != "" {
+ var field string
+ switch strings.ToLower(opts.HashAlgorithm) {
+ case "md5":
+ field = "package_blob.hash_md5"
+ case "sha1":
+ field = "package_blob.hash_sha1"
+ case "sha256":
+ field = "package_blob.hash_sha256"
+ case "sha512":
+ fallthrough
+ default: // default to SHA512 if not specified or unknown
+ field = "package_blob.hash_sha512"
+ }
+ innerCond := builder.
+ Expr("package_blob.id = package_file.blob_id").
+ And(builder.Eq{field: opts.Hash})
+ cond = cond.And(builder.Exists(builder.Select("package_blob.id").From("package_blob").Where(innerCond)))
+ }
+
return cond
}
diff --git a/models/packages/package_version.go b/models/packages/package_version.go
index 759c20abed..ab1bcddae5 100644
--- a/models/packages/package_version.go
+++ b/models/packages/package_version.go
@@ -173,7 +173,7 @@ const (
)
// PackageSearchOptions are options for SearchXXX methods
-// Besides IsInternal are all fields optional and are not used if they have their default value (nil, "", 0)
+// All fields optional and are not used if they have their default value (nil, "", 0)
type PackageSearchOptions struct {
OwnerID int64
RepoID int64
@@ -192,7 +192,9 @@ type PackageSearchOptions struct {
func (opts *PackageSearchOptions) toConds() builder.Cond {
cond := builder.NewCond()
if !opts.IsInternal.IsNone() {
- cond = builder.Eq{"package_version.is_internal": opts.IsInternal.IsTrue()}
+ cond = builder.Eq{
+ "package_version.is_internal": opts.IsInternal.IsTrue(),
+ }
}
if opts.OwnerID != 0 {
diff --git a/models/user/setting.go b/models/user/setting.go
index aec79b756b..a41e494db9 100644
--- a/models/user/setting.go
+++ b/models/user/setting.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/cache"
setting_module "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -42,6 +43,10 @@ func (err ErrUserSettingIsNotExist) Error() string {
return fmt.Sprintf("Setting[%s] is not exist", err.Key)
}
+func (err ErrUserSettingIsNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// IsErrUserSettingIsNotExist return true if err is ErrSettingIsNotExist
func IsErrUserSettingIsNotExist(err error) bool {
_, ok := err.(ErrUserSettingIsNotExist)