2022-03-30 10:42:47 +02:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2022-03-30 10:42:47 +02:00
package packages
import (
"context"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
2022-12-31 12:49:37 +01:00
"code.gitea.io/gitea/modules/util"
2022-03-30 10:42:47 +02:00
"xorm.io/builder"
)
func init ( ) {
db . RegisterModel ( new ( Package ) )
}
var (
// ErrDuplicatePackage indicates a duplicated package error
2022-12-31 12:49:37 +01:00
ErrDuplicatePackage = util . NewAlreadyExistErrorf ( "package already exists" )
2022-03-30 10:42:47 +02:00
// ErrPackageNotExist indicates a package not exist error
2022-12-31 12:49:37 +01:00
ErrPackageNotExist = util . NewNotExistErrorf ( "package does not exist" )
2022-03-30 10:42:47 +02:00
)
// Type of a package
type Type string
// List of supported packages
const (
TypeComposer Type = "composer"
TypeConan Type = "conan"
TypeContainer Type = "container"
TypeGeneric Type = "generic"
2022-04-19 18:55:35 +02:00
TypeHelm Type = "helm"
2022-03-30 10:42:47 +02:00
TypeMaven Type = "maven"
2022-04-19 18:55:35 +02:00
TypeNpm Type = "npm"
TypeNuGet Type = "nuget"
2022-08-07 12:09:54 +02:00
TypePub Type = "pub"
2022-03-30 10:42:47 +02:00
TypePyPI Type = "pypi"
TypeRubyGems Type = "rubygems"
2022-08-29 09:04:45 +02:00
TypeVagrant Type = "vagrant"
2022-03-30 10:42:47 +02:00
)
2022-11-20 15:08:38 +01:00
var TypeList = [ ] Type {
TypeComposer ,
TypeConan ,
TypeContainer ,
TypeGeneric ,
TypeHelm ,
TypeMaven ,
TypeNpm ,
TypeNuGet ,
TypePub ,
TypePyPI ,
TypeRubyGems ,
TypeVagrant ,
}
2022-03-30 10:42:47 +02:00
// Name gets the name of the package type
func ( pt Type ) Name ( ) string {
switch pt {
case TypeComposer :
return "Composer"
case TypeConan :
return "Conan"
case TypeContainer :
return "Container"
case TypeGeneric :
return "Generic"
2022-04-19 18:55:35 +02:00
case TypeHelm :
return "Helm"
2022-03-30 10:42:47 +02:00
case TypeMaven :
return "Maven"
2022-04-19 18:55:35 +02:00
case TypeNpm :
return "npm"
case TypeNuGet :
return "NuGet"
2022-08-07 12:09:54 +02:00
case TypePub :
return "Pub"
2022-03-30 10:42:47 +02:00
case TypePyPI :
return "PyPI"
case TypeRubyGems :
return "RubyGems"
2022-08-29 09:04:45 +02:00
case TypeVagrant :
return "Vagrant"
2022-03-30 10:42:47 +02: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 {
case TypeComposer :
return "gitea-composer"
case TypeConan :
return "gitea-conan"
case TypeContainer :
return "octicon-container"
case TypeGeneric :
return "octicon-package"
2022-04-19 18:55:35 +02:00
case TypeHelm :
return "gitea-helm"
2022-03-30 10:42:47 +02:00
case TypeMaven :
return "gitea-maven"
2022-04-19 18:55:35 +02:00
case TypeNpm :
return "gitea-npm"
case TypeNuGet :
return "gitea-nuget"
2022-08-07 12:09:54 +02:00
case TypePub :
return "gitea-pub"
2022-03-30 10:42:47 +02:00
case TypePyPI :
return "gitea-python"
case TypeRubyGems :
return "gitea-rubygems"
2022-08-29 09:04:45 +02:00
case TypeVagrant :
return "gitea-vagrant"
2022-03-30 10:42:47 +02: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" `
}
// 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 )
key := & Package {
OwnerID : p . OwnerID ,
Type : p . Type ,
LowerName : p . LowerName ,
}
has , err := e . Get ( key )
if err != nil {
return nil , err
}
if has {
return key , ErrDuplicatePackage
}
if _ , err = e . Insert ( p ) ; err != nil {
return nil , err
}
return p , nil
}
2022-07-28 05:59:39 +02: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 10:42:47 +02: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 {
"package.owner_id" : ownerID ,
"package.type" : packageType ,
"package.lower_name" : strings . ToLower ( name ) ,
}
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 {
"package.owner_id" : ownerID ,
"package.type" : packageType ,
}
ps := make ( [ ] * Package , 0 , 10 )
return ps , db . GetEngine ( ctx ) .
Where ( cond ) .
Find ( & ps )
}
2022-07-28 05:59:39 +02:00
// FindUnreferencedPackages gets all packages without associated versions
func FindUnreferencedPackages ( ctx context . Context ) ( [ ] * Package , error ) {
2022-03-30 10:42:47 +02:00
in := builder .
2022-04-06 03:32:09 +02:00
Select ( "package.id" ) .
2022-03-30 10:42:47 +02:00
From ( "package" ) .
2022-04-06 03:32:09 +02:00
LeftJoin ( "package_version" , "package_version.package_id = package.id" ) .
2022-03-30 10:42:47 +02:00
Where ( builder . Expr ( "package_version.id IS NULL" ) )
2022-07-28 05:59:39 +02:00
ps := make ( [ ] * Package , 0 , 10 )
return ps , db . GetEngine ( ctx ) .
2022-04-06 03:32:09 +02: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 05:59:39 +02:00
Find ( & ps )
2022-03-30 10:42:47 +02:00
}
2022-09-03 18:01:00 +02:00
// HasOwnerPackages tests if a user/org has accessible packages
2022-03-30 10:42:47 +02:00
func HasOwnerPackages ( ctx context . Context , ownerID int64 ) ( bool , error ) {
2022-09-03 18:01:00 +02: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 10:42:47 +02: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 { } )
}