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

pull/35143/head^2
Lunny Xiao 2025-07-23 01:02:01 +07:00 committed by GitHub
parent f201dde945
commit 65cd3f5309
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 1950 additions and 2591 deletions

@ -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 run.Repo == nil {
if len(needs) > 0 || run.NeedApproval { repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
status = StatusBlocked if err != nil {
} else { return err
hasWaiting = true }
run.Repo = repo
} }
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 err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
if hasWaiting {
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); 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) {

@ -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 {
return err
// 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()) // Loop through each schedule spec and create a new spec row
now := time.Now()
// Insert the new schedule spec row
if err = db.Insert(ctx, specRow); err != nil { for _, spec := range row.Specs {
return err 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
})
// Commit transaction
return committer.Commit()
} }
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 {
return err
}
if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil { if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil {
return err return err
} }
return committer.Commit() return nil
})
} }
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) { func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {

@ -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")
}
if task.Status.IsDone() {
// the state is final, do nothing
return task, nil
}
// state.Result is not unspecified means the task is finished task := &ActionTask{}
if state.Result != runnerv1.Result_RESULT_UNSPECIFIED { if has, err := e.ID(state.Id).Get(task); err != nil {
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 return nil, err
} else if !has {
return nil, util.ErrNotExist
} else if runnerID != task.RunnerID {
return nil, errors.New("invalid runner for task")
} }
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 { if task.Status.IsDone() {
var result runnerv1.Result // the state is final, do nothing
if v, ok := stepStates[step.Index]; ok { return task, nil
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) // state.Result is not unspecified means the task is finished
} else if step.Started != 0 { if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
step.Status = StatusRunning 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 := e.ID(step.ID).Update(step); err != nil {
if err := task.LoadAttributes(ctx); err != nil {
return nil, err return nil, err
} }
}
if err := committer.Commit(); err != nil { for _, step := range task.Steps {
return nil, err 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
}
}
return task, nil return task, nil
})
} }
func StopTask(ctx context.Context, taskID int64, status Status) error { func StopTask(ctx context.Context, taskID int64, status Status) error {

@ -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
}
}
// 3. increase repo
if repoID > 0 {
if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil {
log.Error("IncreaseTasksVersionByScope(Repo): %v", err)
return err
}
} }
}
return committer.Commit() return nil
})
} }

@ -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 {

@ -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
}
defer committer.Close()
if _, err = deleteGPGKey(ctx, key.KeyID); err != nil {
return err return err
} })
return committer.Commit()
} }
func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) { func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {

@ -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 {
return "", err
} else if !has {
return "", ErrGPGKeyNotExist{}
}
if err := key.LoadSubKeys(ctx); err != nil {
return "", err
}
sig, err := ExtractSignature(signature) has, err := db.GetEngine(ctx).Where("owner_id = ? AND key_id = ?", ownerID, keyID).Get(key)
if err != nil { if err != nil {
return "", ErrGPGInvalidTokenSignature{ return "", err
ID: key.KeyID, } else if !has {
Wrapped: err, return "", ErrGPGKeyNotExist{}
} }
}
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)

@ -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 {
return nil, err
}
// Key name of same user cannot be duplicated. // Key name of same user cannot be duplicated.
has, err := db.GetEngine(ctx). has, err := db.GetEngine(ctx).
Where("owner_id = ? AND name = ?", ownerID, name). Where("owner_id = ? AND name = ?", ownerID, name).
Get(new(PublicKey)) Get(new(PublicKey))
if err != nil { if err != nil {
return nil, err return nil, err
} else if has { } else if has {
return nil, ErrKeyNameAlreadyUsed{ownerID, name} return nil, ErrKeyNameAlreadyUsed{ownerID, name}
} }
key := &PublicKey{ key := &PublicKey{
OwnerID: ownerID, OwnerID: ownerID,
Name: name, Name: name,
Fingerprint: fingerprint, Fingerprint: fingerprint,
Content: content, Content: content,
Mode: perm.AccessModeWrite, Mode: perm.AccessModeWrite,
Type: KeyTypeUser, Type: KeyTypeUser,
LoginSourceID: authSourceID, LoginSourceID: authSourceID,
} }
if err = addKey(ctx, key); err != nil { if err = addKey(ctx, key); err != nil {
return nil, fmt.Errorf("addKey: %w", err) return nil, fmt.Errorf("addKey: %w", err)
} }
return key, committer.Commit() return key, nil
})
} }
// 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.

@ -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,
} else { Type: KeyTypeDeploy,
// First time use this deploy key. Content: content,
pkey = &PublicKey{ Name: name,
Fingerprint: fingerprint, }
Mode: accessMode, if err = addKey(ctx, pkey); err != nil {
Type: KeyTypeDeploy, return nil, fmt.Errorf("addKey: %w", err)
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.

@ -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
} has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key)
defer committer.Close() if err != nil {
return "", err
key := new(PublicKey) } else if !has {
return "", ErrKeyNotExist{}
has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key)
if err != nil {
return "", err
} else if !has {
return "", ErrKeyNotExist{}
}
err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
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 err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil { if err != nil {
return "", err // 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,
}
}
}
if err := committer.Commit(); err != nil { key.Verified = true
return "", err if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
} return "", err
}
return key.Fingerprint, nil return key.Fingerprint, nil
})
} }

@ -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 {
app, err := GetOAuth2ApplicationByID(ctx, opts.ID) return nil, errors.New("UID mismatch")
if err != nil { }
return nil, err builtinApps := BuiltinApplications()
} if _, builtin := builtinApps[app.ClientID]; builtin {
if app.UID != opts.UserID { return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID)
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 app.Name = opts.Name
app.RedirectURIs = opts.RedirectURIs app.RedirectURIs = opts.RedirectURIs
app.ConfidentialClient = opts.ConfidentialClient app.ConfidentialClient = opts.ConfidentialClient
app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization
if err = updateOAuth2Application(ctx, app); err != nil { if err = updateOAuth2Application(ctx, app); err != nil {
return nil, err return nil, err
} }
app.ClientSecret = "" app.ClientSecret = ""
return app, committer.Commit() return app, nil
})
} }
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()
} }
////////////////////////////////////////////////////// //////////////////////////////////////////////////////

@ -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 {
return nil, err if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil {
} else if has {
return nil, fmt.Errorf("session Key: %s already exists", newKey)
}
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": 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
} else if !has {
if err := db.Insert(ctx, &Session{
Key: oldKey,
Expiry: timeutil.TimeStampNow(),
}); err != nil {
return nil, err
}
} }
}
if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil { if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
return nil, err return nil, err
} }
s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey}) s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey})
if err != nil { if err != nil {
// is not exist, it should be impossible // is not exist, it should be impossible
return nil, err return nil, err
} }
return s, committer.Commit() return s, nil
})
} }
// CountSessions returns the number of sessions // CountSessions returns the number of sessions

@ -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
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
if err != nil {
return fmt.Errorf("generate commit status index failed: %w", err)
}
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description) opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context) opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL) opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
opts.CommitStatus.SHA = opts.SHA.String() opts.CommitStatus.SHA = opts.SHA.String()
opts.CommitStatus.CreatorID = opts.Creator.ID opts.CommitStatus.CreatorID = opts.Creator.ID
opts.CommitStatus.RepoID = opts.Repo.ID opts.CommitStatus.RepoID = opts.Repo.ID
opts.CommitStatus.Index = idx opts.CommitStatus.Index = idx
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index) log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context) opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
// Insert new CommitStatus // Insert new CommitStatus
if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil { if err = db.Insert(ctx, opts.CommitStatus); err != nil {
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err) return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
} }
return committer.Commit() return nil
})
} }
// SignCommitWithStatuses represents a commit with validation of signature and status state. // SignCommitWithStatuses represents a commit with validation of signature and status state.

@ -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.

@ -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 {
return nil, err
}
lock.Path = util.PathJoinRel(lock.Path) lock.Path = util.PathJoinRel(lock.Path)
lock.RepoID = repo.ID lock.RepoID = repo.ID
l, err := GetLFSLock(dbCtx, repo, lock.Path) l, err := GetLFSLock(ctx, repo, lock.Path)
if err == nil { if err == nil {
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path} return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
} }
if !IsErrLFSLockNotExist(err) { if !IsErrLFSLockNotExist(err) {
return nil, err return nil, err
} }
if err := db.Insert(dbCtx, lock); err != nil { if err := db.Insert(ctx, lock); err != nil {
return nil, err return nil, err
} }
return lock, committer.Commit() return lock, nil
})
} }
// 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 != nil {
return nil, err
}
if err := CheckLFSAccessForRepo(dbCtx, u.ID, repo, perm.AccessModeWrite); err != nil { if err := CheckLFSAccessForRepo(ctx, u.ID, repo, perm.AccessModeWrite); err != nil {
return nil, err return nil, err
} }
if !force && u.ID != lock.OwnerID { if !force && u.ID != lock.OwnerID {
return nil, errors.New("user doesn't own lock and force flag is not set") return nil, errors.New("user doesn't own lock and force flag is not set")
} }
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil { if _, err := db.GetEngine(ctx).ID(id).Delete(new(LFSLock)); err != nil {
return nil, err return nil, err
} }
return lock, committer.Commit() return lock, nil
})
} }
// CheckLFSAccessForRepo check needed access mode base on action // CheckLFSAccessForRepo check needed access mode base on action

@ -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 var commentMetaData *CommentMetaData
if opts.ProjectColumnTitle != "" { if opts.ProjectColumnTitle != "" {
commentMetaData = &CommentMetaData{ commentMetaData = &CommentMetaData{
ProjectColumnID: opts.ProjectColumnID, ProjectColumnID: opts.ProjectColumnID,
ProjectColumnTitle: opts.ProjectColumnTitle, ProjectColumnTitle: opts.ProjectColumnTitle,
ProjectTitle: opts.ProjectTitle, ProjectTitle: opts.ProjectTitle,
}
} }
}
comment := &Comment{ comment := &Comment{
Type: opts.Type, Type: opts.Type,
PosterID: opts.Doer.ID, PosterID: opts.Doer.ID,
Poster: opts.Doer, Poster: opts.Doer,
IssueID: opts.Issue.ID, IssueID: opts.Issue.ID,
LabelID: LabelID, LabelID: LabelID,
OldMilestoneID: opts.OldMilestoneID, OldMilestoneID: opts.OldMilestoneID,
MilestoneID: opts.MilestoneID, MilestoneID: opts.MilestoneID,
OldProjectID: opts.OldProjectID, OldProjectID: opts.OldProjectID,
ProjectID: opts.ProjectID, ProjectID: opts.ProjectID,
TimeID: opts.TimeID, TimeID: opts.TimeID,
RemovedAssignee: opts.RemovedAssignee, RemovedAssignee: opts.RemovedAssignee,
AssigneeID: opts.AssigneeID, AssigneeID: opts.AssigneeID,
AssigneeTeamID: opts.AssigneeTeamID, AssigneeTeamID: opts.AssigneeTeamID,
CommitID: opts.CommitID, CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA, CommitSHA: opts.CommitSHA,
Line: opts.LineNum, Line: opts.LineNum,
Content: opts.Content, Content: opts.Content,
OldTitle: opts.OldTitle, OldTitle: opts.OldTitle,
NewTitle: opts.NewTitle, NewTitle: opts.NewTitle,
OldRef: opts.OldRef, OldRef: opts.OldRef,
NewRef: opts.NewRef, NewRef: opts.NewRef,
DependentIssueID: opts.DependentIssueID, DependentIssueID: opts.DependentIssueID,
TreePath: opts.TreePath, TreePath: opts.TreePath,
ReviewID: opts.ReviewID, ReviewID: opts.ReviewID,
Patch: opts.Patch, Patch: opts.Patch,
RefRepoID: opts.RefRepoID, RefRepoID: opts.RefRepoID,
RefIssueID: opts.RefIssueID, RefIssueID: opts.RefIssueID,
RefCommentID: opts.RefCommentID, RefCommentID: opts.RefCommentID,
RefAction: opts.RefAction, RefAction: opts.RefAction,
RefIsPull: opts.RefIsPull, RefIsPull: opts.RefIsPull,
IsForcePush: opts.IsForcePush, IsForcePush: opts.IsForcePush,
Invalidated: opts.Invalidated, Invalidated: opts.Invalidated,
CommentMetaData: commentMetaData, CommentMetaData: commentMetaData,
} }
if _, err = e.Insert(comment); err != nil { if err = db.Insert(ctx, comment); err != nil {
return nil, err return nil, err
} }
if err = opts.Repo.LoadOwner(ctx); err != nil { if err = opts.Repo.LoadOwner(ctx); err != nil {
return nil, err return nil, err
} }
if err = updateCommentInfos(ctx, opts, comment); err != nil { if err = updateCommentInfos(ctx, opts, comment); err != nil {
return nil, err return nil, err
} }
if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil { if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil {
return nil, err return nil, err
} }
if err = committer.Commit(); err != nil { return comment, nil
return nil, err })
}
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 := sess.ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
if err != nil {
return err
}
if affected == 0 {
return ErrCommentAlreadyChanged
}
if err := c.LoadIssue(ctx); err != nil {
return err
}
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 affected, err := db.GetEngine(ctx).ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
if err != nil {
return err
}
if affected == 0 {
return ErrCommentAlreadyChanged
}
if err := c.LoadIssue(ctx); err != nil {
return err
}
return c.AddCrossReferences(ctx, doer, true)
})
} }
// 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 {
}
defer committer.Close()
for _, comment := range comments {
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil {
return err
}
for _, reaction := range comment.Reactions {
reaction.IssueID = comment.IssueID
reaction.CommentID = comment.ID
}
if len(comment.Reactions) > 0 {
if err := db.Insert(ctx, comment.Reactions); err != nil {
return err return err
} }
for _, reaction := range comment.Reactions {
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 _, issueID := range issueIDs { for _, issueID := range issueIDs {
if err := UpdateIssueNumComments(ctx, issueID); err != nil { if err := UpdateIssueNumComments(ctx, issueID); err != nil {
return err return err
}
} }
} return nil
return committer.Commit() })
} }

@ -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
}
// Check if it already exists if exists {
exists, err := issueDepExists(ctx, issue.ID, dep.ID) return ErrDependencyExists{issue.ID, dep.ID}
if err != nil { }
return err // And if it would be circular
} circular, err := issueDepExists(ctx, dep.ID, issue.ID)
if exists { if err != nil {
return ErrDependencyExists{issue.ID, dep.ID} return err
} }
// And if it would be circular if circular {
circular, err := issueDepExists(ctx, dep.ID, issue.ID) return ErrCircularDependency{issue.ID, dep.ID}
if err != nil { }
return err
} if err := db.Insert(ctx, &IssueDependency{
if circular { UserID: user.ID,
return ErrCircularDependency{issue.ID, dep.ID} IssueID: issue.ID,
} DependencyID: dep.ID,
}); err != nil {
if err := db.Insert(ctx, &IssueDependency{ return err
UserID: user.ID, }
IssueID: issue.ID,
DependencyID: dep.ID, // Add comment referencing the new dependency
}); err != nil { return createIssueDependencyComment(ctx, user, issue, dep, true)
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
} switch depType {
defer committer.Close() case DependencyTypeBlockedBy:
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
var issueDepToDelete IssueDependency case DependencyTypeBlocking:
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
switch depType { default:
case DependencyTypeBlockedBy: return ErrUnknownDependencyType{depType}
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID} }
case DependencyTypeBlocking:
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID} affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete)
default: if err != nil {
return ErrUnknownDependencyType{depType} return err
} }
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

@ -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 {

@ -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()
} }

@ -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 {
return err
}
// Do NOT add invalid labels
if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
return nil
}
if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil { // Do NOT add invalid labels
return nil if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
} return nil
}
if err = newIssueLabel(ctx, issue, label, doer); err != nil { if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil {
return err return nil
} }
issue.isLabelsLoaded = false if err = newIssueLabel(ctx, issue, label, doer); err != nil {
issue.Labels = nil return err
if err = issue.LoadLabels(ctx); err != nil { }
return err
}
return committer.Commit() issue.isLabelsLoaded = false
issue.Labels = nil
return issue.LoadLabels(ctx)
})
} }
// 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 {
return err
}
// reload all labels
issue.isLabelsLoaded = false
issue.Labels = nil
if err = issue.LoadLabels(ctx); err != nil {
return err
}
return committer.Commit() // reload all labels
issue.isLabelsLoaded = false
issue.Labels = nil
return issue.LoadLabels(ctx)
})
} }
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 {
return err
} else if err = issue.LoadPullRequest(ctx); err != nil {
return err
}
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, 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 { perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
return fmt.Errorf("Commit: %w", err) if err != nil {
} return err
}
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
return ErrRepoLabelNotExist{}
}
return nil return clearIssueLabels(ctx, issue, doer)
})
} }
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++
} }
addIndex++
removeIndex++
} else if addLabel.ID < removeLabel.ID {
// 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:]...)
toAdd = append(toAdd, labels[addIndex:]...) toRemove = append(toRemove, issue.Labels[removeIndex:]...)
toRemove = append(toRemove, issue.Labels[removeIndex:]...)
if len(toAdd) > 0 { if len(toAdd) > 0 {
if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil { if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil {
return fmt.Errorf("addLabels: %w", err) return fmt.Errorf("addLabels: %w", err)
}
} }
}
for _, l := range toRemove { for _, l := range toRemove {
if err = deleteIssueLabel(ctx, issue, l, doer); err != nil { if err = deleteIssueLabel(ctx, issue, l, doer); err != nil {
return fmt.Errorf("removeLabel: %w", err) return fmt.Errorf("removeLabel: %w", err)
}
} }
}
issue.Labels = nil
if err = issue.LoadLabels(ctx); err != nil {
return err
}
return committer.Commit() issue.Labels = nil
return issue.LoadLabels(ctx)
})
} }

@ -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 {
return err
}
opt := &CreateCommentOptions{ opt := &CreateCommentOptions{
Doer: opts.Doer, Doer: opts.Doer,
Issue: opts.Issue, Issue: opts.Issue,
Repo: opts.Issue.Repo, Repo: opts.Issue.Repo,
Type: commentType, Type: commentType,
Content: opts.Reason, Content: opts.Reason,
} }
if _, err := CreateComment(ctx, opt); err != nil { _, err := CreateComment(ctx, opt)
return err return err
} })
return committer.Commit()
} }

@ -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 = UpdateIssueCols(ctx, issue, "name"); err != nil {
return fmt.Errorf("updateIssueCols: %w", err)
}
if err = issue.LoadRepo(ctx); err != nil {
return fmt.Errorf("loadRepo: %w", err)
}
opts := &CreateCommentOptions{ if err = issue.LoadRepo(ctx); err != nil {
Type: CommentTypeChangeTitle, return fmt.Errorf("loadRepo: %w", err)
Doer: doer, }
Repo: issue.Repo,
Issue: issue,
OldTitle: oldTitle,
NewTitle: issue.Title,
}
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() opts := &CreateCommentOptions{
Type: CommentTypeChangeTitle,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldTitle: oldTitle,
NewTitle: issue.Title,
}
if _, err = CreateComment(ctx, opts); err != nil {
return fmt.Errorf("createComment: %w", err)
}
return issue.AddCrossReferences(ctx, doer, true)
})
} }
// 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 {
return fmt.Errorf("updateIssueCols: %w", err)
}
if err = issue.LoadRepo(ctx); err != nil {
return fmt.Errorf("loadRepo: %w", err)
}
oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix)
newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix)
opts := &CreateCommentOptions{ if err = issue.LoadRepo(ctx); err != nil {
Type: CommentTypeChangeIssueRef, return fmt.Errorf("loadRepo: %w", err)
Doer: doer, }
Repo: issue.Repo, oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix)
Issue: issue, newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix)
OldRef: oldRefFriendly,
NewRef: newRefFriendly,
}
if _, err = CreateComment(ctx, opts); err != nil {
return fmt.Errorf("createComment: %w", err)
}
return committer.Commit() opts := &CreateCommentOptions{
Type: CommentTypeChangeIssueRef,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldRef: oldRefFriendly,
NewRef: newRefFriendly,
}
if _, err = CreateComment(ctx, opts); err != nil {
return fmt.Errorf("createComment: %w", err)
}
return nil
})
} }
// 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 {
hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0) if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
if err != nil { issue.CreatedUnix, issue.Content, true); err != nil {
return fmt.Errorf("HasIssueContentHistory: %w", err) return fmt.Errorf("SaveIssueContentHistory: %w", err)
} }
if !hasContentHistory {
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
issue.CreatedUnix, issue.Content, true); err != nil {
return fmt.Errorf("SaveIssueContentHistory: %w", err)
} }
}
issue.Content = content
issue.ContentVersion = contentVersion + 1
affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue) issue.Content = content
if err != nil { issue.ContentVersion = contentVersion + 1
return err
}
if affected == 0 {
return ErrIssueAlreadyChanged
}
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0, affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue)
timeutil.TimeStampNow(), issue.Content, false); err != nil { if err != nil {
return fmt.Errorf("SaveIssueContentHistory: %w", err) return err
} }
if affected == 0 {
return ErrIssueAlreadyChanged
}
if err = issue.AddCrossReferences(ctx, doer, true); err != nil { if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
return fmt.Errorf("addCrossReferences: %w", err) timeutil.TimeStampNow(), issue.Content, false); err != nil {
} return fmt.Errorf("SaveIssueContentHistory: %w", err)
}
return committer.Commit() if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
return fmt.Errorf("addCrossReferences: %w", err)
}
return nil
})
} }
// 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
if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil {
return fmt.Errorf("createRemovedDueDateComment: %w", err)
}
return committer.Commit() // Make the comment
if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil {
return fmt.Errorf("createRemovedDueDateComment: %w", err)
}
return nil
})
} }
// 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.

@ -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
}
for _, l := range labels { l.Color = color
color, err := label.NormalizeColor(l.Color)
if err != nil {
return err
}
l.Color = color
if err := db.Insert(ctx, l); err != nil { if err := db.Insert(ctx, l); err != nil {
return err return err
}
} }
} return nil
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 err return nil
} }
defer committer.Close() if l.BelongsToRepo() && l.RepoID != id {
return nil
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 { if _, err = db.DeleteByID[Label](ctx, labelID); err != nil {
return err return err
} else if _, err = sess. } else if _, err = db.GetEngine(ctx).
Where("label_id = ?", labelID). Where("label_id = ?", labelID).
Delete(new(IssueLabel)); err != nil { Delete(new(IssueLabel)); err != nil {
return err return err
} }
// delete comments about now deleted label_id // delete comments about now deleted label_id
if _, err = sess.Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{}); err != nil { _, err = db.GetEngine(ctx).Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{})
return err return err
} })
return committer.Commit()
} }
// GetLabelByID returns a label by given ID. // GetLabelByID returns a label by given ID.

@ -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 { }
return err
}
if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil { _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID)
return err 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 {
m.ClosedDateUnix = timeutil.TimeStampNow()
}
if err := updateMilestone(ctx, m); err != nil {
return err
}
// if IsClosed changed, update milestone numbers of repository if err := updateMilestone(ctx, m); err != nil {
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{
ID: milestoneID,
RepoID: repoID,
}
has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m)
if err != nil {
return err
} else if !has {
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
}
if err := changeMilestoneStatus(ctx, m, isClosed); err != nil { has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m)
return err if err != nil {
} return err
} else if !has {
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
}
return committer.Commit() return changeMilestoneStatus(ctx, m, isClosed)
})
} }
// 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 {
return err
}
numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
RepoID: repo.ID, RepoID: repo.ID,
}) })
if err != nil { if err != nil {
return err return err
} }
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
RepoID: repo.ID, RepoID: repo.ID,
IsClosed: optional.Some(true), IsClosed: optional.Some(true),
}) })
if err != nil { if err != nil {
return err return err
} }
repo.NumMilestones = int(numMilestones) repo.NumMilestones = int(numMilestones)
repo.NumClosedMilestones = int(numClosedMilestones) repo.NumClosedMilestones = int(numClosedMilestones)
if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil { if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
return err return err
} }
if _, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { _, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID)
return err 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()
} }

@ -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
pr.BaseRepo = repo
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 { issue.Index = idx
return fmt.Errorf("Commit: %w", err) 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)
}
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
if _, err := sess.NoAutoTime().Insert(pr); err != nil {
return err
} }
} return nil
return committer.Commit() })
} }
// GetPullRequestByMergedCommit returns a merged pull request by the given commit // GetPullRequestByMergedCommit returns a merged pull request by the given commit

@ -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.

@ -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
} review := &Review{
defer committer.Close() Issue: opts.Issue,
sess := db.GetEngine(ctx) IssueID: opts.Issue.ID,
Reviewer: opts.Reviewer,
review := &Review{ ReviewerTeam: opts.ReviewerTeam,
Issue: opts.Issue, Content: opts.Content,
IssueID: opts.Issue.ID, Official: opts.Official,
Reviewer: opts.Reviewer, CommitID: opts.CommitID,
ReviewerTeam: opts.ReviewerTeam, Stale: opts.Stale,
Content: opts.Content, }
Official: opts.Official,
CommitID: opts.CommitID,
Stale: opts.Stale,
}
if opts.Reviewer != nil { if opts.Reviewer != nil {
review.Type = opts.Type review.Type = opts.Type
review.ReviewerID = opts.Reviewer.ID review.ReviewerID = opts.Reviewer.ID
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID} reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
// make sure user review requests are cleared // make sure user review requests are cleared
if opts.Type != ReviewTypePending { if opts.Type != ReviewTypePending {
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil { if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
return nil, err return nil, err
}
} }
} // make sure if the created review gets dismissed no old review surface
// make sure if the created review gets dismissed no old review surface // other types can be ignored, as they don't affect branch protection
// other types can be ignored, as they don't affect branch protection if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject { if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))). Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil { return nil, err
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")
} }
} 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 _, err := sess.Insert(review); err != nil {
return nil, err return nil, err
} }
return review, committer.Commit() 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 return err
}
} }
}
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil { if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
return err return err
}
} }
} return nil
})
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 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 review != nil {
if err := issue.LoadPullRequest(ctx); err != nil { // skip it when reviewer has been request to review
return nil, err if review.Type == ReviewTypeRequest {
return nil, nil // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
} }
if issue.PullRequest.HasMerged {
if issue.IsClosed {
return nil, ErrReviewRequestOnClosedPR{} 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, // if the reviewer is an official reviewer,
// remove the official flag in the all previous reviews // remove the official flag in the all previous reviews
official, err := IsOfficialReviewer(ctx, issue, reviewer) official, err := IsOfficialReviewer(ctx, issue, reviewer)
if err != nil { 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
} 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{ review, err = CreateReview(ctx, CreateReviewOptions{
Type: ReviewTypeRequest, Type: ReviewTypeRequest,
Issue: issue, Issue: issue,
Reviewer: reviewer, Reviewer: reviewer,
Official: official, Official: official,
Stale: false, Stale: false,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
comment, err := CreateComment(ctx, &CreateCommentOptions{ comment, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest, Type: CommentTypeReviewRequest,
Doer: doer, Doer: doer,
Repo: issue.Repo, Repo: issue.Repo,
Issue: issue, Issue: issue,
RemovedAssignee: false, // Use RemovedAssignee as !isRequest RemovedAssignee: false, // Use RemovedAssignee as !isRequest
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
ReviewID: review.ID, ReviewID: review.ID,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
// func caller use the created comment to retrieve created review too. // func caller use the created comment to retrieve created review too.
comment.Review = review comment.Review = review
return comment, committer.Commit() return comment, nil
})
} }
// 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) {
} return nil, 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 { if review == nil || review.Type != ReviewTypeRequest {
return nil, nil return nil, nil
} }
if _, err = db.DeleteByBean(ctx, review); err != nil { if _, err = db.DeleteByBean(ctx, review); err != nil {
return nil, err return nil, err
} }
official, err := IsOfficialReviewer(ctx, issue, reviewer) official, err := IsOfficialReviewer(ctx, issue, reviewer)
if err != nil { 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
} else if official {
if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil {
return nil, err
}
} }
}
comment, err := CreateComment(ctx, &CreateCommentOptions{ return CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest, Type: CommentTypeReviewRequest,
Doer: doer, Doer: doer,
Repo: issue.Repo, Repo: issue.Repo,
Issue: issue, Issue: issue,
RemovedAssignee: true, // Use RemovedAssignee as !isRequest RemovedAssignee: true, // Use RemovedAssignee as !isRequest
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID 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 if err != nil && !IsErrReviewNotExist(err) {
} return nil, err
defer committer.Close() }
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID) // This team already has been requested to review - therefore skip this.
if err != nil && !IsErrReviewNotExist(err) { if review != nil {
return nil, err return nil, nil
} }
// This team already has been requested to review - therefore skip this. official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
if review != nil { if err != nil {
return nil, 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)
}
}
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer) if review, err = CreateReview(ctx, CreateReviewOptions{
if err != nil { Type: ReviewTypeRequest,
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) Issue: issue,
} else if !official { ReviewerTeam: reviewer,
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil { Official: official,
return nil, fmt.Errorf("isOfficialReviewer(): %w", err) Stale: false,
}); err != nil {
return nil, err
} }
}
if review, err = CreateReview(ctx, CreateReviewOptions{ if official {
Type: ReviewTypeRequest, if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
Issue: issue, return nil, err
ReviewerTeam: reviewer, }
Official: official, }
Stale: false,
}); err != nil {
return nil, err
}
if official { comment, err := CreateComment(ctx, &CreateCommentOptions{
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil { Type: CommentTypeReviewRequest,
return nil, err 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: 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 // RemoveTeamReviewRequest remove a review request from one team
func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) { func RemoveTeamReviewRequest(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 if err != nil && !IsErrReviewNotExist(err) {
} 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 review == nil {
if err != nil { return nil, nil
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) }
}
if official { if _, err = db.DeleteByBean(ctx, review); err != nil {
// 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 return nil, err
} }
if review != nil { official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil { 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 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 { if doer == nil {
return nil, committer.Commit() return nil, nil
} }
comment, err := CreateComment(ctx, &CreateCommentOptions{ comment, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest, Type: CommentTypeReviewRequest,
Doer: doer, Doer: doer,
Repo: issue.Repo, Repo: issue.Repo,
Issue: issue, Issue: issue,
RemovedAssignee: true, // Use RemovedAssignee as !isRequest RemovedAssignee: true, // Use RemovedAssignee as !isRequest
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("CreateComment(): %w", err) return nil, fmt.Errorf("CreateComment(): %w", err)
} }
return comment, committer.Commit() 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 {
return errors.New("review is not allowed to be 0")
}
if r.Type == ReviewTypeRequest {
return errors.New("review request can not be deleted using this method")
}
opts := FindCommentsOptions{ if r.Type == ReviewTypeRequest {
Type: CommentTypeCode, return errors.New("review request can not be deleted using this method")
IssueID: r.IssueID, }
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil { opts := FindCommentsOptions{
return err Type: CommentTypeCode,
} IssueID: r.IssueID,
ReviewID: r.ID,
}
opts = FindCommentsOptions{ if _, err := db.Delete[Comment](ctx, opts); err != nil {
Type: CommentTypeReview, return err
IssueID: r.IssueID, }
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil { opts = FindCommentsOptions{
return err Type: CommentTypeReview,
} IssueID: r.IssueID,
ReviewID: r.ID,
}
opts = FindCommentsOptions{ if _, err := db.Delete[Comment](ctx, opts); err != nil {
Type: CommentTypeDismissReview, return err
IssueID: r.IssueID, }
ReviewID: r.ID,
}
if _, err := db.Delete[Comment](ctx, opts); err != nil { opts = FindCommentsOptions{
return err Type: CommentTypeDismissReview,
} IssueID: r.IssueID,
ReviewID: r.ID,
}
if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil { if _, err := db.Delete[Comment](ctx, opts); err != nil {
return err return err
} }
if r.Official { if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil {
if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil {
return err return err
} }
}
return committer.Commit() 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

@ -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 != nil {
return nil, err
}
if err := issue.LoadRepo(ctx); err != nil { if err := issue.LoadRepo(ctx); err != nil {
return nil, err return nil, err
} }
if _, err := CreateComment(ctx, &CreateCommentOptions{ if _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: issue, Issue: issue,
Repo: issue.Repo, Repo: issue.Repo,
Doer: user, Doer: user,
// Content before v1.21 did store the formatted string instead of seconds, // Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format // so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", amount), Content: fmt.Sprintf("|%d", amount),
Type: CommentTypeAddTimeManual, Type: CommentTypeAddTimeManual,
TimeID: t.ID, TimeID: t.ID,
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
return t, committer.Commit() return t, nil
})
} }
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{
IssueID: issue.ID,
UserID: user.ID,
}
removedTime, err := deleteTimes(ctx, opts) removedTime, err := deleteTimes(ctx, opts)
if err != nil { if err != nil {
return err return err
} }
if removedTime == 0 { if removedTime == 0 {
return db.ErrNotExist{Resource: "tracked_time"} return db.ErrNotExist{Resource: "tracked_time"}
} }
if err := issue.LoadRepo(ctx); err != nil { if err := issue.LoadRepo(ctx); err != nil {
return err return err
} }
if _, err := CreateComment(ctx, &CreateCommentOptions{ _, err = CreateComment(ctx, &CreateCommentOptions{
Issue: issue, Issue: issue,
Repo: issue.Repo, Repo: issue.Repo,
Doer: user, Doer: user,
// Content before v1.21 did store the formatted string instead of seconds, // Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format // so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", removedTime), Content: fmt.Sprintf("|%d", removedTime),
Type: CommentTypeDeleteTimeManual, Type: CommentTypeDeleteTimeManual,
}); err != nil { })
return err 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 {
return err
}
if err := deleteTime(ctx, t); err != nil { if err := deleteTime(ctx, t); err != nil {
return err return err
} }
if _, err := CreateComment(ctx, &CreateCommentOptions{ _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: t.Issue, Issue: t.Issue,
Repo: t.Issue.Repo, Repo: t.Issue.Repo,
Doer: t.User, Doer: t.User,
// Content before v1.21 did store the formatted string instead of seconds, // Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimiter to mark the new format // so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", t.Time), Content: fmt.Sprintf("|%d", t.Time),
Type: CommentTypeDeleteTimeManual, Type: CommentTypeDeleteTimeManual,
}); err != nil { })
return err 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) {

@ -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, org); err != nil {
if err = db.Insert(ctx, &OrgUser{ return fmt.Errorf("insert organization: %w", err)
UID: owner.ID, }
OrgID: org.ID, if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
IsPublic: setting.Service.DefaultOrgMemberVisible, return fmt.Errorf("generate random avatar: %w", err)
}); err != nil { }
return fmt.Errorf("insert org-user relation: %w", err)
}
// Create default owner team. // Add initial creator to organization and owner team.
t := &Team{ if err = db.Insert(ctx, &OrgUser{
OrgID: org.ID, UID: owner.ID,
LowerName: strings.ToLower(OwnerTeamName), OrgID: org.ID,
Name: OwnerTeamName, IsPublic: setting.Service.DefaultOrgMemberVisible,
AccessMode: perm.AccessModeOwner, }); err != nil {
NumMembers: 1, return fmt.Errorf("insert org-user relation: %w", err)
IncludesAllRepositories: true, }
CanCreateOrgRepo: true,
}
if err = db.Insert(ctx, t); err != nil {
return fmt.Errorf("insert owner team: %w", err)
}
// insert units for team // Create default owner team.
units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes)) t := &Team{
for _, tp := range unit.AllRepoUnitTypes { OrgID: org.ID,
up := perm.AccessModeOwner LowerName: strings.ToLower(OwnerTeamName),
if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki { Name: OwnerTeamName,
up = perm.AccessModeRead 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)
} }
units = append(units, TeamUnit{
OrgID: org.ID,
TeamID: t.ID,
Type: tp,
AccessMode: up,
})
}
if err = db.Insert(ctx, &units); err != nil { // insert units for team
return err 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, &TeamUser{ if err = db.Insert(ctx, &units); err != nil {
UID: owner.ID, return err
OrgID: org.ID, }
TeamID: t.ID,
}); err != nil {
return fmt.Errorf("insert team-user relation: %w", err)
}
return committer.Commit() 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
isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
if err != nil || isAlreadyMember {
return err
}
ou := &OrgUser{
UID: uid,
OrgID: orgID,
IsPublic: setting.Service.DefaultOrgMemberVisible,
}
if err := db.Insert(ctx, ou); err != nil { ou := &OrgUser{
return err UID: uid,
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil { OrgID: orgID,
return err IsPublic: setting.Service.DefaultOrgMemberVisible,
} }
return committer.Commit() 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 nil
})
} }
// GetOrgByID returns the user object by given ID if exists. // GetOrgByID returns the user object by given ID if exists.

@ -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
})
} }

@ -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}
}
if err := changeProjectStatus(ctx, p, isClosed); err != nil { has, err := db.GetEngine(ctx).ID(projectID).Where("repo_id = ?", repoID).Get(p)
return err if err != nil {
} return err
} else if !has {
return ErrProjectNotExist{ID: projectID, RepoID: repoID}
}
return committer.Commit() return changeProjectStatus(ctx, p, isClosed)
})
} }
// 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 {

@ -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

@ -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 topLang string
var s int64 var s int64
for lang, size := range stats { for lang, size := range stats {
if size > s { if size > s {
s = size s = size
topLang = lang topLang = lang
}
} }
}
for lang, size := range stats { for lang, size := range stats {
upd := false upd := false
for _, s := range oldstats { for _, s := range oldstats {
// Update already existing language // Update already existing language
if strings.EqualFold(s.Language, lang) { if strings.EqualFold(s.Language, lang) {
s.CommitID = commitID s.CommitID = commitID
s.IsPrimary = lang == topLang s.IsPrimary = lang == topLang
s.Size = size s.Size = size
if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil { 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 return err
} }
upd = true
break
} }
} }
// Insert new language // Delete old languages
if !upd { statsToDelete := make([]int64, 0, len(oldstats))
if err := db.Insert(ctx, &LanguageStat{ for _, s := range oldstats {
RepoID: repo.ID, if s.CommitID != commitID {
CommitID: commitID, statsToDelete = append(statsToDelete, s.ID)
IsPrimary: lang == topLang,
Language: lang,
Size: size,
}); err != nil {
return err
} }
} }
} if len(statsToDelete) > 0 {
// Delete old languages if _, err := sess.In("`id`", statsToDelete).Delete(&LanguageStat{}); err != nil {
statsToDelete := make([]int64, 0, len(oldstats)) return err
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() })
} }

@ -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 {
} return err
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 { if len(rel.Attachments) > 0 {
return err 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
})
return committer.Commit()
} }
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) {

@ -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
} if star {
defer committer.Close() if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) {
staring := IsStaring(ctx, doer.ID, repo.ID) return user_model.ErrBlockedUser
}
if star {
if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) { if staring {
return user_model.ErrBlockedUser return nil
} }
if staring { if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
return nil return err
} }
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil {
if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil { return err
return err }
} if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", doer.ID); err != nil {
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil { return err
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 _, 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() return nil
})
} }
// IsStaring checks if user has starred given repository. // IsStaring checks if user has starred given repository.

@ -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
topic, err := GetRepoTopicByName(ctx, repoID, topicName) return topic, nil
if err != nil { }
return nil, err
}
if topic != nil {
// Repo already have topic
return topic, nil
}
topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
if err != nil {
return nil, err
}
if err = syncTopicsInRepository(sess, repoID); err != nil { topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
return nil, err if err != nil {
} return nil, err
}
return topic, committer.Commit() if err = syncTopicsInRepository(ctx, repoID); err != nil {
return nil, err
}
return topic, nil
})
} }
// 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 for _, topicName := range topicNames {
} if strings.TrimSpace(topicName) == "" {
defer committer.Close() continue
sess := db.GetEngine(ctx) }
var addedTopicNames []string var found bool
for _, topicName := range topicNames { for _, t := range topics {
if strings.TrimSpace(topicName) == "" { if strings.EqualFold(topicName, t.Name) {
continue found = true
break
}
}
if !found {
addedTopicNames = append(addedTopicNames, topicName)
}
} }
var found bool var removeTopics []*Topic
for _, t := range topics { for _, t := range topics {
if strings.EqualFold(topicName, t.Name) { var found bool
found = true for _, topicName := range topicNames {
break if strings.EqualFold(topicName, t.Name) {
found = true
break
}
} }
} if !found {
if !found { removeTopics = append(removeTopics, t)
addedTopicNames = append(addedTopicNames, topicName)
}
}
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

@ -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

@ -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)

@ -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

@ -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.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
if err = db.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil { return err
return err }
}
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.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 }
} 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()
} }

@ -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.

@ -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
} }

@ -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
}
defer committer.Close()
if _, err = db.DeleteByID[asymkey_model.PublicKey](dbCtx, id); err != nil {
return err
}
if err = committer.Commit(); err != nil {
return err return err
} }
committer.Close()
if key.Type == asymkey_model.KeyTypePrincipal { if key.Type == asymkey_model.KeyTypePrincipal {
return RewriteAllPrincipalKeys(ctx) return RewriteAllPrincipalKeys(ctx)

@ -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)
} }

@ -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
} }

@ -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
} }

@ -15,30 +15,24 @@ 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 return err
} }); err != nil {
if err := committer.Commit(); err != nil {
return err return err
} }
committer.Close()
notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true) notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true)

@ -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
}
}
// Add all repositories to the team if it has access to all of them.
if t.IncludesAllRepositories {
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
if err != nil {
return fmt.Errorf("addAllRepositories: %w", err)
}
} }
}
// Update organization number of teams. // Update organization number of teams.
if _, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { _, 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{ if err != nil {
"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 return err
} else if has {
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
} }
if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
return err 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 access for team members if needed. // update units for team
if authChanged { if len(t.Units) > 0 {
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ for _, unit := range t.Units {
TeamID: t.ID, unit.TeamID = t.ID
}) }
if err != nil { // Delete team-unit.
return fmt.Errorf("GetTeamRepositories: %w", err) 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
}
} }
for _, repo := range repos { // Update access for team members if needed.
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil { if authChanged {
return fmt.Errorf("recalculateTeamAccesses: %w", err) repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
TeamID: t.ID,
})
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)
}
} }
} }
}
// Add all repositories to the team if it has access to all of them. // Add all repositories to the team if it has access to all of them.
if includeAllChanged && t.IncludesAllRepositories { if includeAllChanged && t.IncludesAllRepositories {
err = repo_service.AddAllRepositoriesToTeam(ctx, t) err = repo_service.AddAllRepositoriesToTeam(ctx, t)
if err != nil { if err != nil {
return fmt.Errorf("addAllRepositories: %w", err) return fmt.Errorf("addAllRepositories: %w", err)
}
} }
}
return committer.Commit() 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
return err {
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, if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
&organization.Team{ID: t.ID}, return err
&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 := db.DeleteBeans(ctx,
if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil { &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 return err
} }
}
// Update organization number of teams. for _, tm := range t.Members {
if _, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil { if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
return err return err
} }
}
return committer.Commit() // Update organization number of teams.
_, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID)
return err
})
} }
// 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()
} }

@ -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 {
pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan) return err
if err != nil { }
return err if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
} return err
}
}
for _, pb := range pbs { pbs, err = packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil { if 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
} }

@ -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 { if err != nil {
return err return err
} }
defer committer.Close()
pd, err := packages_model.GetPackageDescriptor(dbCtx, pv)
if err != nil {
return err
}
log.Trace("Deleting package: %v", pv.ID)
if err := DeletePackageVersionAndReferences(dbCtx, pv); err != nil { if err := db.WithTx(ctx, func(ctx context.Context) error {
return err log.Trace("Deleting package: %v", pv.ID)
} return DeletePackageVersionAndReferences(ctx, pv)
}); err != nil {
if err := committer.Commit(); err != nil {
return err return err
} }

@ -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.LoadIssue(ctx); err != nil {
return false, err
}
if err := pr.Issue.LoadRepo(ctx); err != nil {
return false, err
}
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil { if err := pr.Issue.LoadRepo(ctx); err != nil {
return false, err return false, err
} }
// Removing an auto merge pull and ignore if not exist if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) { return false, err
return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err) }
}
// Set issue as closed // Removing an auto merge pull and ignore if not exist
if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil { if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
return false, fmt.Errorf("ChangeIssueStatus: %w", err) return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
} }
// 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. // Set issue as closed
if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID). if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil {
And("has_merged = ?", false). return false, fmt.Errorf("ChangeIssueStatus: %w", err)
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 { // 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 false, err 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
}
return true, nil return true, nil
})
} }

@ -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
} // Users can upload the same image to other repo - prefix it with ID
defer committer.Close() // Then repo will be removed - only it avatar file will be removed
repo.Avatar = newAvatar
oldAvatarPath := repo.CustomAvatarRelativePath() if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
// 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
repo.Avatar = newAvatar
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.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil { _, err := w.Write(avatarData)
return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %w", oldAvatarPath, err) return err
}); err != nil {
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err)
} }
}
return committer.Commit() 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 := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err)
}
if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
}
return committer.Commit() if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
}
return nil
})
} }
// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories // RemoveRandomAvatars removes the randomly generated avatars that were created for repositories

@ -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 {
return err
} else if has == 0 {
return committer.Commit()
}
if err := repo.LoadOwner(ctx); err != nil {
return err
}
if err = access_model.RecalculateAccesses(ctx, repo); err != nil { if err := repo.LoadOwner(ctx); err != nil {
return err return err
} }
if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil { if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
return err return err
} }
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil { if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil {
return err return err
} }
// Unassign a user from any issue (s)he has been assigned to in the repository if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
if err := ReconsiderRepoIssuesAssignee(ctx, repo, collaborator); err != nil { return err
return err }
}
return committer.Commit() // Unassign a user from any issue (s)he has been assigned to in the repository
return ReconsiderRepoIssuesAssignee(ctx, repo, collaborator)
})
} }
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 {

@ -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

@ -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
})
} }

@ -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
u.Avatar = avatar.HashAvatar(u.ID, data)
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
return fmt.Errorf("updateUser: %w", err)
}
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error { if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
_, err := w.Write(avatarData) _, err := w.Write(avatarData)
return err return err
}); err != nil { }); err != nil {
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err) return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
} }
return committer.Commit() return nil
})
} }
// DeleteAvatar deletes the user's custom avatar. // DeleteAvatar deletes the user's custom avatar.