Move PushUpdateAddDeleteTags to repository module from models (#10106)

* Move PushUpdateAddDeleteTags to repository module from models

* Fix deadlock on sqlite
This commit is contained in:
Lunny Xiao 2020-02-03 16:47:04 +08:00 committed by GitHub
parent e959d1a48b
commit 48ce135cc9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 176 additions and 166 deletions

View file

@ -10,6 +10,19 @@ import (
"strings" "strings"
) )
// env keys for git hooks need
const (
EnvRepoName = "GITEA_REPO_NAME"
EnvRepoUsername = "GITEA_REPO_USER_NAME"
EnvRepoIsWiki = "GITEA_REPO_IS_WIKI"
EnvPusherName = "GITEA_PUSHER_NAME"
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
EnvPusherID = "GITEA_PUSHER_ID"
EnvKeyID = "GITEA_KEY_ID"
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
EnvIsInternal = "GITEA_INTERNAL_PUSH"
)
// InternalPushingEnvironment returns an os environment to switch off hooks on push // InternalPushingEnvironment returns an os environment to switch off hooks on push
// It is recommended to avoid using this unless you are pushing within a transaction // It is recommended to avoid using this unless you are pushing within a transaction
// or if you absolutely are sure that post-receive and pre-receive will do nothing // or if you absolutely are sure that post-receive and pre-receive will do nothing

View file

@ -119,9 +119,15 @@ func InsertRelease(rel *Release) error {
return err return err
} }
// InsertReleasesContext insert releases
func InsertReleasesContext(ctx DBContext, rels []*Release) error {
_, err := ctx.e.Insert(rels)
return err
}
// UpdateRelease updates all columns of a release // UpdateRelease updates all columns of a release
func UpdateRelease(rel *Release) error { func UpdateRelease(ctx DBContext, rel *Release) error {
_, err := x.ID(rel.ID).AllCols().Update(rel) _, err := ctx.e.ID(rel.ID).AllCols().Update(rel)
return err return err
} }
@ -212,10 +218,10 @@ func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, er
} }
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames. // GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
func GetReleasesByRepoIDAndNames(repoID int64, tagNames []string) (rels []*Release, err error) { func GetReleasesByRepoIDAndNames(ctx DBContext, repoID int64, tagNames []string) (rels []*Release, err error) {
err = x. err = ctx.e.
Desc("created_unix").
In("tag_name", tagNames). In("tag_name", tagNames).
Desc("created_unix").
Find(&rels, Release{RepoID: repoID}) Find(&rels, Release{RepoID: repoID})
return rels, err return rels, err
} }

View file

@ -7,42 +7,8 @@ package models
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/timeutil"
) )
// env keys for git hooks need
const (
EnvRepoName = "GITEA_REPO_NAME"
EnvRepoUsername = "GITEA_REPO_USER_NAME"
EnvRepoIsWiki = "GITEA_REPO_IS_WIKI"
EnvPusherName = "GITEA_PUSHER_NAME"
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
EnvPusherID = "GITEA_PUSHER_ID"
EnvKeyID = "GITEA_KEY_ID"
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
EnvIsInternal = "GITEA_INTERNAL_PUSH"
)
// PushUpdateAddDeleteTags updates a number of added and delete tags
func PushUpdateAddDeleteTags(repo *Repository, gitRepo *git.Repository, addTags, delTags []string) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return fmt.Errorf("Unable to begin sess in PushUpdateDeleteTags: %v", err)
}
if err := pushUpdateDeleteTags(sess, repo, delTags); err != nil {
return err
}
if err := pushUpdateAddTags(sess, repo, gitRepo, addTags); err != nil {
return err
}
return sess.Commit()
}
// PushUpdateDeleteTags updates a number of delete tags // PushUpdateDeleteTags updates a number of delete tags
func PushUpdateDeleteTags(repo *Repository, tags []string) error { func PushUpdateDeleteTags(repo *Repository, tags []string) error {
sess := x.NewSession() sess := x.NewSession()
@ -57,6 +23,11 @@ func PushUpdateDeleteTags(repo *Repository, tags []string) error {
return sess.Commit() return sess.Commit()
} }
// PushUpdateDeleteTagsContext updates a number of delete tags with context
func PushUpdateDeleteTagsContext(ctx DBContext, repo *Repository, tags []string) error {
return pushUpdateDeleteTags(ctx.e, repo, tags)
}
func pushUpdateDeleteTags(e Engine, repo *Repository, tags []string) error { func pushUpdateDeleteTags(e Engine, repo *Repository, tags []string) error {
if len(tags) == 0 { if len(tags) == 0 {
return nil return nil
@ -111,125 +82,6 @@ func PushUpdateDeleteTag(repo *Repository, tagName string) error {
return nil return nil
} }
// PushUpdateAddTags updates a number of add tags
func PushUpdateAddTags(repo *Repository, gitRepo *git.Repository, tags []string) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return fmt.Errorf("Unable to begin sess in PushUpdateAddTags: %v", err)
}
if err := pushUpdateAddTags(sess, repo, gitRepo, tags); err != nil {
return err
}
return sess.Commit()
}
func pushUpdateAddTags(e Engine, repo *Repository, gitRepo *git.Repository, tags []string) error {
if len(tags) == 0 {
return nil
}
lowerTags := make([]string, 0, len(tags))
for _, tag := range tags {
lowerTags = append(lowerTags, strings.ToLower(tag))
}
releases := make([]Release, 0, len(tags))
if err := e.Where("repo_id = ?", repo.ID).
In("lower_tag_name", lowerTags).Find(&releases); err != nil {
return fmt.Errorf("GetRelease: %v", err)
}
relMap := make(map[string]*Release)
for _, rel := range releases {
relMap[rel.LowerTagName] = &rel
}
newReleases := make([]*Release, 0, len(lowerTags)-len(relMap))
emailToUser := make(map[string]*User)
for i, lowerTag := range lowerTags {
tag, err := gitRepo.GetTag(tags[i])
if err != nil {
return fmt.Errorf("GetTag: %v", err)
}
commit, err := tag.Commit()
if err != nil {
return fmt.Errorf("Commit: %v", err)
}
sig := tag.Tagger
if sig == nil {
sig = commit.Author
}
if sig == nil {
sig = commit.Committer
}
var author *User
var createdAt = time.Unix(1, 0)
if sig != nil {
var ok bool
author, ok = emailToUser[sig.Email]
if !ok {
author, err = GetUserByEmail(sig.Email)
if err != nil && !IsErrUserNotExist(err) {
return fmt.Errorf("GetUserByEmail: %v", err)
}
}
createdAt = sig.When
}
commitsCount, err := commit.CommitsCount()
if err != nil {
return fmt.Errorf("CommitsCount: %v", err)
}
rel, has := relMap[lowerTag]
if !has {
rel = &Release{
RepoID: repo.ID,
Title: "",
TagName: tags[i],
LowerTagName: lowerTag,
Target: "",
Sha1: commit.ID.String(),
NumCommits: commitsCount,
Note: "",
IsDraft: false,
IsPrerelease: false,
IsTag: true,
CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
}
if author != nil {
rel.PublisherID = author.ID
}
newReleases = append(newReleases, rel)
} else {
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
rel.IsDraft = false
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
}
if _, err = e.ID(rel.ID).AllCols().Update(rel); err != nil {
return fmt.Errorf("Update: %v", err)
}
}
}
if len(newReleases) > 0 {
if _, err := e.Insert(newReleases); err != nil {
return fmt.Errorf("Insert: %v", err)
}
}
return nil
}
// SaveOrUpdateTag must be called for any push actions to add tag // SaveOrUpdateTag must be called for any push actions to add tag
func SaveOrUpdateTag(repo *Repository, newRel *Release) error { func SaveOrUpdateTag(repo *Repository, newRel *Release) error {
rel, err := GetRelease(repo.ID, newRel.TagName) rel, err := GetRelease(repo.ID, newRel.TagName)

View file

@ -1452,6 +1452,11 @@ func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
// GetUserByEmail returns the user object by given e-mail if exists. // GetUserByEmail returns the user object by given e-mail if exists.
func GetUserByEmail(email string) (*User, error) { func GetUserByEmail(email string) (*User, error) {
return GetUserByEmailContext(DefaultDBContext(), email)
}
// GetUserByEmailContext returns the user object by given e-mail if exists with db context
func GetUserByEmailContext(ctx DBContext, email string) (*User, error) {
if len(email) == 0 { if len(email) == 0 {
return nil, ErrUserNotExist{0, email, 0} return nil, ErrUserNotExist{0, email, 0}
} }
@ -1459,7 +1464,7 @@ func GetUserByEmail(email string) (*User, error) {
email = strings.ToLower(email) email = strings.ToLower(email)
// First try to find the user by primary email // First try to find the user by primary email
user := &User{Email: email} user := &User{Email: email}
has, err := x.Get(user) has, err := ctx.e.Get(user)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1469,19 +1474,19 @@ func GetUserByEmail(email string) (*User, error) {
// Otherwise, check in alternative list for activated email addresses // Otherwise, check in alternative list for activated email addresses
emailAddress := &EmailAddress{Email: email, IsActivated: true} emailAddress := &EmailAddress{Email: email, IsActivated: true}
has, err = x.Get(emailAddress) has, err = ctx.e.Get(emailAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if has { if has {
return GetUserByID(emailAddress.UID) return getUserByID(ctx.e, emailAddress.UID)
} }
// Finally, if email address is the protected email address: // Finally, if email address is the protected email address:
if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) { if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) {
username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress))
user := &User{LowerName: username} user := &User{LowerName: username}
has, err := x.Get(user) has, err := ctx.e.Get(user)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -732,7 +732,7 @@ func createCommitRepoActions(repo *models.Repository, gitRepo *git.Repository, o
Commits: commits, Commits: commits,
}) })
} }
if err := models.PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil { if err := repo_module.PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil {
return nil, fmt.Errorf("PushUpdateAddDeleteTags: %v", err) return nil, fmt.Errorf("PushUpdateAddDeleteTags: %v", err)
} }
return actions, nil return actions, nil

View file

@ -0,0 +1,134 @@
// Copyright 2020 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 repository
import (
"fmt"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/timeutil"
)
// PushUpdateAddDeleteTags updates a number of added and delete tags
func PushUpdateAddDeleteTags(repo *models.Repository, gitRepo *git.Repository, addTags, delTags []string) error {
return models.WithTx(func(ctx models.DBContext) error {
if err := models.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil {
return err
}
return pushUpdateAddTags(ctx, repo, gitRepo, addTags)
})
}
// pushUpdateAddTags updates a number of add tags
func pushUpdateAddTags(ctx models.DBContext, repo *models.Repository, gitRepo *git.Repository, tags []string) error {
if len(tags) == 0 {
return nil
}
lowerTags := make([]string, 0, len(tags))
for _, tag := range tags {
lowerTags = append(lowerTags, strings.ToLower(tag))
}
releases, err := models.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags)
if err != nil {
return fmt.Errorf("GetReleasesByRepoIDAndNames: %v", err)
}
relMap := make(map[string]*models.Release)
for _, rel := range releases {
relMap[rel.LowerTagName] = rel
}
newReleases := make([]*models.Release, 0, len(lowerTags)-len(relMap))
emailToUser := make(map[string]*models.User)
for i, lowerTag := range lowerTags {
tag, err := gitRepo.GetTag(tags[i])
if err != nil {
return fmt.Errorf("GetTag: %v", err)
}
commit, err := tag.Commit()
if err != nil {
return fmt.Errorf("Commit: %v", err)
}
sig := tag.Tagger
if sig == nil {
sig = commit.Author
}
if sig == nil {
sig = commit.Committer
}
var author *models.User
var createdAt = time.Unix(1, 0)
if sig != nil {
var ok bool
author, ok = emailToUser[sig.Email]
if !ok {
author, err = models.GetUserByEmailContext(ctx, sig.Email)
if err != nil && !models.IsErrUserNotExist(err) {
return fmt.Errorf("GetUserByEmail: %v", err)
}
if author != nil {
emailToUser[sig.Email] = author
}
}
createdAt = sig.When
}
commitsCount, err := commit.CommitsCount()
if err != nil {
return fmt.Errorf("CommitsCount: %v", err)
}
rel, has := relMap[lowerTag]
if !has {
rel = &models.Release{
RepoID: repo.ID,
Title: "",
TagName: tags[i],
LowerTagName: lowerTag,
Target: "",
Sha1: commit.ID.String(),
NumCommits: commitsCount,
Note: "",
IsDraft: false,
IsPrerelease: false,
IsTag: true,
CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
}
if author != nil {
rel.PublisherID = author.ID
}
newReleases = append(newReleases, rel)
} else {
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
rel.IsDraft = false
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
}
if err = models.UpdateRelease(ctx, rel); err != nil {
return fmt.Errorf("Update: %v", err)
}
}
}
if len(newReleases) > 0 {
if err = models.InsertReleasesContext(ctx, newReleases); err != nil {
return fmt.Errorf("Insert: %v", err)
}
}
return nil
}

View file

@ -420,7 +420,7 @@ func RedirectDownload(ctx *context.Context) {
) )
tagNames := []string{vTag} tagNames := []string{vTag}
curRepo := ctx.Repo.Repository curRepo := ctx.Repo.Repository
releases, err := models.GetReleasesByRepoIDAndNames(curRepo.ID, tagNames) releases, err := models.GetReleasesByRepoIDAndNames(models.DefaultDBContext(), curRepo.ID, tagNames)
if err != nil { if err != nil {
if models.IsErrAttachmentNotExist(err) { if models.IsErrAttachmentNotExist(err) {
ctx.Error(404) ctx.Error(404)

View file

@ -102,7 +102,7 @@ func UpdateRelease(doer *models.User, gitRepo *git.Repository, rel *models.Relea
} }
rel.LowerTagName = strings.ToLower(rel.TagName) rel.LowerTagName = strings.ToLower(rel.TagName)
if err = models.UpdateRelease(rel); err != nil { if err = models.UpdateRelease(models.DefaultDBContext(), rel); err != nil {
return err return err
} }
@ -145,7 +145,7 @@ func DeleteReleaseByID(id int64, doer *models.User, delTag bool) error {
rel.Title = "" rel.Title = ""
rel.Note = "" rel.Note = ""
if err = models.UpdateRelease(rel); err != nil { if err = models.UpdateRelease(models.DefaultDBContext(), rel); err != nil {
return fmt.Errorf("Update: %v", err) return fmt.Errorf("Update: %v", err)
} }
} }