2022-03-30 11:42:47 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2022-03-30 11:42:47 +03:00
package packages
import (
"context"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
2022-12-31 14:49:37 +03:00
"code.gitea.io/gitea/modules/util"
2022-03-30 11:42:47 +03:00
"xorm.io/builder"
)
func init ( ) {
db . RegisterModel ( new ( Package ) )
}
var (
// ErrDuplicatePackage indicates a duplicated package error
2022-12-31 14:49:37 +03:00
ErrDuplicatePackage = util . NewAlreadyExistErrorf ( "package already exists" )
2022-03-30 11:42:47 +03:00
// ErrPackageNotExist indicates a package not exist error
2022-12-31 14:49:37 +03:00
ErrPackageNotExist = util . NewNotExistErrorf ( "package does not exist" )
2022-03-30 11:42:47 +03:00
)
// Type of a package
type Type string
// List of supported packages
const (
2023-05-12 20:27:50 +03:00
TypeAlpine Type = "alpine"
2023-02-05 13:12:31 +03:00
TypeCargo Type = "cargo"
2023-02-06 04:49:21 +03:00
TypeChef Type = "chef"
2022-03-30 11:42:47 +03:00
TypeComposer Type = "composer"
TypeConan Type = "conan"
2023-02-01 21:30:39 +03:00
TypeConda Type = "conda"
2022-03-30 11:42:47 +03:00
TypeContainer Type = "container"
2023-05-22 05:57:49 +03:00
TypeCran Type = "cran"
2023-05-02 19:31:35 +03:00
TypeDebian Type = "debian"
2022-03-30 11:42:47 +03:00
TypeGeneric Type = "generic"
2023-05-14 18:38:40 +03:00
TypeGo Type = "go"
2022-04-19 19:55:35 +03:00
TypeHelm Type = "helm"
2022-03-30 11:42:47 +03:00
TypeMaven Type = "maven"
2022-04-19 19:55:35 +03:00
TypeNpm Type = "npm"
TypeNuGet Type = "nuget"
2022-08-07 13:09:54 +03:00
TypePub Type = "pub"
2022-03-30 11:42:47 +03:00
TypePyPI Type = "pypi"
2023-05-05 23:33:37 +03:00
TypeRpm Type = "rpm"
2022-03-30 11:42:47 +03:00
TypeRubyGems Type = "rubygems"
2023-03-13 23:28:39 +03:00
TypeSwift Type = "swift"
2022-08-29 10:04:45 +03:00
TypeVagrant Type = "vagrant"
2022-03-30 11:42:47 +03:00
)
2022-11-20 17:08:38 +03:00
var TypeList = [ ] Type {
2023-05-12 20:27:50 +03:00
TypeAlpine ,
2023-02-05 13:12:31 +03:00
TypeCargo ,
2023-02-06 04:49:21 +03:00
TypeChef ,
2022-11-20 17:08:38 +03:00
TypeComposer ,
TypeConan ,
2023-02-01 21:30:39 +03:00
TypeConda ,
2022-11-20 17:08:38 +03:00
TypeContainer ,
2023-05-22 05:57:49 +03:00
TypeCran ,
2023-05-02 19:31:35 +03:00
TypeDebian ,
2022-11-20 17:08:38 +03:00
TypeGeneric ,
2023-05-14 18:38:40 +03:00
TypeGo ,
2022-11-20 17:08:38 +03:00
TypeHelm ,
TypeMaven ,
TypeNpm ,
TypeNuGet ,
TypePub ,
TypePyPI ,
2023-05-05 23:33:37 +03:00
TypeRpm ,
2022-11-20 17:08:38 +03:00
TypeRubyGems ,
2023-03-13 23:28:39 +03:00
TypeSwift ,
2022-11-20 17:08:38 +03:00
TypeVagrant ,
}
2022-03-30 11:42:47 +03:00
// Name gets the name of the package type
func ( pt Type ) Name ( ) string {
switch pt {
2023-05-12 20:27:50 +03:00
case TypeAlpine :
return "Alpine"
2023-02-05 13:12:31 +03:00
case TypeCargo :
return "Cargo"
2023-02-06 04:49:21 +03:00
case TypeChef :
return "Chef"
2022-03-30 11:42:47 +03:00
case TypeComposer :
return "Composer"
case TypeConan :
return "Conan"
2023-02-01 21:30:39 +03:00
case TypeConda :
return "Conda"
2022-03-30 11:42:47 +03:00
case TypeContainer :
return "Container"
2023-05-22 05:57:49 +03:00
case TypeCran :
return "CRAN"
2023-05-02 19:31:35 +03:00
case TypeDebian :
return "Debian"
2022-03-30 11:42:47 +03:00
case TypeGeneric :
return "Generic"
2023-05-14 18:38:40 +03:00
case TypeGo :
return "Go"
2022-04-19 19:55:35 +03:00
case TypeHelm :
return "Helm"
2022-03-30 11:42:47 +03:00
case TypeMaven :
return "Maven"
2022-04-19 19:55:35 +03:00
case TypeNpm :
return "npm"
case TypeNuGet :
return "NuGet"
2022-08-07 13:09:54 +03:00
case TypePub :
return "Pub"
2022-03-30 11:42:47 +03:00
case TypePyPI :
return "PyPI"
2023-05-05 23:33:37 +03:00
case TypeRpm :
return "RPM"
2022-03-30 11:42:47 +03:00
case TypeRubyGems :
return "RubyGems"
2023-03-13 23:28:39 +03:00
case TypeSwift :
return "Swift"
2022-08-29 10:04:45 +03:00
case TypeVagrant :
return "Vagrant"
2022-03-30 11:42:47 +03:00
}
panic ( fmt . Sprintf ( "unknown package type: %s" , string ( pt ) ) )
}
// SVGName gets the name of the package type svg image
func ( pt Type ) SVGName ( ) string {
switch pt {
2023-05-12 20:27:50 +03:00
case TypeAlpine :
return "gitea-alpine"
2023-02-05 13:12:31 +03:00
case TypeCargo :
return "gitea-cargo"
2023-02-06 04:49:21 +03:00
case TypeChef :
return "gitea-chef"
2022-03-30 11:42:47 +03:00
case TypeComposer :
return "gitea-composer"
case TypeConan :
return "gitea-conan"
2023-02-01 21:30:39 +03:00
case TypeConda :
return "gitea-conda"
2022-03-30 11:42:47 +03:00
case TypeContainer :
return "octicon-container"
2023-05-22 05:57:49 +03:00
case TypeCran :
return "gitea-cran"
2023-05-02 19:31:35 +03:00
case TypeDebian :
return "gitea-debian"
2022-03-30 11:42:47 +03:00
case TypeGeneric :
return "octicon-package"
2023-05-14 18:38:40 +03:00
case TypeGo :
return "gitea-go"
2022-04-19 19:55:35 +03:00
case TypeHelm :
return "gitea-helm"
2022-03-30 11:42:47 +03:00
case TypeMaven :
return "gitea-maven"
2022-04-19 19:55:35 +03:00
case TypeNpm :
return "gitea-npm"
case TypeNuGet :
return "gitea-nuget"
2022-08-07 13:09:54 +03:00
case TypePub :
return "gitea-pub"
2022-03-30 11:42:47 +03:00
case TypePyPI :
return "gitea-python"
2023-05-05 23:33:37 +03:00
case TypeRpm :
return "gitea-rpm"
2022-03-30 11:42:47 +03:00
case TypeRubyGems :
return "gitea-rubygems"
2023-03-13 23:28:39 +03:00
case TypeSwift :
return "gitea-swift"
2022-08-29 10:04:45 +03:00
case TypeVagrant :
return "gitea-vagrant"
2022-03-30 11:42:47 +03:00
}
panic ( fmt . Sprintf ( "unknown package type: %s" , string ( pt ) ) )
}
// Package represents a package
type Package struct {
ID int64 ` xorm:"pk autoincr" `
OwnerID int64 ` xorm:"UNIQUE(s) INDEX NOT NULL" `
RepoID int64 ` xorm:"INDEX" `
Type Type ` 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" `
2023-05-02 19:31:35 +03:00
IsInternal bool ` xorm:"NOT NULL DEFAULT false" `
2022-03-30 11:42:47 +03:00
}
// TryInsertPackage inserts a package. If a package exists already, ErrDuplicatePackage is returned
func TryInsertPackage ( ctx context . Context , p * Package ) ( * Package , error ) {
e := db . GetEngine ( ctx )
2024-01-19 14:37:10 +03:00
existing := & Package { }
2022-03-30 11:42:47 +03:00
2024-01-19 14:37:10 +03:00
has , err := e . Where ( builder . Eq {
"owner_id" : p . OwnerID ,
"type" : p . Type ,
"lower_name" : p . LowerName ,
} ) . Get ( existing )
2022-03-30 11:42:47 +03:00
if err != nil {
return nil , err
}
if has {
2024-01-19 14:37:10 +03:00
return existing , ErrDuplicatePackage
2022-03-30 11:42:47 +03:00
}
if _ , err = e . Insert ( p ) ; err != nil {
return nil , err
}
return p , nil
}
2022-07-28 06:59:39 +03:00
// DeletePackageByID deletes a package by id
func DeletePackageByID ( ctx context . Context , packageID int64 ) error {
_ , err := db . GetEngine ( ctx ) . ID ( packageID ) . Delete ( & Package { } )
return err
}
2022-03-30 11:42:47 +03:00
// SetRepositoryLink sets the linked repository
func SetRepositoryLink ( ctx context . Context , packageID , repoID int64 ) error {
_ , err := db . GetEngine ( ctx ) . ID ( packageID ) . Cols ( "repo_id" ) . Update ( & Package { RepoID : repoID } )
return err
}
// UnlinkRepositoryFromAllPackages unlinks every package from the repository
func UnlinkRepositoryFromAllPackages ( ctx context . Context , repoID int64 ) error {
_ , err := db . GetEngine ( ctx ) . Where ( "repo_id = ?" , repoID ) . Cols ( "repo_id" ) . Update ( & Package { } )
return err
}
// GetPackageByID gets a package by id
func GetPackageByID ( ctx context . Context , packageID int64 ) ( * Package , error ) {
p := & Package { }
has , err := db . GetEngine ( ctx ) . ID ( packageID ) . Get ( p )
if err != nil {
return nil , err
}
if ! has {
return nil , ErrPackageNotExist
}
return p , nil
}
// 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 {
2023-05-02 19:31:35 +03:00
"package.owner_id" : ownerID ,
"package.type" : packageType ,
"package.lower_name" : strings . ToLower ( name ) ,
"package.is_internal" : false ,
2022-03-30 11:42:47 +03:00
}
p := & Package { }
has , err := db . GetEngine ( ctx ) .
Where ( cond ) .
Get ( p )
if err != nil {
return nil , err
}
if ! has {
return nil , ErrPackageNotExist
}
return p , nil
}
// 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 {
2023-05-02 19:31:35 +03:00
"package.owner_id" : ownerID ,
"package.type" : packageType ,
"package.is_internal" : false ,
2022-03-30 11:42:47 +03:00
}
ps := make ( [ ] * Package , 0 , 10 )
return ps , db . GetEngine ( ctx ) .
Where ( cond ) .
Find ( & ps )
}
2022-07-28 06:59:39 +03:00
// FindUnreferencedPackages gets all packages without associated versions
func FindUnreferencedPackages ( ctx context . Context ) ( [ ] * Package , error ) {
2022-03-30 11:42:47 +03:00
in := builder .
2022-04-06 04:32:09 +03:00
Select ( "package.id" ) .
2022-03-30 11:42:47 +03:00
From ( "package" ) .
2022-04-06 04:32:09 +03:00
LeftJoin ( "package_version" , "package_version.package_id = package.id" ) .
2022-03-30 11:42:47 +03:00
Where ( builder . Expr ( "package_version.id IS NULL" ) )
2022-07-28 06:59:39 +03:00
ps := make ( [ ] * Package , 0 , 10 )
return ps , db . GetEngine ( ctx ) .
2022-04-06 04:32:09 +03:00
// double select workaround for MySQL
// https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition
Where ( builder . In ( "package.id" , builder . Select ( "id" ) . From ( in , "temp" ) ) ) .
2022-07-28 06:59:39 +03:00
Find ( & ps )
2022-03-30 11:42:47 +03:00
}
2022-09-03 19:01:00 +03:00
// HasOwnerPackages tests if a user/org has accessible packages
2022-03-30 11:42:47 +03:00
func HasOwnerPackages ( ctx context . Context , ownerID int64 ) ( bool , error ) {
2022-09-03 19:01:00 +03:00
return db . GetEngine ( ctx ) .
Table ( "package_version" ) .
Join ( "INNER" , "package" , "package.id = package_version.package_id" ) .
Where ( builder . Eq {
"package_version.is_internal" : false ,
"package.owner_id" : ownerID ,
} ) .
Exist ( & PackageVersion { } )
2022-03-30 11:42:47 +03:00
}
// HasRepositoryPackages tests if a repository has packages
func HasRepositoryPackages ( ctx context . Context , repositoryID int64 ) ( bool , error ) {
return db . GetEngine ( ctx ) . Where ( "repo_id = ?" , repositoryID ) . Exist ( & Package { } )
}