2014-04-14 09:57:25 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// 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"
"time"
2015-08-24 16:01:23 +03:00
"github.com/go-xorm/xorm"
2016-11-10 19:24:48 +03:00
"code.gitea.io/git"
2015-12-10 04:46:05 +03:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/process"
2014-04-14 09:57:25 +04:00
)
// Release represents a release of repository.
type Release struct {
2015-11-16 07:52:46 +03:00
ID int64 ` xorm:"pk autoincr" `
RepoID int64
PublisherID int64
2014-04-14 09:57:25 +04:00
Publisher * User ` xorm:"-" `
TagName string
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
NumCommitsBehind int64 ` xorm:"-" `
2014-04-14 09:57:25 +04:00
Note string ` xorm:"TEXT" `
2014-06-13 01:47:23 +04:00
IsDraft bool ` xorm:"NOT NULL DEFAULT false" `
2014-04-14 09:57:25 +04:00
IsPrerelease bool
2016-03-10 03:53:30 +03:00
Created time . Time ` xorm:"-" `
CreatedUnix int64
}
2016-11-25 11:11:12 +03:00
// BeforeInsert is invoked from XORM before inserting an object of this type.
2016-03-10 03:53:30 +03:00
func ( r * Release ) BeforeInsert ( ) {
2016-08-06 20:02:15 +03:00
if r . CreatedUnix == 0 {
r . CreatedUnix = time . Now ( ) . Unix ( )
}
2014-04-14 09:57:25 +04:00
}
2016-11-25 11:11:12 +03:00
// AfterSet is invoked from XORM after setting the value of a field of this object.
2015-08-24 16:01:23 +03:00
func ( r * Release ) AfterSet ( colName string , _ xorm . Cell ) {
switch colName {
2016-03-10 03:53:30 +03:00
case "created_unix" :
r . Created = time . Unix ( r . CreatedUnix , 0 ) . Local ( )
2015-08-24 16:01:23 +03:00
}
}
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
}
2014-06-13 01:47:23 +04:00
func createTag ( gitRepo * git . Repository , rel * Release ) error {
// Only actual create when publish.
if ! rel . IsDraft {
if ! gitRepo . IsTagExist ( rel . TagName ) {
2015-12-10 04:46:05 +03:00
commit , err := gitRepo . GetBranchCommit ( rel . Target )
2014-06-13 01:47:23 +04:00
if err != nil {
2015-12-10 04:46:05 +03:00
return fmt . Errorf ( "GetBranchCommit: %v" , err )
2014-06-13 01:47:23 +04:00
}
2016-07-23 10:59:19 +03:00
// Trim '--' prefix to prevent command line argument vulnerability.
2016-05-06 22:40:41 +03:00
rel . TagName = strings . TrimPrefix ( rel . TagName , "--" )
2015-11-04 06:49:06 +03:00
if err = gitRepo . CreateTag ( rel . TagName , commit . ID . String ( ) ) ; err != nil {
2016-07-23 10:59:19 +03:00
if strings . Contains ( err . Error ( ) , "is not a valid tag name" ) {
return ErrInvalidTagName { rel . TagName }
}
2014-06-13 01:47:23 +04:00
return err
}
} else {
2015-12-10 04:46:05 +03:00
commit , err := gitRepo . GetTagCommit ( rel . TagName )
2014-06-13 01:47:23 +04:00
if err != nil {
2015-12-10 04:46:05 +03:00
return fmt . Errorf ( "GetTagCommit: %v" , err )
2014-06-13 01:47:23 +04:00
}
2016-07-28 05:45:35 +03:00
rel . Sha1 = commit . ID . String ( )
2014-06-13 01:47:23 +04:00
rel . NumCommits , err = commit . CommitsCount ( )
if err != nil {
2015-12-10 04:46:05 +03:00
return fmt . Errorf ( "CommitsCount: %v" , err )
2014-06-13 01:47:23 +04:00
}
}
}
return nil
}
2014-04-14 09:57:25 +04:00
// CreateRelease creates a new release of repository.
2014-05-10 07:14:24 +04:00
func CreateRelease ( gitRepo * git . Repository , rel * Release ) error {
2015-11-16 07:52:46 +03:00
isExist , err := IsReleaseExist ( rel . RepoID , rel . TagName )
2014-04-14 09:57:25 +04:00
if err != nil {
return err
} else if isExist {
2015-11-16 07:52:46 +03:00
return ErrReleaseAlreadyExist { rel . TagName }
2014-04-14 09:57:25 +04:00
}
2014-06-13 01:47:23 +04:00
if err = createTag ( gitRepo , rel ) ; err != nil {
return err
}
rel . LowerTagName = strings . ToLower ( rel . TagName )
2014-06-21 08:51:41 +04:00
_ , err = x . InsertOne ( rel )
2014-06-13 01:47:23 +04:00
return err
}
2014-06-12 17:10:39 +04:00
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 .
Id ( id ) .
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
}
// GetReleasesByRepoID returns a list of releases of repository.
2016-11-04 22:28:07 +03:00
func GetReleasesByRepoID ( repoID int64 , page , pageSize int ) ( rels [ ] * Release , err error ) {
if page <= 0 {
page = 1
}
2016-11-10 18:16:32 +03:00
err = x .
Desc ( "created_unix" ) .
Limit ( pageSize , ( page - 1 ) * pageSize ) .
Find ( & rels , Release { RepoID : repoID } )
2014-06-13 01:47:23 +04:00
return rels , err
}
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
}
2014-06-13 01:47:23 +04:00
return rs . rels [ i ] . Created . After ( rs . rels [ j ] . Created )
}
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 )
}
// UpdateRelease updates information of a release.
func UpdateRelease ( gitRepo * git . Repository , rel * Release ) ( err error ) {
if err = createTag ( gitRepo , rel ) ; err != nil {
return err
}
2015-11-16 07:52:46 +03:00
_ , err = x . Id ( rel . ID ) . AllCols ( ) . Update ( rel )
2014-04-14 09:57:25 +04:00
return err
}
2015-11-20 10:38:41 +03:00
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
func DeleteReleaseByID ( id int64 ) error {
rel , err := GetReleaseByID ( id )
if err != nil {
return fmt . Errorf ( "GetReleaseByID: %v" , err )
}
repo , err := GetRepositoryByID ( rel . RepoID )
if err != nil {
return fmt . Errorf ( "GetRepositoryByID: %v" , err )
}
2015-11-27 01:33:45 +03:00
_ , stderr , err := process . ExecDir ( - 1 , repo . RepoPath ( ) ,
fmt . Sprintf ( "DeleteReleaseByID (git tag -d): %d" , rel . ID ) ,
2015-11-20 10:38:41 +03:00
"git" , "tag" , "-d" , rel . TagName )
if err != nil && ! strings . Contains ( stderr , "not found" ) {
return fmt . Errorf ( "git tag -d: %v - %s" , err , stderr )
}
if _ , err = x . Id ( rel . ID ) . Delete ( new ( Release ) ) ; err != nil {
return fmt . Errorf ( "Delete: %v" , err )
}
return nil
}