mirror of
https://github.com/go-gitea/gitea
synced 2025-07-23 10:48:37 +00:00
Use db.WithTx/WithTx2 instead of TxContext when possible (#35130)
This commit is contained in:
@@ -282,77 +282,72 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
|||||||
// InsertRun inserts a run
|
// InsertRun inserts a run
|
||||||
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
||||||
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
run.Index = index
|
|
||||||
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
|
||||||
|
|
||||||
if err := db.Insert(ctx, run); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if run.Repo == nil {
|
|
||||||
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
run.Repo = repo
|
run.Index = index
|
||||||
}
|
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
||||||
|
|
||||||
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
if err := db.Insert(ctx, run); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runJobs := make([]*ActionRunJob, 0, len(jobs))
|
|
||||||
var hasWaiting bool
|
|
||||||
for _, v := range jobs {
|
|
||||||
id, job := v.Job()
|
|
||||||
needs := job.Needs()
|
|
||||||
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
payload, _ := v.Marshal()
|
|
||||||
status := StatusWaiting
|
|
||||||
if len(needs) > 0 || run.NeedApproval {
|
|
||||||
status = StatusBlocked
|
|
||||||
} else {
|
|
||||||
hasWaiting = true
|
|
||||||
}
|
|
||||||
job.Name = util.EllipsisDisplayString(job.Name, 255)
|
|
||||||
runJobs = append(runJobs, &ActionRunJob{
|
|
||||||
RunID: run.ID,
|
|
||||||
RepoID: run.RepoID,
|
|
||||||
OwnerID: run.OwnerID,
|
|
||||||
CommitSHA: run.CommitSHA,
|
|
||||||
IsForkPullRequest: run.IsForkPullRequest,
|
|
||||||
Name: job.Name,
|
|
||||||
WorkflowPayload: payload,
|
|
||||||
JobID: id,
|
|
||||||
Needs: needs,
|
|
||||||
RunsOn: job.RunsOn(),
|
|
||||||
Status: status,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := db.Insert(ctx, runJobs); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a job in the waiting status, increase tasks version.
|
if run.Repo == nil {
|
||||||
if hasWaiting {
|
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
||||||
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
run.Repo = repo
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
runJobs := make([]*ActionRunJob, 0, len(jobs))
|
||||||
|
var hasWaiting bool
|
||||||
|
for _, v := range jobs {
|
||||||
|
id, job := v.Job()
|
||||||
|
needs := job.Needs()
|
||||||
|
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
payload, _ := v.Marshal()
|
||||||
|
status := StatusWaiting
|
||||||
|
if len(needs) > 0 || run.NeedApproval {
|
||||||
|
status = StatusBlocked
|
||||||
|
} else {
|
||||||
|
hasWaiting = true
|
||||||
|
}
|
||||||
|
job.Name = util.EllipsisDisplayString(job.Name, 255)
|
||||||
|
runJobs = append(runJobs, &ActionRunJob{
|
||||||
|
RunID: run.ID,
|
||||||
|
RepoID: run.RepoID,
|
||||||
|
OwnerID: run.OwnerID,
|
||||||
|
CommitSHA: run.CommitSHA,
|
||||||
|
IsForkPullRequest: run.IsForkPullRequest,
|
||||||
|
Name: job.Name,
|
||||||
|
WorkflowPayload: payload,
|
||||||
|
JobID: id,
|
||||||
|
Needs: needs,
|
||||||
|
RunsOn: job.RunsOn(),
|
||||||
|
Status: status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := db.Insert(ctx, runJobs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a job in the waiting status, increase tasks version.
|
||||||
|
if hasWaiting {
|
||||||
|
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
|
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
|
||||||
|
@@ -56,65 +56,54 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin transaction
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
// Loop through each schedule row
|
||||||
if err != nil {
|
for _, row := range rows {
|
||||||
return err
|
row.Title = util.EllipsisDisplayString(row.Title, 255)
|
||||||
}
|
// Create new schedule row
|
||||||
defer committer.Close()
|
if err := db.Insert(ctx, row); err != nil {
|
||||||
|
|
||||||
// Loop through each schedule row
|
|
||||||
for _, row := range rows {
|
|
||||||
row.Title = util.EllipsisDisplayString(row.Title, 255)
|
|
||||||
// Create new schedule row
|
|
||||||
if err = db.Insert(ctx, row); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through each schedule spec and create a new spec row
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
for _, spec := range row.Specs {
|
|
||||||
specRow := &ActionScheduleSpec{
|
|
||||||
RepoID: row.RepoID,
|
|
||||||
ScheduleID: row.ID,
|
|
||||||
Spec: spec,
|
|
||||||
}
|
|
||||||
// Parse the spec and check for errors
|
|
||||||
schedule, err := specRow.Parse()
|
|
||||||
if err != nil {
|
|
||||||
continue // skip to the next spec if there's an error
|
|
||||||
}
|
|
||||||
|
|
||||||
specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix())
|
|
||||||
|
|
||||||
// Insert the new schedule spec row
|
|
||||||
if err = db.Insert(ctx, specRow); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit transaction
|
// Loop through each schedule spec and create a new spec row
|
||||||
return committer.Commit()
|
now := time.Now()
|
||||||
|
|
||||||
|
for _, spec := range row.Specs {
|
||||||
|
specRow := &ActionScheduleSpec{
|
||||||
|
RepoID: row.RepoID,
|
||||||
|
ScheduleID: row.ID,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
// Parse the spec and check for errors
|
||||||
|
schedule, err := specRow.Parse()
|
||||||
|
if err != nil {
|
||||||
|
continue // skip to the next spec if there's an error
|
||||||
|
}
|
||||||
|
|
||||||
|
specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix())
|
||||||
|
|
||||||
|
// Insert the new schedule spec row
|
||||||
|
if err = db.Insert(ctx, specRow); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
|
func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil {
|
if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil {
|
return nil
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
|
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
|
||||||
|
@@ -352,78 +352,70 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
|
|||||||
stepStates[v.Id] = v
|
stepStates[v.Id] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*ActionTask, error) {
|
||||||
if err != nil {
|
e := db.GetEngine(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
e := db.GetEngine(ctx)
|
task := &ActionTask{}
|
||||||
|
if has, err := e.ID(state.Id).Get(task); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, util.ErrNotExist
|
||||||
|
} else if runnerID != task.RunnerID {
|
||||||
|
return nil, errors.New("invalid runner for task")
|
||||||
|
}
|
||||||
|
|
||||||
task := &ActionTask{}
|
if task.Status.IsDone() {
|
||||||
if has, err := e.ID(state.Id).Get(task); err != nil {
|
// the state is final, do nothing
|
||||||
return nil, err
|
return task, nil
|
||||||
} else if !has {
|
}
|
||||||
return nil, util.ErrNotExist
|
|
||||||
} else if runnerID != task.RunnerID {
|
// state.Result is not unspecified means the task is finished
|
||||||
return nil, errors.New("invalid runner for task")
|
if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
|
||||||
}
|
task.Status = Status(state.Result)
|
||||||
|
task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
|
||||||
|
if err := UpdateTask(ctx, task, "status", "stopped"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
||||||
|
ID: task.JobID,
|
||||||
|
Status: task.Status,
|
||||||
|
Stopped: task.Stopped,
|
||||||
|
}, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
|
||||||
|
task.Updated = timeutil.TimeStampNow()
|
||||||
|
if err := UpdateTask(ctx, task, "updated"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.LoadAttributes(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, step := range task.Steps {
|
||||||
|
var result runnerv1.Result
|
||||||
|
if v, ok := stepStates[step.Index]; ok {
|
||||||
|
result = v.Result
|
||||||
|
step.LogIndex = v.LogIndex
|
||||||
|
step.LogLength = v.LogLength
|
||||||
|
step.Started = convertTimestamp(v.StartedAt)
|
||||||
|
step.Stopped = convertTimestamp(v.StoppedAt)
|
||||||
|
}
|
||||||
|
if result != runnerv1.Result_RESULT_UNSPECIFIED {
|
||||||
|
step.Status = Status(result)
|
||||||
|
} else if step.Started != 0 {
|
||||||
|
step.Status = StatusRunning
|
||||||
|
}
|
||||||
|
if _, err := e.ID(step.ID).Update(step); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if task.Status.IsDone() {
|
|
||||||
// the state is final, do nothing
|
|
||||||
return task, nil
|
return task, nil
|
||||||
}
|
})
|
||||||
|
|
||||||
// state.Result is not unspecified means the task is finished
|
|
||||||
if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
|
|
||||||
task.Status = Status(state.Result)
|
|
||||||
task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
|
|
||||||
if err := UpdateTask(ctx, task, "status", "stopped"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
|
||||||
ID: task.JobID,
|
|
||||||
Status: task.Status,
|
|
||||||
Stopped: task.Stopped,
|
|
||||||
}, nil); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
|
|
||||||
task.Updated = timeutil.TimeStampNow()
|
|
||||||
if err := UpdateTask(ctx, task, "updated"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := task.LoadAttributes(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, step := range task.Steps {
|
|
||||||
var result runnerv1.Result
|
|
||||||
if v, ok := stepStates[step.Index]; ok {
|
|
||||||
result = v.Result
|
|
||||||
step.LogIndex = v.LogIndex
|
|
||||||
step.LogLength = v.LogLength
|
|
||||||
step.Started = convertTimestamp(v.StartedAt)
|
|
||||||
step.Stopped = convertTimestamp(v.StoppedAt)
|
|
||||||
}
|
|
||||||
if result != runnerv1.Result_RESULT_UNSPECIFIED {
|
|
||||||
step.Status = Status(result)
|
|
||||||
} else if step.Started != 0 {
|
|
||||||
step.Status = StatusRunning
|
|
||||||
}
|
|
||||||
if _, err := e.ID(step.ID).Update(step); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return task, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func StopTask(ctx context.Context, taskID int64, status Status) error {
|
func StopTask(ctx context.Context, taskID int64, status Status) error {
|
||||||
|
@@ -73,33 +73,29 @@ func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
|
func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// 1. increase global
|
||||||
return err
|
if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
|
||||||
}
|
log.Error("IncreaseTasksVersionByScope(Global): %v", err)
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// 1. increase global
|
|
||||||
if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
|
|
||||||
log.Error("IncreaseTasksVersionByScope(Global): %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. increase owner
|
|
||||||
if ownerID > 0 {
|
|
||||||
if err := increaseTasksVersionByScope(ctx, ownerID, 0); err != nil {
|
|
||||||
log.Error("IncreaseTasksVersionByScope(Owner): %v", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 3. increase repo
|
// 2. increase owner
|
||||||
if repoID > 0 {
|
if ownerID > 0 {
|
||||||
if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil {
|
if err := increaseTasksVersionByScope(ctx, ownerID, 0); err != nil {
|
||||||
log.Error("IncreaseTasksVersionByScope(Repo): %v", err)
|
log.Error("IncreaseTasksVersionByScope(Owner): %v", err)
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
// 3. increase repo
|
||||||
|
if repoID > 0 {
|
||||||
|
if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil {
|
||||||
|
log.Error("IncreaseTasksVersionByScope(Repo): %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@@ -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
|
return err
|
||||||
}
|
})
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = deleteGPGKey(ctx, key.KeyID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
|
func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
|
||||||
|
@@ -14,97 +14,76 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// __________________ ________ ____ __.
|
|
||||||
// / _____/\______ \/ _____/ | |/ _|____ ___.__.
|
|
||||||
// / \ ___ | ___/ \ ___ | <_/ __ < | |
|
|
||||||
// \ \_\ \| | \ \_\ \ | | \ ___/\___ |
|
|
||||||
// \______ /|____| \______ / |____|__ \___ > ____|
|
|
||||||
// \/ \/ \/ \/\/
|
|
||||||
// ____ ____ .__ _____
|
|
||||||
// \ \ / /___________|__|/ ____\__.__.
|
|
||||||
// \ Y // __ \_ __ \ \ __< | |
|
|
||||||
// \ /\ ___/| | \/ || | \___ |
|
|
||||||
// \___/ \___ >__| |__||__| / ____|
|
|
||||||
// \/ \/
|
|
||||||
|
|
||||||
// This file provides functions relating verifying gpg keys
|
// This file provides functions relating verifying gpg keys
|
||||||
|
|
||||||
// VerifyGPGKey marks a GPG key as verified
|
// VerifyGPGKey marks a GPG key as verified
|
||||||
func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature string) (string, error) {
|
func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature string) (string, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (string, error) {
|
||||||
if err != nil {
|
key := new(GPGKey)
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
key := new(GPGKey)
|
has, err := db.GetEngine(ctx).Where("owner_id = ? AND key_id = ?", ownerID, keyID).Get(key)
|
||||||
|
if err != nil {
|
||||||
has, err := db.GetEngine(ctx).Where("owner_id = ? AND key_id = ?", ownerID, keyID).Get(key)
|
return "", err
|
||||||
if err != nil {
|
} else if !has {
|
||||||
return "", err
|
return "", ErrGPGKeyNotExist{}
|
||||||
} else if !has {
|
|
||||||
return "", ErrGPGKeyNotExist{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := key.LoadSubKeys(ctx); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := ExtractSignature(signature)
|
|
||||||
if err != nil {
|
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
|
||||||
ID: key.KeyID,
|
|
||||||
Wrapped: err,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := hashAndVerifyWithSubKeys(sig, token, key)
|
if err := key.LoadSubKeys(ctx); err != nil {
|
||||||
if err != nil {
|
return "", err
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
|
||||||
ID: key.KeyID,
|
|
||||||
Wrapped: err,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if signer == nil {
|
sig, err := ExtractSignature(signature)
|
||||||
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n", key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
ID: key.KeyID,
|
ID: key.KeyID,
|
||||||
Wrapped: err,
|
Wrapped: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if signer == nil {
|
signer, err := hashAndVerifyWithSubKeys(sig, token, key)
|
||||||
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n\n", key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
ID: key.KeyID,
|
ID: key.KeyID,
|
||||||
Wrapped: err,
|
Wrapped: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if signer == nil {
|
||||||
|
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n", key)
|
||||||
if signer == nil {
|
if err != nil {
|
||||||
log.Debug("VerifyGPGKey failed: no signer")
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
ID: key.KeyID,
|
||||||
ID: key.KeyID,
|
Wrapped: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if signer == nil {
|
||||||
|
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n\n", key)
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
|
ID: key.KeyID,
|
||||||
|
Wrapped: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if signer.PrimaryKeyID != key.KeyID && signer.KeyID != key.KeyID {
|
if signer == nil {
|
||||||
return "", ErrGPGKeyNotExist{}
|
log.Debug("VerifyGPGKey failed: no signer")
|
||||||
}
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
|
ID: key.KeyID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
key.Verified = true
|
if signer.PrimaryKeyID != key.KeyID && signer.KeyID != key.KeyID {
|
||||||
if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil {
|
return "", ErrGPGKeyNotExist{}
|
||||||
return "", err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
key.Verified = true
|
||||||
return "", err
|
if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil {
|
||||||
}
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
return key.KeyID, nil
|
return key.KeyID, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerificationToken returns token for the user that will be valid in minutes (time)
|
// VerificationToken returns token for the user that will be valid in minutes (time)
|
||||||
|
@@ -99,40 +99,36 @@ func AddPublicKey(ctx context.Context, ownerID int64, name, content string, auth
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*PublicKey, error) {
|
||||||
if err != nil {
|
if err := checkKeyFingerprint(ctx, fingerprint); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := checkKeyFingerprint(ctx, fingerprint); err != nil {
|
// Key name of same user cannot be duplicated.
|
||||||
return nil, err
|
has, err := db.GetEngine(ctx).
|
||||||
}
|
Where("owner_id = ? AND name = ?", ownerID, name).
|
||||||
|
Get(new(PublicKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if has {
|
||||||
|
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
||||||
|
}
|
||||||
|
|
||||||
// Key name of same user cannot be duplicated.
|
key := &PublicKey{
|
||||||
has, err := db.GetEngine(ctx).
|
OwnerID: ownerID,
|
||||||
Where("owner_id = ? AND name = ?", ownerID, name).
|
Name: name,
|
||||||
Get(new(PublicKey))
|
Fingerprint: fingerprint,
|
||||||
if err != nil {
|
Content: content,
|
||||||
return nil, err
|
Mode: perm.AccessModeWrite,
|
||||||
} else if has {
|
Type: KeyTypeUser,
|
||||||
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
LoginSourceID: authSourceID,
|
||||||
}
|
}
|
||||||
|
if err = addKey(ctx, key); err != nil {
|
||||||
|
return nil, fmt.Errorf("addKey: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
key := &PublicKey{
|
return key, nil
|
||||||
OwnerID: ownerID,
|
})
|
||||||
Name: name,
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
Content: content,
|
|
||||||
Mode: perm.AccessModeWrite,
|
|
||||||
Type: KeyTypeUser,
|
|
||||||
LoginSourceID: authSourceID,
|
|
||||||
}
|
|
||||||
if err = addKey(ctx, key); err != nil {
|
|
||||||
return nil, fmt.Errorf("addKey: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublicKeyByID returns public key by given ID.
|
// GetPublicKeyByID returns public key by given ID.
|
||||||
@@ -288,33 +284,24 @@ func PublicKeyIsExternallyManaged(ctx context.Context, id int64) (bool, error) {
|
|||||||
|
|
||||||
// deleteKeysMarkedForDeletion returns true if ssh keys needs update
|
// deleteKeysMarkedForDeletion returns true if ssh keys needs update
|
||||||
func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, error) {
|
func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, error) {
|
||||||
// Start session
|
return db.WithTx2(ctx, func(ctx context.Context) (bool, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
// Delete keys marked for deletion
|
||||||
if err != nil {
|
var sshKeysNeedUpdate bool
|
||||||
return false, err
|
for _, KeyToDelete := range keys {
|
||||||
}
|
key, err := SearchPublicKeyByContent(ctx, KeyToDelete)
|
||||||
defer committer.Close()
|
if err != nil {
|
||||||
|
log.Error("SearchPublicKeyByContent: %v", err)
|
||||||
// Delete keys marked for deletion
|
continue
|
||||||
var sshKeysNeedUpdate bool
|
}
|
||||||
for _, KeyToDelete := range keys {
|
if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
|
||||||
key, err := SearchPublicKeyByContent(ctx, KeyToDelete)
|
log.Error("DeleteByID[PublicKey]: %v", err)
|
||||||
if err != nil {
|
continue
|
||||||
log.Error("SearchPublicKeyByContent: %v", err)
|
}
|
||||||
continue
|
sshKeysNeedUpdate = true
|
||||||
}
|
}
|
||||||
if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
|
|
||||||
log.Error("DeleteByID[PublicKey]: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sshKeysNeedUpdate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
return sshKeysNeedUpdate, nil
|
||||||
return false, err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return sshKeysNeedUpdate, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPublicKeysBySource add a users public keys. Returns true if there are changes.
|
// AddPublicKeysBySource add a users public keys. Returns true if there are changes.
|
||||||
|
@@ -125,39 +125,35 @@ func AddDeployKey(ctx context.Context, repoID int64, name, content string, readO
|
|||||||
accessMode = perm.AccessModeWrite
|
accessMode = perm.AccessModeWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
|
||||||
if err != nil {
|
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
} else if exist {
|
||||||
|
if pkey.Type != KeyTypeDeploy {
|
||||||
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
|
return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
} else {
|
||||||
} else if exist {
|
// First time use this deploy key.
|
||||||
if pkey.Type != KeyTypeDeploy {
|
pkey = &PublicKey{
|
||||||
return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
|
Fingerprint: fingerprint,
|
||||||
|
Mode: accessMode,
|
||||||
|
Type: KeyTypeDeploy,
|
||||||
|
Content: content,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
if err = addKey(ctx, pkey); err != nil {
|
||||||
|
return nil, fmt.Errorf("addKey: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// First time use this deploy key.
|
|
||||||
pkey = &PublicKey{
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
Mode: accessMode,
|
|
||||||
Type: KeyTypeDeploy,
|
|
||||||
Content: content,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
if err = addKey(ctx, pkey); err != nil {
|
|
||||||
return nil, fmt.Errorf("addKey: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
|
key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, committer.Commit()
|
return key, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDeployKeyByID returns deploy key by given ID.
|
// GetDeployKeyByID returns deploy key by given ID.
|
||||||
|
@@ -15,41 +15,33 @@ import (
|
|||||||
|
|
||||||
// VerifySSHKey marks a SSH key as verified
|
// VerifySSHKey marks a SSH key as verified
|
||||||
func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signature string) (string, error) {
|
func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signature string) (string, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (string, error) {
|
||||||
if err != nil {
|
key := new(PublicKey)
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
key := new(PublicKey)
|
has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if !has {
|
||||||
|
return "", ErrKeyNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key)
|
err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
|
||||||
} else if !has {
|
// see https://github.com/PowerShell/PowerShell/issues/5974
|
||||||
return "", ErrKeyNotExist{}
|
if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
|
||||||
}
|
log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
|
||||||
|
return "", ErrSSHInvalidTokenSignature{
|
||||||
err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
|
Fingerprint: key.Fingerprint,
|
||||||
if err != nil {
|
}
|
||||||
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
|
|
||||||
// see https://github.com/PowerShell/PowerShell/issues/5974
|
|
||||||
if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
|
|
||||||
log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
|
|
||||||
return "", ErrSSHInvalidTokenSignature{
|
|
||||||
Fingerprint: key.Fingerprint,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
key.Verified = true
|
key.Verified = true
|
||||||
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
|
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
return key.Fingerprint, nil
|
||||||
return "", err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return key.Fingerprint, nil
|
|
||||||
}
|
}
|
||||||
|
@@ -289,35 +289,31 @@ type UpdateOAuth2ApplicationOptions struct {
|
|||||||
|
|
||||||
// UpdateOAuth2Application updates an oauth2 application
|
// UpdateOAuth2Application updates an oauth2 application
|
||||||
func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) {
|
func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*OAuth2Application, error) {
|
||||||
if err != nil {
|
app, err := GetOAuth2ApplicationByID(ctx, opts.ID)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
if app.UID != opts.UserID {
|
||||||
|
return nil, errors.New("UID mismatch")
|
||||||
|
}
|
||||||
|
builtinApps := BuiltinApplications()
|
||||||
|
if _, builtin := builtinApps[app.ClientID]; builtin {
|
||||||
|
return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID)
|
||||||
|
}
|
||||||
|
|
||||||
app, err := GetOAuth2ApplicationByID(ctx, opts.ID)
|
app.Name = opts.Name
|
||||||
if err != nil {
|
app.RedirectURIs = opts.RedirectURIs
|
||||||
return nil, err
|
app.ConfidentialClient = opts.ConfidentialClient
|
||||||
}
|
app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization
|
||||||
if app.UID != opts.UserID {
|
|
||||||
return nil, errors.New("UID mismatch")
|
|
||||||
}
|
|
||||||
builtinApps := BuiltinApplications()
|
|
||||||
if _, builtin := builtinApps[app.ClientID]; builtin {
|
|
||||||
return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Name = opts.Name
|
if err = updateOAuth2Application(ctx, app); err != nil {
|
||||||
app.RedirectURIs = opts.RedirectURIs
|
return nil, err
|
||||||
app.ConfidentialClient = opts.ConfidentialClient
|
}
|
||||||
app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization
|
app.ClientSecret = ""
|
||||||
|
|
||||||
if err = updateOAuth2Application(ctx, app); err != nil {
|
return app, nil
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
app.ClientSecret = ""
|
|
||||||
|
|
||||||
return app, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
|
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
|
||||||
@@ -358,23 +354,17 @@ func deleteOAuth2Application(ctx context.Context, id, userid int64) error {
|
|||||||
|
|
||||||
// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
|
// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
|
||||||
func DeleteOAuth2Application(ctx context.Context, id, userid int64) error {
|
func DeleteOAuth2Application(ctx context.Context, id, userid int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
app, err := GetOAuth2ApplicationByID(ctx, id)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
app, err := GetOAuth2ApplicationByID(ctx, id)
|
builtinApps := BuiltinApplications()
|
||||||
if err != nil {
|
if _, builtin := builtinApps[app.ClientID]; builtin {
|
||||||
return err
|
return fmt.Errorf("failed to delete OAuth2 application: application is locked: %s", app.ClientID)
|
||||||
}
|
}
|
||||||
builtinApps := BuiltinApplications()
|
return deleteOAuth2Application(ctx, id, userid)
|
||||||
if _, builtin := builtinApps[app.ClientID]; builtin {
|
})
|
||||||
return fmt.Errorf("failed to delete OAuth2 application: application is locked: %s", app.ClientID)
|
|
||||||
}
|
|
||||||
if err := deleteOAuth2Application(ctx, id, userid); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
|
@@ -35,26 +35,22 @@ func UpdateSession(ctx context.Context, key string, data []byte) error {
|
|||||||
|
|
||||||
// ReadSession reads the data for the provided session
|
// ReadSession reads the data for the provided session
|
||||||
func ReadSession(ctx context.Context, key string) (*Session, error) {
|
func ReadSession(ctx context.Context, key string) (*Session, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Session, error) {
|
||||||
if err != nil {
|
session, exist, err := db.Get[Session](ctx, builder.Eq{"`key`": key})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
session, exist, err := db.Get[Session](ctx, builder.Eq{"`key`": key})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !exist {
|
|
||||||
session = &Session{
|
|
||||||
Key: key,
|
|
||||||
Expiry: timeutil.TimeStampNow(),
|
|
||||||
}
|
|
||||||
if err := db.Insert(ctx, session); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
|
} else if !exist {
|
||||||
|
session = &Session{
|
||||||
|
Key: key,
|
||||||
|
Expiry: timeutil.TimeStampNow(),
|
||||||
|
}
|
||||||
|
if err := db.Insert(ctx, session); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return session, committer.Commit()
|
return session, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExistSession checks if a session exists
|
// ExistSession checks if a session exists
|
||||||
@@ -72,40 +68,36 @@ func DestroySession(ctx context.Context, key string) error {
|
|||||||
|
|
||||||
// RegenerateSession regenerates a session from the old id
|
// RegenerateSession regenerates a session from the old id
|
||||||
func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, error) {
|
func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Session, error) {
|
||||||
if err != nil {
|
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": newKey}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
} else if has {
|
||||||
defer committer.Close()
|
return nil, fmt.Errorf("session Key: %s already exists", newKey)
|
||||||
|
}
|
||||||
|
|
||||||
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": newKey}); err != nil {
|
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if has {
|
} else if !has {
|
||||||
return nil, fmt.Errorf("session Key: %s already exists", newKey)
|
if err := db.Insert(ctx, &Session{
|
||||||
}
|
Key: oldKey,
|
||||||
|
Expiry: timeutil.TimeStampNow(),
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil {
|
if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
if err := db.Insert(ctx, &Session{
|
|
||||||
Key: oldKey,
|
|
||||||
Expiry: timeutil.TimeStampNow(),
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
|
s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
// is not exist, it should be impossible
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey})
|
return s, nil
|
||||||
if err != nil {
|
})
|
||||||
// is not exist, it should be impossible
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountSessions returns the number of sessions
|
// CountSessions returns the number of sessions
|
||||||
|
@@ -470,35 +470,31 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
|
|||||||
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
|
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// Get the next Status Index
|
||||||
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", opts.Repo.ID, opts.Creator.ID, opts.SHA, err)
|
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
|
||||||
}
|
if err != nil {
|
||||||
defer committer.Close()
|
return fmt.Errorf("generate commit status index failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the next Status Index
|
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
|
||||||
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
|
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
|
||||||
if err != nil {
|
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
|
||||||
return fmt.Errorf("generate commit status index failed: %w", err)
|
opts.CommitStatus.SHA = opts.SHA.String()
|
||||||
}
|
opts.CommitStatus.CreatorID = opts.Creator.ID
|
||||||
|
opts.CommitStatus.RepoID = opts.Repo.ID
|
||||||
|
opts.CommitStatus.Index = idx
|
||||||
|
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
|
||||||
|
|
||||||
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
|
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
|
||||||
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
|
|
||||||
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
|
|
||||||
opts.CommitStatus.SHA = opts.SHA.String()
|
|
||||||
opts.CommitStatus.CreatorID = opts.Creator.ID
|
|
||||||
opts.CommitStatus.RepoID = opts.Repo.ID
|
|
||||||
opts.CommitStatus.Index = idx
|
|
||||||
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
|
|
||||||
|
|
||||||
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
|
// Insert new CommitStatus
|
||||||
|
if err = db.Insert(ctx, opts.CommitStatus); err != nil {
|
||||||
|
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Insert new CommitStatus
|
return nil
|
||||||
if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil {
|
})
|
||||||
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignCommitWithStatuses represents a commit with validation of signature and status state.
|
// SignCommitWithStatuses represents a commit with validation of signature and status state.
|
||||||
|
@@ -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 {
|
lock.Path = util.PathJoinRel(lock.Path)
|
||||||
return nil, err
|
lock.RepoID = repo.ID
|
||||||
}
|
|
||||||
|
|
||||||
lock.Path = util.PathJoinRel(lock.Path)
|
l, err := GetLFSLock(ctx, repo, lock.Path)
|
||||||
lock.RepoID = repo.ID
|
if err == nil {
|
||||||
|
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
|
||||||
|
}
|
||||||
|
if !IsErrLFSLockNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
l, err := GetLFSLock(dbCtx, repo, lock.Path)
|
if err := db.Insert(ctx, lock); err != nil {
|
||||||
if err == nil {
|
return nil, err
|
||||||
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
|
}
|
||||||
}
|
|
||||||
if !IsErrLFSLockNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Insert(dbCtx, lock); err != nil {
|
return lock, nil
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return lock, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLFSLock returns release by given path.
|
// GetLFSLock returns release by given path.
|
||||||
@@ -163,30 +159,26 @@ func CountLFSLockByRepoID(ctx context.Context, repoID int64) (int64, error) {
|
|||||||
|
|
||||||
// DeleteLFSLockByID deletes a lock by given ID.
|
// DeleteLFSLockByID deletes a lock by given ID.
|
||||||
func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
|
func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
|
||||||
if err != nil {
|
lock, err := GetLFSLockByID(ctx, id)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
lock, err := GetLFSLockByID(dbCtx, id)
|
if err := CheckLFSAccessForRepo(ctx, u.ID, repo, perm.AccessModeWrite); err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := CheckLFSAccessForRepo(dbCtx, u.ID, repo, perm.AccessModeWrite); err != nil {
|
if !force && u.ID != lock.OwnerID {
|
||||||
return nil, err
|
return nil, errors.New("user doesn't own lock and force flag is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !force && u.ID != lock.OwnerID {
|
if _, err := db.GetEngine(ctx).ID(id).Delete(new(LFSLock)); err != nil {
|
||||||
return nil, errors.New("user doesn't own lock and force flag is not set")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
|
return lock, nil
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return lock, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckLFSAccessForRepo check needed access mode base on action
|
// CheckLFSAccessForRepo check needed access mode base on action
|
||||||
|
@@ -766,81 +766,73 @@ func (c *Comment) CodeCommentLink(ctx context.Context) string {
|
|||||||
|
|
||||||
// CreateComment creates comment with context
|
// CreateComment creates comment with context
|
||||||
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
|
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
var LabelID int64
|
||||||
return nil, err
|
if opts.Label != nil {
|
||||||
}
|
LabelID = opts.Label.ID
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
e := db.GetEngine(ctx)
|
|
||||||
var LabelID int64
|
|
||||||
if opts.Label != nil {
|
|
||||||
LabelID = opts.Label.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
var commentMetaData *CommentMetaData
|
|
||||||
if opts.ProjectColumnTitle != "" {
|
|
||||||
commentMetaData = &CommentMetaData{
|
|
||||||
ProjectColumnID: opts.ProjectColumnID,
|
|
||||||
ProjectColumnTitle: opts.ProjectColumnTitle,
|
|
||||||
ProjectTitle: opts.ProjectTitle,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
comment := &Comment{
|
var commentMetaData *CommentMetaData
|
||||||
Type: opts.Type,
|
if opts.ProjectColumnTitle != "" {
|
||||||
PosterID: opts.Doer.ID,
|
commentMetaData = &CommentMetaData{
|
||||||
Poster: opts.Doer,
|
ProjectColumnID: opts.ProjectColumnID,
|
||||||
IssueID: opts.Issue.ID,
|
ProjectColumnTitle: opts.ProjectColumnTitle,
|
||||||
LabelID: LabelID,
|
ProjectTitle: opts.ProjectTitle,
|
||||||
OldMilestoneID: opts.OldMilestoneID,
|
}
|
||||||
MilestoneID: opts.MilestoneID,
|
}
|
||||||
OldProjectID: opts.OldProjectID,
|
|
||||||
ProjectID: opts.ProjectID,
|
|
||||||
TimeID: opts.TimeID,
|
|
||||||
RemovedAssignee: opts.RemovedAssignee,
|
|
||||||
AssigneeID: opts.AssigneeID,
|
|
||||||
AssigneeTeamID: opts.AssigneeTeamID,
|
|
||||||
CommitID: opts.CommitID,
|
|
||||||
CommitSHA: opts.CommitSHA,
|
|
||||||
Line: opts.LineNum,
|
|
||||||
Content: opts.Content,
|
|
||||||
OldTitle: opts.OldTitle,
|
|
||||||
NewTitle: opts.NewTitle,
|
|
||||||
OldRef: opts.OldRef,
|
|
||||||
NewRef: opts.NewRef,
|
|
||||||
DependentIssueID: opts.DependentIssueID,
|
|
||||||
TreePath: opts.TreePath,
|
|
||||||
ReviewID: opts.ReviewID,
|
|
||||||
Patch: opts.Patch,
|
|
||||||
RefRepoID: opts.RefRepoID,
|
|
||||||
RefIssueID: opts.RefIssueID,
|
|
||||||
RefCommentID: opts.RefCommentID,
|
|
||||||
RefAction: opts.RefAction,
|
|
||||||
RefIsPull: opts.RefIsPull,
|
|
||||||
IsForcePush: opts.IsForcePush,
|
|
||||||
Invalidated: opts.Invalidated,
|
|
||||||
CommentMetaData: commentMetaData,
|
|
||||||
}
|
|
||||||
if _, err = e.Insert(comment); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = opts.Repo.LoadOwner(ctx); err != nil {
|
comment := &Comment{
|
||||||
return nil, err
|
Type: opts.Type,
|
||||||
}
|
PosterID: opts.Doer.ID,
|
||||||
|
Poster: opts.Doer,
|
||||||
|
IssueID: opts.Issue.ID,
|
||||||
|
LabelID: LabelID,
|
||||||
|
OldMilestoneID: opts.OldMilestoneID,
|
||||||
|
MilestoneID: opts.MilestoneID,
|
||||||
|
OldProjectID: opts.OldProjectID,
|
||||||
|
ProjectID: opts.ProjectID,
|
||||||
|
TimeID: opts.TimeID,
|
||||||
|
RemovedAssignee: opts.RemovedAssignee,
|
||||||
|
AssigneeID: opts.AssigneeID,
|
||||||
|
AssigneeTeamID: opts.AssigneeTeamID,
|
||||||
|
CommitID: opts.CommitID,
|
||||||
|
CommitSHA: opts.CommitSHA,
|
||||||
|
Line: opts.LineNum,
|
||||||
|
Content: opts.Content,
|
||||||
|
OldTitle: opts.OldTitle,
|
||||||
|
NewTitle: opts.NewTitle,
|
||||||
|
OldRef: opts.OldRef,
|
||||||
|
NewRef: opts.NewRef,
|
||||||
|
DependentIssueID: opts.DependentIssueID,
|
||||||
|
TreePath: opts.TreePath,
|
||||||
|
ReviewID: opts.ReviewID,
|
||||||
|
Patch: opts.Patch,
|
||||||
|
RefRepoID: opts.RefRepoID,
|
||||||
|
RefIssueID: opts.RefIssueID,
|
||||||
|
RefCommentID: opts.RefCommentID,
|
||||||
|
RefAction: opts.RefAction,
|
||||||
|
RefIsPull: opts.RefIsPull,
|
||||||
|
IsForcePush: opts.IsForcePush,
|
||||||
|
Invalidated: opts.Invalidated,
|
||||||
|
CommentMetaData: commentMetaData,
|
||||||
|
}
|
||||||
|
if err = db.Insert(ctx, comment); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err = updateCommentInfos(ctx, opts, comment); err != nil {
|
if err = opts.Repo.LoadOwner(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil {
|
if err = updateCommentInfos(ctx, opts, comment); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil {
|
||||||
}
|
return nil, err
|
||||||
return comment, nil
|
}
|
||||||
|
return comment, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment *Comment) (err error) {
|
func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment *Comment) (err error) {
|
||||||
@@ -1092,33 +1084,21 @@ func UpdateCommentInvalidate(ctx context.Context, c *Comment) error {
|
|||||||
|
|
||||||
// UpdateComment updates information of comment.
|
// UpdateComment updates information of comment.
|
||||||
func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error {
|
func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
c.ContentVersion = contentVersion + 1
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
c.ContentVersion = contentVersion + 1
|
affected, err := db.GetEngine(ctx).ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
|
||||||
|
if err != nil {
|
||||||
affected, err := sess.ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return err
|
if affected == 0 {
|
||||||
}
|
return ErrCommentAlreadyChanged
|
||||||
if affected == 0 {
|
}
|
||||||
return ErrCommentAlreadyChanged
|
if err := c.LoadIssue(ctx); err != nil {
|
||||||
}
|
return err
|
||||||
if err := c.LoadIssue(ctx); err != nil {
|
}
|
||||||
return err
|
return c.AddCrossReferences(ctx, doer, true)
|
||||||
}
|
})
|
||||||
if err := c.AddCrossReferences(ctx, doer, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return fmt.Errorf("Commit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteComment deletes the comment
|
// DeleteComment deletes the comment
|
||||||
@@ -1277,31 +1257,28 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
|
|||||||
return comment.IssueID, true
|
return comment.IssueID, true
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, comment := range comments {
|
||||||
return err
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
for _, comment := range comments {
|
|
||||||
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil {
|
for _, reaction := range comment.Reactions {
|
||||||
return err
|
reaction.IssueID = comment.IssueID
|
||||||
|
reaction.CommentID = comment.ID
|
||||||
|
}
|
||||||
|
if len(comment.Reactions) > 0 {
|
||||||
|
if err := db.Insert(ctx, comment.Reactions); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, reaction := range comment.Reactions {
|
for _, issueID := range issueIDs {
|
||||||
reaction.IssueID = comment.IssueID
|
if err := UpdateIssueNumComments(ctx, issueID); err != nil {
|
||||||
reaction.CommentID = comment.ID
|
|
||||||
}
|
|
||||||
if len(comment.Reactions) > 0 {
|
|
||||||
if err := db.Insert(ctx, comment.Reactions); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
for _, issueID := range issueIDs {
|
|
||||||
if err := UpdateIssueNumComments(ctx, issueID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@@ -128,79 +128,64 @@ const (
|
|||||||
|
|
||||||
// CreateIssueDependency creates a new dependency for an issue
|
// CreateIssueDependency creates a new dependency for an issue
|
||||||
func CreateIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue) error {
|
func CreateIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// Check if it already exists
|
||||||
return err
|
exists, err := issueDepExists(ctx, issue.ID, dep.ID)
|
||||||
}
|
if err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return ErrDependencyExists{issue.ID, dep.ID}
|
||||||
|
}
|
||||||
|
// And if it would be circular
|
||||||
|
circular, err := issueDepExists(ctx, dep.ID, issue.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if circular {
|
||||||
|
return ErrCircularDependency{issue.ID, dep.ID}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if it already exists
|
if err := db.Insert(ctx, &IssueDependency{
|
||||||
exists, err := issueDepExists(ctx, issue.ID, dep.ID)
|
UserID: user.ID,
|
||||||
if err != nil {
|
IssueID: issue.ID,
|
||||||
return err
|
DependencyID: dep.ID,
|
||||||
}
|
}); err != nil {
|
||||||
if exists {
|
return err
|
||||||
return ErrDependencyExists{issue.ID, dep.ID}
|
}
|
||||||
}
|
|
||||||
// And if it would be circular
|
|
||||||
circular, err := issueDepExists(ctx, dep.ID, issue.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if circular {
|
|
||||||
return ErrCircularDependency{issue.ID, dep.ID}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Insert(ctx, &IssueDependency{
|
// Add comment referencing the new dependency
|
||||||
UserID: user.ID,
|
return createIssueDependencyComment(ctx, user, issue, dep, true)
|
||||||
IssueID: issue.ID,
|
})
|
||||||
DependencyID: dep.ID,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add comment referencing the new dependency
|
|
||||||
if err = createIssueDependencyComment(ctx, user, issue, dep, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveIssueDependency removes a dependency from an issue
|
// RemoveIssueDependency removes a dependency from an issue
|
||||||
func RemoveIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) {
|
func RemoveIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
var issueDepToDelete IssueDependency
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
var issueDepToDelete IssueDependency
|
switch depType {
|
||||||
|
case DependencyTypeBlockedBy:
|
||||||
|
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
|
||||||
|
case DependencyTypeBlocking:
|
||||||
|
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
|
||||||
|
default:
|
||||||
|
return ErrUnknownDependencyType{depType}
|
||||||
|
}
|
||||||
|
|
||||||
switch depType {
|
affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete)
|
||||||
case DependencyTypeBlockedBy:
|
if err != nil {
|
||||||
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
|
return err
|
||||||
case DependencyTypeBlocking:
|
}
|
||||||
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
|
|
||||||
default:
|
|
||||||
return ErrUnknownDependencyType{depType}
|
|
||||||
}
|
|
||||||
|
|
||||||
affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete)
|
// If we deleted nothing, the dependency did not exist
|
||||||
if err != nil {
|
if affected <= 0 {
|
||||||
return err
|
return ErrDependencyNotExists{issue.ID, dep.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we deleted nothing, the dependency did not exist
|
// Add comment referencing the removed dependency
|
||||||
if affected <= 0 {
|
return createIssueDependencyComment(ctx, user, issue, dep, false)
|
||||||
return ErrDependencyNotExists{issue.ID, dep.ID}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// Add comment referencing the removed dependency
|
|
||||||
if err = createIssueDependencyComment(ctx, user, issue, dep, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the dependency already exists
|
// Check if the dependency already exists
|
||||||
|
@@ -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 {
|
// Do NOT add invalid labels
|
||||||
return err
|
if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
|
||||||
}
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Do NOT add invalid labels
|
if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil {
|
||||||
if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
|
return nil
|
||||||
return nil
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil {
|
if err = newIssueLabel(ctx, issue, label, doer); err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = newIssueLabel(ctx, issue, label, doer); err != nil {
|
issue.isLabelsLoaded = false
|
||||||
return err
|
issue.Labels = nil
|
||||||
}
|
return issue.LoadLabels(ctx)
|
||||||
|
})
|
||||||
issue.isLabelsLoaded = false
|
|
||||||
issue.Labels = nil
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIssueLabels add labels to an issue. It will check if the labels are valid for the issue
|
// newIssueLabels add labels to an issue. It will check if the labels are valid for the issue
|
||||||
@@ -151,24 +143,16 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *us
|
|||||||
|
|
||||||
// NewIssueLabels creates a list of issue-label relations.
|
// NewIssueLabels creates a list of issue-label relations.
|
||||||
func NewIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
func NewIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = newIssueLabels(ctx, issue, labels, doer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = newIssueLabels(ctx, issue, labels, doer); err != nil {
|
// reload all labels
|
||||||
return err
|
issue.isLabelsLoaded = false
|
||||||
}
|
issue.Labels = nil
|
||||||
|
return issue.LoadLabels(ctx)
|
||||||
// reload all labels
|
})
|
||||||
issue.isLabelsLoaded = false
|
|
||||||
issue.Labels = nil
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) {
|
func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) {
|
||||||
@@ -365,35 +349,23 @@ func clearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User)
|
|||||||
// ClearIssueLabels removes all issue labels as the given user.
|
// ClearIssueLabels removes all issue labels as the given user.
|
||||||
// Triggers appropriate WebHooks, if any.
|
// Triggers appropriate WebHooks, if any.
|
||||||
func ClearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) {
|
func ClearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := issue.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
} else if err = issue.LoadPullRequest(ctx); err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := issue.LoadRepo(ctx); err != nil {
|
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
||||||
return err
|
if err != nil {
|
||||||
} else if err = issue.LoadPullRequest(ctx); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||||
|
return ErrRepoLabelNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
return clearIssueLabels(ctx, issue, doer)
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
|
||||||
return ErrRepoLabelNotExist{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = clearIssueLabels(ctx, issue, doer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return fmt.Errorf("Commit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type labelSorter []*Label
|
type labelSorter []*Label
|
||||||
@@ -438,69 +410,61 @@ func RemoveDuplicateExclusiveLabels(labels []*Label) []*Label {
|
|||||||
// ReplaceIssueLabels removes all current labels and add new labels to the issue.
|
// ReplaceIssueLabels removes all current labels and add new labels to the issue.
|
||||||
// Triggers appropriate WebHooks, if any.
|
// Triggers appropriate WebHooks, if any.
|
||||||
func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = issue.LoadRepo(ctx); err != nil {
|
if err = issue.LoadLabels(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
labels = RemoveDuplicateExclusiveLabels(labels)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
labels = RemoveDuplicateExclusiveLabels(labels)
|
sort.Sort(labelSorter(labels))
|
||||||
|
sort.Sort(labelSorter(issue.Labels))
|
||||||
|
|
||||||
sort.Sort(labelSorter(labels))
|
var toAdd, toRemove []*Label
|
||||||
sort.Sort(labelSorter(issue.Labels))
|
|
||||||
|
|
||||||
var toAdd, toRemove []*Label
|
addIndex, removeIndex := 0, 0
|
||||||
|
for addIndex < len(labels) && removeIndex < len(issue.Labels) {
|
||||||
|
addLabel := labels[addIndex]
|
||||||
|
removeLabel := issue.Labels[removeIndex]
|
||||||
|
if addLabel.ID == removeLabel.ID {
|
||||||
|
// Silently drop invalid labels
|
||||||
|
if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID {
|
||||||
|
toRemove = append(toRemove, removeLabel)
|
||||||
|
}
|
||||||
|
|
||||||
addIndex, removeIndex := 0, 0
|
addIndex++
|
||||||
for addIndex < len(labels) && removeIndex < len(issue.Labels) {
|
removeIndex++
|
||||||
addLabel := labels[addIndex]
|
} else if addLabel.ID < removeLabel.ID {
|
||||||
removeLabel := issue.Labels[removeIndex]
|
// Only add if the label is valid
|
||||||
if addLabel.ID == removeLabel.ID {
|
if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID {
|
||||||
// Silently drop invalid labels
|
toAdd = append(toAdd, addLabel)
|
||||||
if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID {
|
}
|
||||||
|
addIndex++
|
||||||
|
} else {
|
||||||
toRemove = append(toRemove, removeLabel)
|
toRemove = append(toRemove, removeLabel)
|
||||||
|
removeIndex++
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
toAdd = append(toAdd, labels[addIndex:]...)
|
||||||
|
toRemove = append(toRemove, issue.Labels[removeIndex:]...)
|
||||||
|
|
||||||
addIndex++
|
if len(toAdd) > 0 {
|
||||||
removeIndex++
|
if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil {
|
||||||
} else if addLabel.ID < removeLabel.ID {
|
return fmt.Errorf("addLabels: %w", err)
|
||||||
// Only add if the label is valid
|
|
||||||
if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID {
|
|
||||||
toAdd = append(toAdd, addLabel)
|
|
||||||
}
|
}
|
||||||
addIndex++
|
|
||||||
} else {
|
|
||||||
toRemove = append(toRemove, removeLabel)
|
|
||||||
removeIndex++
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
toAdd = append(toAdd, labels[addIndex:]...)
|
|
||||||
toRemove = append(toRemove, issue.Labels[removeIndex:]...)
|
|
||||||
|
|
||||||
if len(toAdd) > 0 {
|
for _, l := range toRemove {
|
||||||
if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil {
|
if err = deleteIssueLabel(ctx, issue, l, doer); err != nil {
|
||||||
return fmt.Errorf("addLabels: %w", err)
|
return fmt.Errorf("removeLabel: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, l := range toRemove {
|
issue.Labels = nil
|
||||||
if err = deleteIssueLabel(ctx, issue, l, doer); err != nil {
|
return issue.LoadLabels(ctx)
|
||||||
return fmt.Errorf("removeLabel: %w", err)
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
issue.Labels = nil
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@@ -47,26 +47,19 @@ func updateIssueLock(ctx context.Context, opts *IssueLockOptions, lock bool) err
|
|||||||
commentType = CommentTypeUnlock
|
commentType = CommentTypeUnlock
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil {
|
opt := &CreateCommentOptions{
|
||||||
|
Doer: opts.Doer,
|
||||||
|
Issue: opts.Issue,
|
||||||
|
Repo: opts.Issue.Repo,
|
||||||
|
Type: commentType,
|
||||||
|
Content: opts.Reason,
|
||||||
|
}
|
||||||
|
_, err := CreateComment(ctx, opt)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
opt := &CreateCommentOptions{
|
|
||||||
Doer: opts.Doer,
|
|
||||||
Issue: opts.Issue,
|
|
||||||
Repo: opts.Issue.Repo,
|
|
||||||
Type: commentType,
|
|
||||||
Content: opts.Reason,
|
|
||||||
}
|
|
||||||
if _, err := CreateComment(ctx, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@@ -167,20 +167,9 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
return SetIssueAsClosed(ctx, issue, doer, false)
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
comment, err := SetIssueAsClosed(ctx, issue, doer, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return comment, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReopenIssue changes issue status to open.
|
// ReopenIssue changes issue status to open.
|
||||||
@@ -192,88 +181,64 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
return setIssueAsReopen(ctx, issue, doer)
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
comment, err := setIssueAsReopen(ctx, issue, doer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return comment, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeIssueTitle changes the title of this issue, as the given user.
|
// ChangeIssueTitle changes the title of this issue, as the given user.
|
||||||
func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) {
|
func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
||||||
return err
|
if err = UpdateIssueCols(ctx, issue, "name"); err != nil {
|
||||||
}
|
return fmt.Errorf("updateIssueCols: %w", err)
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
if err = UpdateIssueCols(ctx, issue, "name"); err != nil {
|
return fmt.Errorf("loadRepo: %w", err)
|
||||||
return fmt.Errorf("updateIssueCols: %w", err)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = issue.LoadRepo(ctx); err != nil {
|
opts := &CreateCommentOptions{
|
||||||
return fmt.Errorf("loadRepo: %w", err)
|
Type: CommentTypeChangeTitle,
|
||||||
}
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
opts := &CreateCommentOptions{
|
Issue: issue,
|
||||||
Type: CommentTypeChangeTitle,
|
OldTitle: oldTitle,
|
||||||
Doer: doer,
|
NewTitle: issue.Title,
|
||||||
Repo: issue.Repo,
|
}
|
||||||
Issue: issue,
|
if _, err = CreateComment(ctx, opts); err != nil {
|
||||||
OldTitle: oldTitle,
|
return fmt.Errorf("createComment: %w", err)
|
||||||
NewTitle: issue.Title,
|
}
|
||||||
}
|
return issue.AddCrossReferences(ctx, doer, true)
|
||||||
if _, err = CreateComment(ctx, opts); err != nil {
|
})
|
||||||
return fmt.Errorf("createComment: %w", err)
|
|
||||||
}
|
|
||||||
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeIssueRef changes the branch of this issue, as the given user.
|
// ChangeIssueRef changes the branch of this issue, as the given user.
|
||||||
func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) {
|
func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = UpdateIssueCols(ctx, issue, "ref"); err != nil {
|
||||||
return err
|
return fmt.Errorf("updateIssueCols: %w", err)
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = UpdateIssueCols(ctx, issue, "ref"); err != nil {
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
return fmt.Errorf("updateIssueCols: %w", err)
|
return fmt.Errorf("loadRepo: %w", err)
|
||||||
}
|
}
|
||||||
|
oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix)
|
||||||
|
newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix)
|
||||||
|
|
||||||
if err = issue.LoadRepo(ctx); err != nil {
|
opts := &CreateCommentOptions{
|
||||||
return fmt.Errorf("loadRepo: %w", err)
|
Type: CommentTypeChangeIssueRef,
|
||||||
}
|
Doer: doer,
|
||||||
oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix)
|
Repo: issue.Repo,
|
||||||
newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix)
|
Issue: issue,
|
||||||
|
OldRef: oldRefFriendly,
|
||||||
opts := &CreateCommentOptions{
|
NewRef: newRefFriendly,
|
||||||
Type: CommentTypeChangeIssueRef,
|
}
|
||||||
Doer: doer,
|
if _, err = CreateComment(ctx, opts); err != nil {
|
||||||
Repo: issue.Repo,
|
return fmt.Errorf("createComment: %w", err)
|
||||||
Issue: issue,
|
}
|
||||||
OldRef: oldRefFriendly,
|
return nil
|
||||||
NewRef: newRefFriendly,
|
})
|
||||||
}
|
|
||||||
if _, err = CreateComment(ctx, opts); err != nil {
|
|
||||||
return fmt.Errorf("createComment: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDeletePRBranchComment adds delete branch comment for pull request issue
|
// AddDeletePRBranchComment adds delete branch comment for pull request issue
|
||||||
@@ -295,64 +260,56 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *
|
|||||||
|
|
||||||
// UpdateIssueAttachments update attachments by UUIDs for the issue
|
// UpdateIssueAttachments update attachments by UUIDs for the issue
|
||||||
func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) {
|
func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
|
||||||
defer committer.Close()
|
|
||||||
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
|
|
||||||
}
|
|
||||||
for i := range attachments {
|
|
||||||
attachments[i].IssueID = issueID
|
|
||||||
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
|
|
||||||
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
|
|
||||||
}
|
}
|
||||||
}
|
for i := range attachments {
|
||||||
return committer.Commit()
|
attachments[i].IssueID = issueID
|
||||||
|
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
|
||||||
|
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeIssueContent changes issue content, as the given user.
|
// ChangeIssueContent changes issue content, as the given user.
|
||||||
func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) {
|
func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("HasIssueContentHistory: %w", err)
|
||||||
defer committer.Close()
|
}
|
||||||
|
if !hasContentHistory {
|
||||||
|
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
|
||||||
|
issue.CreatedUnix, issue.Content, true); err != nil {
|
||||||
|
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0)
|
issue.Content = content
|
||||||
if err != nil {
|
issue.ContentVersion = contentVersion + 1
|
||||||
return fmt.Errorf("HasIssueContentHistory: %w", err)
|
|
||||||
}
|
affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue)
|
||||||
if !hasContentHistory {
|
if err != nil {
|
||||||
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
|
return err
|
||||||
issue.CreatedUnix, issue.Content, true); err != nil {
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return ErrIssueAlreadyChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
|
||||||
|
timeutil.TimeStampNow(), issue.Content, false); err != nil {
|
||||||
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
issue.Content = content
|
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
|
||||||
issue.ContentVersion = contentVersion + 1
|
return fmt.Errorf("addCrossReferences: %w", err)
|
||||||
|
}
|
||||||
affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue)
|
return nil
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
if affected == 0 {
|
|
||||||
return ErrIssueAlreadyChanged
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
|
|
||||||
timeutil.TimeStampNow(), issue.Content, false); err != nil {
|
|
||||||
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
|
|
||||||
return fmt.Errorf("addCrossReferences: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIssueOptions represents the options of a new issue.
|
// NewIssueOptions represents the options of a new issue.
|
||||||
@@ -512,23 +469,19 @@ func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeuti
|
|||||||
if issue.DeadlineUnix == deadlineUnix {
|
if issue.DeadlineUnix == deadlineUnix {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// Update the deadline
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil {
|
// Update the deadline
|
||||||
return err
|
if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Make the comment
|
// Make the comment
|
||||||
if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil {
|
if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil {
|
||||||
return fmt.Errorf("createRemovedDueDateComment: %w", err)
|
return fmt.Errorf("createRemovedDueDateComment: %w", err)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return committer.Commit()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.
|
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.
|
||||||
|
@@ -209,24 +209,20 @@ func NewLabel(ctx context.Context, l *Label) error {
|
|||||||
|
|
||||||
// NewLabels creates new labels
|
// NewLabels creates new labels
|
||||||
func NewLabels(ctx context.Context, labels ...*Label) error {
|
func NewLabels(ctx context.Context, labels ...*Label) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, l := range labels {
|
||||||
return err
|
color, err := label.NormalizeColor(l.Color)
|
||||||
}
|
if err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
l.Color = color
|
||||||
|
|
||||||
for _, l := range labels {
|
if err := db.Insert(ctx, l); err != nil {
|
||||||
color, err := label.NormalizeColor(l.Color)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
l.Color = color
|
return nil
|
||||||
|
})
|
||||||
if err := db.Insert(ctx, l); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLabel updates label information.
|
// UpdateLabel updates label information.
|
||||||
@@ -250,35 +246,26 @@ func DeleteLabel(ctx context.Context, id, labelID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if l.BelongsToOrg() && l.OrgID != id {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if l.BelongsToRepo() && l.RepoID != id {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = db.DeleteByID[Label](ctx, labelID); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _, err = db.GetEngine(ctx).
|
||||||
|
Where("label_id = ?", labelID).
|
||||||
|
Delete(new(IssueLabel)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete comments about now deleted label_id
|
||||||
|
_, err = db.GetEngine(ctx).Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{})
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
if l.BelongsToOrg() && l.OrgID != id {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if l.BelongsToRepo() && l.RepoID != id {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.DeleteByID[Label](ctx, labelID); err != nil {
|
|
||||||
return err
|
|
||||||
} else if _, err = sess.
|
|
||||||
Where("label_id = ?", labelID).
|
|
||||||
Delete(new(IssueLabel)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete comments about now deleted label_id
|
|
||||||
if _, err = sess.Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelByID returns a label by given ID.
|
// GetLabelByID returns a label by given ID.
|
||||||
|
@@ -105,22 +105,16 @@ func (m *Milestone) State() api.StateType {
|
|||||||
|
|
||||||
// NewMilestone creates new milestone of repository.
|
// NewMilestone creates new milestone of repository.
|
||||||
func NewMilestone(ctx context.Context, m *Milestone) (err error) {
|
func NewMilestone(ctx context.Context, m *Milestone) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
m.Name = strings.TrimSpace(m.Name)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
m.Name = strings.TrimSpace(m.Name)
|
if err = db.Insert(ctx, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err = db.Insert(ctx, m); err != nil {
|
_, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasMilestoneByRepoID returns if the milestone exists in the repository.
|
// HasMilestoneByRepoID returns if the milestone exists in the repository.
|
||||||
@@ -155,28 +149,23 @@ func GetMilestoneByRepoIDANDName(ctx context.Context, repoID int64, name string)
|
|||||||
|
|
||||||
// UpdateMilestone updates information of given milestone.
|
// UpdateMilestone updates information of given milestone.
|
||||||
func UpdateMilestone(ctx context.Context, m *Milestone, oldIsClosed bool) error {
|
func UpdateMilestone(ctx context.Context, m *Milestone, oldIsClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if m.IsClosed && !oldIsClosed {
|
||||||
return err
|
m.ClosedDateUnix = timeutil.TimeStampNow()
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if m.IsClosed && !oldIsClosed {
|
if err := updateMilestone(ctx, m); err != nil {
|
||||||
m.ClosedDateUnix = timeutil.TimeStampNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := updateMilestone(ctx, m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if IsClosed changed, update milestone numbers of repository
|
|
||||||
if oldIsClosed != m.IsClosed {
|
|
||||||
if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
// if IsClosed changed, update milestone numbers of repository
|
||||||
|
if oldIsClosed != m.IsClosed {
|
||||||
|
if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestone(ctx context.Context, m *Milestone) error {
|
func updateMilestone(ctx context.Context, m *Milestone) error {
|
||||||
@@ -213,44 +202,28 @@ func UpdateMilestoneCounters(ctx context.Context, id int64) error {
|
|||||||
|
|
||||||
// ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo.
|
// ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo.
|
||||||
func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID int64, isClosed bool) error {
|
func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID int64, isClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
m := &Milestone{
|
||||||
return err
|
ID: milestoneID,
|
||||||
}
|
RepoID: repoID,
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
m := &Milestone{
|
has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m)
|
||||||
ID: milestoneID,
|
if err != nil {
|
||||||
RepoID: repoID,
|
return err
|
||||||
}
|
} else if !has {
|
||||||
|
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
|
||||||
|
}
|
||||||
|
|
||||||
has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m)
|
return changeMilestoneStatus(ctx, m, isClosed)
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
} else if !has {
|
|
||||||
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := changeMilestoneStatus(ctx, m, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeMilestoneStatus changes the milestone open/closed status.
|
// ChangeMilestoneStatus changes the milestone open/closed status.
|
||||||
func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) {
|
func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return changeMilestoneStatus(ctx, m, isClosed)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := changeMilestoneStatus(ctx, m, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error {
|
func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error {
|
||||||
@@ -284,40 +257,34 @@ func DeleteMilestoneByRepoID(ctx context.Context, repoID, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil {
|
numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
||||||
return err
|
RepoID: repo.ID,
|
||||||
}
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
IsClosed: optional.Some(true),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repo.NumMilestones = int(numMilestones)
|
||||||
|
repo.NumClosedMilestones = int(numClosedMilestones)
|
||||||
|
|
||||||
numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
|
||||||
RepoID: repo.ID,
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
|
||||||
RepoID: repo.ID,
|
|
||||||
IsClosed: optional.Some(true),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
repo.NumMilestones = int(numMilestones)
|
|
||||||
repo.NumClosedMilestones = int(numClosedMilestones)
|
|
||||||
|
|
||||||
if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRepoMilestoneNum(ctx context.Context, repoID int64) error {
|
func updateRepoMilestoneNum(ctx context.Context, repoID int64) error {
|
||||||
@@ -360,22 +327,15 @@ func InsertMilestones(ctx context.Context, ms ...*Milestone) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// to return the id, so we should not use batch insert
|
||||||
return err
|
for _, m := range ms {
|
||||||
}
|
if _, err = db.GetEngine(ctx).NoAutoTime().Insert(m); err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
sess := db.GetEngine(ctx)
|
}
|
||||||
|
|
||||||
// to return the id, so we should not use batch insert
|
|
||||||
for _, m := range ms {
|
|
||||||
if _, err = sess.NoAutoTime().Insert(m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID); err != nil {
|
_, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@@ -364,17 +364,10 @@ func (pr *PullRequest) GetApprovers(ctx context.Context) string {
|
|||||||
|
|
||||||
func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) error {
|
func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) error {
|
||||||
maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers
|
maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers
|
||||||
|
|
||||||
if maxReviewers == 0 {
|
if maxReviewers == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// Note: This doesn't page as we only expect a very limited number of reviews
|
// Note: This doesn't page as we only expect a very limited number of reviews
|
||||||
reviews, err := FindLatestReviews(ctx, FindReviewOptions{
|
reviews, err := FindLatestReviews(ctx, FindReviewOptions{
|
||||||
Types: []ReviewType{ReviewTypeApprove},
|
Types: []ReviewType{ReviewTypeApprove},
|
||||||
@@ -410,7 +403,7 @@ func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer)
|
|||||||
}
|
}
|
||||||
reviewersWritten++
|
reviewersWritten++
|
||||||
}
|
}
|
||||||
return committer.Commit()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGitHeadRefName returns git ref for hidden pull request branch
|
// GetGitHeadRefName returns git ref for hidden pull request branch
|
||||||
@@ -464,45 +457,36 @@ func (pr *PullRequest) IsFromFork() bool {
|
|||||||
|
|
||||||
// NewPullRequest creates new pull request with labels for repository.
|
// NewPullRequest creates new pull request with labels for repository.
|
||||||
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
|
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("generate pull request index failed: %w", err)
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("generate pull request index failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
issue.Index = idx
|
|
||||||
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
|
||||||
|
|
||||||
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
|
||||||
Repo: repo,
|
|
||||||
Issue: issue,
|
|
||||||
LabelIDs: labelIDs,
|
|
||||||
Attachments: uuids,
|
|
||||||
IsPull: true,
|
|
||||||
}); err != nil {
|
|
||||||
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("newIssue: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.Index = issue.Index
|
issue.Index = idx
|
||||||
pr.BaseRepo = repo
|
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
||||||
pr.IssueID = issue.ID
|
|
||||||
if err = db.Insert(ctx, pr); err != nil {
|
|
||||||
return fmt.Errorf("insert pull repo: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
||||||
return fmt.Errorf("Commit: %w", err)
|
Repo: repo,
|
||||||
}
|
Issue: issue,
|
||||||
|
LabelIDs: labelIDs,
|
||||||
|
Attachments: uuids,
|
||||||
|
IsPull: true,
|
||||||
|
}); err != nil {
|
||||||
|
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fmt.Errorf("newIssue: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
pr.Index = issue.Index
|
||||||
|
pr.BaseRepo = repo
|
||||||
|
pr.IssueID = issue.ID
|
||||||
|
if err = db.Insert(ctx, pr); err != nil {
|
||||||
|
return fmt.Errorf("insert pull repo: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo.
|
// ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo.
|
||||||
@@ -977,22 +961,18 @@ func TokenizeCodeOwnersLine(line string) []string {
|
|||||||
|
|
||||||
// InsertPullRequests inserted pull requests
|
// InsertPullRequests inserted pull requests
|
||||||
func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error {
|
func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, pr := range prs {
|
||||||
return err
|
if err := insertIssue(ctx, pr.Issue); err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
sess := db.GetEngine(ctx)
|
pr.IssueID = pr.Issue.ID
|
||||||
for _, pr := range prs {
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(pr); err != nil {
|
||||||
if err := insertIssue(ctx, pr.Issue); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
}
|
||||||
pr.IssueID = pr.Issue.ID
|
return nil
|
||||||
if _, err := sess.NoAutoTime().Insert(pr); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequestByMergedCommit returns a merged pull request by the given commit
|
// GetPullRequestByMergedCommit returns a merged pull request by the given commit
|
||||||
|
@@ -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
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
review := &Review{
|
review := &Review{
|
||||||
Issue: opts.Issue,
|
Issue: opts.Issue,
|
||||||
IssueID: opts.Issue.ID,
|
IssueID: opts.Issue.ID,
|
||||||
Reviewer: opts.Reviewer,
|
Reviewer: opts.Reviewer,
|
||||||
ReviewerTeam: opts.ReviewerTeam,
|
ReviewerTeam: opts.ReviewerTeam,
|
||||||
Content: opts.Content,
|
Content: opts.Content,
|
||||||
Official: opts.Official,
|
Official: opts.Official,
|
||||||
CommitID: opts.CommitID,
|
CommitID: opts.CommitID,
|
||||||
Stale: opts.Stale,
|
Stale: opts.Stale,
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Reviewer != nil {
|
|
||||||
review.Type = opts.Type
|
|
||||||
review.ReviewerID = opts.Reviewer.ID
|
|
||||||
|
|
||||||
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
|
|
||||||
// make sure user review requests are cleared
|
|
||||||
if opts.Type != ReviewTypePending {
|
|
||||||
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// make sure if the created review gets dismissed no old review surface
|
|
||||||
// other types can be ignored, as they don't affect branch protection
|
|
||||||
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
|
|
||||||
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
|
|
||||||
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if opts.ReviewerTeam != nil {
|
|
||||||
review.Type = ReviewTypeRequest
|
|
||||||
review.ReviewerTeamID = opts.ReviewerTeam.ID
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("provide either reviewer or reviewer team")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := sess.Insert(review); err != nil {
|
if opts.Reviewer != nil {
|
||||||
return nil, err
|
review.Type = opts.Type
|
||||||
}
|
review.ReviewerID = opts.Reviewer.ID
|
||||||
return review, committer.Commit()
|
|
||||||
|
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
|
||||||
|
// make sure user review requests are cleared
|
||||||
|
if opts.Type != ReviewTypePending {
|
||||||
|
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make sure if the created review gets dismissed no old review surface
|
||||||
|
// other types can be ignored, as they don't affect branch protection
|
||||||
|
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
|
||||||
|
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
|
||||||
|
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if opts.ReviewerTeam != nil {
|
||||||
|
review.Type = ReviewTypeRequest
|
||||||
|
review.ReviewerTeamID = opts.ReviewerTeam.ID
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("provide either reviewer or reviewer team")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Insert(review); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return review, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentReview returns the current pending review of reviewer for given issue
|
// GetCurrentReview returns the current pending review of reviewer for given issue
|
||||||
@@ -605,168 +602,152 @@ func DismissReview(ctx context.Context, review *Review, isDismiss bool) (err err
|
|||||||
|
|
||||||
// InsertReviews inserts review and review comments
|
// InsertReviews inserts review and review comments
|
||||||
func InsertReviews(ctx context.Context, reviews []*Review) error {
|
func InsertReviews(ctx context.Context, reviews []*Review) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
sess := db.GetEngine(ctx)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
for _, review := range reviews {
|
for _, review := range reviews {
|
||||||
if _, err := sess.NoAutoTime().Insert(review); err != nil {
|
if _, err := sess.NoAutoTime().Insert(review); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := sess.NoAutoTime().Insert(&Comment{
|
if _, err := sess.NoAutoTime().Insert(&Comment{
|
||||||
Type: CommentTypeReview,
|
Type: CommentTypeReview,
|
||||||
Content: review.Content,
|
Content: review.Content,
|
||||||
PosterID: review.ReviewerID,
|
PosterID: review.ReviewerID,
|
||||||
OriginalAuthor: review.OriginalAuthor,
|
OriginalAuthor: review.OriginalAuthor,
|
||||||
OriginalAuthorID: review.OriginalAuthorID,
|
OriginalAuthorID: review.OriginalAuthorID,
|
||||||
IssueID: review.IssueID,
|
IssueID: review.IssueID,
|
||||||
ReviewID: review.ID,
|
ReviewID: review.ID,
|
||||||
CreatedUnix: review.CreatedUnix,
|
CreatedUnix: review.CreatedUnix,
|
||||||
UpdatedUnix: review.UpdatedUnix,
|
UpdatedUnix: review.UpdatedUnix,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range review.Comments {
|
for _, c := range review.Comments {
|
||||||
c.ReviewID = review.ID
|
c.ReviewID = review.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(review.Comments) > 0 {
|
if len(review.Comments) > 0 {
|
||||||
if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil {
|
if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddReviewRequest add a review request from one reviewer
|
// AddReviewRequest add a review request from one reviewer
|
||||||
func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
sess := db.GetEngine(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if review != nil {
|
|
||||||
// skip it when reviewer has been request to review
|
|
||||||
if review.Type == ReviewTypeRequest {
|
|
||||||
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
|
|
||||||
}
|
|
||||||
|
|
||||||
if issue.IsClosed {
|
|
||||||
return nil, ErrReviewRequestOnClosedPR{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if issue.IsPull {
|
|
||||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if issue.PullRequest.HasMerged {
|
|
||||||
return nil, ErrReviewRequestOnClosedPR{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the reviewer is an official reviewer,
|
|
||||||
// remove the official flag in the all previous reviews
|
|
||||||
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if official {
|
|
||||||
if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
review, err = CreateReview(ctx, CreateReviewOptions{
|
if review != nil {
|
||||||
Type: ReviewTypeRequest,
|
// skip it when reviewer has been request to review
|
||||||
Issue: issue,
|
if review.Type == ReviewTypeRequest {
|
||||||
Reviewer: reviewer,
|
return nil, nil // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
|
||||||
Official: official,
|
}
|
||||||
Stale: false,
|
|
||||||
|
if issue.IsClosed {
|
||||||
|
return nil, ErrReviewRequestOnClosedPR{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.IsPull {
|
||||||
|
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if issue.PullRequest.HasMerged {
|
||||||
|
return nil, ErrReviewRequestOnClosedPR{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the reviewer is an official reviewer,
|
||||||
|
// remove the official flag in the all previous reviews
|
||||||
|
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if official {
|
||||||
|
if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
review, err = CreateReview(ctx, CreateReviewOptions{
|
||||||
|
Type: ReviewTypeRequest,
|
||||||
|
Issue: issue,
|
||||||
|
Reviewer: reviewer,
|
||||||
|
Official: official,
|
||||||
|
Stale: false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Type: CommentTypeReviewRequest,
|
||||||
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
||||||
|
ReviewID: review.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// func caller use the created comment to retrieve created review too.
|
||||||
|
comment.Review = review
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Type: CommentTypeReviewRequest,
|
|
||||||
Doer: doer,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
|
||||||
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
|
||||||
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
|
||||||
ReviewID: review.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// func caller use the created comment to retrieve created review too.
|
|
||||||
comment.Review = review
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveReviewRequest remove a review request from one reviewer
|
// RemoveReviewRequest remove a review request from one reviewer
|
||||||
func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
||||||
return nil, err
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if review == nil || review.Type != ReviewTypeRequest {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if official {
|
|
||||||
if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
if review == nil || review.Type != ReviewTypeRequest {
|
||||||
Type: CommentTypeReviewRequest,
|
return nil, nil
|
||||||
Doer: doer,
|
}
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
||||||
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
return nil, err
|
||||||
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
}
|
||||||
|
|
||||||
|
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if official {
|
||||||
|
if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Type: CommentTypeReviewRequest,
|
||||||
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
||||||
|
})
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recalculate the latest official review for reviewer
|
// Recalculate the latest official review for reviewer
|
||||||
@@ -787,120 +768,112 @@ func restoreLatestOfficialReview(ctx context.Context, issueID, reviewerID int64)
|
|||||||
|
|
||||||
// AddTeamReviewRequest add a review request from one team
|
// AddTeamReviewRequest add a review request from one team
|
||||||
func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// This team already has been requested to review - therefore skip this.
|
|
||||||
if review != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
|
||||||
} else if !official {
|
|
||||||
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil {
|
|
||||||
return nil, fmt.Errorf("isOfficialReviewer(): %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if review, err = CreateReview(ctx, CreateReviewOptions{
|
|
||||||
Type: ReviewTypeRequest,
|
|
||||||
Issue: issue,
|
|
||||||
ReviewerTeam: reviewer,
|
|
||||||
Official: official,
|
|
||||||
Stale: false,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if official {
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Type: CommentTypeReviewRequest,
|
|
||||||
Doer: doer,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
|
||||||
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
|
||||||
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
|
||||||
ReviewID: review.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("CreateComment(): %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTeamReviewRequest remove a review request from one team
|
|
||||||
func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if review == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if official {
|
|
||||||
// recalculate which is the latest official review from that team
|
|
||||||
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This team already has been requested to review - therefore skip this.
|
||||||
if review != nil {
|
if review != nil {
|
||||||
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil {
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
||||||
|
} else if !official {
|
||||||
|
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil {
|
||||||
|
return nil, fmt.Errorf("isOfficialReviewer(): %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if review, err = CreateReview(ctx, CreateReviewOptions{
|
||||||
|
Type: ReviewTypeRequest,
|
||||||
|
Issue: issue,
|
||||||
|
ReviewerTeam: reviewer,
|
||||||
|
Official: official,
|
||||||
|
Stale: false,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if official {
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if doer == nil {
|
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
return nil, committer.Commit()
|
Type: CommentTypeReviewRequest,
|
||||||
}
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
||||||
|
ReviewID: review.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CreateComment(): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
return comment, nil
|
||||||
Type: CommentTypeReviewRequest,
|
|
||||||
Doer: doer,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
|
||||||
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
|
||||||
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
}
|
||||||
return nil, fmt.Errorf("CreateComment(): %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
// RemoveTeamReviewRequest remove a review request from one team
|
||||||
|
func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
||||||
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
|
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
||||||
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if review == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if official {
|
||||||
|
// recalculate which is the latest official review from that team
|
||||||
|
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID)
|
||||||
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if review != nil {
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if doer == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Type: CommentTypeReviewRequest,
|
||||||
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CreateComment(): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkConversation Add or remove Conversation mark for a code comment
|
// MarkConversation Add or remove Conversation mark for a code comment
|
||||||
@@ -966,61 +939,56 @@ func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.Use
|
|||||||
|
|
||||||
// DeleteReview delete a review and it's code comments
|
// DeleteReview delete a review and it's code comments
|
||||||
func DeleteReview(ctx context.Context, r *Review) error {
|
func DeleteReview(ctx context.Context, r *Review) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if r.ID == 0 {
|
||||||
return err
|
return errors.New("review is not allowed to be 0")
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if r.ID == 0 {
|
if r.Type == ReviewTypeRequest {
|
||||||
return errors.New("review is not allowed to be 0")
|
return errors.New("review request can not be deleted using this method")
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Type == ReviewTypeRequest {
|
opts := FindCommentsOptions{
|
||||||
return errors.New("review request can not be deleted using this method")
|
Type: CommentTypeCode,
|
||||||
}
|
IssueID: r.IssueID,
|
||||||
|
ReviewID: r.ID,
|
||||||
|
}
|
||||||
|
|
||||||
opts := FindCommentsOptions{
|
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
||||||
Type: CommentTypeCode,
|
|
||||||
IssueID: r.IssueID,
|
|
||||||
ReviewID: r.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = FindCommentsOptions{
|
|
||||||
Type: CommentTypeReview,
|
|
||||||
IssueID: r.IssueID,
|
|
||||||
ReviewID: r.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = FindCommentsOptions{
|
|
||||||
Type: CommentTypeDismissReview,
|
|
||||||
IssueID: r.IssueID,
|
|
||||||
ReviewID: r.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Official {
|
|
||||||
if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
opts = FindCommentsOptions{
|
||||||
|
Type: CommentTypeReview,
|
||||||
|
IssueID: r.IssueID,
|
||||||
|
ReviewID: r.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = FindCommentsOptions{
|
||||||
|
Type: CommentTypeDismissReview,
|
||||||
|
IssueID: r.IssueID,
|
||||||
|
ReviewID: r.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Official {
|
||||||
|
if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCodeCommentsCount return count of CodeComments a Review has
|
// GetCodeCommentsCount return count of CodeComments a Review has
|
||||||
|
@@ -168,35 +168,31 @@ func GetTrackedSeconds(ctx context.Context, opts FindTrackedTimesOptions) (track
|
|||||||
|
|
||||||
// AddTime will add the given time (in seconds) to the issue
|
// AddTime will add the given time (in seconds) to the issue
|
||||||
func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*TrackedTime, error) {
|
||||||
if err != nil {
|
t, err := addTime(ctx, user, issue, amount, created)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
t, err := addTime(ctx, user, issue, amount, created)
|
if err := issue.LoadRepo(ctx); err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := issue.LoadRepo(ctx); err != nil {
|
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
return nil, err
|
Issue: issue,
|
||||||
}
|
Repo: issue.Repo,
|
||||||
|
Doer: user,
|
||||||
|
// Content before v1.21 did store the formatted string instead of seconds,
|
||||||
|
// so use "|" as delimiter to mark the new format
|
||||||
|
Content: fmt.Sprintf("|%d", amount),
|
||||||
|
Type: CommentTypeAddTimeManual,
|
||||||
|
TimeID: t.ID,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
return t, nil
|
||||||
Issue: issue,
|
})
|
||||||
Repo: issue.Repo,
|
|
||||||
Doer: user,
|
|
||||||
// Content before v1.21 did store the formatted string instead of seconds,
|
|
||||||
// so use "|" as delimiter to mark the new format
|
|
||||||
Content: fmt.Sprintf("|%d", amount),
|
|
||||||
Type: CommentTypeAddTimeManual,
|
|
||||||
TimeID: t.ID,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
||||||
@@ -241,72 +237,58 @@ func TotalTimesForEachUser(ctx context.Context, options *FindTrackedTimesOptions
|
|||||||
|
|
||||||
// DeleteIssueUserTimes deletes times for issue
|
// DeleteIssueUserTimes deletes times for issue
|
||||||
func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.User) error {
|
func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.User) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
opts := FindTrackedTimesOptions{
|
||||||
return err
|
IssueID: issue.ID,
|
||||||
}
|
UserID: user.ID,
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
opts := FindTrackedTimesOptions{
|
removedTime, err := deleteTimes(ctx, opts)
|
||||||
IssueID: issue.ID,
|
if err != nil {
|
||||||
UserID: user.ID,
|
return err
|
||||||
}
|
}
|
||||||
|
if removedTime == 0 {
|
||||||
|
return db.ErrNotExist{Resource: "tracked_time"}
|
||||||
|
}
|
||||||
|
|
||||||
removedTime, err := deleteTimes(ctx, opts)
|
if err := issue.LoadRepo(ctx); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
|
}
|
||||||
|
_, err = CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Issue: issue,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Doer: user,
|
||||||
|
// Content before v1.21 did store the formatted string instead of seconds,
|
||||||
|
// so use "|" as delimiter to mark the new format
|
||||||
|
Content: fmt.Sprintf("|%d", removedTime),
|
||||||
|
Type: CommentTypeDeleteTimeManual,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
if removedTime == 0 {
|
|
||||||
return db.ErrNotExist{Resource: "tracked_time"}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := issue.LoadRepo(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Issue: issue,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Doer: user,
|
|
||||||
// Content before v1.21 did store the formatted string instead of seconds,
|
|
||||||
// so use "|" as delimiter to mark the new format
|
|
||||||
Content: fmt.Sprintf("|%d", removedTime),
|
|
||||||
Type: CommentTypeDeleteTimeManual,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTime delete a specific Time
|
// DeleteTime delete a specific Time
|
||||||
func DeleteTime(ctx context.Context, t *TrackedTime) error {
|
func DeleteTime(ctx context.Context, t *TrackedTime) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := t.LoadAttributes(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := t.LoadAttributes(ctx); err != nil {
|
if err := deleteTime(ctx, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deleteTime(ctx, t); err != nil {
|
_, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Issue: t.Issue,
|
||||||
|
Repo: t.Issue.Repo,
|
||||||
|
Doer: t.User,
|
||||||
|
// Content before v1.21 did store the formatted string instead of seconds,
|
||||||
|
// so use "|" as delimiter to mark the new format
|
||||||
|
Content: fmt.Sprintf("|%d", t.Time),
|
||||||
|
Type: CommentTypeDeleteTimeManual,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Issue: t.Issue,
|
|
||||||
Repo: t.Issue.Repo,
|
|
||||||
Doer: t.User,
|
|
||||||
// Content before v1.21 did store the formatted string instead of seconds,
|
|
||||||
// so use "|" as delimiter to mark the new format
|
|
||||||
Content: fmt.Sprintf("|%d", t.Time),
|
|
||||||
Type: CommentTypeDeleteTimeManual,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) {
|
func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) {
|
||||||
|
@@ -310,74 +310,69 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
|
|||||||
org.NumMembers = 1
|
org.NumMembers = 1
|
||||||
org.Type = user_model.UserTypeOrganization
|
org.Type = user_model.UserTypeOrganization
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = db.Insert(ctx, org); err != nil {
|
|
||||||
return fmt.Errorf("insert organization: %w", err)
|
|
||||||
}
|
|
||||||
if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
|
|
||||||
return fmt.Errorf("generate random avatar: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add initial creator to organization and owner team.
|
|
||||||
if err = db.Insert(ctx, &OrgUser{
|
|
||||||
UID: owner.ID,
|
|
||||||
OrgID: org.ID,
|
|
||||||
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("insert org-user relation: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create default owner team.
|
|
||||||
t := &Team{
|
|
||||||
OrgID: org.ID,
|
|
||||||
LowerName: strings.ToLower(OwnerTeamName),
|
|
||||||
Name: OwnerTeamName,
|
|
||||||
AccessMode: perm.AccessModeOwner,
|
|
||||||
NumMembers: 1,
|
|
||||||
IncludesAllRepositories: true,
|
|
||||||
CanCreateOrgRepo: true,
|
|
||||||
}
|
|
||||||
if err = db.Insert(ctx, t); err != nil {
|
|
||||||
return fmt.Errorf("insert owner team: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert units for team
|
|
||||||
units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
|
|
||||||
for _, tp := range unit.AllRepoUnitTypes {
|
|
||||||
up := perm.AccessModeOwner
|
|
||||||
if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
|
|
||||||
up = perm.AccessModeRead
|
|
||||||
}
|
}
|
||||||
units = append(units, TeamUnit{
|
|
||||||
OrgID: org.ID,
|
|
||||||
TeamID: t.ID,
|
|
||||||
Type: tp,
|
|
||||||
AccessMode: up,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = db.Insert(ctx, &units); err != nil {
|
if err = db.Insert(ctx, org); err != nil {
|
||||||
return err
|
return fmt.Errorf("insert organization: %w", err)
|
||||||
}
|
}
|
||||||
|
if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
|
||||||
|
return fmt.Errorf("generate random avatar: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err = db.Insert(ctx, &TeamUser{
|
// Add initial creator to organization and owner team.
|
||||||
UID: owner.ID,
|
if err = db.Insert(ctx, &OrgUser{
|
||||||
OrgID: org.ID,
|
UID: owner.ID,
|
||||||
TeamID: t.ID,
|
OrgID: org.ID,
|
||||||
}); err != nil {
|
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
||||||
return fmt.Errorf("insert team-user relation: %w", err)
|
}); err != nil {
|
||||||
}
|
return fmt.Errorf("insert org-user relation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return committer.Commit()
|
// Create default owner team.
|
||||||
|
t := &Team{
|
||||||
|
OrgID: org.ID,
|
||||||
|
LowerName: strings.ToLower(OwnerTeamName),
|
||||||
|
Name: OwnerTeamName,
|
||||||
|
AccessMode: perm.AccessModeOwner,
|
||||||
|
NumMembers: 1,
|
||||||
|
IncludesAllRepositories: true,
|
||||||
|
CanCreateOrgRepo: true,
|
||||||
|
}
|
||||||
|
if err = db.Insert(ctx, t); err != nil {
|
||||||
|
return fmt.Errorf("insert owner team: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert units for team
|
||||||
|
units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
|
||||||
|
for _, tp := range unit.AllRepoUnitTypes {
|
||||||
|
up := perm.AccessModeOwner
|
||||||
|
if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
|
||||||
|
up = perm.AccessModeRead
|
||||||
|
}
|
||||||
|
units = append(units, TeamUnit{
|
||||||
|
OrgID: org.ID,
|
||||||
|
TeamID: t.ID,
|
||||||
|
Type: tp,
|
||||||
|
AccessMode: up,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = db.Insert(ctx, &units); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = db.Insert(ctx, &TeamUser{
|
||||||
|
UID: owner.ID,
|
||||||
|
OrgID: org.ID,
|
||||||
|
TeamID: t.ID,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("insert team-user relation: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgByName returns organization by given name.
|
// GetOrgByName returns organization by given name.
|
||||||
@@ -499,31 +494,26 @@ func AddOrgUser(ctx context.Context, orgID, uid int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// check in transaction
|
||||||
return err
|
isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
|
||||||
}
|
if err != nil || isAlreadyMember {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// check in transaction
|
ou := &OrgUser{
|
||||||
isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
|
UID: uid,
|
||||||
if err != nil || isAlreadyMember {
|
OrgID: orgID,
|
||||||
return err
|
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
||||||
}
|
}
|
||||||
|
|
||||||
ou := &OrgUser{
|
if err := db.Insert(ctx, ou); err != nil {
|
||||||
UID: uid,
|
return err
|
||||||
OrgID: orgID,
|
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
|
||||||
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if err := db.Insert(ctx, ou); err != nil {
|
})
|
||||||
return err
|
|
||||||
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgByID returns the user object by given ID if exists.
|
// GetOrgByID returns the user object by given ID if exists.
|
||||||
|
@@ -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}
|
||||||
|
}
|
||||||
|
|
||||||
has, err := db.GetEngine(ctx).ID(projectID).Where("repo_id = ?", repoID).Get(p)
|
return changeProjectStatus(ctx, p, isClosed)
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
} else if !has {
|
|
||||||
return ErrProjectNotExist{ID: projectID, RepoID: repoID}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := changeProjectStatus(ctx, p, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeProjectStatus toggle a project between opened and closed
|
// ChangeProjectStatus toggle a project between opened and closed
|
||||||
func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return changeProjectStatus(ctx, p, isClosed)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := changeProjectStatus(ctx, p, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
||||||
|
@@ -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 s int64
|
|
||||||
for lang, size := range stats {
|
|
||||||
if size > s {
|
|
||||||
s = size
|
|
||||||
topLang = lang
|
|
||||||
}
|
}
|
||||||
}
|
var topLang string
|
||||||
|
var s int64
|
||||||
for lang, size := range stats {
|
for lang, size := range stats {
|
||||||
upd := false
|
if size > s {
|
||||||
for _, s := range oldstats {
|
s = size
|
||||||
// Update already existing language
|
topLang = lang
|
||||||
if strings.EqualFold(s.Language, lang) {
|
|
||||||
s.CommitID = commitID
|
|
||||||
s.IsPrimary = lang == topLang
|
|
||||||
s.Size = size
|
|
||||||
if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
upd = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Insert new language
|
|
||||||
if !upd {
|
for lang, size := range stats {
|
||||||
if err := db.Insert(ctx, &LanguageStat{
|
upd := false
|
||||||
RepoID: repo.ID,
|
for _, s := range oldstats {
|
||||||
CommitID: commitID,
|
// Update already existing language
|
||||||
IsPrimary: lang == topLang,
|
if strings.EqualFold(s.Language, lang) {
|
||||||
Language: lang,
|
s.CommitID = commitID
|
||||||
Size: size,
|
s.IsPrimary = lang == topLang
|
||||||
}); err != nil {
|
s.Size = size
|
||||||
|
if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upd = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert new language
|
||||||
|
if !upd {
|
||||||
|
if err := db.Insert(ctx, &LanguageStat{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
CommitID: commitID,
|
||||||
|
IsPrimary: lang == topLang,
|
||||||
|
Language: lang,
|
||||||
|
Size: size,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delete old languages
|
||||||
|
statsToDelete := make([]int64, 0, len(oldstats))
|
||||||
|
for _, s := range oldstats {
|
||||||
|
if s.CommitID != commitID {
|
||||||
|
statsToDelete = append(statsToDelete, s.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(statsToDelete) > 0 {
|
||||||
|
if _, err := sess.In("`id`", statsToDelete).Delete(&LanguageStat{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Delete old languages
|
|
||||||
statsToDelete := make([]int64, 0, len(oldstats))
|
|
||||||
for _, s := range oldstats {
|
|
||||||
if s.CommitID != commitID {
|
|
||||||
statsToDelete = append(statsToDelete, s.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(statsToDelete) > 0 {
|
|
||||||
if _, err := sess.In("`id`", statsToDelete).Delete(&LanguageStat{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update indexer status
|
// Update indexer status
|
||||||
if err = UpdateIndexerStatus(ctx, repo, RepoIndexerTypeStats, commitID); err != nil {
|
return UpdateIndexerStatus(ctx, repo, RepoIndexerTypeStats, commitID)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo)
|
// CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo)
|
||||||
func CopyLanguageStat(ctx context.Context, originalRepo, destRepo *Repository) error {
|
func CopyLanguageStat(ctx context.Context, originalRepo, destRepo *Repository) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
RepoLang := make(LanguageStatList, 0, 6)
|
||||||
return err
|
if err := db.GetEngine(ctx).Where("`repo_id` = ?", originalRepo.ID).Desc("`size`").Find(&RepoLang); err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
RepoLang := make(LanguageStatList, 0, 6)
|
|
||||||
if err := db.GetEngine(ctx).Where("`repo_id` = ?", originalRepo.ID).Desc("`size`").Find(&RepoLang); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(RepoLang) > 0 {
|
|
||||||
for i := range RepoLang {
|
|
||||||
RepoLang[i].ID = 0
|
|
||||||
RepoLang[i].RepoID = destRepo.ID
|
|
||||||
RepoLang[i].CreatedUnix = timeutil.TimeStampNow()
|
|
||||||
}
|
|
||||||
// update destRepo's indexer status
|
|
||||||
tmpCommitID := RepoLang[0].CommitID
|
|
||||||
if err := UpdateIndexerStatus(ctx, destRepo, RepoIndexerTypeStats, tmpCommitID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := db.Insert(ctx, &RepoLang); err != nil {
|
if len(RepoLang) > 0 {
|
||||||
return err
|
for i := range RepoLang {
|
||||||
|
RepoLang[i].ID = 0
|
||||||
|
RepoLang[i].RepoID = destRepo.ID
|
||||||
|
RepoLang[i].CreatedUnix = timeutil.TimeStampNow()
|
||||||
|
}
|
||||||
|
// update destRepo's indexer status
|
||||||
|
tmpCommitID := RepoLang[0].CommitID
|
||||||
|
if err := UpdateIndexerStatus(ctx, destRepo, RepoIndexerTypeStats, tmpCommitID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := db.Insert(ctx, &RepoLang); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
return committer.Commit()
|
})
|
||||||
}
|
}
|
||||||
|
@@ -472,30 +472,24 @@ func (r *Release) GetExternalID() int64 { return r.OriginalAuthorID }
|
|||||||
|
|
||||||
// InsertReleases migrates release
|
// InsertReleases migrates release
|
||||||
func InsertReleases(ctx context.Context, rels ...*Release) error {
|
func InsertReleases(ctx context.Context, rels ...*Release) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, rel := range rels {
|
||||||
return err
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(rel); err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
for _, rel := range rels {
|
|
||||||
if _, err := sess.NoAutoTime().Insert(rel); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rel.Attachments) > 0 {
|
|
||||||
for i := range rel.Attachments {
|
|
||||||
rel.Attachments[i].ReleaseID = rel.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := sess.NoAutoTime().Insert(rel.Attachments); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
if len(rel.Attachments) > 0 {
|
||||||
|
for i := range rel.Attachments {
|
||||||
|
rel.Attachments[i].ReleaseID = rel.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(rel.Attachments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) {
|
func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) {
|
||||||
|
@@ -25,48 +25,45 @@ func init() {
|
|||||||
|
|
||||||
// StarRepo or unstar repository.
|
// StarRepo or unstar repository.
|
||||||
func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error {
|
func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
staring := IsStaring(ctx, doer.ID, repo.ID)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
staring := IsStaring(ctx, doer.ID, repo.ID)
|
|
||||||
|
|
||||||
if star {
|
if star {
|
||||||
if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) {
|
if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) {
|
||||||
return user_model.ErrBlockedUser
|
return user_model.ErrBlockedUser
|
||||||
|
}
|
||||||
|
|
||||||
|
if staring {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", doer.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !staring {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.DeleteByBean(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", doer.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if staring {
|
return nil
|
||||||
return nil
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", doer.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !staring {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.DeleteByBean(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", doer.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStaring checks if user has starred given repository.
|
// IsStaring checks if user has starred given repository.
|
||||||
|
@@ -227,32 +227,26 @@ func GetRepoTopicByName(ctx context.Context, repoID int64, topicName string) (*T
|
|||||||
|
|
||||||
// AddTopic adds a topic name to a repository (if it does not already have it)
|
// AddTopic adds a topic name to a repository (if it does not already have it)
|
||||||
func AddTopic(ctx context.Context, repoID int64, topicName string) (*Topic, error) {
|
func AddTopic(ctx context.Context, repoID int64, topicName string) (*Topic, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Topic, error) {
|
||||||
if err != nil {
|
topic, err := GetRepoTopicByName(ctx, repoID, topicName)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
sess := db.GetEngine(ctx)
|
if topic != nil {
|
||||||
|
// Repo already have topic
|
||||||
|
return topic, nil
|
||||||
|
}
|
||||||
|
|
||||||
topic, err := GetRepoTopicByName(ctx, repoID, topicName)
|
topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if topic != nil {
|
|
||||||
// Repo already have topic
|
if err = syncTopicsInRepository(ctx, repoID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return topic, nil
|
return topic, nil
|
||||||
}
|
})
|
||||||
|
|
||||||
topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = syncTopicsInRepository(sess, repoID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return topic, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTopic removes a topic name from a repository (if it has it)
|
// DeleteTopic removes a topic name from a repository (if it has it)
|
||||||
@@ -266,14 +260,15 @@ func DeleteTopic(ctx context.Context, repoID int64, topicName string) (*Topic, e
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = removeTopicFromRepo(ctx, repoID, topic)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Topic, error) {
|
||||||
if err != nil {
|
if err = removeTopicFromRepo(ctx, repoID, topic); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err = syncTopicsInRepository(ctx, repoID); err != nil {
|
||||||
err = syncTopicsInRepository(db.GetEngine(ctx), repoID)
|
return nil, err
|
||||||
|
}
|
||||||
return topic, err
|
return topic, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveTopics save topics to a repository
|
// SaveTopics save topics to a repository
|
||||||
@@ -285,64 +280,55 @@ func SaveTopics(ctx context.Context, repoID int64, topicNames ...string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
var addedTopicNames []string
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
var addedTopicNames []string
|
|
||||||
for _, topicName := range topicNames {
|
|
||||||
if strings.TrimSpace(topicName) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var found bool
|
|
||||||
for _, t := range topics {
|
|
||||||
if strings.EqualFold(topicName, t.Name) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
addedTopicNames = append(addedTopicNames, topicName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var removeTopics []*Topic
|
|
||||||
for _, t := range topics {
|
|
||||||
var found bool
|
|
||||||
for _, topicName := range topicNames {
|
for _, topicName := range topicNames {
|
||||||
if strings.EqualFold(topicName, t.Name) {
|
if strings.TrimSpace(topicName) == "" {
|
||||||
found = true
|
continue
|
||||||
break
|
}
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
for _, t := range topics {
|
||||||
|
if strings.EqualFold(topicName, t.Name) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
addedTopicNames = append(addedTopicNames, topicName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
|
||||||
removeTopics = append(removeTopics, t)
|
var removeTopics []*Topic
|
||||||
|
for _, t := range topics {
|
||||||
|
var found bool
|
||||||
|
for _, topicName := range topicNames {
|
||||||
|
if strings.EqualFold(topicName, t.Name) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
removeTopics = append(removeTopics, t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, topicName := range addedTopicNames {
|
for _, topicName := range addedTopicNames {
|
||||||
_, err := addTopicByNameToRepo(ctx, repoID, topicName)
|
_, err := addTopicByNameToRepo(ctx, repoID, topicName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, topic := range removeTopics {
|
for _, topic := range removeTopics {
|
||||||
err := removeTopicFromRepo(ctx, repoID, topic)
|
err := removeTopicFromRepo(ctx, repoID, topic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := syncTopicsInRepository(sess, repoID); err != nil {
|
return syncTopicsInRepository(ctx, repoID)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateTopics generates topics from a template repository
|
// GenerateTopics generates topics from a template repository
|
||||||
@@ -353,19 +339,19 @@ func GenerateTopics(ctx context.Context, templateRepo, generateRepo *Repository)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return syncTopicsInRepository(db.GetEngine(ctx), generateRepo.ID)
|
return syncTopicsInRepository(ctx, generateRepo.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// syncTopicsInRepository makes sure topics in the topics table are copied into the topics field of the repository
|
// syncTopicsInRepository makes sure topics in the topics table are copied into the topics field of the repository
|
||||||
func syncTopicsInRepository(sess db.Engine, repoID int64) error {
|
func syncTopicsInRepository(ctx context.Context, repoID int64) error {
|
||||||
topicNames := make([]string, 0, 25)
|
topicNames := make([]string, 0, 25)
|
||||||
if err := sess.Table("topic").Cols("name").
|
if err := db.GetEngine(ctx).Table("topic").Cols("name").
|
||||||
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
|
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
|
||||||
Where("repo_topic.repo_id = ?", repoID).Asc("topic.name").Find(&topicNames); err != nil {
|
Where("repo_topic.repo_id = ?", repoID).Asc("topic.name").Find(&topicNames); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{
|
if _, err := db.GetEngine(ctx).ID(repoID).Cols("topics").Update(&Repository{
|
||||||
Topics: topicNames,
|
Topics: topicNames,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@@ -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.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnfollowUser unmarks someone as another's follower.
|
// UnfollowUser unmarks someone as another's follower.
|
||||||
@@ -64,22 +60,18 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = db.DeleteByID[asymkey_model.PublicKey](dbCtx, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
committer.Close()
|
|
||||||
|
|
||||||
if key.Type == asymkey_model.KeyTypePrincipal {
|
if key.Type == asymkey_model.KeyTypePrincipal {
|
||||||
return RewriteAllPrincipalKeys(ctx)
|
return RewriteAllPrincipalKeys(ctx)
|
||||||
|
@@ -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,31 +15,25 @@ import (
|
|||||||
|
|
||||||
// CloseIssue close an issue.
|
// CloseIssue close an issue.
|
||||||
func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error {
|
func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
var comment *issues_model.Comment
|
||||||
if err != nil {
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
return err
|
var err error
|
||||||
}
|
comment, err = issues_model.CloseIssue(ctx, issue, doer)
|
||||||
defer committer.Close()
|
if err != nil {
|
||||||
|
if issues_model.IsErrDependenciesLeft(err) {
|
||||||
comment, err := issues_model.CloseIssue(dbCtx, issue, doer)
|
if _, err := issues_model.FinishIssueStopwatch(ctx, doer, issue); err != nil {
|
||||||
if err != nil {
|
log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
|
||||||
if issues_model.IsErrDependenciesLeft(err) {
|
}
|
||||||
if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil {
|
|
||||||
log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil {
|
_, err = issues_model.FinishIssueStopwatch(ctx, doer, issue)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
committer.Close()
|
|
||||||
|
|
||||||
notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true)
|
notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -54,39 +54,33 @@ func NewTeam(ctx context.Context, t *organization.Team) (err error) {
|
|||||||
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = db.Insert(ctx, t); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = db.Insert(ctx, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert units for team
|
|
||||||
if len(t.Units) > 0 {
|
|
||||||
for _, unit := range t.Units {
|
|
||||||
unit.TeamID = t.ID
|
|
||||||
}
|
|
||||||
if err = db.Insert(ctx, &t.Units); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add all repositories to the team if it has access to all of them.
|
// insert units for team
|
||||||
if t.IncludesAllRepositories {
|
if len(t.Units) > 0 {
|
||||||
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
for _, unit := range t.Units {
|
||||||
if err != nil {
|
unit.TeamID = t.ID
|
||||||
return fmt.Errorf("addAllRepositories: %w", err)
|
}
|
||||||
|
if err = db.Insert(ctx, &t.Units); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update organization number of teams.
|
// Add all repositories to the team if it has access to all of them.
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
|
if t.IncludesAllRepositories {
|
||||||
|
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("addAllRepositories: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update organization number of teams.
|
||||||
|
_, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTeam updates information of team.
|
// UpdateTeam updates information of team.
|
||||||
@@ -99,128 +93,117 @@ func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeA
|
|||||||
t.Description = t.Description[:255]
|
t.Description = t.Description[:255]
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
t.LowerName = strings.ToLower(t.Name)
|
||||||
return err
|
has, err := db.Exist[organization.Team](ctx, builder.Eq{
|
||||||
}
|
"org_id": t.OrgID,
|
||||||
defer committer.Close()
|
"lower_name": t.LowerName,
|
||||||
|
}.And(builder.Neq{"id": t.ID}),
|
||||||
t.LowerName = strings.ToLower(t.Name)
|
)
|
||||||
has, err := db.Exist[organization.Team](ctx, builder.Eq{
|
|
||||||
"org_id": t.OrgID,
|
|
||||||
"lower_name": t.LowerName,
|
|
||||||
}.And(builder.Neq{"id": t.ID}),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
|
|
||||||
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
|
|
||||||
return fmt.Errorf("update: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update units for team
|
|
||||||
if len(t.Units) > 0 {
|
|
||||||
for _, unit := range t.Units {
|
|
||||||
unit.TeamID = t.ID
|
|
||||||
}
|
|
||||||
// Delete team-unit.
|
|
||||||
if _, err := sess.
|
|
||||||
Where("team_id=?", t.ID).
|
|
||||||
Delete(new(organization.TeamUnit)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update access for team members if needed.
|
|
||||||
if authChanged {
|
|
||||||
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
|
|
||||||
TeamID: t.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetTeamRepositories: %w", err)
|
return err
|
||||||
|
} else if has {
|
||||||
|
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, repo := range repos {
|
sess := db.GetEngine(ctx)
|
||||||
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
|
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
|
||||||
return fmt.Errorf("recalculateTeamAccesses: %w", err)
|
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
|
||||||
|
return fmt.Errorf("update: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update units for team
|
||||||
|
if len(t.Units) > 0 {
|
||||||
|
for _, unit := range t.Units {
|
||||||
|
unit.TeamID = t.ID
|
||||||
|
}
|
||||||
|
// Delete team-unit.
|
||||||
|
if _, err := sess.
|
||||||
|
Where("team_id=?", t.ID).
|
||||||
|
Delete(new(organization.TeamUnit)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add all repositories to the team if it has access to all of them.
|
// Update access for team members if needed.
|
||||||
if includeAllChanged && t.IncludesAllRepositories {
|
if authChanged {
|
||||||
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
|
||||||
if err != nil {
|
TeamID: t.ID,
|
||||||
return fmt.Errorf("addAllRepositories: %w", err)
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetTeamRepositories: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, repo := range repos {
|
||||||
|
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
|
||||||
|
return fmt.Errorf("recalculateTeamAccesses: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
// Add all repositories to the team if it has access to all of them.
|
||||||
|
if includeAllChanged && t.IncludesAllRepositories {
|
||||||
|
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("addAllRepositories: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTeam deletes given team.
|
// DeleteTeam deletes given team.
|
||||||
// It's caller's responsibility to assign organization ID.
|
// It's caller's responsibility to assign organization ID.
|
||||||
func DeleteTeam(ctx context.Context, t *organization.Team) error {
|
func DeleteTeam(ctx context.Context, t *organization.Team) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := t.LoadMembers(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := t.LoadMembers(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update branch protections
|
|
||||||
{
|
|
||||||
protections := make([]*git_model.ProtectedBranch, 0, 10)
|
|
||||||
err := db.GetEngine(ctx).In("repo_id",
|
|
||||||
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
|
|
||||||
Find(&protections)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("findProtectedBranches: %w", err)
|
|
||||||
}
|
}
|
||||||
for _, p := range protections {
|
|
||||||
if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil {
|
// update branch protections
|
||||||
|
{
|
||||||
|
protections := make([]*git_model.ProtectedBranch, 0, 10)
|
||||||
|
err := db.GetEngine(ctx).In("repo_id",
|
||||||
|
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
|
||||||
|
Find(&protections)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("findProtectedBranches: %w", err)
|
||||||
|
}
|
||||||
|
for _, p := range protections {
|
||||||
|
if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.DeleteBeans(ctx,
|
||||||
|
&organization.Team{ID: t.ID},
|
||||||
|
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
|
||||||
|
&organization.TeamUnit{TeamID: t.ID},
|
||||||
|
&organization.TeamInvite{TeamID: t.ID},
|
||||||
|
&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tm := range t.Members {
|
||||||
|
if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
|
// Update organization number of teams.
|
||||||
|
_, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
if err := db.DeleteBeans(ctx,
|
|
||||||
&organization.Team{ID: t.ID},
|
|
||||||
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
|
|
||||||
&organization.TeamUnit{TeamID: t.ID},
|
|
||||||
&organization.TeamInvite{TeamID: t.ID},
|
|
||||||
&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tm := range t.Members {
|
|
||||||
if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update organization number of teams.
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTeamMember adds new membership of given team to given organization,
|
// AddTeamMember adds new membership of given team to given organization,
|
||||||
@@ -363,13 +346,7 @@ func removeInvalidOrgUser(ctx context.Context, orgID int64, user *user_model.Use
|
|||||||
|
|
||||||
// RemoveTeamMember removes member from given team of given organization.
|
// RemoveTeamMember removes member from given team of given organization.
|
||||||
func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
|
func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return removeTeamMember(ctx, team, user)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
if err := removeTeamMember(ctx, team, user); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@@ -164,42 +164,38 @@ func ExecuteCleanupRules(ctx context.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
|
func CleanupExpiredData(ctx context.Context, olderThan time.Duration) error {
|
||||||
ctx, committer, err := db.TxContext(outerCtx)
|
pbs := make([]*packages_model.PackageBlob, 0, 100)
|
||||||
if err != nil {
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
return err
|
if err := container_service.Cleanup(ctx, olderThan); err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := container_service.Cleanup(ctx, olderThan); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps, err := packages_model.FindUnreferencedPackages(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, p := range ps {
|
|
||||||
if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
|
|
||||||
|
ps, err := packages_model.FindUnreferencedPackages(ctx)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
for _, p := range ps {
|
||||||
|
if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
|
pbs, err = packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pb := range pbs {
|
|
||||||
if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
for _, pb := range pbs {
|
||||||
|
if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -469,24 +469,15 @@ func RemovePackageVersionByNameAndVersion(ctx context.Context, doer *user_model.
|
|||||||
|
|
||||||
// RemovePackageVersion deletes the package version and all associated files
|
// RemovePackageVersion deletes the package version and all associated files
|
||||||
func RemovePackageVersion(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
|
func RemovePackageVersion(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
pd, err := packages_model.GetPackageDescriptor(dbCtx, pv)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Deleting package: %v", pv.ID)
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
log.Trace("Deleting package: %v", pv.ID)
|
||||||
if err := DeletePackageVersionAndReferences(dbCtx, pv); err != nil {
|
return DeletePackageVersionAndReferences(ctx, pv)
|
||||||
return err
|
}); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -687,48 +687,40 @@ func SetMerged(ctx context.Context, pr *issues_model.PullRequest, mergedCommitID
|
|||||||
return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (bool, error) {
|
||||||
if err != nil {
|
pr.Issue = nil
|
||||||
return false, err
|
if err := pr.LoadIssue(ctx); err != nil {
|
||||||
}
|
return false, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
pr.Issue = nil
|
if err := pr.Issue.LoadRepo(ctx); err != nil {
|
||||||
if err := pr.LoadIssue(ctx); err != nil {
|
return false, err
|
||||||
return false, err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := pr.Issue.LoadRepo(ctx); err != nil {
|
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
|
// Removing an auto merge pull and ignore if not exist
|
||||||
return false, err
|
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
|
||||||
}
|
return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Removing an auto merge pull and ignore if not exist
|
// Set issue as closed
|
||||||
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
|
if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil {
|
||||||
return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
|
return false, fmt.Errorf("ChangeIssueStatus: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set issue as closed
|
// We need to save all of the data used to compute this merge as it may have already been changed by testPullRequestBranchMergeable. FIXME: need to set some state to prevent testPullRequestBranchMergeable from running whilst we are merging.
|
||||||
if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil {
|
if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID).
|
||||||
return false, fmt.Errorf("ChangeIssueStatus: %w", err)
|
And("has_merged = ?", false).
|
||||||
}
|
Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").
|
||||||
|
Update(pr); err != nil {
|
||||||
|
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
|
||||||
|
} else if cnt != 1 {
|
||||||
|
return false, issues_model.ErrIssueAlreadyChanged
|
||||||
|
}
|
||||||
|
|
||||||
// We need to save all of the data used to compute this merge as it may have already been changed by testPullRequestBranchMergeable. FIXME: need to set some state to prevent testPullRequestBranchMergeable from running whilst we are merging.
|
return true, nil
|
||||||
if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID).
|
})
|
||||||
And("has_merged = ?", false).
|
|
||||||
Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").
|
|
||||||
Update(pr); err != nil {
|
|
||||||
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
|
|
||||||
} else if cnt != 1 {
|
|
||||||
return false, issues_model.ErrIssueAlreadyChanged
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
}
|
||||||
|
@@ -29,35 +29,30 @@ func UploadAvatar(ctx context.Context, repo *repo_model.Repository, data []byte)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
oldAvatarPath := repo.CustomAvatarRelativePath()
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
oldAvatarPath := repo.CustomAvatarRelativePath()
|
// Users can upload the same image to other repo - prefix it with ID
|
||||||
|
// Then repo will be removed - only it avatar file will be removed
|
||||||
// Users can upload the same image to other repo - prefix it with ID
|
repo.Avatar = newAvatar
|
||||||
// Then repo will be removed - only it avatar file will be removed
|
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
||||||
repo.Avatar = newAvatar
|
return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
|
||||||
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
|
||||||
return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
|
|
||||||
_, err := w.Write(avatarData)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(oldAvatarPath) > 0 {
|
|
||||||
if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil {
|
|
||||||
return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %w", oldAvatarPath, err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
|
||||||
|
_, err := w.Write(avatarData)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(oldAvatarPath) > 0 {
|
||||||
|
if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil {
|
||||||
|
return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %w", oldAvatarPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAvatar deletes the repos's custom avatar.
|
// DeleteAvatar deletes the repos's custom avatar.
|
||||||
@@ -70,22 +65,17 @@ func DeleteAvatar(ctx context.Context, repo *repo_model.Repository) error {
|
|||||||
avatarPath := repo.CustomAvatarRelativePath()
|
avatarPath := repo.CustomAvatarRelativePath()
|
||||||
log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath)
|
log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath)
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
repo.Avatar = ""
|
||||||
return err
|
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
||||||
}
|
return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err)
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
repo.Avatar = ""
|
if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
|
||||||
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
|
||||||
return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err)
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
|
|
||||||
return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories
|
// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories
|
||||||
|
@@ -71,40 +71,32 @@ func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, colla
|
|||||||
UserID: collaborator.ID,
|
UserID: collaborator.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
} else if has == 0 {
|
||||||
defer committer.Close()
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil {
|
if err := repo.LoadOwner(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has == 0 {
|
}
|
||||||
return committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := repo.LoadOwner(ctx); err != nil {
|
if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil {
|
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
|
// Unassign a user from any issue (s)he has been assigned to in the repository
|
||||||
return err
|
return ReconsiderRepoIssuesAssignee(ctx, repo, collaborator)
|
||||||
}
|
})
|
||||||
|
|
||||||
// Unassign a user from any issue (s)he has been assigned to in the repository
|
|
||||||
if err := ReconsiderRepoIssuesAssignee(ctx, repo, collaborator); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
||||||
|
@@ -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
|
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
|
||||||
u.Avatar = avatar.HashAvatar(u.ID, data)
|
_, err := w.Write(avatarData)
|
||||||
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
|
return err
|
||||||
return fmt.Errorf("updateUser: %w", err)
|
}); err != nil {
|
||||||
}
|
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
|
return nil
|
||||||
_, err := w.Write(avatarData)
|
})
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAvatar deletes the user's custom avatar.
|
// DeleteAvatar deletes the user's custom avatar.
|
||||||
|
Reference in New Issue
Block a user