1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-23 10:48:37 +00:00

Use db.WithTx/WithTx2 instead of TxContext when possible (#35130)

This commit is contained in:
Lunny Xiao
2025-07-23 01:02:01 +08:00
committed by GitHub
parent f201dde945
commit 65cd3f5309
56 changed files with 1999 additions and 2640 deletions

View File

@@ -282,77 +282,72 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
// InsertRun inserts a run // InsertRun inserts a run
// The title will be cut off at 255 characters if it's longer than 255 characters. // The title will be cut off at 255 characters if it's longer than 255 characters.
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error { func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
return err
}
defer committer.Close()
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
if err != nil {
return err
}
run.Index = index
run.Title = util.EllipsisDisplayString(run.Title, 255)
if err := db.Insert(ctx, run); err != nil {
return err
}
if run.Repo == nil {
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
if err != nil { if err != nil {
return err return err
} }
run.Repo = repo run.Index = index
} run.Title = util.EllipsisDisplayString(run.Title, 255)
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil { if err := db.Insert(ctx, run); err != nil {
return err
}
runJobs := make([]*ActionRunJob, 0, len(jobs))
var hasWaiting bool
for _, v := range jobs {
id, job := v.Job()
needs := job.Needs()
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
return err return err
} }
payload, _ := v.Marshal()
status := StatusWaiting
if len(needs) > 0 || run.NeedApproval {
status = StatusBlocked
} else {
hasWaiting = true
}
job.Name = util.EllipsisDisplayString(job.Name, 255)
runJobs = append(runJobs, &ActionRunJob{
RunID: run.ID,
RepoID: run.RepoID,
OwnerID: run.OwnerID,
CommitSHA: run.CommitSHA,
IsForkPullRequest: run.IsForkPullRequest,
Name: job.Name,
WorkflowPayload: payload,
JobID: id,
Needs: needs,
RunsOn: job.RunsOn(),
Status: status,
})
}
if err := db.Insert(ctx, runJobs); err != nil {
return err
}
// if there is a job in the waiting status, increase tasks version. if run.Repo == nil {
if hasWaiting { repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil { if err != nil {
return err
}
run.Repo = repo
}
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
return err return err
} }
}
return committer.Commit() runJobs := make([]*ActionRunJob, 0, len(jobs))
var hasWaiting bool
for _, v := range jobs {
id, job := v.Job()
needs := job.Needs()
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
return err
}
payload, _ := v.Marshal()
status := StatusWaiting
if len(needs) > 0 || run.NeedApproval {
status = StatusBlocked
} else {
hasWaiting = true
}
job.Name = util.EllipsisDisplayString(job.Name, 255)
runJobs = append(runJobs, &ActionRunJob{
RunID: run.ID,
RepoID: run.RepoID,
OwnerID: run.OwnerID,
CommitSHA: run.CommitSHA,
IsForkPullRequest: run.IsForkPullRequest,
Name: job.Name,
WorkflowPayload: payload,
JobID: id,
Needs: needs,
RunsOn: job.RunsOn(),
Status: status,
})
}
if err := db.Insert(ctx, runJobs); err != nil {
return err
}
// if there is a job in the waiting status, increase tasks version.
if hasWaiting {
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil {
return err
}
}
return nil
})
} }
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) { func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {

View File

@@ -56,65 +56,54 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
return nil return nil
} }
// Begin transaction return db.WithTx(ctx, func(ctx context.Context) error {
ctx, committer, err := db.TxContext(ctx) // Loop through each schedule row
if err != nil { for _, row := range rows {
return err row.Title = util.EllipsisDisplayString(row.Title, 255)
} // Create new schedule row
defer committer.Close() if err := db.Insert(ctx, row); err != nil {
// Loop through each schedule row
for _, row := range rows {
row.Title = util.EllipsisDisplayString(row.Title, 255)
// Create new schedule row
if err = db.Insert(ctx, row); err != nil {
return err
}
// Loop through each schedule spec and create a new spec row
now := time.Now()
for _, spec := range row.Specs {
specRow := &ActionScheduleSpec{
RepoID: row.RepoID,
ScheduleID: row.ID,
Spec: spec,
}
// Parse the spec and check for errors
schedule, err := specRow.Parse()
if err != nil {
continue // skip to the next spec if there's an error
}
specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix())
// Insert the new schedule spec row
if err = db.Insert(ctx, specRow); err != nil {
return err return err
} }
}
}
// Commit transaction // Loop through each schedule spec and create a new spec row
return committer.Commit() now := time.Now()
for _, spec := range row.Specs {
specRow := &ActionScheduleSpec{
RepoID: row.RepoID,
ScheduleID: row.ID,
Spec: spec,
}
// Parse the spec and check for errors
schedule, err := specRow.Parse()
if err != nil {
continue // skip to the next spec if there's an error
}
specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix())
// Insert the new schedule spec row
if err = db.Insert(ctx, specRow); err != nil {
return err
}
}
}
return nil
})
} }
func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error { func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil {
return err return err
} }
defer committer.Close()
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil { if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil {
return err return err
} }
if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil { return nil
return err })
}
return committer.Commit()
} }
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) { func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {

View File

@@ -352,78 +352,70 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
stepStates[v.Id] = v stepStates[v.Id] = v
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*ActionTask, error) {
if err != nil { e := db.GetEngine(ctx)
return nil, err
}
defer committer.Close()
e := db.GetEngine(ctx) task := &ActionTask{}
if has, err := e.ID(state.Id).Get(task); err != nil {
return nil, err
} else if !has {
return nil, util.ErrNotExist
} else if runnerID != task.RunnerID {
return nil, errors.New("invalid runner for task")
}
task := &ActionTask{} if task.Status.IsDone() {
if has, err := e.ID(state.Id).Get(task); err != nil { // the state is final, do nothing
return nil, err return task, nil
} else if !has { }
return nil, util.ErrNotExist
} else if runnerID != task.RunnerID { // state.Result is not unspecified means the task is finished
return nil, errors.New("invalid runner for task") if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
} task.Status = Status(state.Result)
task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
if err := UpdateTask(ctx, task, "status", "stopped"); err != nil {
return nil, err
}
if _, err := UpdateRunJob(ctx, &ActionRunJob{
ID: task.JobID,
Status: task.Status,
Stopped: task.Stopped,
}, nil); err != nil {
return nil, err
}
} else {
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
task.Updated = timeutil.TimeStampNow()
if err := UpdateTask(ctx, task, "updated"); err != nil {
return nil, err
}
}
if err := task.LoadAttributes(ctx); err != nil {
return nil, err
}
for _, step := range task.Steps {
var result runnerv1.Result
if v, ok := stepStates[step.Index]; ok {
result = v.Result
step.LogIndex = v.LogIndex
step.LogLength = v.LogLength
step.Started = convertTimestamp(v.StartedAt)
step.Stopped = convertTimestamp(v.StoppedAt)
}
if result != runnerv1.Result_RESULT_UNSPECIFIED {
step.Status = Status(result)
} else if step.Started != 0 {
step.Status = StatusRunning
}
if _, err := e.ID(step.ID).Update(step); err != nil {
return nil, err
}
}
if task.Status.IsDone() {
// the state is final, do nothing
return task, nil return task, nil
} })
// state.Result is not unspecified means the task is finished
if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
task.Status = Status(state.Result)
task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
if err := UpdateTask(ctx, task, "status", "stopped"); err != nil {
return nil, err
}
if _, err := UpdateRunJob(ctx, &ActionRunJob{
ID: task.JobID,
Status: task.Status,
Stopped: task.Stopped,
}, nil); err != nil {
return nil, err
}
} else {
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
task.Updated = timeutil.TimeStampNow()
if err := UpdateTask(ctx, task, "updated"); err != nil {
return nil, err
}
}
if err := task.LoadAttributes(ctx); err != nil {
return nil, err
}
for _, step := range task.Steps {
var result runnerv1.Result
if v, ok := stepStates[step.Index]; ok {
result = v.Result
step.LogIndex = v.LogIndex
step.LogLength = v.LogLength
step.Started = convertTimestamp(v.StartedAt)
step.Stopped = convertTimestamp(v.StoppedAt)
}
if result != runnerv1.Result_RESULT_UNSPECIFIED {
step.Status = Status(result)
} else if step.Started != 0 {
step.Status = StatusRunning
}
if _, err := e.ID(step.ID).Update(step); err != nil {
return nil, err
}
}
if err := committer.Commit(); err != nil {
return nil, err
}
return task, nil
} }
func StopTask(ctx context.Context, taskID int64, status Status) error { func StopTask(ctx context.Context, taskID int64, status Status) error {

View File

@@ -73,33 +73,29 @@ func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) err
} }
func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error { func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { // 1. increase global
return err if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
} log.Error("IncreaseTasksVersionByScope(Global): %v", err)
defer committer.Close()
// 1. increase global
if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
log.Error("IncreaseTasksVersionByScope(Global): %v", err)
return err
}
// 2. increase owner
if ownerID > 0 {
if err := increaseTasksVersionByScope(ctx, ownerID, 0); err != nil {
log.Error("IncreaseTasksVersionByScope(Owner): %v", err)
return err return err
} }
}
// 3. increase repo // 2. increase owner
if repoID > 0 { if ownerID > 0 {
if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil { if err := increaseTasksVersionByScope(ctx, ownerID, 0); err != nil {
log.Error("IncreaseTasksVersionByScope(Repo): %v", err) log.Error("IncreaseTasksVersionByScope(Owner): %v", err)
return err return err
}
} }
}
return committer.Commit() // 3. increase repo
if repoID > 0 {
if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil {
log.Error("IncreaseTasksVersionByScope(Repo): %v", err)
return err
}
}
return nil
})
} }

View File

@@ -70,17 +70,9 @@ func (opts FindNotificationOptions) ToOrders() string {
// for each watcher, or updates it if already exists // for each watcher, or updates it if already exists
// receiverID > 0 just send to receiver, else send to all watcher // receiverID > 0 just send to receiver, else send to all watcher
func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error { func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { return createOrUpdateIssueNotifications(ctx, issueID, commentID, notificationAuthorID, receiverID)
return err })
}
defer committer.Close()
if err := createOrUpdateIssueNotifications(ctx, issueID, commentID, notificationAuthorID, receiverID); err != nil {
return err
}
return committer.Commit()
} }
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error { func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {

View File

@@ -228,17 +228,10 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
return fmt.Errorf("GetPublicKeyByID: %w", err) return fmt.Errorf("GetPublicKeyByID: %w", err)
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { _, err = deleteGPGKey(ctx, key.KeyID)
return err return err
} })
defer committer.Close()
if _, err = deleteGPGKey(ctx, key.KeyID); err != nil {
return err
}
return committer.Commit()
} }
func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) { func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {

View File

@@ -14,97 +14,76 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
) )
// __________________ ________ ____ __.
// / _____/\______ \/ _____/ | |/ _|____ ___.__.
// / \ ___ | ___/ \ ___ | <_/ __ < | |
// \ \_\ \| | \ \_\ \ | | \ ___/\___ |
// \______ /|____| \______ / |____|__ \___ > ____|
// \/ \/ \/ \/\/
// ____ ____ .__ _____
// \ \ / /___________|__|/ ____\__.__.
// \ Y // __ \_ __ \ \ __< | |
// \ /\ ___/| | \/ || | \___ |
// \___/ \___ >__| |__||__| / ____|
// \/ \/
// This file provides functions relating verifying gpg keys // This file provides functions relating verifying gpg keys
// VerifyGPGKey marks a GPG key as verified // VerifyGPGKey marks a GPG key as verified
func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature string) (string, error) { func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature string) (string, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (string, error) {
if err != nil { key := new(GPGKey)
return "", err
}
defer committer.Close()
key := new(GPGKey) has, err := db.GetEngine(ctx).Where("owner_id = ? AND key_id = ?", ownerID, keyID).Get(key)
if err != nil {
has, err := db.GetEngine(ctx).Where("owner_id = ? AND key_id = ?", ownerID, keyID).Get(key) return "", err
if err != nil { } else if !has {
return "", err return "", ErrGPGKeyNotExist{}
} else if !has {
return "", ErrGPGKeyNotExist{}
}
if err := key.LoadSubKeys(ctx); err != nil {
return "", err
}
sig, err := ExtractSignature(signature)
if err != nil {
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
Wrapped: err,
} }
}
signer, err := hashAndVerifyWithSubKeys(sig, token, key) if err := key.LoadSubKeys(ctx); err != nil {
if err != nil { return "", err
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
Wrapped: err,
} }
}
if signer == nil { sig, err := ExtractSignature(signature)
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n", key)
if err != nil { if err != nil {
return "", ErrGPGInvalidTokenSignature{ return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID, ID: key.KeyID,
Wrapped: err, Wrapped: err,
} }
} }
}
if signer == nil { signer, err := hashAndVerifyWithSubKeys(sig, token, key)
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n\n", key)
if err != nil { if err != nil {
return "", ErrGPGInvalidTokenSignature{ return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID, ID: key.KeyID,
Wrapped: err, Wrapped: err,
} }
} }
} if signer == nil {
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n", key)
if signer == nil { if err != nil {
log.Debug("VerifyGPGKey failed: no signer") return "", ErrGPGInvalidTokenSignature{
return "", ErrGPGInvalidTokenSignature{ ID: key.KeyID,
ID: key.KeyID, Wrapped: err,
}
}
}
if signer == nil {
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n\n", key)
if err != nil {
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
Wrapped: err,
}
}
} }
}
if signer.PrimaryKeyID != key.KeyID && signer.KeyID != key.KeyID { if signer == nil {
return "", ErrGPGKeyNotExist{} log.Debug("VerifyGPGKey failed: no signer")
} return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
}
}
key.Verified = true if signer.PrimaryKeyID != key.KeyID && signer.KeyID != key.KeyID {
if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil { return "", ErrGPGKeyNotExist{}
return "", err }
}
if err := committer.Commit(); err != nil { key.Verified = true
return "", err if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil {
} return "", err
}
return key.KeyID, nil return key.KeyID, nil
})
} }
// VerificationToken returns token for the user that will be valid in minutes (time) // VerificationToken returns token for the user that will be valid in minutes (time)

View File

@@ -99,40 +99,36 @@ func AddPublicKey(ctx context.Context, ownerID int64, name, content string, auth
return nil, err return nil, err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*PublicKey, error) {
if err != nil { if err := checkKeyFingerprint(ctx, fingerprint); err != nil {
return nil, err return nil, err
} }
defer committer.Close()
if err := checkKeyFingerprint(ctx, fingerprint); err != nil { // Key name of same user cannot be duplicated.
return nil, err has, err := db.GetEngine(ctx).
} Where("owner_id = ? AND name = ?", ownerID, name).
Get(new(PublicKey))
if err != nil {
return nil, err
} else if has {
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
}
// Key name of same user cannot be duplicated. key := &PublicKey{
has, err := db.GetEngine(ctx). OwnerID: ownerID,
Where("owner_id = ? AND name = ?", ownerID, name). Name: name,
Get(new(PublicKey)) Fingerprint: fingerprint,
if err != nil { Content: content,
return nil, err Mode: perm.AccessModeWrite,
} else if has { Type: KeyTypeUser,
return nil, ErrKeyNameAlreadyUsed{ownerID, name} LoginSourceID: authSourceID,
} }
if err = addKey(ctx, key); err != nil {
return nil, fmt.Errorf("addKey: %w", err)
}
key := &PublicKey{ return key, nil
OwnerID: ownerID, })
Name: name,
Fingerprint: fingerprint,
Content: content,
Mode: perm.AccessModeWrite,
Type: KeyTypeUser,
LoginSourceID: authSourceID,
}
if err = addKey(ctx, key); err != nil {
return nil, fmt.Errorf("addKey: %w", err)
}
return key, committer.Commit()
} }
// GetPublicKeyByID returns public key by given ID. // GetPublicKeyByID returns public key by given ID.
@@ -288,33 +284,24 @@ func PublicKeyIsExternallyManaged(ctx context.Context, id int64) (bool, error) {
// deleteKeysMarkedForDeletion returns true if ssh keys needs update // deleteKeysMarkedForDeletion returns true if ssh keys needs update
func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, error) { func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, error) {
// Start session return db.WithTx2(ctx, func(ctx context.Context) (bool, error) {
ctx, committer, err := db.TxContext(ctx) // Delete keys marked for deletion
if err != nil { var sshKeysNeedUpdate bool
return false, err for _, KeyToDelete := range keys {
} key, err := SearchPublicKeyByContent(ctx, KeyToDelete)
defer committer.Close() if err != nil {
log.Error("SearchPublicKeyByContent: %v", err)
// Delete keys marked for deletion continue
var sshKeysNeedUpdate bool }
for _, KeyToDelete := range keys { if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
key, err := SearchPublicKeyByContent(ctx, KeyToDelete) log.Error("DeleteByID[PublicKey]: %v", err)
if err != nil { continue
log.Error("SearchPublicKeyByContent: %v", err) }
continue sshKeysNeedUpdate = true
} }
if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
log.Error("DeleteByID[PublicKey]: %v", err)
continue
}
sshKeysNeedUpdate = true
}
if err := committer.Commit(); err != nil { return sshKeysNeedUpdate, nil
return false, err })
}
return sshKeysNeedUpdate, nil
} }
// AddPublicKeysBySource add a users public keys. Returns true if there are changes. // AddPublicKeysBySource add a users public keys. Returns true if there are changes.

View File

@@ -125,39 +125,35 @@ func AddDeployKey(ctx context.Context, repoID int64, name, content string, readO
accessMode = perm.AccessModeWrite accessMode = perm.AccessModeWrite
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
if err != nil { pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
return nil, err if err != nil {
} return nil, err
defer committer.Close() } else if exist {
if pkey.Type != KeyTypeDeploy {
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint}) return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
if err != nil { }
return nil, err } else {
} else if exist { // First time use this deploy key.
if pkey.Type != KeyTypeDeploy { pkey = &PublicKey{
return nil, ErrKeyAlreadyExist{0, fingerprint, ""} Fingerprint: fingerprint,
Mode: accessMode,
Type: KeyTypeDeploy,
Content: content,
Name: name,
}
if err = addKey(ctx, pkey); err != nil {
return nil, fmt.Errorf("addKey: %w", err)
}
} }
} else {
// First time use this deploy key.
pkey = &PublicKey{
Fingerprint: fingerprint,
Mode: accessMode,
Type: KeyTypeDeploy,
Content: content,
Name: name,
}
if err = addKey(ctx, pkey); err != nil {
return nil, fmt.Errorf("addKey: %w", err)
}
}
key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode) key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return key, committer.Commit() return key, nil
})
} }
// GetDeployKeyByID returns deploy key by given ID. // GetDeployKeyByID returns deploy key by given ID.

View File

@@ -15,41 +15,33 @@ import (
// VerifySSHKey marks a SSH key as verified // VerifySSHKey marks a SSH key as verified
func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signature string) (string, error) { func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signature string) (string, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (string, error) {
if err != nil { key := new(PublicKey)
return "", err
}
defer committer.Close()
key := new(PublicKey) has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key)
if err != nil {
return "", err
} else if !has {
return "", ErrKeyNotExist{}
}
has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key) err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
if err != nil { if err != nil {
return "", err // edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
} else if !has { // see https://github.com/PowerShell/PowerShell/issues/5974
return "", ErrKeyNotExist{} if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
} log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
return "", ErrSSHInvalidTokenSignature{
err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea") Fingerprint: key.Fingerprint,
if err != nil { }
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
// see https://github.com/PowerShell/PowerShell/issues/5974
if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
return "", ErrSSHInvalidTokenSignature{
Fingerprint: key.Fingerprint,
} }
} }
}
key.Verified = true key.Verified = true
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil { if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
return "", err return "", err
} }
if err := committer.Commit(); err != nil { return key.Fingerprint, nil
return "", err })
}
return key.Fingerprint, nil
} }

View File

@@ -289,35 +289,31 @@ type UpdateOAuth2ApplicationOptions struct {
// UpdateOAuth2Application updates an oauth2 application // UpdateOAuth2Application updates an oauth2 application
func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) { func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*OAuth2Application, error) {
if err != nil { app, err := GetOAuth2ApplicationByID(ctx, opts.ID)
return nil, err if err != nil {
} return nil, err
defer committer.Close() }
if app.UID != opts.UserID {
return nil, errors.New("UID mismatch")
}
builtinApps := BuiltinApplications()
if _, builtin := builtinApps[app.ClientID]; builtin {
return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID)
}
app, err := GetOAuth2ApplicationByID(ctx, opts.ID) app.Name = opts.Name
if err != nil { app.RedirectURIs = opts.RedirectURIs
return nil, err app.ConfidentialClient = opts.ConfidentialClient
} app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization
if app.UID != opts.UserID {
return nil, errors.New("UID mismatch")
}
builtinApps := BuiltinApplications()
if _, builtin := builtinApps[app.ClientID]; builtin {
return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID)
}
app.Name = opts.Name if err = updateOAuth2Application(ctx, app); err != nil {
app.RedirectURIs = opts.RedirectURIs return nil, err
app.ConfidentialClient = opts.ConfidentialClient }
app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization app.ClientSecret = ""
if err = updateOAuth2Application(ctx, app); err != nil { return app, nil
return nil, err })
}
app.ClientSecret = ""
return app, committer.Commit()
} }
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error { func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
@@ -358,23 +354,17 @@ func deleteOAuth2Application(ctx context.Context, id, userid int64) error {
// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app. // DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
func DeleteOAuth2Application(ctx context.Context, id, userid int64) error { func DeleteOAuth2Application(ctx context.Context, id, userid int64) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { app, err := GetOAuth2ApplicationByID(ctx, id)
return err if err != nil {
} return err
defer committer.Close() }
app, err := GetOAuth2ApplicationByID(ctx, id) builtinApps := BuiltinApplications()
if err != nil { if _, builtin := builtinApps[app.ClientID]; builtin {
return err return fmt.Errorf("failed to delete OAuth2 application: application is locked: %s", app.ClientID)
} }
builtinApps := BuiltinApplications() return deleteOAuth2Application(ctx, id, userid)
if _, builtin := builtinApps[app.ClientID]; builtin { })
return fmt.Errorf("failed to delete OAuth2 application: application is locked: %s", app.ClientID)
}
if err := deleteOAuth2Application(ctx, id, userid); err != nil {
return err
}
return committer.Commit()
} }
////////////////////////////////////////////////////// //////////////////////////////////////////////////////

View File

@@ -35,26 +35,22 @@ func UpdateSession(ctx context.Context, key string, data []byte) error {
// ReadSession reads the data for the provided session // ReadSession reads the data for the provided session
func ReadSession(ctx context.Context, key string) (*Session, error) { func ReadSession(ctx context.Context, key string) (*Session, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Session, error) {
if err != nil { session, exist, err := db.Get[Session](ctx, builder.Eq{"`key`": key})
return nil, err if err != nil {
}
defer committer.Close()
session, exist, err := db.Get[Session](ctx, builder.Eq{"`key`": key})
if err != nil {
return nil, err
} else if !exist {
session = &Session{
Key: key,
Expiry: timeutil.TimeStampNow(),
}
if err := db.Insert(ctx, session); err != nil {
return nil, err return nil, err
} else if !exist {
session = &Session{
Key: key,
Expiry: timeutil.TimeStampNow(),
}
if err := db.Insert(ctx, session); err != nil {
return nil, err
}
} }
}
return session, committer.Commit() return session, nil
})
} }
// ExistSession checks if a session exists // ExistSession checks if a session exists
@@ -72,40 +68,36 @@ func DestroySession(ctx context.Context, key string) error {
// RegenerateSession regenerates a session from the old id // RegenerateSession regenerates a session from the old id
func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, error) { func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Session, error) {
if err != nil { if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": newKey}); err != nil {
return nil, err return nil, err
} } else if has {
defer committer.Close() return nil, fmt.Errorf("session Key: %s already exists", newKey)
}
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": newKey}); err != nil { if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil {
return nil, err return nil, err
} else if has { } else if !has {
return nil, fmt.Errorf("session Key: %s already exists", newKey) if err := db.Insert(ctx, &Session{
} Key: oldKey,
Expiry: timeutil.TimeStampNow(),
}); err != nil {
return nil, err
}
}
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil { if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
return nil, err
} else if !has {
if err := db.Insert(ctx, &Session{
Key: oldKey,
Expiry: timeutil.TimeStampNow(),
}); err != nil {
return nil, err return nil, err
} }
}
if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil { s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey})
return nil, err if err != nil {
} // is not exist, it should be impossible
return nil, err
}
s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey}) return s, nil
if err != nil { })
// is not exist, it should be impossible
return nil, err
}
return s, committer.Commit()
} }
// CountSessions returns the number of sessions // CountSessions returns the number of sessions

View File

@@ -470,35 +470,31 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA) return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { // Get the next Status Index
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", opts.Repo.ID, opts.Creator.ID, opts.SHA, err) idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
} if err != nil {
defer committer.Close() return fmt.Errorf("generate commit status index failed: %w", err)
}
// Get the next Status Index opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String()) opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
if err != nil { opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
return fmt.Errorf("generate commit status index failed: %w", err) opts.CommitStatus.SHA = opts.SHA.String()
} opts.CommitStatus.CreatorID = opts.Creator.ID
opts.CommitStatus.RepoID = opts.Repo.ID
opts.CommitStatus.Index = idx
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description) opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
opts.CommitStatus.SHA = opts.SHA.String()
opts.CommitStatus.CreatorID = opts.Creator.ID
opts.CommitStatus.RepoID = opts.Repo.ID
opts.CommitStatus.Index = idx
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context) // Insert new CommitStatus
if err = db.Insert(ctx, opts.CommitStatus); err != nil {
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
}
// Insert new CommitStatus return nil
if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil { })
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
}
return committer.Commit()
} }
// SignCommitWithStatuses represents a commit with validation of signature and status state. // SignCommitWithStatuses represents a commit with validation of signature and status state.

View File

@@ -135,25 +135,18 @@ var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database // NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
// if it is not already present. // if it is not already present.
func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMetaObject, error) { func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMetaObject, error) {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err
}
defer committer.Close()
m, exist, err := db.Get[LFSMetaObject](ctx, builder.Eq{"repository_id": repoID, "oid": p.Oid}) m, exist, err := db.Get[LFSMetaObject](ctx, builder.Eq{"repository_id": repoID, "oid": p.Oid})
if err != nil { if err != nil {
return nil, err return nil, err
} else if exist { } else if exist {
return m, committer.Commit() return m, nil
} }
m = &LFSMetaObject{Pointer: p, RepositoryID: repoID} m = &LFSMetaObject{Pointer: p, RepositoryID: repoID}
if err = db.Insert(ctx, m); err != nil { if err = db.Insert(ctx, m); err != nil {
return nil, err return nil, err
} }
return m, nil
return m, committer.Commit()
} }
// GetLFSMetaObjectByOid selects a LFSMetaObject entry from database by its OID. // GetLFSMetaObjectByOid selects a LFSMetaObject entry from database by its OID.

View File

@@ -70,32 +70,28 @@ func (l *LFSLock) LoadOwner(ctx context.Context) error {
// CreateLFSLock creates a new lock. // CreateLFSLock creates a new lock.
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) { func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
dbCtx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
if err != nil { if err := CheckLFSAccessForRepo(ctx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil {
return nil, err return nil, err
} }
defer committer.Close()
if err := CheckLFSAccessForRepo(dbCtx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil { lock.Path = util.PathJoinRel(lock.Path)
return nil, err lock.RepoID = repo.ID
}
lock.Path = util.PathJoinRel(lock.Path) l, err := GetLFSLock(ctx, repo, lock.Path)
lock.RepoID = repo.ID if err == nil {
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
}
if !IsErrLFSLockNotExist(err) {
return nil, err
}
l, err := GetLFSLock(dbCtx, repo, lock.Path) if err := db.Insert(ctx, lock); err != nil {
if err == nil { return nil, err
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path} }
}
if !IsErrLFSLockNotExist(err) {
return nil, err
}
if err := db.Insert(dbCtx, lock); err != nil { return lock, nil
return nil, err })
}
return lock, committer.Commit()
} }
// GetLFSLock returns release by given path. // GetLFSLock returns release by given path.
@@ -163,30 +159,26 @@ func CountLFSLockByRepoID(ctx context.Context, repoID int64) (int64, error) {
// DeleteLFSLockByID deletes a lock by given ID. // DeleteLFSLockByID deletes a lock by given ID.
func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) { func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
dbCtx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
if err != nil { lock, err := GetLFSLockByID(ctx, id)
return nil, err if err != nil {
} return nil, err
defer committer.Close() }
lock, err := GetLFSLockByID(dbCtx, id) if err := CheckLFSAccessForRepo(ctx, u.ID, repo, perm.AccessModeWrite); err != nil {
if err != nil { return nil, err
return nil, err }
}
if err := CheckLFSAccessForRepo(dbCtx, u.ID, repo, perm.AccessModeWrite); err != nil { if !force && u.ID != lock.OwnerID {
return nil, err return nil, errors.New("user doesn't own lock and force flag is not set")
} }
if !force && u.ID != lock.OwnerID { if _, err := db.GetEngine(ctx).ID(id).Delete(new(LFSLock)); err != nil {
return nil, errors.New("user doesn't own lock and force flag is not set") return nil, err
} }
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil { return lock, nil
return nil, err })
}
return lock, committer.Commit()
} }
// CheckLFSAccessForRepo check needed access mode base on action // CheckLFSAccessForRepo check needed access mode base on action

View File

@@ -766,81 +766,73 @@ func (c *Comment) CodeCommentLink(ctx context.Context) string {
// CreateComment creates comment with context // CreateComment creates comment with context
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) { func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
if err != nil { var LabelID int64
return nil, err if opts.Label != nil {
} LabelID = opts.Label.ID
defer committer.Close()
e := db.GetEngine(ctx)
var LabelID int64
if opts.Label != nil {
LabelID = opts.Label.ID
}
var commentMetaData *CommentMetaData
if opts.ProjectColumnTitle != "" {
commentMetaData = &CommentMetaData{
ProjectColumnID: opts.ProjectColumnID,
ProjectColumnTitle: opts.ProjectColumnTitle,
ProjectTitle: opts.ProjectTitle,
} }
}
comment := &Comment{ var commentMetaData *CommentMetaData
Type: opts.Type, if opts.ProjectColumnTitle != "" {
PosterID: opts.Doer.ID, commentMetaData = &CommentMetaData{
Poster: opts.Doer, ProjectColumnID: opts.ProjectColumnID,
IssueID: opts.Issue.ID, ProjectColumnTitle: opts.ProjectColumnTitle,
LabelID: LabelID, ProjectTitle: opts.ProjectTitle,
OldMilestoneID: opts.OldMilestoneID, }
MilestoneID: opts.MilestoneID, }
OldProjectID: opts.OldProjectID,
ProjectID: opts.ProjectID,
TimeID: opts.TimeID,
RemovedAssignee: opts.RemovedAssignee,
AssigneeID: opts.AssigneeID,
AssigneeTeamID: opts.AssigneeTeamID,
CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
Content: opts.Content,
OldTitle: opts.OldTitle,
NewTitle: opts.NewTitle,
OldRef: opts.OldRef,
NewRef: opts.NewRef,
DependentIssueID: opts.DependentIssueID,
TreePath: opts.TreePath,
ReviewID: opts.ReviewID,
Patch: opts.Patch,
RefRepoID: opts.RefRepoID,
RefIssueID: opts.RefIssueID,
RefCommentID: opts.RefCommentID,
RefAction: opts.RefAction,
RefIsPull: opts.RefIsPull,
IsForcePush: opts.IsForcePush,
Invalidated: opts.Invalidated,
CommentMetaData: commentMetaData,
}
if _, err = e.Insert(comment); err != nil {
return nil, err
}
if err = opts.Repo.LoadOwner(ctx); err != nil { comment := &Comment{
return nil, err Type: opts.Type,
} PosterID: opts.Doer.ID,
Poster: opts.Doer,
IssueID: opts.Issue.ID,
LabelID: LabelID,
OldMilestoneID: opts.OldMilestoneID,
MilestoneID: opts.MilestoneID,
OldProjectID: opts.OldProjectID,
ProjectID: opts.ProjectID,
TimeID: opts.TimeID,
RemovedAssignee: opts.RemovedAssignee,
AssigneeID: opts.AssigneeID,
AssigneeTeamID: opts.AssigneeTeamID,
CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
Content: opts.Content,
OldTitle: opts.OldTitle,
NewTitle: opts.NewTitle,
OldRef: opts.OldRef,
NewRef: opts.NewRef,
DependentIssueID: opts.DependentIssueID,
TreePath: opts.TreePath,
ReviewID: opts.ReviewID,
Patch: opts.Patch,
RefRepoID: opts.RefRepoID,
RefIssueID: opts.RefIssueID,
RefCommentID: opts.RefCommentID,
RefAction: opts.RefAction,
RefIsPull: opts.RefIsPull,
IsForcePush: opts.IsForcePush,
Invalidated: opts.Invalidated,
CommentMetaData: commentMetaData,
}
if err = db.Insert(ctx, comment); err != nil {
return nil, err
}
if err = updateCommentInfos(ctx, opts, comment); err != nil { if err = opts.Repo.LoadOwner(ctx); err != nil {
return nil, err return nil, err
} }
if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil { if err = updateCommentInfos(ctx, opts, comment); err != nil {
return nil, err return nil, err
} }
if err = committer.Commit(); err != nil {
return nil, err if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil {
} return nil, err
return comment, nil }
return comment, nil
})
} }
func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment *Comment) (err error) { func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment *Comment) (err error) {
@@ -1092,33 +1084,21 @@ func UpdateCommentInvalidate(ctx context.Context, c *Comment) error {
// UpdateComment updates information of comment. // UpdateComment updates information of comment.
func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error { func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { c.ContentVersion = contentVersion + 1
return err
}
defer committer.Close()
sess := db.GetEngine(ctx)
c.ContentVersion = contentVersion + 1 affected, err := db.GetEngine(ctx).ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
if err != nil {
affected, err := sess.ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c) return err
if err != nil { }
return err if affected == 0 {
} return ErrCommentAlreadyChanged
if affected == 0 { }
return ErrCommentAlreadyChanged if err := c.LoadIssue(ctx); err != nil {
} return err
if err := c.LoadIssue(ctx); err != nil { }
return err return c.AddCrossReferences(ctx, doer, true)
} })
if err := c.AddCrossReferences(ctx, doer, true); err != nil {
return err
}
if err := committer.Commit(); err != nil {
return fmt.Errorf("Commit: %w", err)
}
return nil
} }
// DeleteComment deletes the comment // DeleteComment deletes the comment
@@ -1277,31 +1257,28 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
return comment.IssueID, true return comment.IssueID, true
}) })
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { for _, comment := range comments {
return err if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil {
} return err
defer committer.Close() }
for _, comment := range comments {
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil { for _, reaction := range comment.Reactions {
return err reaction.IssueID = comment.IssueID
reaction.CommentID = comment.ID
}
if len(comment.Reactions) > 0 {
if err := db.Insert(ctx, comment.Reactions); err != nil {
return err
}
}
} }
for _, reaction := range comment.Reactions { for _, issueID := range issueIDs {
reaction.IssueID = comment.IssueID if err := UpdateIssueNumComments(ctx, issueID); err != nil {
reaction.CommentID = comment.ID
}
if len(comment.Reactions) > 0 {
if err := db.Insert(ctx, comment.Reactions); err != nil {
return err return err
} }
} }
} return nil
})
for _, issueID := range issueIDs {
if err := UpdateIssueNumComments(ctx, issueID); err != nil {
return err
}
}
return committer.Commit()
} }

View File

@@ -128,79 +128,64 @@ const (
// CreateIssueDependency creates a new dependency for an issue // CreateIssueDependency creates a new dependency for an issue
func CreateIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue) error { func CreateIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { // Check if it already exists
return err exists, err := issueDepExists(ctx, issue.ID, dep.ID)
} if err != nil {
defer committer.Close() return err
}
if exists {
return ErrDependencyExists{issue.ID, dep.ID}
}
// And if it would be circular
circular, err := issueDepExists(ctx, dep.ID, issue.ID)
if err != nil {
return err
}
if circular {
return ErrCircularDependency{issue.ID, dep.ID}
}
// Check if it already exists if err := db.Insert(ctx, &IssueDependency{
exists, err := issueDepExists(ctx, issue.ID, dep.ID) UserID: user.ID,
if err != nil { IssueID: issue.ID,
return err DependencyID: dep.ID,
} }); err != nil {
if exists { return err
return ErrDependencyExists{issue.ID, dep.ID} }
}
// And if it would be circular
circular, err := issueDepExists(ctx, dep.ID, issue.ID)
if err != nil {
return err
}
if circular {
return ErrCircularDependency{issue.ID, dep.ID}
}
if err := db.Insert(ctx, &IssueDependency{ // Add comment referencing the new dependency
UserID: user.ID, return createIssueDependencyComment(ctx, user, issue, dep, true)
IssueID: issue.ID, })
DependencyID: dep.ID,
}); err != nil {
return err
}
// Add comment referencing the new dependency
if err = createIssueDependencyComment(ctx, user, issue, dep, true); err != nil {
return err
}
return committer.Commit()
} }
// RemoveIssueDependency removes a dependency from an issue // RemoveIssueDependency removes a dependency from an issue
func RemoveIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) { func RemoveIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { var issueDepToDelete IssueDependency
return err
}
defer committer.Close()
var issueDepToDelete IssueDependency switch depType {
case DependencyTypeBlockedBy:
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
case DependencyTypeBlocking:
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
default:
return ErrUnknownDependencyType{depType}
}
switch depType { affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete)
case DependencyTypeBlockedBy: if err != nil {
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID} return err
case DependencyTypeBlocking: }
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
default:
return ErrUnknownDependencyType{depType}
}
affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete) // If we deleted nothing, the dependency did not exist
if err != nil { if affected <= 0 {
return err return ErrDependencyNotExists{issue.ID, dep.ID}
} }
// If we deleted nothing, the dependency did not exist // Add comment referencing the removed dependency
if affected <= 0 { return createIssueDependencyComment(ctx, user, issue, dep, false)
return ErrDependencyNotExists{issue.ID, dep.ID} })
}
// Add comment referencing the removed dependency
if err = createIssueDependencyComment(ctx, user, issue, dep, false); err != nil {
return err
}
return committer.Commit()
} }
// Check if the dependency already exists // Check if the dependency already exists

View File

@@ -755,18 +755,14 @@ func (issue *Issue) HasOriginalAuthor() bool {
// InsertIssues insert issues to database // InsertIssues insert issues to database
func InsertIssues(ctx context.Context, issues ...*Issue) error { func InsertIssues(ctx context.Context, issues ...*Issue) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { for _, issue := range issues {
return err if err := insertIssue(ctx, issue); err != nil {
} return err
defer committer.Close() }
for _, issue := range issues {
if err := insertIssue(ctx, issue); err != nil {
return err
} }
} return nil
return committer.Commit() })
} }
func insertIssue(ctx context.Context, issue *Issue) error { func insertIssue(ctx context.Context, issue *Issue) error {

View File

@@ -12,20 +12,12 @@ import (
// RecalculateIssueIndexForRepo create issue_index for repo if not exist and // RecalculateIssueIndexForRepo create issue_index for repo if not exist and
// update it based on highest index of existing issues assigned to a repo // update it based on highest index of existing issues assigned to a repo
func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error { func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { var maxIndex int64
return err if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&maxIndex); err != nil {
} return err
defer committer.Close() }
var maxIndex int64 return db.SyncMaxResourceIndex(ctx, "issue_index", repoID, maxIndex)
if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&maxIndex); err != nil { })
return err
}
if err = db.SyncMaxResourceIndex(ctx, "issue_index", repoID, maxIndex); err != nil {
return err
}
return committer.Commit()
} }

View File

@@ -88,36 +88,28 @@ func NewIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_m
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err = issue.LoadRepo(ctx); err != nil {
return err return err
} }
defer committer.Close()
if err = issue.LoadRepo(ctx); err != nil { // Do NOT add invalid labels
return err if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
} return nil
}
// Do NOT add invalid labels if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil {
if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID { return nil
return nil }
}
if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil { if err = newIssueLabel(ctx, issue, label, doer); err != nil {
return nil return err
} }
if err = newIssueLabel(ctx, issue, label, doer); err != nil { issue.isLabelsLoaded = false
return err issue.Labels = nil
} return issue.LoadLabels(ctx)
})
issue.isLabelsLoaded = false
issue.Labels = nil
if err = issue.LoadLabels(ctx); err != nil {
return err
}
return committer.Commit()
} }
// newIssueLabels add labels to an issue. It will check if the labels are valid for the issue // newIssueLabels add labels to an issue. It will check if the labels are valid for the issue
@@ -151,24 +143,16 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *us
// NewIssueLabels creates a list of issue-label relations. // NewIssueLabels creates a list of issue-label relations.
func NewIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) { func NewIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err = newIssueLabels(ctx, issue, labels, doer); err != nil {
return err return err
} }
defer committer.Close()
if err = newIssueLabels(ctx, issue, labels, doer); err != nil { // reload all labels
return err issue.isLabelsLoaded = false
} issue.Labels = nil
return issue.LoadLabels(ctx)
// reload all labels })
issue.isLabelsLoaded = false
issue.Labels = nil
if err = issue.LoadLabels(ctx); err != nil {
return err
}
return committer.Commit()
} }
func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) { func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) {
@@ -365,35 +349,23 @@ func clearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User)
// ClearIssueLabels removes all issue labels as the given user. // ClearIssueLabels removes all issue labels as the given user.
// Triggers appropriate WebHooks, if any. // Triggers appropriate WebHooks, if any.
func ClearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) { func ClearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err := issue.LoadRepo(ctx); err != nil {
return err return err
} } else if err = issue.LoadPullRequest(ctx); err != nil {
defer committer.Close() return err
}
if err := issue.LoadRepo(ctx); err != nil { perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
return err if err != nil {
} else if err = issue.LoadPullRequest(ctx); err != nil { return err
return err }
} if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
return ErrRepoLabelNotExist{}
}
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer) return clearIssueLabels(ctx, issue, doer)
if err != nil { })
return err
}
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
return ErrRepoLabelNotExist{}
}
if err = clearIssueLabels(ctx, issue, doer); err != nil {
return err
}
if err = committer.Commit(); err != nil {
return fmt.Errorf("Commit: %w", err)
}
return nil
} }
type labelSorter []*Label type labelSorter []*Label
@@ -438,69 +410,61 @@ func RemoveDuplicateExclusiveLabels(labels []*Label) []*Label {
// ReplaceIssueLabels removes all current labels and add new labels to the issue. // ReplaceIssueLabels removes all current labels and add new labels to the issue.
// Triggers appropriate WebHooks, if any. // Triggers appropriate WebHooks, if any.
func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) { func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err = issue.LoadRepo(ctx); err != nil {
return err return err
} }
defer committer.Close()
if err = issue.LoadRepo(ctx); err != nil { if err = issue.LoadLabels(ctx); err != nil {
return err return err
} }
if err = issue.LoadLabels(ctx); err != nil { labels = RemoveDuplicateExclusiveLabels(labels)
return err
}
labels = RemoveDuplicateExclusiveLabels(labels) sort.Sort(labelSorter(labels))
sort.Sort(labelSorter(issue.Labels))
sort.Sort(labelSorter(labels)) var toAdd, toRemove []*Label
sort.Sort(labelSorter(issue.Labels))
var toAdd, toRemove []*Label addIndex, removeIndex := 0, 0
for addIndex < len(labels) && removeIndex < len(issue.Labels) {
addLabel := labels[addIndex]
removeLabel := issue.Labels[removeIndex]
if addLabel.ID == removeLabel.ID {
// Silently drop invalid labels
if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID {
toRemove = append(toRemove, removeLabel)
}
addIndex, removeIndex := 0, 0 addIndex++
for addIndex < len(labels) && removeIndex < len(issue.Labels) { removeIndex++
addLabel := labels[addIndex] } else if addLabel.ID < removeLabel.ID {
removeLabel := issue.Labels[removeIndex] // Only add if the label is valid
if addLabel.ID == removeLabel.ID { if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID {
// Silently drop invalid labels toAdd = append(toAdd, addLabel)
if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID { }
addIndex++
} else {
toRemove = append(toRemove, removeLabel) toRemove = append(toRemove, removeLabel)
removeIndex++
} }
}
toAdd = append(toAdd, labels[addIndex:]...)
toRemove = append(toRemove, issue.Labels[removeIndex:]...)
addIndex++ if len(toAdd) > 0 {
removeIndex++ if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil {
} else if addLabel.ID < removeLabel.ID { return fmt.Errorf("addLabels: %w", err)
// Only add if the label is valid
if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID {
toAdd = append(toAdd, addLabel)
} }
addIndex++
} else {
toRemove = append(toRemove, removeLabel)
removeIndex++
} }
}
toAdd = append(toAdd, labels[addIndex:]...)
toRemove = append(toRemove, issue.Labels[removeIndex:]...)
if len(toAdd) > 0 { for _, l := range toRemove {
if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil { if err = deleteIssueLabel(ctx, issue, l, doer); err != nil {
return fmt.Errorf("addLabels: %w", err) return fmt.Errorf("removeLabel: %w", err)
}
} }
}
for _, l := range toRemove { issue.Labels = nil
if err = deleteIssueLabel(ctx, issue, l, doer); err != nil { return issue.LoadLabels(ctx)
return fmt.Errorf("removeLabel: %w", err) })
}
}
issue.Labels = nil
if err = issue.LoadLabels(ctx); err != nil {
return err
}
return committer.Commit()
} }

View File

@@ -47,26 +47,19 @@ func updateIssueLock(ctx context.Context, opts *IssueLockOptions, lock bool) err
commentType = CommentTypeUnlock commentType = CommentTypeUnlock
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil {
return err return err
} }
defer committer.Close()
if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil { opt := &CreateCommentOptions{
Doer: opts.Doer,
Issue: opts.Issue,
Repo: opts.Issue.Repo,
Type: commentType,
Content: opts.Reason,
}
_, err := CreateComment(ctx, opt)
return err return err
} })
opt := &CreateCommentOptions{
Doer: opts.Doer,
Issue: opts.Issue,
Repo: opts.Issue.Repo,
Type: commentType,
Content: opts.Reason,
}
if _, err := CreateComment(ctx, opt); err != nil {
return err
}
return committer.Commit()
} }

View File

@@ -167,20 +167,9 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
return nil, err return nil, err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
if err != nil { return SetIssueAsClosed(ctx, issue, doer, false)
return nil, err })
}
defer committer.Close()
comment, err := SetIssueAsClosed(ctx, issue, doer, false)
if err != nil {
return nil, err
}
if err := committer.Commit(); err != nil {
return nil, err
}
return comment, nil
} }
// ReopenIssue changes issue status to open. // ReopenIssue changes issue status to open.
@@ -192,88 +181,64 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com
return nil, err return nil, err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
if err != nil { return setIssueAsReopen(ctx, issue, doer)
return nil, err })
}
defer committer.Close()
comment, err := setIssueAsReopen(ctx, issue, doer)
if err != nil {
return nil, err
}
if err := committer.Commit(); err != nil {
return nil, err
}
return comment, nil
} }
// ChangeIssueTitle changes the title of this issue, as the given user. // ChangeIssueTitle changes the title of this issue, as the given user.
func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) { func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { issue.Title = util.EllipsisDisplayString(issue.Title, 255)
return err if err = UpdateIssueCols(ctx, issue, "name"); err != nil {
} return fmt.Errorf("updateIssueCols: %w", err)
defer committer.Close() }
issue.Title = util.EllipsisDisplayString(issue.Title, 255) if err = issue.LoadRepo(ctx); err != nil {
if err = UpdateIssueCols(ctx, issue, "name"); err != nil { return fmt.Errorf("loadRepo: %w", err)
return fmt.Errorf("updateIssueCols: %w", err) }
}
if err = issue.LoadRepo(ctx); err != nil { opts := &CreateCommentOptions{
return fmt.Errorf("loadRepo: %w", err) Type: CommentTypeChangeTitle,
} Doer: doer,
Repo: issue.Repo,
opts := &CreateCommentOptions{ Issue: issue,
Type: CommentTypeChangeTitle, OldTitle: oldTitle,
Doer: doer, NewTitle: issue.Title,
Repo: issue.Repo, }
Issue: issue, if _, err = CreateComment(ctx, opts); err != nil {
OldTitle: oldTitle, return fmt.Errorf("createComment: %w", err)
NewTitle: issue.Title, }
} return issue.AddCrossReferences(ctx, doer, true)
if _, err = CreateComment(ctx, opts); err != nil { })
return fmt.Errorf("createComment: %w", err)
}
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
return err
}
return committer.Commit()
} }
// ChangeIssueRef changes the branch of this issue, as the given user. // ChangeIssueRef changes the branch of this issue, as the given user.
func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) { func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err = UpdateIssueCols(ctx, issue, "ref"); err != nil {
return err return fmt.Errorf("updateIssueCols: %w", err)
} }
defer committer.Close()
if err = UpdateIssueCols(ctx, issue, "ref"); err != nil { if err = issue.LoadRepo(ctx); err != nil {
return fmt.Errorf("updateIssueCols: %w", err) return fmt.Errorf("loadRepo: %w", err)
} }
oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix)
newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix)
if err = issue.LoadRepo(ctx); err != nil { opts := &CreateCommentOptions{
return fmt.Errorf("loadRepo: %w", err) Type: CommentTypeChangeIssueRef,
} Doer: doer,
oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix) Repo: issue.Repo,
newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix) Issue: issue,
OldRef: oldRefFriendly,
opts := &CreateCommentOptions{ NewRef: newRefFriendly,
Type: CommentTypeChangeIssueRef, }
Doer: doer, if _, err = CreateComment(ctx, opts); err != nil {
Repo: issue.Repo, return fmt.Errorf("createComment: %w", err)
Issue: issue, }
OldRef: oldRefFriendly, return nil
NewRef: newRefFriendly, })
}
if _, err = CreateComment(ctx, opts); err != nil {
return fmt.Errorf("createComment: %w", err)
}
return committer.Commit()
} }
// AddDeletePRBranchComment adds delete branch comment for pull request issue // AddDeletePRBranchComment adds delete branch comment for pull request issue
@@ -295,64 +260,56 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *
// UpdateIssueAttachments update attachments by UUIDs for the issue // UpdateIssueAttachments update attachments by UUIDs for the issue
func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) { func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
return err if err != nil {
} return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
defer committer.Close()
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
if err != nil {
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
}
for i := range attachments {
attachments[i].IssueID = issueID
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
} }
} for i := range attachments {
return committer.Commit() attachments[i].IssueID = issueID
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
}
}
return nil
})
} }
// ChangeIssueContent changes issue content, as the given user. // ChangeIssueContent changes issue content, as the given user.
func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) { func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0)
return err if err != nil {
} return fmt.Errorf("HasIssueContentHistory: %w", err)
defer committer.Close() }
if !hasContentHistory {
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
issue.CreatedUnix, issue.Content, true); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %w", err)
}
}
hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0) issue.Content = content
if err != nil { issue.ContentVersion = contentVersion + 1
return fmt.Errorf("HasIssueContentHistory: %w", err)
} affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue)
if !hasContentHistory { if err != nil {
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0, return err
issue.CreatedUnix, issue.Content, true); err != nil { }
if affected == 0 {
return ErrIssueAlreadyChanged
}
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
timeutil.TimeStampNow(), issue.Content, false); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %w", err) return fmt.Errorf("SaveIssueContentHistory: %w", err)
} }
}
issue.Content = content if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
issue.ContentVersion = contentVersion + 1 return fmt.Errorf("addCrossReferences: %w", err)
}
affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue) return nil
if err != nil { })
return err
}
if affected == 0 {
return ErrIssueAlreadyChanged
}
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
timeutil.TimeStampNow(), issue.Content, false); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %w", err)
}
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
return fmt.Errorf("addCrossReferences: %w", err)
}
return committer.Commit()
} }
// NewIssueOptions represents the options of a new issue. // NewIssueOptions represents the options of a new issue.
@@ -512,23 +469,19 @@ func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeuti
if issue.DeadlineUnix == deadlineUnix { if issue.DeadlineUnix == deadlineUnix {
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
// Update the deadline return db.WithTx(ctx, func(ctx context.Context) error {
if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil { // Update the deadline
return err if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil {
} return err
}
// Make the comment // Make the comment
if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil { if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil {
return fmt.Errorf("createRemovedDueDateComment: %w", err) return fmt.Errorf("createRemovedDueDateComment: %w", err)
} }
return nil
return committer.Commit() })
} }
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database. // FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.

View File

@@ -209,24 +209,20 @@ func NewLabel(ctx context.Context, l *Label) error {
// NewLabels creates new labels // NewLabels creates new labels
func NewLabels(ctx context.Context, labels ...*Label) error { func NewLabels(ctx context.Context, labels ...*Label) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { for _, l := range labels {
return err color, err := label.NormalizeColor(l.Color)
} if err != nil {
defer committer.Close() return err
}
l.Color = color
for _, l := range labels { if err := db.Insert(ctx, l); err != nil {
color, err := label.NormalizeColor(l.Color) return err
if err != nil { }
return err
} }
l.Color = color return nil
})
if err := db.Insert(ctx, l); err != nil {
return err
}
}
return committer.Commit()
} }
// UpdateLabel updates label information. // UpdateLabel updates label information.
@@ -250,35 +246,26 @@ func DeleteLabel(ctx context.Context, id, labelID int64) error {
return err return err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if l.BelongsToOrg() && l.OrgID != id {
return nil
}
if l.BelongsToRepo() && l.RepoID != id {
return nil
}
if _, err = db.DeleteByID[Label](ctx, labelID); err != nil {
return err
} else if _, err = db.GetEngine(ctx).
Where("label_id = ?", labelID).
Delete(new(IssueLabel)); err != nil {
return err
}
// delete comments about now deleted label_id
_, err = db.GetEngine(ctx).Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{})
return err return err
} })
defer committer.Close()
sess := db.GetEngine(ctx)
if l.BelongsToOrg() && l.OrgID != id {
return nil
}
if l.BelongsToRepo() && l.RepoID != id {
return nil
}
if _, err = db.DeleteByID[Label](ctx, labelID); err != nil {
return err
} else if _, err = sess.
Where("label_id = ?", labelID).
Delete(new(IssueLabel)); err != nil {
return err
}
// delete comments about now deleted label_id
if _, err = sess.Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{}); err != nil {
return err
}
return committer.Commit()
} }
// GetLabelByID returns a label by given ID. // GetLabelByID returns a label by given ID.

View File

@@ -105,22 +105,16 @@ func (m *Milestone) State() api.StateType {
// NewMilestone creates new milestone of repository. // NewMilestone creates new milestone of repository.
func NewMilestone(ctx context.Context, m *Milestone) (err error) { func NewMilestone(ctx context.Context, m *Milestone) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { m.Name = strings.TrimSpace(m.Name)
return err
}
defer committer.Close()
m.Name = strings.TrimSpace(m.Name) if err = db.Insert(ctx, m); err != nil {
return err
}
if err = db.Insert(ctx, m); err != nil { _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID)
return err return err
} })
if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
return err
}
return committer.Commit()
} }
// HasMilestoneByRepoID returns if the milestone exists in the repository. // HasMilestoneByRepoID returns if the milestone exists in the repository.
@@ -155,28 +149,23 @@ func GetMilestoneByRepoIDANDName(ctx context.Context, repoID int64, name string)
// UpdateMilestone updates information of given milestone. // UpdateMilestone updates information of given milestone.
func UpdateMilestone(ctx context.Context, m *Milestone, oldIsClosed bool) error { func UpdateMilestone(ctx context.Context, m *Milestone, oldIsClosed bool) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if m.IsClosed && !oldIsClosed {
return err m.ClosedDateUnix = timeutil.TimeStampNow()
} }
defer committer.Close()
if m.IsClosed && !oldIsClosed { if err := updateMilestone(ctx, m); err != nil {
m.ClosedDateUnix = timeutil.TimeStampNow()
}
if err := updateMilestone(ctx, m); err != nil {
return err
}
// if IsClosed changed, update milestone numbers of repository
if oldIsClosed != m.IsClosed {
if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil {
return err return err
} }
}
return committer.Commit() // if IsClosed changed, update milestone numbers of repository
if oldIsClosed != m.IsClosed {
if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil {
return err
}
}
return nil
})
} }
func updateMilestone(ctx context.Context, m *Milestone) error { func updateMilestone(ctx context.Context, m *Milestone) error {
@@ -213,44 +202,28 @@ func UpdateMilestoneCounters(ctx context.Context, id int64) error {
// ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo. // ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo.
func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID int64, isClosed bool) error { func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID int64, isClosed bool) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { m := &Milestone{
return err ID: milestoneID,
} RepoID: repoID,
defer committer.Close() }
m := &Milestone{ has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m)
ID: milestoneID, if err != nil {
RepoID: repoID, return err
} } else if !has {
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
}
has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m) return changeMilestoneStatus(ctx, m, isClosed)
if err != nil { })
return err
} else if !has {
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
}
if err := changeMilestoneStatus(ctx, m, isClosed); err != nil {
return err
}
return committer.Commit()
} }
// ChangeMilestoneStatus changes the milestone open/closed status. // ChangeMilestoneStatus changes the milestone open/closed status.
func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) { func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { return changeMilestoneStatus(ctx, m, isClosed)
return err })
}
defer committer.Close()
if err := changeMilestoneStatus(ctx, m, isClosed); err != nil {
return err
}
return committer.Commit()
} }
func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error { func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error {
@@ -284,40 +257,34 @@ func DeleteMilestoneByRepoID(ctx context.Context, repoID, id int64) error {
return err return err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil {
return err return err
} }
defer committer.Close()
if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil { numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
return err RepoID: repo.ID,
} })
if err != nil {
return err
}
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
RepoID: repo.ID,
IsClosed: optional.Some(true),
})
if err != nil {
return err
}
repo.NumMilestones = int(numMilestones)
repo.NumClosedMilestones = int(numClosedMilestones)
numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
RepoID: repo.ID, return err
}
_, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID)
return err
}) })
if err != nil {
return err
}
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
RepoID: repo.ID,
IsClosed: optional.Some(true),
})
if err != nil {
return err
}
repo.NumMilestones = int(numMilestones)
repo.NumClosedMilestones = int(numClosedMilestones)
if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
return err
}
if _, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
}
return committer.Commit()
} }
func updateRepoMilestoneNum(ctx context.Context, repoID int64) error { func updateRepoMilestoneNum(ctx context.Context, repoID int64) error {
@@ -360,22 +327,15 @@ func InsertMilestones(ctx context.Context, ms ...*Milestone) (err error) {
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { // to return the id, so we should not use batch insert
return err for _, m := range ms {
} if _, err = db.GetEngine(ctx).NoAutoTime().Insert(m); err != nil {
defer committer.Close() return err
sess := db.GetEngine(ctx) }
// to return the id, so we should not use batch insert
for _, m := range ms {
if _, err = sess.NoAutoTime().Insert(m); err != nil {
return err
} }
}
if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID); err != nil { _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID)
return err return err
} })
return committer.Commit()
} }

View File

@@ -364,17 +364,10 @@ func (pr *PullRequest) GetApprovers(ctx context.Context) string {
func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) error { func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) error {
maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers
if maxReviewers == 0 { if maxReviewers == 0 {
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
// Note: This doesn't page as we only expect a very limited number of reviews // Note: This doesn't page as we only expect a very limited number of reviews
reviews, err := FindLatestReviews(ctx, FindReviewOptions{ reviews, err := FindLatestReviews(ctx, FindReviewOptions{
Types: []ReviewType{ReviewTypeApprove}, Types: []ReviewType{ReviewTypeApprove},
@@ -410,7 +403,7 @@ func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer)
} }
reviewersWritten++ reviewersWritten++
} }
return committer.Commit() return nil
} }
// GetGitHeadRefName returns git ref for hidden pull request branch // GetGitHeadRefName returns git ref for hidden pull request branch
@@ -464,45 +457,36 @@ func (pr *PullRequest) IsFromFork() bool {
// NewPullRequest creates new pull request with labels for repository. // NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) { func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
return err if err != nil {
} return fmt.Errorf("generate pull request index failed: %w", err)
defer committer.Close()
idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
if err != nil {
return fmt.Errorf("generate pull request index failed: %w", err)
}
issue.Index = idx
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
Issue: issue,
LabelIDs: labelIDs,
Attachments: uuids,
IsPull: true,
}); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
return err
} }
return fmt.Errorf("newIssue: %w", err)
}
pr.Index = issue.Index issue.Index = idx
pr.BaseRepo = repo issue.Title = util.EllipsisDisplayString(issue.Title, 255)
pr.IssueID = issue.ID
if err = db.Insert(ctx, pr); err != nil {
return fmt.Errorf("insert pull repo: %w", err)
}
if err = committer.Commit(); err != nil { if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
return fmt.Errorf("Commit: %w", err) Repo: repo,
} Issue: issue,
LabelIDs: labelIDs,
Attachments: uuids,
IsPull: true,
}); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
return err
}
return fmt.Errorf("newIssue: %w", err)
}
return nil pr.Index = issue.Index
pr.BaseRepo = repo
pr.IssueID = issue.ID
if err = db.Insert(ctx, pr); err != nil {
return fmt.Errorf("insert pull repo: %w", err)
}
return nil
})
} }
// ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo. // ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo.
@@ -977,22 +961,18 @@ func TokenizeCodeOwnersLine(line string) []string {
// InsertPullRequests inserted pull requests // InsertPullRequests inserted pull requests
func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error { func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { for _, pr := range prs {
return err if err := insertIssue(ctx, pr.Issue); err != nil {
} return err
defer committer.Close() }
sess := db.GetEngine(ctx) pr.IssueID = pr.Issue.ID
for _, pr := range prs { if _, err := db.GetEngine(ctx).NoAutoTime().Insert(pr); err != nil {
if err := insertIssue(ctx, pr.Issue); err != nil { return err
return err }
} }
pr.IssueID = pr.Issue.ID return nil
if _, err := sess.NoAutoTime().Insert(pr); err != nil { })
return err
}
}
return committer.Commit()
} }
// GetPullRequestByMergedCommit returns a merged pull request by the given commit // GetPullRequestByMergedCommit returns a merged pull request by the given commit

View File

@@ -224,21 +224,9 @@ func CreateReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, erro
return nil, ErrForbiddenIssueReaction{opts.Type} return nil, ErrForbiddenIssueReaction{opts.Type}
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Reaction, error) {
if err != nil { return createReaction(ctx, opts)
return nil, err })
}
defer committer.Close()
reaction, err := createReaction(ctx, opts)
if err != nil {
return reaction, err
}
if err := committer.Commit(); err != nil {
return nil, err
}
return reaction, nil
} }
// DeleteReaction deletes reaction for issue or comment. // DeleteReaction deletes reaction for issue or comment.

View File

@@ -334,54 +334,51 @@ func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organizatio
// CreateReview creates a new review based on opts // CreateReview creates a new review based on opts
func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error) { func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Review, error) {
if err != nil { sess := db.GetEngine(ctx)
return nil, err
}
defer committer.Close()
sess := db.GetEngine(ctx)
review := &Review{ review := &Review{
Issue: opts.Issue, Issue: opts.Issue,
IssueID: opts.Issue.ID, IssueID: opts.Issue.ID,
Reviewer: opts.Reviewer, Reviewer: opts.Reviewer,
ReviewerTeam: opts.ReviewerTeam, ReviewerTeam: opts.ReviewerTeam,
Content: opts.Content, Content: opts.Content,
Official: opts.Official, Official: opts.Official,
CommitID: opts.CommitID, CommitID: opts.CommitID,
Stale: opts.Stale, Stale: opts.Stale,
}
if opts.Reviewer != nil {
review.Type = opts.Type
review.ReviewerID = opts.Reviewer.ID
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
// make sure user review requests are cleared
if opts.Type != ReviewTypePending {
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
return nil, err
}
} }
// make sure if the created review gets dismissed no old review surface
// other types can be ignored, as they don't affect branch protection
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
return nil, err
}
}
} else if opts.ReviewerTeam != nil {
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID
} else {
return nil, errors.New("provide either reviewer or reviewer team")
}
if _, err := sess.Insert(review); err != nil { if opts.Reviewer != nil {
return nil, err review.Type = opts.Type
} review.ReviewerID = opts.Reviewer.ID
return review, committer.Commit()
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
// make sure user review requests are cleared
if opts.Type != ReviewTypePending {
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
return nil, err
}
}
// make sure if the created review gets dismissed no old review surface
// other types can be ignored, as they don't affect branch protection
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
return nil, err
}
}
} else if opts.ReviewerTeam != nil {
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID
} else {
return nil, errors.New("provide either reviewer or reviewer team")
}
if _, err := sess.Insert(review); err != nil {
return nil, err
}
return review, nil
})
} }
// GetCurrentReview returns the current pending review of reviewer for given issue // GetCurrentReview returns the current pending review of reviewer for given issue
@@ -605,168 +602,152 @@ func DismissReview(ctx context.Context, review *Review, isDismiss bool) (err err
// InsertReviews inserts review and review comments // InsertReviews inserts review and review comments
func InsertReviews(ctx context.Context, reviews []*Review) error { func InsertReviews(ctx context.Context, reviews []*Review) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { sess := db.GetEngine(ctx)
return err
}
defer committer.Close()
sess := db.GetEngine(ctx)
for _, review := range reviews { for _, review := range reviews {
if _, err := sess.NoAutoTime().Insert(review); err != nil { if _, err := sess.NoAutoTime().Insert(review); err != nil {
return err return err
} }
if _, err := sess.NoAutoTime().Insert(&Comment{ if _, err := sess.NoAutoTime().Insert(&Comment{
Type: CommentTypeReview, Type: CommentTypeReview,
Content: review.Content, Content: review.Content,
PosterID: review.ReviewerID, PosterID: review.ReviewerID,
OriginalAuthor: review.OriginalAuthor, OriginalAuthor: review.OriginalAuthor,
OriginalAuthorID: review.OriginalAuthorID, OriginalAuthorID: review.OriginalAuthorID,
IssueID: review.IssueID, IssueID: review.IssueID,
ReviewID: review.ID, ReviewID: review.ID,
CreatedUnix: review.CreatedUnix, CreatedUnix: review.CreatedUnix,
UpdatedUnix: review.UpdatedUnix, UpdatedUnix: review.UpdatedUnix,
}); err != nil { }); err != nil {
return err return err
} }
for _, c := range review.Comments { for _, c := range review.Comments {
c.ReviewID = review.ID c.ReviewID = review.ID
} }
if len(review.Comments) > 0 { if len(review.Comments) > 0 {
if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil { if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil {
return err
}
}
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
return err return err
} }
} }
return nil
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil { })
return err
}
}
return committer.Commit()
} }
// AddReviewRequest add a review request from one reviewer // AddReviewRequest add a review request from one reviewer
func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
if err != nil { sess := db.GetEngine(ctx)
return nil, err
}
defer committer.Close()
sess := db.GetEngine(ctx)
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID) review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
if err != nil && !IsErrReviewNotExist(err) { if err != nil && !IsErrReviewNotExist(err) {
return nil, err
}
if review != nil {
// skip it when reviewer has been request to review
if review.Type == ReviewTypeRequest {
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
}
if issue.IsClosed {
return nil, ErrReviewRequestOnClosedPR{}
}
if issue.IsPull {
if err := issue.LoadPullRequest(ctx); err != nil {
return nil, err
}
if issue.PullRequest.HasMerged {
return nil, ErrReviewRequestOnClosedPR{}
}
}
}
// if the reviewer is an official reviewer,
// remove the official flag in the all previous reviews
official, err := IsOfficialReviewer(ctx, issue, reviewer)
if err != nil {
return nil, err
} else if official {
if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil {
return nil, err return nil, err
} }
}
review, err = CreateReview(ctx, CreateReviewOptions{ if review != nil {
Type: ReviewTypeRequest, // skip it when reviewer has been request to review
Issue: issue, if review.Type == ReviewTypeRequest {
Reviewer: reviewer, return nil, nil // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
Official: official, }
Stale: false,
if issue.IsClosed {
return nil, ErrReviewRequestOnClosedPR{}
}
if issue.IsPull {
if err := issue.LoadPullRequest(ctx); err != nil {
return nil, err
}
if issue.PullRequest.HasMerged {
return nil, ErrReviewRequestOnClosedPR{}
}
}
}
// if the reviewer is an official reviewer,
// remove the official flag in the all previous reviews
official, err := IsOfficialReviewer(ctx, issue, reviewer)
if err != nil {
return nil, err
} else if official {
if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil {
return nil, err
}
}
review, err = CreateReview(ctx, CreateReviewOptions{
Type: ReviewTypeRequest,
Issue: issue,
Reviewer: reviewer,
Official: official,
Stale: false,
})
if err != nil {
return nil, err
}
comment, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
ReviewID: review.ID,
})
if err != nil {
return nil, err
}
// func caller use the created comment to retrieve created review too.
comment.Review = review
return comment, nil
}) })
if err != nil {
return nil, err
}
comment, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
ReviewID: review.ID,
})
if err != nil {
return nil, err
}
// func caller use the created comment to retrieve created review too.
comment.Review = review
return comment, committer.Commit()
} }
// RemoveReviewRequest remove a review request from one reviewer // RemoveReviewRequest remove a review request from one reviewer
func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
if err != nil { review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
return nil, err if err != nil && !IsErrReviewNotExist(err) {
}
defer committer.Close()
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
if err != nil && !IsErrReviewNotExist(err) {
return nil, err
}
if review == nil || review.Type != ReviewTypeRequest {
return nil, nil
}
if _, err = db.DeleteByBean(ctx, review); err != nil {
return nil, err
}
official, err := IsOfficialReviewer(ctx, issue, reviewer)
if err != nil {
return nil, err
} else if official {
if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil {
return nil, err return nil, err
} }
}
comment, err := CreateComment(ctx, &CreateCommentOptions{ if review == nil || review.Type != ReviewTypeRequest {
Type: CommentTypeReviewRequest, return nil, nil
Doer: doer, }
Repo: issue.Repo,
Issue: issue, if _, err = db.DeleteByBean(ctx, review); err != nil {
RemovedAssignee: true, // Use RemovedAssignee as !isRequest return nil, err
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID }
official, err := IsOfficialReviewer(ctx, issue, reviewer)
if err != nil {
return nil, err
} else if official {
if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil {
return nil, err
}
}
return CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
})
}) })
if err != nil {
return nil, err
}
return comment, committer.Commit()
} }
// Recalculate the latest official review for reviewer // Recalculate the latest official review for reviewer
@@ -787,120 +768,112 @@ func restoreLatestOfficialReview(ctx context.Context, issueID, reviewerID int64)
// AddTeamReviewRequest add a review request from one team // AddTeamReviewRequest add a review request from one team
func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) { func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
if err != nil { review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
return nil, err
}
defer committer.Close()
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
if err != nil && !IsErrReviewNotExist(err) {
return nil, err
}
// This team already has been requested to review - therefore skip this.
if review != nil {
return nil, nil
}
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
if err != nil {
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
} else if !official {
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil {
return nil, fmt.Errorf("isOfficialReviewer(): %w", err)
}
}
if review, err = CreateReview(ctx, CreateReviewOptions{
Type: ReviewTypeRequest,
Issue: issue,
ReviewerTeam: reviewer,
Official: official,
Stale: false,
}); err != nil {
return nil, err
}
if official {
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
return nil, err
}
}
comment, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
ReviewID: review.ID,
})
if err != nil {
return nil, fmt.Errorf("CreateComment(): %w", err)
}
return comment, committer.Commit()
}
// RemoveTeamReviewRequest remove a review request from one team
func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err
}
defer committer.Close()
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
if err != nil && !IsErrReviewNotExist(err) {
return nil, err
}
if review == nil {
return nil, nil
}
if _, err = db.DeleteByBean(ctx, review); err != nil {
return nil, err
}
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
if err != nil {
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
}
if official {
// recalculate which is the latest official review from that team
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID)
if err != nil && !IsErrReviewNotExist(err) { if err != nil && !IsErrReviewNotExist(err) {
return nil, err return nil, err
} }
// This team already has been requested to review - therefore skip this.
if review != nil { if review != nil {
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil { return nil, nil
}
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
if err != nil {
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
} else if !official {
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil {
return nil, fmt.Errorf("isOfficialReviewer(): %w", err)
}
}
if review, err = CreateReview(ctx, CreateReviewOptions{
Type: ReviewTypeRequest,
Issue: issue,
ReviewerTeam: reviewer,
Official: official,
Stale: false,
}); err != nil {
return nil, err
}
if official {
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
return nil, err return nil, err
} }
} }
}
if doer == nil { comment, err := CreateComment(ctx, &CreateCommentOptions{
return nil, committer.Commit() Type: CommentTypeReviewRequest,
} Doer: doer,
Repo: issue.Repo,
Issue: issue,
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
ReviewID: review.ID,
})
if err != nil {
return nil, fmt.Errorf("CreateComment(): %w", err)
}
comment, err := CreateComment(ctx, &CreateCommentOptions{ return comment, nil
Type: CommentTypeReviewRequest,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
}) })
if err != nil { }
return nil, fmt.Errorf("CreateComment(): %w", err)
}
return comment, committer.Commit() // RemoveTeamReviewRequest remove a review request from one team
func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
if err != nil && !IsErrReviewNotExist(err) {
return nil, err
}
if review == nil {
return nil, nil
}
if _, err = db.DeleteByBean(ctx, review); err != nil {
return nil, err
}
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
if err != nil {
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
}
if official {
// recalculate which is the latest official review from that team
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID)
if err != nil && !IsErrReviewNotExist(err) {
return nil, err
}
if review != nil {
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil {
return nil, err
}
}
}
if doer == nil {
return nil, nil
}
comment, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
})
if err != nil {
return nil, fmt.Errorf("CreateComment(): %w", err)
}
return comment, nil
})
} }
// MarkConversation Add or remove Conversation mark for a code comment // MarkConversation Add or remove Conversation mark for a code comment
@@ -966,61 +939,56 @@ func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.Use
// DeleteReview delete a review and it's code comments // DeleteReview delete a review and it's code comments
func DeleteReview(ctx context.Context, r *Review) error { func DeleteReview(ctx context.Context, r *Review) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if r.ID == 0 {
return err return errors.New("review is not allowed to be 0")
} }
defer committer.Close()
if r.ID == 0 { if r.Type == ReviewTypeRequest {
return errors.New("review is not allowed to be 0") return errors.New("review request can not be deleted using this method")
} }
if r.Type == ReviewTypeRequest { opts := FindCommentsOptions{
return errors.New("review request can not be deleted using this method") Type: CommentTypeCode,
} IssueID: r.IssueID,
ReviewID: r.ID,
}
opts := FindCommentsOptions{ if _, err := db.Delete[Comment](ctx, opts); err != nil {
Type: CommentTypeCode,
IssueID: r.IssueID,
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil {
return err
}
opts = FindCommentsOptions{
Type: CommentTypeReview,
IssueID: r.IssueID,
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil {
return err
}
opts = FindCommentsOptions{
Type: CommentTypeDismissReview,
IssueID: r.IssueID,
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil {
return err
}
if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil {
return err
}
if r.Official {
if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil {
return err return err
} }
}
return committer.Commit() opts = FindCommentsOptions{
Type: CommentTypeReview,
IssueID: r.IssueID,
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil {
return err
}
opts = FindCommentsOptions{
Type: CommentTypeDismissReview,
IssueID: r.IssueID,
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil {
return err
}
if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil {
return err
}
if r.Official {
if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil {
return err
}
}
return nil
})
} }
// GetCodeCommentsCount return count of CodeComments a Review has // GetCodeCommentsCount return count of CodeComments a Review has

View File

@@ -168,35 +168,31 @@ func GetTrackedSeconds(ctx context.Context, opts FindTrackedTimesOptions) (track
// AddTime will add the given time (in seconds) to the issue // AddTime will add the given time (in seconds) to the issue
func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*TrackedTime, error) {
if err != nil { t, err := addTime(ctx, user, issue, amount, created)
return nil, err if err != nil {
} return nil, err
defer committer.Close() }
t, err := addTime(ctx, user, issue, amount, created) if err := issue.LoadRepo(ctx); err != nil {
if err != nil { return nil, err
return nil, err }
}
if err := issue.LoadRepo(ctx); err != nil { if _, err := CreateComment(ctx, &CreateCommentOptions{
return nil, err Issue: issue,
} Repo: issue.Repo,
Doer: user,
// Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", amount),
Type: CommentTypeAddTimeManual,
TimeID: t.ID,
}); err != nil {
return nil, err
}
if _, err := CreateComment(ctx, &CreateCommentOptions{ return t, nil
Issue: issue, })
Repo: issue.Repo,
Doer: user,
// Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", amount),
Type: CommentTypeAddTimeManual,
TimeID: t.ID,
}); err != nil {
return nil, err
}
return t, committer.Commit()
} }
func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
@@ -241,72 +237,58 @@ func TotalTimesForEachUser(ctx context.Context, options *FindTrackedTimesOptions
// DeleteIssueUserTimes deletes times for issue // DeleteIssueUserTimes deletes times for issue
func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.User) error { func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.User) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { opts := FindTrackedTimesOptions{
return err IssueID: issue.ID,
} UserID: user.ID,
defer committer.Close() }
opts := FindTrackedTimesOptions{ removedTime, err := deleteTimes(ctx, opts)
IssueID: issue.ID, if err != nil {
UserID: user.ID, return err
} }
if removedTime == 0 {
return db.ErrNotExist{Resource: "tracked_time"}
}
removedTime, err := deleteTimes(ctx, opts) if err := issue.LoadRepo(ctx); err != nil {
if err != nil { return err
}
_, err = CreateComment(ctx, &CreateCommentOptions{
Issue: issue,
Repo: issue.Repo,
Doer: user,
// Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", removedTime),
Type: CommentTypeDeleteTimeManual,
})
return err return err
} })
if removedTime == 0 {
return db.ErrNotExist{Resource: "tracked_time"}
}
if err := issue.LoadRepo(ctx); err != nil {
return err
}
if _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: issue,
Repo: issue.Repo,
Doer: user,
// Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", removedTime),
Type: CommentTypeDeleteTimeManual,
}); err != nil {
return err
}
return committer.Commit()
} }
// DeleteTime delete a specific Time // DeleteTime delete a specific Time
func DeleteTime(ctx context.Context, t *TrackedTime) error { func DeleteTime(ctx context.Context, t *TrackedTime) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err := t.LoadAttributes(ctx); err != nil {
return err return err
} }
defer committer.Close()
if err := t.LoadAttributes(ctx); err != nil { if err := deleteTime(ctx, t); err != nil {
return err return err
} }
if err := deleteTime(ctx, t); err != nil { _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: t.Issue,
Repo: t.Issue.Repo,
Doer: t.User,
// Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", t.Time),
Type: CommentTypeDeleteTimeManual,
})
return err return err
} })
if _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: t.Issue,
Repo: t.Issue.Repo,
Doer: t.User,
// Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", t.Time),
Type: CommentTypeDeleteTimeManual,
}); err != nil {
return err
}
return committer.Commit()
} }
func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) { func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) {

View File

@@ -310,74 +310,69 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
org.NumMembers = 1 org.NumMembers = 1
org.Type = user_model.UserTypeOrganization org.Type = user_model.UserTypeOrganization
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
return err return err
}
defer committer.Close()
if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
return err
}
if err = db.Insert(ctx, org); err != nil {
return fmt.Errorf("insert organization: %w", err)
}
if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
return fmt.Errorf("generate random avatar: %w", err)
}
// Add initial creator to organization and owner team.
if err = db.Insert(ctx, &OrgUser{
UID: owner.ID,
OrgID: org.ID,
IsPublic: setting.Service.DefaultOrgMemberVisible,
}); err != nil {
return fmt.Errorf("insert org-user relation: %w", err)
}
// Create default owner team.
t := &Team{
OrgID: org.ID,
LowerName: strings.ToLower(OwnerTeamName),
Name: OwnerTeamName,
AccessMode: perm.AccessModeOwner,
NumMembers: 1,
IncludesAllRepositories: true,
CanCreateOrgRepo: true,
}
if err = db.Insert(ctx, t); err != nil {
return fmt.Errorf("insert owner team: %w", err)
}
// insert units for team
units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
for _, tp := range unit.AllRepoUnitTypes {
up := perm.AccessModeOwner
if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
up = perm.AccessModeRead
} }
units = append(units, TeamUnit{
OrgID: org.ID,
TeamID: t.ID,
Type: tp,
AccessMode: up,
})
}
if err = db.Insert(ctx, &units); err != nil { if err = db.Insert(ctx, org); err != nil {
return err return fmt.Errorf("insert organization: %w", err)
} }
if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
return fmt.Errorf("generate random avatar: %w", err)
}
if err = db.Insert(ctx, &TeamUser{ // Add initial creator to organization and owner team.
UID: owner.ID, if err = db.Insert(ctx, &OrgUser{
OrgID: org.ID, UID: owner.ID,
TeamID: t.ID, OrgID: org.ID,
}); err != nil { IsPublic: setting.Service.DefaultOrgMemberVisible,
return fmt.Errorf("insert team-user relation: %w", err) }); err != nil {
} return fmt.Errorf("insert org-user relation: %w", err)
}
return committer.Commit() // Create default owner team.
t := &Team{
OrgID: org.ID,
LowerName: strings.ToLower(OwnerTeamName),
Name: OwnerTeamName,
AccessMode: perm.AccessModeOwner,
NumMembers: 1,
IncludesAllRepositories: true,
CanCreateOrgRepo: true,
}
if err = db.Insert(ctx, t); err != nil {
return fmt.Errorf("insert owner team: %w", err)
}
// insert units for team
units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
for _, tp := range unit.AllRepoUnitTypes {
up := perm.AccessModeOwner
if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
up = perm.AccessModeRead
}
units = append(units, TeamUnit{
OrgID: org.ID,
TeamID: t.ID,
Type: tp,
AccessMode: up,
})
}
if err = db.Insert(ctx, &units); err != nil {
return err
}
if err = db.Insert(ctx, &TeamUser{
UID: owner.ID,
OrgID: org.ID,
TeamID: t.ID,
}); err != nil {
return fmt.Errorf("insert team-user relation: %w", err)
}
return nil
})
} }
// GetOrgByName returns organization by given name. // GetOrgByName returns organization by given name.
@@ -499,31 +494,26 @@ func AddOrgUser(ctx context.Context, orgID, uid int64) error {
return err return err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { // check in transaction
return err isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
} if err != nil || isAlreadyMember {
defer committer.Close() return err
}
// check in transaction ou := &OrgUser{
isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid) UID: uid,
if err != nil || isAlreadyMember { OrgID: orgID,
return err IsPublic: setting.Service.DefaultOrgMemberVisible,
} }
ou := &OrgUser{ if err := db.Insert(ctx, ou); err != nil {
UID: uid, return err
OrgID: orgID, } else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
IsPublic: setting.Service.DefaultOrgMemberVisible, return err
} }
return nil
if err := db.Insert(ctx, ou); err != nil { })
return err
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
return err
}
return committer.Commit()
} }
// GetOrgByID returns the user object by given ID if exists. // GetOrgByID returns the user object by given ID if exists.

View File

@@ -31,21 +31,16 @@ func getUnitsByTeamID(ctx context.Context, teamID int64) (units []*TeamUnit, err
// UpdateTeamUnits updates a teams's units // UpdateTeamUnits updates a teams's units
func UpdateTeamUnits(ctx context.Context, team *Team, units []TeamUnit) (err error) { func UpdateTeamUnits(ctx context.Context, team *Team, units []TeamUnit) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil {
return err
}
defer committer.Close()
if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil {
return err
}
if len(units) > 0 {
if err = db.Insert(ctx, units); err != nil {
return err return err
} }
}
return committer.Commit() if len(units) > 0 {
if err = db.Insert(ctx, units); err != nil {
return err
}
}
return nil
})
} }

View File

@@ -359,41 +359,25 @@ func updateRepositoryProjectCount(ctx context.Context, repoID int64) error {
// ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed // ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed
func ChangeProjectStatusByRepoIDAndID(ctx context.Context, repoID, projectID int64, isClosed bool) error { func ChangeProjectStatusByRepoIDAndID(ctx context.Context, repoID, projectID int64, isClosed bool) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { p := new(Project)
return err
}
defer committer.Close()
p := new(Project) has, err := db.GetEngine(ctx).ID(projectID).Where("repo_id = ?", repoID).Get(p)
if err != nil {
return err
} else if !has {
return ErrProjectNotExist{ID: projectID, RepoID: repoID}
}
has, err := db.GetEngine(ctx).ID(projectID).Where("repo_id = ?", repoID).Get(p) return changeProjectStatus(ctx, p, isClosed)
if err != nil { })
return err
} else if !has {
return ErrProjectNotExist{ID: projectID, RepoID: repoID}
}
if err := changeProjectStatus(ctx, p, isClosed); err != nil {
return err
}
return committer.Commit()
} }
// ChangeProjectStatus toggle a project between opened and closed // ChangeProjectStatus toggle a project between opened and closed
func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { return changeProjectStatus(ctx, p, isClosed)
return err })
}
defer committer.Close()
if err := changeProjectStatus(ctx, p, isClosed); err != nil {
return err
}
return committer.Commit()
} }
func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {

View File

@@ -290,19 +290,14 @@ func UpdateRepoStats(ctx context.Context, id int64) error {
} }
func updateUserStarNumbers(ctx context.Context, users []user_model.User) error { func updateUserStarNumbers(ctx context.Context, users []user_model.User) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { for _, user := range users {
return err if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil {
} return err
defer committer.Close() }
for _, user := range users {
if _, err = db.Exec(ctx, "UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil {
return err
} }
} return nil
})
return committer.Commit()
} }
// DoctorUserStarNum recalculate Stars number for all user // DoctorUserStarNum recalculate Stars number for all user

View File

@@ -141,101 +141,90 @@ func GetTopLanguageStats(ctx context.Context, repo *Repository, limit int) (Lang
// UpdateLanguageStats updates the language statistics for repository // UpdateLanguageStats updates the language statistics for repository
func UpdateLanguageStats(ctx context.Context, repo *Repository, commitID string, stats map[string]int64) error { func UpdateLanguageStats(ctx context.Context, repo *Repository, commitID string, stats map[string]int64) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { sess := db.GetEngine(ctx)
return err
}
defer committer.Close()
sess := db.GetEngine(ctx)
oldstats, err := GetLanguageStats(ctx, repo) oldstats, err := GetLanguageStats(ctx, repo)
if err != nil { if err != nil {
return err return err
}
var topLang string
var s int64
for lang, size := range stats {
if size > s {
s = size
topLang = lang
} }
} var topLang string
var s int64
for lang, size := range stats { for lang, size := range stats {
upd := false if size > s {
for _, s := range oldstats { s = size
// Update already existing language topLang = lang
if strings.EqualFold(s.Language, lang) {
s.CommitID = commitID
s.IsPrimary = lang == topLang
s.Size = size
if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil {
return err
}
upd = true
break
} }
} }
// Insert new language
if !upd { for lang, size := range stats {
if err := db.Insert(ctx, &LanguageStat{ upd := false
RepoID: repo.ID, for _, s := range oldstats {
CommitID: commitID, // Update already existing language
IsPrimary: lang == topLang, if strings.EqualFold(s.Language, lang) {
Language: lang, s.CommitID = commitID
Size: size, s.IsPrimary = lang == topLang
}); err != nil { s.Size = size
if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil {
return err
}
upd = true
break
}
}
// Insert new language
if !upd {
if err := db.Insert(ctx, &LanguageStat{
RepoID: repo.ID,
CommitID: commitID,
IsPrimary: lang == topLang,
Language: lang,
Size: size,
}); err != nil {
return err
}
}
}
// Delete old languages
statsToDelete := make([]int64, 0, len(oldstats))
for _, s := range oldstats {
if s.CommitID != commitID {
statsToDelete = append(statsToDelete, s.ID)
}
}
if len(statsToDelete) > 0 {
if _, err := sess.In("`id`", statsToDelete).Delete(&LanguageStat{}); err != nil {
return err return err
} }
} }
}
// Delete old languages
statsToDelete := make([]int64, 0, len(oldstats))
for _, s := range oldstats {
if s.CommitID != commitID {
statsToDelete = append(statsToDelete, s.ID)
}
}
if len(statsToDelete) > 0 {
if _, err := sess.In("`id`", statsToDelete).Delete(&LanguageStat{}); err != nil {
return err
}
}
// Update indexer status // Update indexer status
if err = UpdateIndexerStatus(ctx, repo, RepoIndexerTypeStats, commitID); err != nil { return UpdateIndexerStatus(ctx, repo, RepoIndexerTypeStats, commitID)
return err })
}
return committer.Commit()
} }
// CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo) // CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo)
func CopyLanguageStat(ctx context.Context, originalRepo, destRepo *Repository) error { func CopyLanguageStat(ctx context.Context, originalRepo, destRepo *Repository) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { RepoLang := make(LanguageStatList, 0, 6)
return err if err := db.GetEngine(ctx).Where("`repo_id` = ?", originalRepo.ID).Desc("`size`").Find(&RepoLang); err != nil {
}
defer committer.Close()
RepoLang := make(LanguageStatList, 0, 6)
if err := db.GetEngine(ctx).Where("`repo_id` = ?", originalRepo.ID).Desc("`size`").Find(&RepoLang); err != nil {
return err
}
if len(RepoLang) > 0 {
for i := range RepoLang {
RepoLang[i].ID = 0
RepoLang[i].RepoID = destRepo.ID
RepoLang[i].CreatedUnix = timeutil.TimeStampNow()
}
// update destRepo's indexer status
tmpCommitID := RepoLang[0].CommitID
if err := UpdateIndexerStatus(ctx, destRepo, RepoIndexerTypeStats, tmpCommitID); err != nil {
return err return err
} }
if err := db.Insert(ctx, &RepoLang); err != nil { if len(RepoLang) > 0 {
return err for i := range RepoLang {
RepoLang[i].ID = 0
RepoLang[i].RepoID = destRepo.ID
RepoLang[i].CreatedUnix = timeutil.TimeStampNow()
}
// update destRepo's indexer status
tmpCommitID := RepoLang[0].CommitID
if err := UpdateIndexerStatus(ctx, destRepo, RepoIndexerTypeStats, tmpCommitID); err != nil {
return err
}
if err := db.Insert(ctx, &RepoLang); err != nil {
return err
}
} }
} return nil
return committer.Commit() })
} }

View File

@@ -472,30 +472,24 @@ func (r *Release) GetExternalID() int64 { return r.OriginalAuthorID }
// InsertReleases migrates release // InsertReleases migrates release
func InsertReleases(ctx context.Context, rels ...*Release) error { func InsertReleases(ctx context.Context, rels ...*Release) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { for _, rel := range rels {
return err if _, err := db.GetEngine(ctx).NoAutoTime().Insert(rel); err != nil {
}
defer committer.Close()
sess := db.GetEngine(ctx)
for _, rel := range rels {
if _, err := sess.NoAutoTime().Insert(rel); err != nil {
return err
}
if len(rel.Attachments) > 0 {
for i := range rel.Attachments {
rel.Attachments[i].ReleaseID = rel.ID
}
if _, err := sess.NoAutoTime().Insert(rel.Attachments); err != nil {
return err return err
} }
}
}
return committer.Commit() if len(rel.Attachments) > 0 {
for i := range rel.Attachments {
rel.Attachments[i].ReleaseID = rel.ID
}
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(rel.Attachments); err != nil {
return err
}
}
}
return nil
})
} }
func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) { func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) {

View File

@@ -25,48 +25,45 @@ func init() {
// StarRepo or unstar repository. // StarRepo or unstar repository.
func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error { func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { staring := IsStaring(ctx, doer.ID, repo.ID)
return err
}
defer committer.Close()
staring := IsStaring(ctx, doer.ID, repo.ID)
if star { if star {
if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) { if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) {
return user_model.ErrBlockedUser return user_model.ErrBlockedUser
}
if staring {
return nil
}
if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", doer.ID); err != nil {
return err
}
} else {
if !staring {
return nil
}
if _, err := db.DeleteByBean(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repo.ID); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", doer.ID); err != nil {
return err
}
} }
if staring { return nil
return nil })
}
if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", doer.ID); err != nil {
return err
}
} else {
if !staring {
return nil
}
if _, err := db.DeleteByBean(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repo.ID); err != nil {
return err
}
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", doer.ID); err != nil {
return err
}
}
return committer.Commit()
} }
// IsStaring checks if user has starred given repository. // IsStaring checks if user has starred given repository.

View File

@@ -227,32 +227,26 @@ func GetRepoTopicByName(ctx context.Context, repoID int64, topicName string) (*T
// AddTopic adds a topic name to a repository (if it does not already have it) // AddTopic adds a topic name to a repository (if it does not already have it)
func AddTopic(ctx context.Context, repoID int64, topicName string) (*Topic, error) { func AddTopic(ctx context.Context, repoID int64, topicName string) (*Topic, error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (*Topic, error) {
if err != nil { topic, err := GetRepoTopicByName(ctx, repoID, topicName)
return nil, err if err != nil {
} return nil, err
defer committer.Close() }
sess := db.GetEngine(ctx) if topic != nil {
// Repo already have topic
return topic, nil
}
topic, err := GetRepoTopicByName(ctx, repoID, topicName) topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if topic != nil {
// Repo already have topic if err = syncTopicsInRepository(ctx, repoID); err != nil {
return nil, err
}
return topic, nil return topic, nil
} })
topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
if err != nil {
return nil, err
}
if err = syncTopicsInRepository(sess, repoID); err != nil {
return nil, err
}
return topic, committer.Commit()
} }
// DeleteTopic removes a topic name from a repository (if it has it) // DeleteTopic removes a topic name from a repository (if it has it)
@@ -266,14 +260,15 @@ func DeleteTopic(ctx context.Context, repoID int64, topicName string) (*Topic, e
return nil, nil return nil, nil
} }
err = removeTopicFromRepo(ctx, repoID, topic) return db.WithTx2(ctx, func(ctx context.Context) (*Topic, error) {
if err != nil { if err = removeTopicFromRepo(ctx, repoID, topic); err != nil {
return nil, err return nil, err
} }
if err = syncTopicsInRepository(ctx, repoID); err != nil {
err = syncTopicsInRepository(db.GetEngine(ctx), repoID) return nil, err
}
return topic, err return topic, nil
})
} }
// SaveTopics save topics to a repository // SaveTopics save topics to a repository
@@ -285,64 +280,55 @@ func SaveTopics(ctx context.Context, repoID int64, topicNames ...string) error {
return err return err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { var addedTopicNames []string
return err
}
defer committer.Close()
sess := db.GetEngine(ctx)
var addedTopicNames []string
for _, topicName := range topicNames {
if strings.TrimSpace(topicName) == "" {
continue
}
var found bool
for _, t := range topics {
if strings.EqualFold(topicName, t.Name) {
found = true
break
}
}
if !found {
addedTopicNames = append(addedTopicNames, topicName)
}
}
var removeTopics []*Topic
for _, t := range topics {
var found bool
for _, topicName := range topicNames { for _, topicName := range topicNames {
if strings.EqualFold(topicName, t.Name) { if strings.TrimSpace(topicName) == "" {
found = true continue
break }
var found bool
for _, t := range topics {
if strings.EqualFold(topicName, t.Name) {
found = true
break
}
}
if !found {
addedTopicNames = append(addedTopicNames, topicName)
} }
} }
if !found {
removeTopics = append(removeTopics, t) var removeTopics []*Topic
for _, t := range topics {
var found bool
for _, topicName := range topicNames {
if strings.EqualFold(topicName, t.Name) {
found = true
break
}
}
if !found {
removeTopics = append(removeTopics, t)
}
} }
}
for _, topicName := range addedTopicNames { for _, topicName := range addedTopicNames {
_, err := addTopicByNameToRepo(ctx, repoID, topicName) _, err := addTopicByNameToRepo(ctx, repoID, topicName)
if err != nil { if err != nil {
return err return err
}
} }
}
for _, topic := range removeTopics { for _, topic := range removeTopics {
err := removeTopicFromRepo(ctx, repoID, topic) err := removeTopicFromRepo(ctx, repoID, topic)
if err != nil { if err != nil {
return err return err
}
} }
}
if err := syncTopicsInRepository(sess, repoID); err != nil { return syncTopicsInRepository(ctx, repoID)
return err })
}
return committer.Commit()
} }
// GenerateTopics generates topics from a template repository // GenerateTopics generates topics from a template repository
@@ -353,19 +339,19 @@ func GenerateTopics(ctx context.Context, templateRepo, generateRepo *Repository)
} }
} }
return syncTopicsInRepository(db.GetEngine(ctx), generateRepo.ID) return syncTopicsInRepository(ctx, generateRepo.ID)
} }
// syncTopicsInRepository makes sure topics in the topics table are copied into the topics field of the repository // syncTopicsInRepository makes sure topics in the topics table are copied into the topics field of the repository
func syncTopicsInRepository(sess db.Engine, repoID int64) error { func syncTopicsInRepository(ctx context.Context, repoID int64) error {
topicNames := make([]string, 0, 25) topicNames := make([]string, 0, 25)
if err := sess.Table("topic").Cols("name"). if err := db.GetEngine(ctx).Table("topic").Cols("name").
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id"). Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
Where("repo_topic.repo_id = ?", repoID).Asc("topic.name").Find(&topicNames); err != nil { Where("repo_topic.repo_id = ?", repoID).Asc("topic.name").Find(&topicNames); err != nil {
return err return err
} }
if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{ if _, err := db.GetEngine(ctx).ID(repoID).Cols("topics").Update(&Repository{
Topics: topicNames, Topics: topicNames,
}); err != nil { }); err != nil {
return err return err

View File

@@ -19,11 +19,6 @@ func UpdateRepositoryOwnerNames(ctx context.Context, ownerID int64, ownerName st
if ownerID == 0 { if ownerID == 0 {
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").NoAutoTime().Update(&Repository{ if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").NoAutoTime().Update(&Repository{
OwnerName: ownerName, OwnerName: ownerName,
@@ -31,7 +26,7 @@ func UpdateRepositoryOwnerNames(ctx context.Context, ownerID int64, ownerName st
return err return err
} }
return committer.Commit() return nil
} }
// UpdateRepositoryUpdatedTime updates a repository's updated time // UpdateRepositoryUpdatedTime updates a repository's updated time

View File

@@ -117,12 +117,6 @@ func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) {
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
ids := make([]int64, len(uploads)) ids := make([]int64, len(uploads))
for i := range uploads { for i := range uploads {
ids[i] = uploads[i].ID ids[i] = uploads[i].ID
@@ -131,10 +125,6 @@ func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) {
return fmt.Errorf("delete uploads: %w", err) return fmt.Errorf("delete uploads: %w", err)
} }
if err = committer.Commit(); err != nil {
return err
}
for _, upload := range uploads { for _, upload := range uploads {
localPath := upload.LocalPath() localPath := upload.LocalPath()
isFile, err := util.IsFile(localPath) isFile, err := util.IsFile(localPath)

View File

@@ -256,15 +256,9 @@ func IsEmailUsed(ctx context.Context, email string) (bool, error) {
// ActivateEmail activates the email address to given user. // ActivateEmail activates the email address to given user.
func ActivateEmail(ctx context.Context, email *EmailAddress) error { func ActivateEmail(ctx context.Context, email *EmailAddress) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { return updateActivation(ctx, email, true)
return err })
}
defer committer.Close()
if err := updateActivation(ctx, email, true); err != nil {
return err
}
return committer.Commit()
} }
func updateActivation(ctx context.Context, email *EmailAddress, activate bool) error { func updateActivation(ctx context.Context, email *EmailAddress, activate bool) error {
@@ -305,33 +299,30 @@ func makeEmailPrimaryInternal(ctx context.Context, emailID int64, isActive bool)
return ErrUserNotExist{UID: email.UID} return ErrUserNotExist{UID: email.UID}
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { sess := db.GetEngine(ctx)
return err
}
defer committer.Close()
sess := db.GetEngine(ctx)
// 1. Update user table // 1. Update user table
user.Email = email.Email user.Email = email.Email
if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil { if _, err := sess.ID(user.ID).Cols("email").Update(user); err != nil {
return err return err
} }
// 2. Update old primary email // 2. Update old primary email
if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{ if _, err := sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{
IsPrimary: false, IsPrimary: false,
}); err != nil { }); err != nil {
return err return err
} }
// 3. update new primary email // 3. update new primary email
email.IsPrimary = true email.IsPrimary = true
if _, err = sess.ID(email.ID).Cols("is_primary").Update(email); err != nil { if _, err := sess.ID(email.ID).Cols("is_primary").Update(email); err != nil {
return err return err
} }
return committer.Commit() return nil
})
} }
// ChangeInactivePrimaryEmail replaces the inactive primary email of a given user // ChangeInactivePrimaryEmail replaces the inactive primary email of a given user

View File

@@ -38,24 +38,20 @@ func FollowUser(ctx context.Context, user, follow *User) (err error) {
return ErrBlockedUser return ErrBlockedUser
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err = db.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil {
return err return err
} }
defer committer.Close()
if err = db.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil { if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
return err return err
} }
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil { if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil {
return err return err
} }
return nil
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil { })
return err
}
return committer.Commit()
} }
// UnfollowUser unmarks someone as another's follower. // UnfollowUser unmarks someone as another's follower.
@@ -64,22 +60,18 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) {
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil {
return err return err
} }
defer committer.Close()
if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
return err return err
} }
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
return err return err
} }
return nil
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { })
return err
}
return committer.Commit()
} }

View File

@@ -319,21 +319,16 @@ func UpdateWebhookLastStatus(ctx context.Context, w *Webhook) error {
// DeleteWebhookByID uses argument bean as query condition, // DeleteWebhookByID uses argument bean as query condition,
// ID must be specified and do not assign unnecessary fields. // ID must be specified and do not assign unnecessary fields.
func DeleteWebhookByID(ctx context.Context, id int64) (err error) { func DeleteWebhookByID(ctx context.Context, id int64) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if count, err := db.DeleteByID[Webhook](ctx, id); err != nil {
return err return err
} } else if count == 0 {
defer committer.Close() return ErrWebhookNotExist{ID: id}
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: id}); err != nil {
if count, err := db.DeleteByID[Webhook](ctx, id); err != nil { return err
return err }
} else if count == 0 { return nil
return ErrWebhookNotExist{ID: id} })
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: id}); err != nil {
return err
}
return committer.Commit()
} }
// DeleteWebhookByRepoID deletes webhook of repository by given ID. // DeleteWebhookByRepoID deletes webhook of repository by given ID.

View File

@@ -49,28 +49,21 @@ func deleteDeployKeyFromDB(ctx context.Context, key *asymkey_model.DeployKey) er
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed. // DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
// Permissions check should be done outside. // Permissions check should be done outside.
func DeleteDeployKey(ctx context.Context, repo *repo_model.Repository, id int64) error { func DeleteDeployKey(ctx context.Context, repo *repo_model.Repository, id int64) error {
dbCtx, committer, err := db.TxContext(ctx) if err := db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { key, err := asymkey_model.GetDeployKeyByID(ctx, id)
return err if err != nil {
} if asymkey_model.IsErrDeployKeyNotExist(err) {
defer committer.Close() return nil
}
key, err := asymkey_model.GetDeployKeyByID(ctx, id) return fmt.Errorf("GetDeployKeyByID: %w", err)
if err != nil {
if asymkey_model.IsErrDeployKeyNotExist(err) {
return nil
} }
return fmt.Errorf("GetDeployKeyByID: %w", err)
}
if key.RepoID != repo.ID { if key.RepoID != repo.ID {
return fmt.Errorf("deploy key %d does not belong to repository %d", id, repo.ID) return fmt.Errorf("deploy key %d does not belong to repository %d", id, repo.ID)
} }
if err := deleteDeployKeyFromDB(dbCtx, key); err != nil { return deleteDeployKeyFromDB(ctx, key)
return err }); err != nil {
}
if err := committer.Commit(); err != nil {
return err return err
} }

View File

@@ -27,20 +27,9 @@ func DeletePublicKey(ctx context.Context, doer *user_model.User, id int64) (err
} }
} }
dbCtx, committer, err := db.TxContext(ctx) if _, err = db.DeleteByID[asymkey_model.PublicKey](ctx, id); err != nil {
if err != nil {
return err return err
} }
defer committer.Close()
if _, err = db.DeleteByID[asymkey_model.PublicKey](dbCtx, id); err != nil {
return err
}
if err = committer.Commit(); err != nil {
return err
}
committer.Close()
if key.Type == asymkey_model.KeyTypePrincipal { if key.Type == asymkey_model.KeyTypePrincipal {
return RewriteAllPrincipalKeys(ctx) return RewriteAllPrincipalKeys(ctx)

View File

@@ -14,24 +14,6 @@ import (
// AddPrincipalKey adds new principal to database and authorized_principals file. // AddPrincipalKey adds new principal to database and authorized_principals file.
func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*asymkey_model.PublicKey, error) { func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*asymkey_model.PublicKey, error) {
dbCtx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err
}
defer committer.Close()
// Principals cannot be duplicated.
has, err := db.GetEngine(dbCtx).
Where("content = ? AND type = ?", content, asymkey_model.KeyTypePrincipal).
Get(new(asymkey_model.PublicKey))
if err != nil {
return nil, err
} else if has {
return nil, asymkey_model.ErrKeyAlreadyExist{
Content: content,
}
}
key := &asymkey_model.PublicKey{ key := &asymkey_model.PublicKey{
OwnerID: ownerID, OwnerID: ownerID,
Name: content, Name: content,
@@ -40,15 +22,27 @@ func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSou
Type: asymkey_model.KeyTypePrincipal, Type: asymkey_model.KeyTypePrincipal,
LoginSourceID: authSourceID, LoginSourceID: authSourceID,
} }
if err = db.Insert(dbCtx, key); err != nil {
return nil, fmt.Errorf("addKey: %w", err)
}
if err = committer.Commit(); err != nil { if err := db.WithTx(ctx, func(ctx context.Context) error {
// Principals cannot be duplicated.
has, err := db.GetEngine(ctx).
Where("content = ? AND type = ?", content, asymkey_model.KeyTypePrincipal).
Get(new(asymkey_model.PublicKey))
if err != nil {
return err
} else if has {
return asymkey_model.ErrKeyAlreadyExist{
Content: content,
}
}
if err = db.Insert(ctx, key); err != nil {
return fmt.Errorf("addKey: %w", err)
}
return nil
}); err != nil {
return nil, err return nil, err
} }
committer.Close()
return key, RewriteAllPrincipalKeys(ctx) return key, RewriteAllPrincipalKeys(ctx)
} }

View File

@@ -46,32 +46,24 @@ func AddLabels(ctx context.Context, issue *issues_model.Issue, doer *user_model.
// RemoveLabel removes a label from issue by given ID. // RemoveLabel removes a label from issue by given ID.
func RemoveLabel(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, label *issues_model.Label) error { func RemoveLabel(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, label *issues_model.Label) error {
dbCtx, committer, err := db.TxContext(ctx) if err := db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err := issue.LoadRepo(ctx); err != nil {
return err return err
}
defer committer.Close()
if err := issue.LoadRepo(dbCtx); err != nil {
return err
}
perm, err := access_model.GetUserRepoPermission(dbCtx, issue.Repo, doer)
if err != nil {
return err
}
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
if label.OrgID > 0 {
return issues_model.ErrOrgLabelNotExist{}
} }
return issues_model.ErrRepoLabelNotExist{}
}
if err := issues_model.DeleteIssueLabel(dbCtx, issue, label, doer); err != nil { perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
return err if err != nil {
} return err
}
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
if label.OrgID > 0 {
return issues_model.ErrOrgLabelNotExist{}
}
return issues_model.ErrRepoLabelNotExist{}
}
if err := committer.Commit(); err != nil { return issues_model.DeleteIssueLabel(ctx, issue, label, doer)
}); err != nil {
return err return err
} }

View File

@@ -69,21 +69,12 @@ func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *is
// ChangeMilestoneAssign changes assignment of milestone for issue. // ChangeMilestoneAssign changes assignment of milestone for issue.
func ChangeMilestoneAssign(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, oldMilestoneID int64) (err error) { func ChangeMilestoneAssign(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, oldMilestoneID int64) (err error) {
dbCtx, committer, err := db.TxContext(ctx) if err := db.WithTx(ctx, func(dbCtx context.Context) error {
if err != nil { return changeMilestoneAssign(dbCtx, doer, issue, oldMilestoneID)
}); err != nil {
return err return err
} }
defer committer.Close()
if err = changeMilestoneAssign(dbCtx, doer, issue, oldMilestoneID); err != nil {
return err
}
if err = committer.Commit(); err != nil {
return fmt.Errorf("Commit: %w", err)
}
notify_service.IssueChangeMilestone(ctx, doer, issue, oldMilestoneID) notify_service.IssueChangeMilestone(ctx, doer, issue, oldMilestoneID)
return nil return nil
} }

View File

@@ -15,31 +15,25 @@ import (
// CloseIssue close an issue. // CloseIssue close an issue.
func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error { func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error {
dbCtx, committer, err := db.TxContext(ctx) var comment *issues_model.Comment
if err != nil { if err := db.WithTx(ctx, func(ctx context.Context) error {
return err var err error
} comment, err = issues_model.CloseIssue(ctx, issue, doer)
defer committer.Close() if err != nil {
if issues_model.IsErrDependenciesLeft(err) {
comment, err := issues_model.CloseIssue(dbCtx, issue, doer) if _, err := issues_model.FinishIssueStopwatch(ctx, doer, issue); err != nil {
if err != nil { log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
if issues_model.IsErrDependenciesLeft(err) { }
if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil {
log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
} }
return err
} }
return err
}
if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil { _, err = issues_model.FinishIssueStopwatch(ctx, doer, issue)
return err
}); err != nil {
return err return err
} }
if err := committer.Commit(); err != nil {
return err
}
committer.Close()
notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true) notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true)
return nil return nil

View File

@@ -54,39 +54,33 @@ func NewTeam(ctx context.Context, t *organization.Team) (err error) {
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName} return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err = db.Insert(ctx, t); err != nil {
return err
}
defer committer.Close()
if err = db.Insert(ctx, t); err != nil {
return err
}
// insert units for team
if len(t.Units) > 0 {
for _, unit := range t.Units {
unit.TeamID = t.ID
}
if err = db.Insert(ctx, &t.Units); err != nil {
return err return err
} }
}
// Add all repositories to the team if it has access to all of them. // insert units for team
if t.IncludesAllRepositories { if len(t.Units) > 0 {
err = repo_service.AddAllRepositoriesToTeam(ctx, t) for _, unit := range t.Units {
if err != nil { unit.TeamID = t.ID
return fmt.Errorf("addAllRepositories: %w", err) }
if err = db.Insert(ctx, &t.Units); err != nil {
return err
}
} }
}
// Update organization number of teams. // Add all repositories to the team if it has access to all of them.
if _, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { if t.IncludesAllRepositories {
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
if err != nil {
return fmt.Errorf("addAllRepositories: %w", err)
}
}
// Update organization number of teams.
_, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID)
return err return err
} })
return committer.Commit()
} }
// UpdateTeam updates information of team. // UpdateTeam updates information of team.
@@ -99,128 +93,117 @@ func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeA
t.Description = t.Description[:255] t.Description = t.Description[:255]
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { t.LowerName = strings.ToLower(t.Name)
return err has, err := db.Exist[organization.Team](ctx, builder.Eq{
} "org_id": t.OrgID,
defer committer.Close() "lower_name": t.LowerName,
}.And(builder.Neq{"id": t.ID}),
t.LowerName = strings.ToLower(t.Name) )
has, err := db.Exist[organization.Team](ctx, builder.Eq{
"org_id": t.OrgID,
"lower_name": t.LowerName,
}.And(builder.Neq{"id": t.ID}),
)
if err != nil {
return err
} else if has {
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
}
sess := db.GetEngine(ctx)
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
return fmt.Errorf("update: %w", err)
}
// update units for team
if len(t.Units) > 0 {
for _, unit := range t.Units {
unit.TeamID = t.ID
}
// Delete team-unit.
if _, err := sess.
Where("team_id=?", t.ID).
Delete(new(organization.TeamUnit)); err != nil {
return err
}
if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
return err
}
}
// Update access for team members if needed.
if authChanged {
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
TeamID: t.ID,
})
if err != nil { if err != nil {
return fmt.Errorf("GetTeamRepositories: %w", err) return err
} else if has {
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
} }
for _, repo := range repos { sess := db.GetEngine(ctx)
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil { if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
return fmt.Errorf("recalculateTeamAccesses: %w", err) "can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
return fmt.Errorf("update: %w", err)
}
// update units for team
if len(t.Units) > 0 {
for _, unit := range t.Units {
unit.TeamID = t.ID
}
// Delete team-unit.
if _, err := sess.
Where("team_id=?", t.ID).
Delete(new(organization.TeamUnit)); err != nil {
return err
}
if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
return err
} }
} }
}
// Add all repositories to the team if it has access to all of them. // Update access for team members if needed.
if includeAllChanged && t.IncludesAllRepositories { if authChanged {
err = repo_service.AddAllRepositoriesToTeam(ctx, t) repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
if err != nil { TeamID: t.ID,
return fmt.Errorf("addAllRepositories: %w", err) })
if err != nil {
return fmt.Errorf("GetTeamRepositories: %w", err)
}
for _, repo := range repos {
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
return fmt.Errorf("recalculateTeamAccesses: %w", err)
}
}
} }
}
return committer.Commit() // Add all repositories to the team if it has access to all of them.
if includeAllChanged && t.IncludesAllRepositories {
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
if err != nil {
return fmt.Errorf("addAllRepositories: %w", err)
}
}
return nil
})
} }
// DeleteTeam deletes given team. // DeleteTeam deletes given team.
// It's caller's responsibility to assign organization ID. // It's caller's responsibility to assign organization ID.
func DeleteTeam(ctx context.Context, t *organization.Team) error { func DeleteTeam(ctx context.Context, t *organization.Team) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if err := t.LoadMembers(ctx); err != nil {
return err return err
}
defer committer.Close()
if err := t.LoadMembers(ctx); err != nil {
return err
}
// update branch protections
{
protections := make([]*git_model.ProtectedBranch, 0, 10)
err := db.GetEngine(ctx).In("repo_id",
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
Find(&protections)
if err != nil {
return fmt.Errorf("findProtectedBranches: %w", err)
} }
for _, p := range protections {
if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil { // update branch protections
{
protections := make([]*git_model.ProtectedBranch, 0, 10)
err := db.GetEngine(ctx).In("repo_id",
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
Find(&protections)
if err != nil {
return fmt.Errorf("findProtectedBranches: %w", err)
}
for _, p := range protections {
if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil {
return err
}
}
}
if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
return err
}
if err := db.DeleteBeans(ctx,
&organization.Team{ID: t.ID},
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
&organization.TeamUnit{TeamID: t.ID},
&organization.TeamInvite{TeamID: t.ID},
&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
); err != nil {
return err
}
for _, tm := range t.Members {
if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
return err return err
} }
} }
}
if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil { // Update organization number of teams.
_, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID)
return err return err
} })
if err := db.DeleteBeans(ctx,
&organization.Team{ID: t.ID},
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
&organization.TeamUnit{TeamID: t.ID},
&organization.TeamInvite{TeamID: t.ID},
&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
); err != nil {
return err
}
for _, tm := range t.Members {
if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
return err
}
}
// Update organization number of teams.
if _, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
return err
}
return committer.Commit()
} }
// AddTeamMember adds new membership of given team to given organization, // AddTeamMember adds new membership of given team to given organization,
@@ -363,13 +346,7 @@ func removeInvalidOrgUser(ctx context.Context, orgID int64, user *user_model.Use
// RemoveTeamMember removes member from given team of given organization. // RemoveTeamMember removes member from given team of given organization.
func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error { func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { return removeTeamMember(ctx, team, user)
return err })
}
defer committer.Close()
if err := removeTeamMember(ctx, team, user); err != nil {
return err
}
return committer.Commit()
} }

View File

@@ -164,42 +164,38 @@ func ExecuteCleanupRules(ctx context.Context) error {
}) })
} }
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error { func CleanupExpiredData(ctx context.Context, olderThan time.Duration) error {
ctx, committer, err := db.TxContext(outerCtx) pbs := make([]*packages_model.PackageBlob, 0, 100)
if err != nil { if err := db.WithTx(ctx, func(ctx context.Context) error {
return err if err := container_service.Cleanup(ctx, olderThan); err != nil {
}
defer committer.Close()
if err := container_service.Cleanup(ctx, olderThan); err != nil {
return err
}
ps, err := packages_model.FindUnreferencedPackages(ctx)
if err != nil {
return err
}
for _, p := range ps {
if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
return err return err
} }
if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
ps, err := packages_model.FindUnreferencedPackages(ctx)
if err != nil {
return err return err
} }
} for _, p := range ps {
if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
return err
}
if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
return err
}
}
pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan) pbs, err = packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
if err != nil { if err != nil {
return err
}
for _, pb := range pbs {
if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil {
return err return err
} }
}
if err := committer.Commit(); err != nil { for _, pb := range pbs {
if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil {
return err
}
}
return nil
}); err != nil {
return err return err
} }

View File

@@ -469,24 +469,15 @@ func RemovePackageVersionByNameAndVersion(ctx context.Context, doer *user_model.
// RemovePackageVersion deletes the package version and all associated files // RemovePackageVersion deletes the package version and all associated files
func RemovePackageVersion(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error { func RemovePackageVersion(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
dbCtx, committer, err := db.TxContext(ctx) pd, err := packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
return err
}
defer committer.Close()
pd, err := packages_model.GetPackageDescriptor(dbCtx, pv)
if err != nil { if err != nil {
return err return err
} }
log.Trace("Deleting package: %v", pv.ID) if err := db.WithTx(ctx, func(ctx context.Context) error {
log.Trace("Deleting package: %v", pv.ID)
if err := DeletePackageVersionAndReferences(dbCtx, pv); err != nil { return DeletePackageVersionAndReferences(ctx, pv)
return err }); err != nil {
}
if err := committer.Commit(); err != nil {
return err return err
} }

View File

@@ -687,48 +687,40 @@ func SetMerged(ctx context.Context, pr *issues_model.PullRequest, mergedCommitID
return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index) return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx2(ctx, func(ctx context.Context) (bool, error) {
if err != nil { pr.Issue = nil
return false, err if err := pr.LoadIssue(ctx); err != nil {
} return false, err
defer committer.Close() }
pr.Issue = nil if err := pr.Issue.LoadRepo(ctx); err != nil {
if err := pr.LoadIssue(ctx); err != nil { return false, err
return false, err }
}
if err := pr.Issue.LoadRepo(ctx); err != nil { if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
return false, err return false, err
} }
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil { // Removing an auto merge pull and ignore if not exist
return false, err if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
} return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
}
// Removing an auto merge pull and ignore if not exist // Set issue as closed
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) { if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil {
return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err) return false, fmt.Errorf("ChangeIssueStatus: %w", err)
} }
// Set issue as closed // We need to save all of the data used to compute this merge as it may have already been changed by testPullRequestBranchMergeable. FIXME: need to set some state to prevent testPullRequestBranchMergeable from running whilst we are merging.
if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil { if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID).
return false, fmt.Errorf("ChangeIssueStatus: %w", err) And("has_merged = ?", false).
} Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").
Update(pr); err != nil {
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
} else if cnt != 1 {
return false, issues_model.ErrIssueAlreadyChanged
}
// We need to save all of the data used to compute this merge as it may have already been changed by testPullRequestBranchMergeable. FIXME: need to set some state to prevent testPullRequestBranchMergeable from running whilst we are merging. return true, nil
if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID). })
And("has_merged = ?", false).
Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").
Update(pr); err != nil {
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
} else if cnt != 1 {
return false, issues_model.ErrIssueAlreadyChanged
}
if err := committer.Commit(); err != nil {
return false, err
}
return true, nil
} }

View File

@@ -29,35 +29,30 @@ func UploadAvatar(ctx context.Context, repo *repo_model.Repository, data []byte)
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { oldAvatarPath := repo.CustomAvatarRelativePath()
return err
}
defer committer.Close()
oldAvatarPath := repo.CustomAvatarRelativePath() // Users can upload the same image to other repo - prefix it with ID
// Then repo will be removed - only it avatar file will be removed
// Users can upload the same image to other repo - prefix it with ID repo.Avatar = newAvatar
// Then repo will be removed - only it avatar file will be removed if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
repo.Avatar = newAvatar return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
}
if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
_, err := w.Write(avatarData)
return err
}); err != nil {
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err)
}
if len(oldAvatarPath) > 0 {
if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil {
return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %w", oldAvatarPath, err)
} }
}
return committer.Commit() if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
_, err := w.Write(avatarData)
return err
}); err != nil {
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err)
}
if len(oldAvatarPath) > 0 {
if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil {
return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %w", oldAvatarPath, err)
}
}
return nil
})
} }
// DeleteAvatar deletes the repos's custom avatar. // DeleteAvatar deletes the repos's custom avatar.
@@ -70,22 +65,17 @@ func DeleteAvatar(ctx context.Context, repo *repo_model.Repository) error {
avatarPath := repo.CustomAvatarRelativePath() avatarPath := repo.CustomAvatarRelativePath()
log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath) log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath)
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { repo.Avatar = ""
return err if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
} return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err)
defer committer.Close() }
repo.Avatar = "" if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil { return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err) }
} return nil
})
if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
}
return committer.Commit()
} }
// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories // RemoveRandomAvatars removes the randomly generated avatars that were created for repositories

View File

@@ -71,40 +71,32 @@ func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, colla
UserID: collaborator.ID, UserID: collaborator.ID,
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil {
return err return err
} } else if has == 0 {
defer committer.Close() return nil
}
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil { if err := repo.LoadOwner(ctx); err != nil {
return err return err
} else if has == 0 { }
return committer.Commit()
}
if err := repo.LoadOwner(ctx); err != nil { if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
return err return err
} }
if err = access_model.RecalculateAccesses(ctx, repo); err != nil { if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil {
return err return err
} }
if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil { if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
return err return err
} }
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil { // Unassign a user from any issue (s)he has been assigned to in the repository
return err return ReconsiderRepoIssuesAssignee(ctx, repo, collaborator)
} })
// Unassign a user from any issue (s)he has been assigned to in the repository
if err := ReconsiderRepoIssuesAssignee(ctx, repo, collaborator); err != nil {
return err
}
return committer.Commit()
} }
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error { func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {

View File

@@ -86,17 +86,9 @@ func RemoveAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (e
return nil return nil
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { return removeAllRepositoriesFromTeam(ctx, t)
return err })
}
defer committer.Close()
if err = removeAllRepositoriesFromTeam(ctx, t); err != nil {
return err
}
return committer.Commit()
} }
// removeAllRepositoriesFromTeam removes all repositories from team and recalculates access // removeAllRepositoriesFromTeam removes all repositories from team and recalculates access
@@ -167,17 +159,9 @@ func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID
return err return err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { return removeRepositoryFromTeam(ctx, t, repo, true)
return err })
}
defer committer.Close()
if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil {
return err
}
return committer.Commit()
} }
// removeRepositoryFromTeam removes a repository from a team and recalculates access // removeRepositoryFromTeam removes a repository from a team and recalculates access

View File

@@ -16,41 +16,37 @@ import (
// UpdateRepositoryUnits updates a repository's units // UpdateRepositoryUnits updates a repository's units
func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, units []repo_model.RepoUnit, deleteUnitTypes []unit.Type) (err error) { func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, units []repo_model.RepoUnit, deleteUnitTypes []unit.Type) (err error) {
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { // Delete existing settings of units before adding again
return err for _, u := range units {
} deleteUnitTypes = append(deleteUnitTypes, u.Type)
defer committer.Close()
// Delete existing settings of units before adding again
for _, u := range units {
deleteUnitTypes = append(deleteUnitTypes, u.Type)
}
if slices.Contains(deleteUnitTypes, unit.TypeActions) {
if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
log.Error("CleanRepoScheduleTasks: %v", err)
} }
}
for _, u := range units { if slices.Contains(deleteUnitTypes, unit.TypeActions) {
if u.Type == unit.TypeActions { if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil { log.Error("CleanRepoScheduleTasks: %v", err)
log.Error("DetectAndHandleSchedules: %v", err)
} }
break
} }
}
if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil { for _, u := range units {
return err if u.Type == unit.TypeActions {
} if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
log.Error("DetectAndHandleSchedules: %v", err)
}
break
}
}
if len(units) > 0 { if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil {
if err = db.Insert(ctx, units); err != nil {
return err return err
} }
}
return committer.Commit() if len(units) > 0 {
if err = db.Insert(ctx, units); err != nil {
return err
}
}
return nil
})
} }

View File

@@ -24,26 +24,22 @@ func UploadAvatar(ctx context.Context, u *user_model.User, data []byte) error {
return err return err
} }
ctx, committer, err := db.TxContext(ctx) return db.WithTx(ctx, func(ctx context.Context) error {
if err != nil { u.UseCustomAvatar = true
return err u.Avatar = avatar.HashAvatar(u.ID, data)
} if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
defer committer.Close() return fmt.Errorf("updateUser: %w", err)
}
u.UseCustomAvatar = true if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
u.Avatar = avatar.HashAvatar(u.ID, data) _, err := w.Write(avatarData)
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil { return err
return fmt.Errorf("updateUser: %w", err) }); err != nil {
} return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
}
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error { return nil
_, err := w.Write(avatarData) })
return err
}); err != nil {
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
}
return committer.Commit()
} }
// DeleteAvatar deletes the user's custom avatar. // DeleteAvatar deletes the user's custom avatar.