2014-04-14 09:57:25 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-09-22 12:05:48 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2014-04-14 09:57:25 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
2015-11-20 10:38:41 +03:00
"fmt"
2014-06-13 01:47:23 +04:00
"sort"
2014-04-14 09:57:25 +04:00
"strings"
2016-12-31 19:51:22 +03:00
"code.gitea.io/gitea/modules/setting"
2019-10-14 09:10:42 +03:00
"code.gitea.io/gitea/modules/structs"
2019-08-15 17:46:21 +03:00
"code.gitea.io/gitea/modules/timeutil"
2019-03-27 12:33:00 +03:00
2019-06-23 18:22:43 +03:00
"xorm.io/builder"
2014-04-14 09:57:25 +04:00
)
// Release represents a release of repository.
type Release struct {
2017-01-06 04:51:15 +03:00
ID int64 ` xorm:"pk autoincr" `
2017-01-06 18:14:33 +03:00
RepoID int64 ` xorm:"INDEX UNIQUE(n)" `
2016-12-31 19:51:22 +03:00
Repo * Repository ` xorm:"-" `
2017-01-06 18:14:33 +03:00
PublisherID int64 ` xorm:"INDEX" `
Publisher * User ` xorm:"-" `
TagName string ` xorm:"INDEX UNIQUE(n)" `
2019-10-05 14:09:27 +03:00
OriginalAuthor string
OriginalAuthorID int64 ` xorm:"index" `
2014-04-14 09:57:25 +04:00
LowerTagName string
2014-06-12 17:10:39 +04:00
Target string
2014-06-13 01:47:23 +04:00
Title string
2014-06-12 17:10:39 +04:00
Sha1 string ` xorm:"VARCHAR(40)" `
2015-12-10 04:46:05 +03:00
NumCommits int64
2019-08-15 17:46:21 +03:00
NumCommitsBehind int64 ` xorm:"-" `
Note string ` xorm:"TEXT" `
2020-08-23 18:03:18 +03:00
RenderedNote string ` xorm:"-" `
2019-08-15 17:46:21 +03:00
IsDraft bool ` xorm:"NOT NULL DEFAULT false" `
IsPrerelease bool ` xorm:"NOT NULL DEFAULT false" `
IsTag bool ` xorm:"NOT NULL DEFAULT false" `
Attachments [ ] * Attachment ` xorm:"-" `
CreatedUnix timeutil . TimeStamp ` xorm:"INDEX" `
2015-08-24 16:01:23 +03:00
}
2016-12-31 19:51:22 +03:00
func ( r * Release ) loadAttributes ( e Engine ) error {
var err error
if r . Repo == nil {
r . Repo , err = GetRepositoryByID ( r . RepoID )
if err != nil {
return err
}
}
if r . Publisher == nil {
2019-06-12 22:41:28 +03:00
r . Publisher , err = getUserByID ( e , r . PublisherID )
2016-12-31 19:51:22 +03:00
if err != nil {
2020-11-03 02:10:22 +03:00
if IsErrUserNotExist ( err ) {
r . Publisher = NewGhostUser ( )
} else {
return err
}
2016-12-31 19:51:22 +03:00
}
}
2019-06-12 22:41:28 +03:00
return getReleaseAttachments ( e , r )
2016-12-31 19:51:22 +03:00
}
2017-03-15 03:52:01 +03:00
// LoadAttributes load repo and publisher attributes for a release
2016-12-31 19:51:22 +03:00
func ( r * Release ) LoadAttributes ( ) error {
return r . loadAttributes ( x )
}
// APIURL the api url for a release. release must have attributes loaded
func ( r * Release ) APIURL ( ) string {
2019-09-19 03:23:45 +03:00
return fmt . Sprintf ( "%sapi/v1/repos/%s/releases/%d" ,
2016-12-31 19:51:22 +03:00
setting . AppURL , r . Repo . FullName ( ) , r . ID )
}
// ZipURL the zip url for a release. release must have attributes loaded
func ( r * Release ) ZipURL ( ) string {
return fmt . Sprintf ( "%s/archive/%s.zip" , r . Repo . HTMLURL ( ) , r . TagName )
}
// TarURL the tar.gz url for a release. release must have attributes loaded
func ( r * Release ) TarURL ( ) string {
return fmt . Sprintf ( "%s/archive/%s.tar.gz" , r . Repo . HTMLURL ( ) , r . TagName )
}
2020-04-18 17:47:15 +03:00
// HTMLURL the url for a release on the web UI. release must have attributes loaded
func ( r * Release ) HTMLURL ( ) string {
return fmt . Sprintf ( "%s/releases/tag/%s" , r . Repo . HTMLURL ( ) , r . TagName )
}
2014-04-14 09:57:25 +04:00
// IsReleaseExist returns true if release with given tag name already exists.
2015-11-16 07:52:46 +03:00
func IsReleaseExist ( repoID int64 , tagName string ) ( bool , error ) {
2014-04-14 09:57:25 +04:00
if len ( tagName ) == 0 {
return false , nil
}
2015-11-16 07:52:46 +03:00
return x . Get ( & Release { RepoID : repoID , LowerTagName : strings . ToLower ( tagName ) } )
2014-04-14 09:57:25 +04:00
}
2019-09-15 18:28:25 +03:00
// InsertRelease inserts a release
func InsertRelease ( rel * Release ) error {
_ , err := x . Insert ( rel )
return err
}
2014-06-13 01:47:23 +04:00
2020-02-03 11:47:04 +03:00
// InsertReleasesContext insert releases
func InsertReleasesContext ( ctx DBContext , rels [ ] * Release ) error {
_ , err := ctx . e . Insert ( rels )
return err
}
2019-09-15 18:28:25 +03:00
// UpdateRelease updates all columns of a release
2020-02-03 11:47:04 +03:00
func UpdateRelease ( ctx DBContext , rel * Release ) error {
_ , err := ctx . e . ID ( rel . ID ) . AllCols ( ) . Update ( rel )
2019-09-15 18:28:25 +03:00
return err
2014-06-13 01:47:23 +04:00
}
2019-09-15 18:28:25 +03:00
// AddReleaseAttachments adds a release attachments
func AddReleaseAttachments ( releaseID int64 , attachmentUUIDs [ ] string ) ( err error ) {
2017-01-15 17:57:00 +03:00
// Check attachments
2019-12-11 03:01:52 +03:00
attachments , err := GetAttachmentsByUUIDs ( attachmentUUIDs )
if err != nil {
return fmt . Errorf ( "GetAttachmentsByUUIDs [uuids: %v]: %v" , attachmentUUIDs , err )
2017-01-15 17:57:00 +03:00
}
for i := range attachments {
attachments [ i ] . ReleaseID = releaseID
// No assign value could be 0, so ignore AllCols().
2017-10-05 07:43:04 +03:00
if _ , err = x . ID ( attachments [ i ] . ID ) . Update ( attachments [ i ] ) ; err != nil {
2017-01-15 17:57:00 +03:00
return fmt . Errorf ( "update attachment [%d]: %v" , attachments [ i ] . ID , err )
}
}
return
}
2014-06-13 01:47:23 +04:00
// GetRelease returns release by given ID.
2015-11-16 07:52:46 +03:00
func GetRelease ( repoID int64 , tagName string ) ( * Release , error ) {
isExist , err := IsReleaseExist ( repoID , tagName )
2014-06-13 01:47:23 +04:00
if err != nil {
return nil , err
} else if ! isExist {
2015-11-20 10:38:41 +03:00
return nil , ErrReleaseNotExist { 0 , tagName }
2014-06-13 01:47:23 +04:00
}
2014-04-14 09:57:25 +04:00
2015-11-16 07:52:46 +03:00
rel := & Release { RepoID : repoID , LowerTagName : strings . ToLower ( tagName ) }
2014-06-21 08:51:41 +04:00
_ , err = x . Get ( rel )
2014-06-13 01:47:23 +04:00
return rel , err
}
2015-11-20 10:38:41 +03:00
// GetReleaseByID returns release with given ID.
func GetReleaseByID ( id int64 ) ( * Release , error ) {
rel := new ( Release )
2016-11-10 18:16:32 +03:00
has , err := x .
2017-10-05 07:43:04 +03:00
ID ( id ) .
2016-11-10 18:16:32 +03:00
Get ( rel )
2015-11-20 10:38:41 +03:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrReleaseNotExist { id , "" }
}
return rel , nil
}
2017-06-29 18:11:38 +03:00
// FindReleasesOptions describes the conditions to Find releases
type FindReleasesOptions struct {
2020-01-24 22:00:29 +03:00
ListOptions
2017-06-29 18:11:38 +03:00
IncludeDrafts bool
2017-09-20 08:26:49 +03:00
IncludeTags bool
2017-06-29 18:11:38 +03:00
TagNames [ ] string
2014-06-13 01:47:23 +04:00
}
2017-06-29 18:11:38 +03:00
func ( opts * FindReleasesOptions ) toConds ( repoID int64 ) builder . Cond {
2017-06-28 17:47:00 +03:00
var cond = builder . NewCond ( )
cond = cond . And ( builder . Eq { "repo_id" : repoID } )
2017-06-29 18:11:38 +03:00
if ! opts . IncludeDrafts {
cond = cond . And ( builder . Eq { "is_draft" : false } )
2017-06-28 17:47:00 +03:00
}
2017-09-20 08:26:49 +03:00
if ! opts . IncludeTags {
cond = cond . And ( builder . Eq { "is_tag" : false } )
}
2017-06-29 18:11:38 +03:00
if len ( opts . TagNames ) > 0 {
cond = cond . And ( builder . In ( "tag_name" , opts . TagNames ) )
}
return cond
2017-06-28 17:47:00 +03:00
}
2017-06-29 18:11:38 +03:00
// GetReleasesByRepoID returns a list of releases of repository.
2020-01-24 22:00:29 +03:00
func GetReleasesByRepoID ( repoID int64 , opts FindReleasesOptions ) ( [ ] * Release , error ) {
sess := x .
Desc ( "created_unix" , "id" ) .
Where ( opts . toConds ( repoID ) )
if opts . PageSize != 0 {
sess = opts . setSessionPagination ( sess )
2017-06-29 18:11:38 +03:00
}
2020-01-24 22:00:29 +03:00
rels := make ( [ ] * Release , 0 , opts . PageSize )
return rels , sess . Find ( & rels )
2017-01-06 04:51:15 +03:00
}
2020-04-18 17:47:15 +03:00
// GetLatestReleaseByRepoID returns the latest release for a repository
func GetLatestReleaseByRepoID ( repoID int64 ) ( * Release , error ) {
cond := builder . NewCond ( ) .
And ( builder . Eq { "repo_id" : repoID } ) .
And ( builder . Eq { "is_draft" : false } ) .
And ( builder . Eq { "is_prerelease" : false } ) .
And ( builder . Eq { "is_tag" : false } )
rel := new ( Release )
has , err := x .
Desc ( "created_unix" , "id" ) .
Where ( cond ) .
Get ( rel )
if err != nil {
return nil , err
} else if ! has {
return nil , ErrReleaseNotExist { 0 , "latest" }
}
return rel , nil
}
2019-01-07 01:37:30 +03:00
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
2020-02-03 11:47:04 +03:00
func GetReleasesByRepoIDAndNames ( ctx DBContext , repoID int64 , tagNames [ ] string ) ( rels [ ] * Release , err error ) {
err = ctx . e .
2019-01-07 01:37:30 +03:00
In ( "tag_name" , tagNames ) .
2020-02-03 11:47:04 +03:00
Desc ( "created_unix" ) .
2019-01-07 01:37:30 +03:00
Find ( & rels , Release { RepoID : repoID } )
return rels , err
}
2017-06-29 18:11:38 +03:00
// GetReleaseCountByRepoID returns the count of releases of repository
func GetReleaseCountByRepoID ( repoID int64 , opts FindReleasesOptions ) ( int64 , error ) {
return x . Where ( opts . toConds ( repoID ) ) . Count ( & Release { } )
}
2017-01-15 17:57:00 +03:00
type releaseMetaSearch struct {
2017-01-17 08:58:58 +03:00
ID [ ] int64
Rel [ ] * Release
2017-01-15 17:57:00 +03:00
}
2017-01-17 08:58:58 +03:00
2017-01-15 17:57:00 +03:00
func ( s releaseMetaSearch ) Len ( ) int {
return len ( s . ID )
}
func ( s releaseMetaSearch ) Swap ( i , j int ) {
s . ID [ i ] , s . ID [ j ] = s . ID [ j ] , s . ID [ i ]
s . Rel [ i ] , s . Rel [ j ] = s . Rel [ j ] , s . Rel [ i ]
}
func ( s releaseMetaSearch ) Less ( i , j int ) bool {
return s . ID [ i ] < s . ID [ j ]
}
// GetReleaseAttachments retrieves the attachments for releases
2017-01-17 08:58:58 +03:00
func GetReleaseAttachments ( rels ... * Release ) ( err error ) {
2019-06-12 22:41:28 +03:00
return getReleaseAttachments ( x , rels ... )
}
func getReleaseAttachments ( e Engine , rels ... * Release ) ( err error ) {
2017-01-15 17:57:00 +03:00
if len ( rels ) == 0 {
return
}
2017-01-17 08:58:58 +03:00
// To keep this efficient as possible sort all releases by id,
2017-01-15 17:57:00 +03:00
// select attachments by release id,
// then merge join them
// Sort
var sortedRels = releaseMetaSearch { ID : make ( [ ] int64 , len ( rels ) ) , Rel : make ( [ ] * Release , len ( rels ) ) }
2017-01-17 08:58:58 +03:00
var attachments [ ] * Attachment
2017-01-15 17:57:00 +03:00
for index , element := range rels {
element . Attachments = [ ] * Attachment { }
sortedRels . ID [ index ] = element . ID
sortedRels . Rel [ index ] = element
}
sort . Sort ( sortedRels )
// Select attachments
2019-06-12 22:41:28 +03:00
err = e .
2017-01-15 17:57:00 +03:00
Asc ( "release_id" ) .
In ( "release_id" , sortedRels . ID ) .
Find ( & attachments , Attachment { } )
if err != nil {
return err
}
// merge join
var currentIndex = 0
for _ , attachment := range attachments {
for sortedRels . ID [ currentIndex ] < attachment . ReleaseID {
currentIndex ++
}
sortedRels . Rel [ currentIndex ] . Attachments = append ( sortedRels . Rel [ currentIndex ] . Attachments , attachment )
}
return
}
2016-11-25 11:11:12 +03:00
type releaseSorter struct {
2014-06-13 01:47:23 +04:00
rels [ ] * Release
}
2016-11-25 11:11:12 +03:00
func ( rs * releaseSorter ) Len ( ) int {
2014-06-13 01:47:23 +04:00
return len ( rs . rels )
}
2016-11-25 11:11:12 +03:00
func ( rs * releaseSorter ) Less ( i , j int ) bool {
2014-06-13 01:47:23 +04:00
diffNum := rs . rels [ i ] . NumCommits - rs . rels [ j ] . NumCommits
if diffNum != 0 {
return diffNum > 0
2014-04-14 09:57:25 +04:00
}
2017-12-11 07:37:04 +03:00
return rs . rels [ i ] . CreatedUnix > rs . rels [ j ] . CreatedUnix
2014-06-13 01:47:23 +04:00
}
2014-04-14 09:57:25 +04:00
2016-11-25 11:11:12 +03:00
func ( rs * releaseSorter ) Swap ( i , j int ) {
2014-06-13 01:47:23 +04:00
rs . rels [ i ] , rs . rels [ j ] = rs . rels [ j ] , rs . rels [ i ]
}
// SortReleases sorts releases by number of commits and created time.
func SortReleases ( rels [ ] * Release ) {
2016-11-25 11:11:12 +03:00
sorter := & releaseSorter { rels : rels }
2014-06-13 01:47:23 +04:00
sort . Sort ( sorter )
}
2019-09-30 19:10:00 +03:00
// DeleteReleaseByID deletes a release from database by given ID.
func DeleteReleaseByID ( id int64 ) error {
_ , err := x . ID ( id ) . Delete ( new ( Release ) )
return err
2015-11-20 10:38:41 +03:00
}
2017-09-20 08:26:49 +03:00
2019-10-14 09:10:42 +03:00
// UpdateReleasesMigrationsByType updates all migrated repositories' releases from gitServiceType to replace originalAuthorID to posterID
2019-10-17 05:06:28 +03:00
func UpdateReleasesMigrationsByType ( gitServiceType structs . GitServiceType , originalAuthorID string , posterID int64 ) error {
2019-10-14 09:10:42 +03:00
_ , err := x . Table ( "release" ) .
Where ( "repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)" , gitServiceType ) .
And ( "original_author_id = ?" , originalAuthorID ) .
Update ( map [ string ] interface { } {
"publisher_id" : posterID ,
"original_author" : "" ,
"original_author_id" : 0 ,
} )
return err
}