2014-04-14 01:57:25 -04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-09-22 10:05:48 +01:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2014-04-14 01: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 (
2021-09-23 16:45:36 +01:00
"context"
2021-03-23 00:09:51 +08:00
"errors"
2015-11-20 02:38:41 -05:00
"fmt"
2014-06-12 17:47:23 -04:00
"sort"
2021-11-16 18:18:25 +00:00
"strconv"
2014-04-14 01:57:25 -04:00
"strings"
2021-09-19 19:49:59 +08:00
"code.gitea.io/gitea/models/db"
2021-11-19 21:39:57 +08:00
repo_model "code.gitea.io/gitea/models/repo"
2019-10-14 14:10:42 +08:00
"code.gitea.io/gitea/modules/structs"
2019-08-15 22:46:21 +08:00
"code.gitea.io/gitea/modules/timeutil"
2021-06-17 10:58:10 +02:00
"code.gitea.io/gitea/modules/util"
2019-03-27 17:33:00 +08:00
2019-06-23 23:22:43 +08:00
"xorm.io/builder"
2014-04-14 01:57:25 -04:00
)
// Release represents a release of repository.
type Release struct {
2017-01-06 09:51:15 +08:00
ID int64 ` xorm:"pk autoincr" `
2017-01-06 13:14:33 -02:00
RepoID int64 ` xorm:"INDEX UNIQUE(n)" `
2016-12-31 11:51:22 -05:00
Repo * Repository ` xorm:"-" `
2017-01-06 13:14:33 -02:00
PublisherID int64 ` xorm:"INDEX" `
Publisher * User ` xorm:"-" `
TagName string ` xorm:"INDEX UNIQUE(n)" `
2019-10-05 19:09:27 +08:00
OriginalAuthor string
OriginalAuthorID int64 ` xorm:"index" `
2014-04-14 01:57:25 -04:00
LowerTagName string
2014-06-12 09:10:39 -04:00
Target string
2014-06-12 17:47:23 -04:00
Title string
2014-06-12 09:10:39 -04:00
Sha1 string ` xorm:"VARCHAR(40)" `
2015-12-09 20:46:05 -05:00
NumCommits int64
2021-11-19 21:39:57 +08:00
NumCommitsBehind int64 ` xorm:"-" `
Note string ` xorm:"TEXT" `
RenderedNote string ` xorm:"-" `
IsDraft bool ` xorm:"NOT NULL DEFAULT false" `
IsPrerelease bool ` xorm:"NOT NULL DEFAULT false" `
IsTag bool ` xorm:"NOT NULL DEFAULT false" `
Attachments [ ] * repo_model . Attachment ` xorm:"-" `
CreatedUnix timeutil . TimeStamp ` xorm:"INDEX" `
2015-08-24 21:01:23 +08:00
}
2021-09-19 19:49:59 +08:00
func init ( ) {
db . RegisterModel ( new ( Release ) )
}
func ( r * Release ) loadAttributes ( e db . Engine ) error {
2016-12-31 11:51:22 -05:00
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 21:41:28 +02:00
r . Publisher , err = getUserByID ( e , r . PublisherID )
2016-12-31 11:51:22 -05:00
if err != nil {
2020-11-03 07:10:22 +08:00
if IsErrUserNotExist ( err ) {
r . Publisher = NewGhostUser ( )
} else {
return err
}
2016-12-31 11:51:22 -05:00
}
}
2019-06-12 21:41:28 +02:00
return getReleaseAttachments ( e , r )
2016-12-31 11:51:22 -05:00
}
2017-03-14 20:52:01 -04:00
// LoadAttributes load repo and publisher attributes for a release
2016-12-31 11:51:22 -05:00
func ( r * Release ) LoadAttributes ( ) error {
2021-09-23 16:45:36 +01:00
return r . loadAttributes ( db . GetEngine ( db . DefaultContext ) )
2016-12-31 11:51:22 -05:00
}
// APIURL the api url for a release. release must have attributes loaded
func ( r * Release ) APIURL ( ) string {
2021-11-16 18:18:25 +00:00
return r . Repo . APIURL ( ) + "/releases/" + strconv . FormatInt ( r . ID , 10 )
2016-12-31 11:51:22 -05:00
}
// ZipURL the zip url for a release. release must have attributes loaded
func ( r * Release ) ZipURL ( ) string {
2021-11-16 18:18:25 +00:00
return r . Repo . HTMLURL ( ) + "/archive/" + util . PathEscapeSegments ( r . TagName ) + ".zip"
2016-12-31 11:51:22 -05:00
}
// TarURL the tar.gz url for a release. release must have attributes loaded
func ( r * Release ) TarURL ( ) string {
2021-11-16 18:18:25 +00:00
return r . Repo . HTMLURL ( ) + "/archive/" + util . PathEscapeSegments ( r . TagName ) + ".tar.gz"
2016-12-31 11:51:22 -05:00
}
2020-04-18 09:47:15 -05:00
// HTMLURL the url for a release on the web UI. release must have attributes loaded
func ( r * Release ) HTMLURL ( ) string {
2021-11-16 18:18:25 +00:00
return r . Repo . HTMLURL ( ) + "/releases/tag/" + util . PathEscapeSegments ( r . TagName )
2020-04-18 09:47:15 -05:00
}
2014-04-14 01:57:25 -04:00
// IsReleaseExist returns true if release with given tag name already exists.
2015-11-15 23:52:46 -05:00
func IsReleaseExist ( repoID int64 , tagName string ) ( bool , error ) {
2014-04-14 01:57:25 -04:00
if len ( tagName ) == 0 {
return false , nil
}
2021-09-23 16:45:36 +01:00
return db . GetEngine ( db . DefaultContext ) . Get ( & Release { RepoID : repoID , LowerTagName : strings . ToLower ( tagName ) } )
2014-04-14 01:57:25 -04:00
}
2019-09-15 23:28:25 +08:00
// InsertRelease inserts a release
func InsertRelease ( rel * Release ) error {
2021-09-23 16:45:36 +01:00
_ , err := db . GetEngine ( db . DefaultContext ) . Insert ( rel )
2019-09-15 23:28:25 +08:00
return err
}
2014-06-12 17:47:23 -04:00
2020-02-03 16:47:04 +08:00
// InsertReleasesContext insert releases
2021-09-23 16:45:36 +01:00
func InsertReleasesContext ( ctx context . Context , rels [ ] * Release ) error {
_ , err := db . GetEngine ( ctx ) . Insert ( rels )
2020-02-03 16:47:04 +08:00
return err
}
2019-09-15 23:28:25 +08:00
// UpdateRelease updates all columns of a release
2021-09-23 16:45:36 +01:00
func UpdateRelease ( ctx context . Context , rel * Release ) error {
_ , err := db . GetEngine ( ctx ) . ID ( rel . ID ) . AllCols ( ) . Update ( rel )
2019-09-15 23:28:25 +08:00
return err
2014-06-12 17:47:23 -04:00
}
2019-09-15 23:28:25 +08:00
// AddReleaseAttachments adds a release attachments
2021-09-23 16:45:36 +01:00
func AddReleaseAttachments ( ctx context . Context , releaseID int64 , attachmentUUIDs [ ] string ) ( err error ) {
2017-01-15 14:57:00 +00:00
// Check attachments
2021-11-19 21:39:57 +08:00
attachments , err := repo_model . GetAttachmentsByUUIDs ( ctx , attachmentUUIDs )
2019-12-11 01:01:52 +01:00
if err != nil {
return fmt . Errorf ( "GetAttachmentsByUUIDs [uuids: %v]: %v" , attachmentUUIDs , err )
2017-01-15 14:57:00 +00:00
}
for i := range attachments {
2021-03-23 00:09:51 +08:00
if attachments [ i ] . ReleaseID != 0 {
return errors . New ( "release permission denied" )
}
2017-01-15 14:57:00 +00:00
attachments [ i ] . ReleaseID = releaseID
// No assign value could be 0, so ignore AllCols().
2021-09-23 16:45:36 +01:00
if _ , err = db . GetEngine ( ctx ) . ID ( attachments [ i ] . ID ) . Update ( attachments [ i ] ) ; err != nil {
2017-01-15 14:57:00 +00:00
return fmt . Errorf ( "update attachment [%d]: %v" , attachments [ i ] . ID , err )
}
}
return
}
2014-06-12 17:47:23 -04:00
// GetRelease returns release by given ID.
2015-11-15 23:52:46 -05:00
func GetRelease ( repoID int64 , tagName string ) ( * Release , error ) {
isExist , err := IsReleaseExist ( repoID , tagName )
2014-06-12 17:47:23 -04:00
if err != nil {
return nil , err
} else if ! isExist {
2015-11-20 02:38:41 -05:00
return nil , ErrReleaseNotExist { 0 , tagName }
2014-06-12 17:47:23 -04:00
}
2014-04-14 01:57:25 -04:00
2015-11-15 23:52:46 -05:00
rel := & Release { RepoID : repoID , LowerTagName : strings . ToLower ( tagName ) }
2021-09-23 16:45:36 +01:00
_ , err = db . GetEngine ( db . DefaultContext ) . Get ( rel )
2014-06-12 17:47:23 -04:00
return rel , err
}
2015-11-20 02:38:41 -05:00
// GetReleaseByID returns release with given ID.
func GetReleaseByID ( id int64 ) ( * Release , error ) {
rel := new ( Release )
2021-09-23 16:45:36 +01:00
has , err := db . GetEngine ( db . DefaultContext ) .
2017-10-04 21:43:04 -07:00
ID ( id ) .
2016-11-10 16:16:32 +01:00
Get ( rel )
2015-11-20 02:38:41 -05: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 {
2021-09-24 19:32:56 +08:00
db . ListOptions
2017-06-29 18:11:38 +03:00
IncludeDrafts bool
2017-09-20 08:26:49 +03:00
IncludeTags bool
2021-06-17 10:58:10 +02:00
IsPreRelease util . OptionalBool
IsDraft util . OptionalBool
2017-06-29 18:11:38 +03:00
TagNames [ ] string
2014-06-12 17:47:23 -04:00
}
2017-06-29 18:11:38 +03:00
func ( opts * FindReleasesOptions ) toConds ( repoID int64 ) builder . Cond {
2021-03-15 02:52:12 +08:00
cond := builder . NewCond ( )
2017-06-28 16:47:00 +02:00
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 16:47:00 +02: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 ) )
}
2021-06-17 10:58:10 +02:00
if ! opts . IsPreRelease . IsNone ( ) {
cond = cond . And ( builder . Eq { "is_prerelease" : opts . IsPreRelease . IsTrue ( ) } )
}
if ! opts . IsDraft . IsNone ( ) {
cond = cond . And ( builder . Eq { "is_draft" : opts . IsDraft . IsTrue ( ) } )
}
2017-06-29 18:11:38 +03:00
return cond
2017-06-28 16:47:00 +02:00
}
2017-06-29 18:11:38 +03:00
// GetReleasesByRepoID returns a list of releases of repository.
2020-01-24 19:00:29 +00:00
func GetReleasesByRepoID ( repoID int64 , opts FindReleasesOptions ) ( [ ] * Release , error ) {
2021-09-23 16:45:36 +01:00
sess := db . GetEngine ( db . DefaultContext ) .
2020-01-24 19:00:29 +00:00
Desc ( "created_unix" , "id" ) .
Where ( opts . toConds ( repoID ) )
if opts . PageSize != 0 {
2021-09-24 19:32:56 +08:00
sess = db . SetSessionPagination ( sess , & opts . ListOptions )
2017-06-29 18:11:38 +03:00
}
2020-01-24 19:00:29 +00:00
rels := make ( [ ] * Release , 0 , opts . PageSize )
return rels , sess . Find ( & rels )
2017-01-06 09:51:15 +08:00
}
2021-06-17 10:58:10 +02:00
// CountReleasesByRepoID returns a number of releases matching FindReleaseOptions and RepoID.
func CountReleasesByRepoID ( repoID int64 , opts FindReleasesOptions ) ( int64 , error ) {
2021-09-23 16:45:36 +01:00
return db . GetEngine ( db . DefaultContext ) . Where ( opts . toConds ( repoID ) ) . Count ( new ( Release ) )
2021-06-17 10:58:10 +02:00
}
2020-04-18 09:47:15 -05: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 )
2021-09-23 16:45:36 +01:00
has , err := db . GetEngine ( db . DefaultContext ) .
2020-04-18 09:47:15 -05:00
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-06 23:37:30 +01:00
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
2021-09-23 16:45:36 +01:00
func GetReleasesByRepoIDAndNames ( ctx context . Context , repoID int64 , tagNames [ ] string ) ( rels [ ] * Release , err error ) {
err = db . GetEngine ( ctx ) .
2019-01-06 23:37:30 +01:00
In ( "tag_name" , tagNames ) .
2020-02-03 16:47:04 +08:00
Desc ( "created_unix" ) .
2019-01-06 23:37:30 +01: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 ) {
2021-09-23 16:45:36 +01:00
return db . GetEngine ( db . DefaultContext ) . Where ( opts . toConds ( repoID ) ) . Count ( & Release { } )
2017-06-29 18:11:38 +03:00
}
2017-01-15 14:57:00 +00:00
type releaseMetaSearch struct {
2017-01-17 06:58:58 +01:00
ID [ ] int64
Rel [ ] * Release
2017-01-15 14:57:00 +00:00
}
2017-01-17 06:58:58 +01:00
2017-01-15 14:57:00 +00:00
func ( s releaseMetaSearch ) Len ( ) int {
return len ( s . ID )
}
2021-03-15 02:52:12 +08:00
2017-01-15 14:57:00 +00:00
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 ]
}
2021-03-15 02:52:12 +08:00
2017-01-15 14:57:00 +00:00
func ( s releaseMetaSearch ) Less ( i , j int ) bool {
return s . ID [ i ] < s . ID [ j ]
}
// GetReleaseAttachments retrieves the attachments for releases
2017-01-17 06:58:58 +01:00
func GetReleaseAttachments ( rels ... * Release ) ( err error ) {
2021-09-23 16:45:36 +01:00
return getReleaseAttachments ( db . GetEngine ( db . DefaultContext ) , rels ... )
2019-06-12 21:41:28 +02:00
}
2021-09-19 19:49:59 +08:00
func getReleaseAttachments ( e db . Engine , rels ... * Release ) ( err error ) {
2017-01-15 14:57:00 +00:00
if len ( rels ) == 0 {
return
}
2017-01-17 06:58:58 +01:00
// To keep this efficient as possible sort all releases by id,
2017-01-15 14:57:00 +00:00
// select attachments by release id,
// then merge join them
// Sort
2021-03-15 02:52:12 +08:00
sortedRels := releaseMetaSearch { ID : make ( [ ] int64 , len ( rels ) ) , Rel : make ( [ ] * Release , len ( rels ) ) }
2021-11-19 21:39:57 +08:00
var attachments [ ] * repo_model . Attachment
2017-01-15 14:57:00 +00:00
for index , element := range rels {
2021-11-19 21:39:57 +08:00
element . Attachments = [ ] * repo_model . Attachment { }
2017-01-15 14:57:00 +00:00
sortedRels . ID [ index ] = element . ID
sortedRels . Rel [ index ] = element
}
sort . Sort ( sortedRels )
// Select attachments
2019-06-12 21:41:28 +02:00
err = e .
2021-03-17 09:25:49 +00:00
Asc ( "release_id" , "name" ) .
2017-01-15 14:57:00 +00:00
In ( "release_id" , sortedRels . ID ) .
2021-11-19 21:39:57 +08:00
Find ( & attachments , repo_model . Attachment { } )
2017-01-15 14:57:00 +00:00
if err != nil {
return err
}
// merge join
2021-03-15 02:52:12 +08:00
currentIndex := 0
2017-01-15 14:57:00 +00:00
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 09:11:12 +01:00
type releaseSorter struct {
2014-06-12 17:47:23 -04:00
rels [ ] * Release
}
2016-11-25 09:11:12 +01:00
func ( rs * releaseSorter ) Len ( ) int {
2014-06-12 17:47:23 -04:00
return len ( rs . rels )
}
2016-11-25 09:11:12 +01:00
func ( rs * releaseSorter ) Less ( i , j int ) bool {
2014-06-12 17:47:23 -04:00
diffNum := rs . rels [ i ] . NumCommits - rs . rels [ j ] . NumCommits
if diffNum != 0 {
return diffNum > 0
2014-04-14 01:57:25 -04:00
}
2017-12-11 12:37:04 +08:00
return rs . rels [ i ] . CreatedUnix > rs . rels [ j ] . CreatedUnix
2014-06-12 17:47:23 -04:00
}
2014-04-14 01:57:25 -04:00
2016-11-25 09:11:12 +01:00
func ( rs * releaseSorter ) Swap ( i , j int ) {
2014-06-12 17: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 09:11:12 +01:00
sorter := & releaseSorter { rels : rels }
2014-06-12 17:47:23 -04:00
sort . Sort ( sorter )
}
2019-10-01 00:10:00 +08:00
// DeleteReleaseByID deletes a release from database by given ID.
func DeleteReleaseByID ( id int64 ) error {
2021-09-23 16:45:36 +01:00
_ , err := db . GetEngine ( db . DefaultContext ) . ID ( id ) . Delete ( new ( Release ) )
2019-10-01 00:10:00 +08:00
return err
2015-11-20 02:38:41 -05:00
}
2017-09-20 08:26:49 +03:00
2019-10-14 14:10:42 +08:00
// UpdateReleasesMigrationsByType updates all migrated repositories' releases from gitServiceType to replace originalAuthorID to posterID
2019-10-16 19:06:28 -07:00
func UpdateReleasesMigrationsByType ( gitServiceType structs . GitServiceType , originalAuthorID string , posterID int64 ) error {
2021-09-23 16:45:36 +01:00
_ , err := db . GetEngine ( db . DefaultContext ) . Table ( "release" ) .
2019-10-14 14:10:42 +08:00
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
}