2019-09-15 18:28:25 +03:00
// Copyright 2019 The Gitea 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 release
import (
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
2019-11-06 11:25:50 +03:00
"code.gitea.io/gitea/modules/notification"
2020-01-10 12:34:21 +03:00
"code.gitea.io/gitea/modules/repository"
2020-08-18 07:23:45 +03:00
"code.gitea.io/gitea/modules/storage"
2019-09-15 18:28:25 +03:00
"code.gitea.io/gitea/modules/timeutil"
)
func createTag ( gitRepo * git . Repository , rel * models . Release ) error {
// Only actual create when publish.
if ! rel . IsDraft {
if ! gitRepo . IsTagExist ( rel . TagName ) {
commit , err := gitRepo . GetCommit ( rel . Target )
if err != nil {
return fmt . Errorf ( "GetCommit: %v" , err )
}
// Trim '--' prefix to prevent command line argument vulnerability.
rel . TagName = strings . TrimPrefix ( rel . TagName , "--" )
if err = gitRepo . CreateTag ( rel . TagName , commit . ID . String ( ) ) ; err != nil {
if strings . Contains ( err . Error ( ) , "is not a valid tag name" ) {
return models . ErrInvalidTagName {
TagName : rel . TagName ,
}
}
return err
}
rel . LowerTagName = strings . ToLower ( rel . TagName )
2019-11-23 01:13:57 +03:00
// Prepare Notify
if err := rel . LoadAttributes ( ) ; err != nil {
log . Error ( "LoadAttributes: %v" , err )
return err
}
notification . NotifyPushCommits (
rel . Publisher , rel . Repo , git . TagPrefix + rel . TagName ,
2020-01-10 12:34:21 +03:00
git . EmptySHA , commit . ID . String ( ) , repository . NewPushCommits ( ) )
2019-11-23 01:13:57 +03:00
notification . NotifyCreateRef ( rel . Publisher , rel . Repo , "tag" , git . TagPrefix + rel . TagName )
2020-07-28 21:08:45 +03:00
rel . CreatedUnix = timeutil . TimeStampNow ( )
2019-09-15 18:28:25 +03:00
}
commit , err := gitRepo . GetTagCommit ( rel . TagName )
if err != nil {
return fmt . Errorf ( "GetTagCommit: %v" , err )
}
rel . Sha1 = commit . ID . String ( )
rel . NumCommits , err = commit . CommitsCount ( )
if err != nil {
return fmt . Errorf ( "CommitsCount: %v" , err )
}
} else {
rel . CreatedUnix = timeutil . TimeStampNow ( )
}
return nil
}
// CreateRelease creates a new release of repository.
func CreateRelease ( gitRepo * git . Repository , rel * models . Release , attachmentUUIDs [ ] string ) error {
isExist , err := models . IsReleaseExist ( rel . RepoID , rel . TagName )
if err != nil {
return err
} else if isExist {
return models . ErrReleaseAlreadyExist {
TagName : rel . TagName ,
}
}
if err = createTag ( gitRepo , rel ) ; err != nil {
return err
}
rel . LowerTagName = strings . ToLower ( rel . TagName )
if err = models . InsertRelease ( rel ) ; err != nil {
return err
}
if err = models . AddReleaseAttachments ( rel . ID , attachmentUUIDs ) ; err != nil {
return err
}
if ! rel . IsDraft {
2019-11-06 11:25:50 +03:00
notification . NotifyNewRelease ( rel )
2019-09-15 18:28:25 +03:00
}
return nil
}
2020-07-29 22:20:54 +03:00
// UpdateReleaseOrCreatReleaseFromTag updates information of a release or create release from tag.
func UpdateReleaseOrCreatReleaseFromTag ( doer * models . User , gitRepo * git . Repository , rel * models . Release , attachmentUUIDs [ ] string , isCreate bool ) ( err error ) {
2019-09-15 18:28:25 +03:00
if err = createTag ( gitRepo , rel ) ; err != nil {
return err
}
rel . LowerTagName = strings . ToLower ( rel . TagName )
2020-02-03 11:47:04 +03:00
if err = models . UpdateRelease ( models . DefaultDBContext ( ) , rel ) ; err != nil {
2019-09-15 18:28:25 +03:00
return err
}
2019-09-24 09:55:07 +03:00
if err = models . AddReleaseAttachments ( rel . ID , attachmentUUIDs ) ; err != nil {
log . Error ( "AddReleaseAttachments: %v" , err )
}
2020-07-29 22:20:54 +03:00
if ! isCreate {
notification . NotifyUpdateRelease ( doer , rel )
return
}
if ! rel . IsDraft {
notification . NotifyNewRelease ( rel )
}
2019-09-15 18:28:25 +03:00
return err
}
2019-09-30 19:10:00 +03:00
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
func DeleteReleaseByID ( id int64 , doer * models . User , delTag bool ) error {
rel , err := models . GetReleaseByID ( id )
if err != nil {
return fmt . Errorf ( "GetReleaseByID: %v" , err )
}
repo , err := models . GetRepositoryByID ( rel . RepoID )
if err != nil {
return fmt . Errorf ( "GetRepositoryByID: %v" , err )
}
if delTag {
2019-11-30 17:40:22 +03:00
if stdout , err := git . NewCommand ( "tag" , "-d" , rel . TagName ) .
SetDescription ( fmt . Sprintf ( "DeleteReleaseByID (git tag -d): %d" , rel . ID ) ) .
RunInDir ( repo . RepoPath ( ) ) ; err != nil && ! strings . Contains ( err . Error ( ) , "not found" ) {
log . Error ( "DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v" , rel . ID , repo , stdout , err )
return fmt . Errorf ( "git tag -d: %v" , err )
2019-09-30 19:10:00 +03:00
}
if err := models . DeleteReleaseByID ( id ) ; err != nil {
return fmt . Errorf ( "DeleteReleaseByID: %v" , err )
}
} else {
rel . IsTag = true
rel . IsDraft = false
rel . IsPrerelease = false
rel . Title = ""
rel . Note = ""
2020-02-03 11:47:04 +03:00
if err = models . UpdateRelease ( models . DefaultDBContext ( ) , rel ) ; err != nil {
2019-09-30 19:10:00 +03:00
return fmt . Errorf ( "Update: %v" , err )
}
}
rel . Repo = repo
if err = rel . LoadAttributes ( ) ; err != nil {
return fmt . Errorf ( "LoadAttributes: %v" , err )
}
if err := models . DeleteAttachmentsByRelease ( rel . ID ) ; err != nil {
return fmt . Errorf ( "DeleteAttachments: %v" , err )
}
for i := range rel . Attachments {
attachment := rel . Attachments [ i ]
2020-08-18 07:23:45 +03:00
if err := storage . Attachments . Delete ( attachment . RelativePath ( ) ) ; err != nil {
2019-09-30 19:10:00 +03:00
log . Error ( "Delete attachment %s of release %s failed: %v" , attachment . UUID , rel . ID , err )
}
}
2019-11-06 11:25:50 +03:00
notification . NotifyDeleteRelease ( doer , rel )
2019-09-30 19:10:00 +03:00
return nil
}