diff --git a/models/release.go b/models/release.go index e35a55741a2..e3386d074f8 100644 --- a/models/release.go +++ b/models/release.go @@ -112,43 +112,20 @@ func IsReleaseExist(repoID int64, tagName string) (bool, error) { return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}) } -func createTag(gitRepo *git.Repository, rel *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 ErrInvalidTagName{rel.TagName} - } - return err - } - rel.LowerTagName = strings.ToLower(rel.TagName) - } - commit, err := gitRepo.GetTagCommit(rel.TagName) - if err != nil { - return fmt.Errorf("GetTagCommit: %v", err) - } +// InsertRelease inserts a release +func InsertRelease(rel *Release) error { + _, err := x.Insert(rel) + return err +} - rel.Sha1 = commit.ID.String() - rel.CreatedUnix = timeutil.TimeStamp(commit.Author.When.Unix()) - rel.NumCommits, err = commit.CommitsCount() - if err != nil { - return fmt.Errorf("CommitsCount: %v", err) - } - } else { - rel.CreatedUnix = timeutil.TimeStampNow() - } - return nil +// UpdateRelease updates all columns of a release +func UpdateRelease(rel *Release) error { + _, err := x.ID(rel.ID).AllCols().Update(rel) + return err } -func addReleaseAttachments(releaseID int64, attachmentUUIDs []string) (err error) { +// AddReleaseAttachments adds a release attachments +func AddReleaseAttachments(releaseID int64, attachmentUUIDs []string) (err error) { // Check attachments var attachments = make([]*Attachment, 0) for _, uuid := range attachmentUUIDs { @@ -173,51 +150,6 @@ func addReleaseAttachments(releaseID int64, attachmentUUIDs []string) (err error return } -// CreateRelease creates a new release of repository. -func CreateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []string) error { - isExist, err := IsReleaseExist(rel.RepoID, rel.TagName) - if err != nil { - return err - } else if isExist { - return ErrReleaseAlreadyExist{rel.TagName} - } - - if err = createTag(gitRepo, rel); err != nil { - return err - } - rel.LowerTagName = strings.ToLower(rel.TagName) - - _, err = x.InsertOne(rel) - if err != nil { - return err - } - - err = addReleaseAttachments(rel.ID, attachmentUUIDs) - if err != nil { - return err - } - - if !rel.IsDraft { - if err := rel.LoadAttributes(); err != nil { - log.Error("LoadAttributes: %v", err) - } else { - mode, _ := AccessLevel(rel.Publisher, rel.Repo) - if err := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{ - Action: api.HookReleasePublished, - Release: rel.APIFormat(), - Repository: rel.Repo.APIFormat(mode), - Sender: rel.Publisher.APIFormat(), - }); err != nil { - log.Error("PrepareWebhooks: %v", err) - } else { - go HookQueue.Add(rel.Repo.ID) - } - } - } - - return nil -} - // GetRelease returns release by given ID. func GetRelease(repoID int64, tagName string) (*Release, error) { isExist, err := IsReleaseExist(repoID, tagName) @@ -385,40 +317,6 @@ func SortReleases(rels []*Release) { sort.Sort(sorter) } -// UpdateRelease updates information of a release. -func UpdateRelease(doer *User, gitRepo *git.Repository, rel *Release, attachmentUUIDs []string) (err error) { - if err = createTag(gitRepo, rel); err != nil { - return err - } - rel.LowerTagName = strings.ToLower(rel.TagName) - - _, err = x.ID(rel.ID).AllCols().Update(rel) - if err != nil { - return err - } - - err = rel.loadAttributes(x) - if err != nil { - return err - } - - err = addReleaseAttachments(rel.ID, attachmentUUIDs) - - mode, _ := AccessLevel(doer, rel.Repo) - if err1 := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{ - Action: api.HookReleaseUpdated, - Release: rel.APIFormat(), - Repository: rel.Repo.APIFormat(mode), - Sender: doer.APIFormat(), - }); err1 != nil { - log.Error("PrepareWebhooks: %v", err) - } else { - go HookQueue.Add(rel.Repo.ID) - } - - return err -} - // DeleteReleaseByID deletes a release and corresponding Git tag by given ID. func DeleteReleaseByID(id int64, doer *User, delTag bool) error { rel, err := GetReleaseByID(id) diff --git a/models/repo_mirror.go b/models/repo_mirror.go index 9801c05ae4d..e1c1aefc39c 100644 --- a/models/repo_mirror.go +++ b/models/repo_mirror.go @@ -318,6 +318,12 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { return parseRemoteUpdateOutput(output), true } +// RunMirrorSync will invoke Mirror's runSync +func RunMirrorSync(mirror *Mirror) bool { + _, ok := mirror.runSync() + return ok +} + func getMirrorByRepoID(e Engine, repoID int64) (*Mirror, error) { m := &Mirror{RepoID: repoID} has, err := e.Get(m) diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 9296f22e80f..da32b955fb8 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + releaseservice "code.gitea.io/gitea/services/release" ) // GetRelease get a single release of a repository @@ -168,7 +169,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { IsTag: false, Repo: ctx.Repo.Repository, } - if err := models.CreateRelease(ctx.Repo.GitRepo, rel, nil); err != nil { + if err := releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, nil); err != nil { if models.IsErrReleaseAlreadyExist(err) { ctx.Status(409) } else { @@ -191,7 +192,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { rel.Repo = ctx.Repo.Repository rel.Publisher = ctx.User - if err = models.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, nil); err != nil { + if err = releaseservice.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, nil); err != nil { ctx.ServerError("UpdateRelease", err) return } @@ -262,7 +263,7 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) { if form.IsPrerelease != nil { rel.IsPrerelease = *form.IsPrerelease } - if err := models.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, nil); err != nil { + if err := releaseservice.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, nil); err != nil { ctx.Error(500, "UpdateRelease", err) return } diff --git a/routers/repo/release.go b/routers/repo/release.go index fdef533f105..95423a92b29 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" + releaseservice "code.gitea.io/gitea/services/release" ) const ( @@ -175,7 +176,7 @@ func NewReleasePost(ctx *context.Context, form auth.NewReleaseForm) { IsTag: false, } - if err = models.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil { + if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil { ctx.Data["Err_TagName"] = true switch { case models.IsErrReleaseAlreadyExist(err): @@ -202,7 +203,7 @@ func NewReleasePost(ctx *context.Context, form auth.NewReleaseForm) { rel.PublisherID = ctx.User.ID rel.IsTag = false - if err = models.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil { + if err = releaseservice.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil { ctx.Data["Err_TagName"] = true ctx.ServerError("UpdateRelease", err) return @@ -281,7 +282,7 @@ func EditReleasePost(ctx *context.Context, form auth.EditReleaseForm) { rel.Note = form.Content rel.IsDraft = len(form.Draft) > 0 rel.IsPrerelease = form.Prerelease - if err = models.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil { + if err = releaseservice.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil { ctx.ServerError("UpdateRelease", err) return } diff --git a/services/release/release.go b/services/release/release.go new file mode 100644 index 00000000000..4451633798c --- /dev/null +++ b/services/release/release.go @@ -0,0 +1,132 @@ +// 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" + api "code.gitea.io/gitea/modules/structs" + "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) + } + commit, err := gitRepo.GetTagCommit(rel.TagName) + if err != nil { + return fmt.Errorf("GetTagCommit: %v", err) + } + + rel.Sha1 = commit.ID.String() + rel.CreatedUnix = timeutil.TimeStamp(commit.Author.When.Unix()) + 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 { + if err := rel.LoadAttributes(); err != nil { + log.Error("LoadAttributes: %v", err) + } else { + mode, _ := models.AccessLevel(rel.Publisher, rel.Repo) + if err := models.PrepareWebhooks(rel.Repo, models.HookEventRelease, &api.ReleasePayload{ + Action: api.HookReleasePublished, + Release: rel.APIFormat(), + Repository: rel.Repo.APIFormat(mode), + Sender: rel.Publisher.APIFormat(), + }); err != nil { + log.Error("PrepareWebhooks: %v", err) + } else { + go models.HookQueue.Add(rel.Repo.ID) + } + } + } + + return nil +} + +// UpdateRelease updates information of a release. +func UpdateRelease(doer *models.User, gitRepo *git.Repository, rel *models.Release, attachmentUUIDs []string) (err error) { + if err = createTag(gitRepo, rel); err != nil { + return err + } + rel.LowerTagName = strings.ToLower(rel.TagName) + + if err = models.UpdateRelease(rel); err != nil { + return err + } + + if err = rel.LoadAttributes(); err != nil { + return err + } + + err = models.AddReleaseAttachments(rel.ID, attachmentUUIDs) + + // even if attachments added failed, hooks will be still triggered + mode, _ := models.AccessLevel(doer, rel.Repo) + if err1 := models.PrepareWebhooks(rel.Repo, models.HookEventRelease, &api.ReleasePayload{ + Action: api.HookReleaseUpdated, + Release: rel.APIFormat(), + Repository: rel.Repo.APIFormat(mode), + Sender: doer.APIFormat(), + }); err1 != nil { + log.Error("PrepareWebhooks: %v", err) + } else { + go models.HookQueue.Add(rel.Repo.ID) + } + + return err +} diff --git a/models/release_test.go b/services/release/release_test.go similarity index 62% rename from models/release_test.go rename to services/release/release_test.go index 6da3cd76281..91dd0a73b5a 100644 --- a/models/release_test.go +++ b/services/release/release_test.go @@ -2,27 +2,33 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package release import ( + "path/filepath" "testing" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" "github.com/stretchr/testify/assert" ) +func TestMain(m *testing.M) { + models.MainTest(m, filepath.Join("..", "..")) +} + func TestRelease_Create(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) + assert.NoError(t, models.PrepareTestDatabase()) - user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - repoPath := RepoPath(user.Name, repo.Name) + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + repoPath := models.RepoPath(user.Name, repo.Name) gitRepo, err := git.OpenRepository(repoPath) assert.NoError(t, err) - assert.NoError(t, CreateRelease(gitRepo, &Release{ + assert.NoError(t, CreateRelease(gitRepo, &models.Release{ RepoID: repo.ID, PublisherID: user.ID, TagName: "v0.1", @@ -34,7 +40,7 @@ func TestRelease_Create(t *testing.T) { IsTag: false, }, nil)) - assert.NoError(t, CreateRelease(gitRepo, &Release{ + assert.NoError(t, CreateRelease(gitRepo, &models.Release{ RepoID: repo.ID, PublisherID: user.ID, TagName: "v0.1.1", @@ -46,7 +52,7 @@ func TestRelease_Create(t *testing.T) { IsTag: false, }, nil)) - assert.NoError(t, CreateRelease(gitRepo, &Release{ + assert.NoError(t, CreateRelease(gitRepo, &models.Release{ RepoID: repo.ID, PublisherID: user.ID, TagName: "v0.1.2", @@ -58,7 +64,7 @@ func TestRelease_Create(t *testing.T) { IsTag: false, }, nil)) - assert.NoError(t, CreateRelease(gitRepo, &Release{ + assert.NoError(t, CreateRelease(gitRepo, &models.Release{ RepoID: repo.ID, PublisherID: user.ID, TagName: "v0.1.3", @@ -70,7 +76,7 @@ func TestRelease_Create(t *testing.T) { IsTag: false, }, nil)) - assert.NoError(t, CreateRelease(gitRepo, &Release{ + assert.NoError(t, CreateRelease(gitRepo, &models.Release{ RepoID: repo.ID, PublisherID: user.ID, TagName: "v0.1.4", @@ -82,7 +88,7 @@ func TestRelease_Create(t *testing.T) { IsTag: false, }, nil)) - assert.NoError(t, CreateRelease(gitRepo, &Release{ + assert.NoError(t, CreateRelease(gitRepo, &models.Release{ RepoID: repo.ID, PublisherID: user.ID, TagName: "v0.1.5", @@ -96,12 +102,12 @@ func TestRelease_Create(t *testing.T) { } func TestRelease_MirrorDelete(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) + assert.NoError(t, models.PrepareTestDatabase()) - user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - repoPath := RepoPath(user.Name, repo.Name) - migrationOptions := MigrateRepoOptions{ + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + repoPath := models.RepoPath(user.Name, repo.Name) + migrationOptions := models.MigrateRepoOptions{ Name: "test_mirror", Description: "Test mirror", IsPrivate: false, @@ -110,17 +116,17 @@ func TestRelease_MirrorDelete(t *testing.T) { Wiki: true, SyncReleasesWithTags: true, } - mirror, err := MigrateRepository(user, user, migrationOptions) + mirror, err := models.MigrateRepository(user, user, migrationOptions) assert.NoError(t, err) gitRepo, err := git.OpenRepository(repoPath) assert.NoError(t, err) - findOptions := FindReleasesOptions{IncludeDrafts: true, IncludeTags: true} - initCount, err := GetReleaseCountByRepoID(mirror.ID, findOptions) + findOptions := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true} + initCount, err := models.GetReleaseCountByRepoID(mirror.ID, findOptions) assert.NoError(t, err) - assert.NoError(t, CreateRelease(gitRepo, &Release{ + assert.NoError(t, CreateRelease(gitRepo, &models.Release{ RepoID: repo.ID, PublisherID: user.ID, TagName: "v0.2", @@ -135,21 +141,21 @@ func TestRelease_MirrorDelete(t *testing.T) { err = mirror.GetMirror() assert.NoError(t, err) - _, ok := mirror.Mirror.runSync() + ok := models.RunMirrorSync(mirror.Mirror) assert.True(t, ok) - count, err := GetReleaseCountByRepoID(mirror.ID, findOptions) + count, err := models.GetReleaseCountByRepoID(mirror.ID, findOptions) assert.NoError(t, err) assert.EqualValues(t, initCount+1, count) - release, err := GetRelease(repo.ID, "v0.2") + release, err := models.GetRelease(repo.ID, "v0.2") assert.NoError(t, err) - assert.NoError(t, DeleteReleaseByID(release.ID, user, true)) + assert.NoError(t, models.DeleteReleaseByID(release.ID, user, true)) - _, ok = mirror.Mirror.runSync() + ok = models.RunMirrorSync(mirror.Mirror) assert.True(t, ok) - count, err = GetReleaseCountByRepoID(mirror.ID, findOptions) + count, err = models.GetReleaseCountByRepoID(mirror.ID, findOptions) assert.NoError(t, err) assert.EqualValues(t, initCount, count) }