Skip to content

Commit 342e68f

Browse files
committed
fix
1 parent cd9a13e commit 342e68f

File tree

10 files changed

+126
-105
lines changed

10 files changed

+126
-105
lines changed

models/admin/task.go

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import (
1717
"code.gitea.io/gitea/modules/structs"
1818
"code.gitea.io/gitea/modules/timeutil"
1919
"code.gitea.io/gitea/modules/util"
20-
21-
"xorm.io/builder"
2220
)
2321

2422
// Task represents a task
@@ -35,7 +33,7 @@ type Task struct {
3533
StartTime timeutil.TimeStamp
3634
EndTime timeutil.TimeStamp
3735
PayloadContent string `xorm:"TEXT"`
38-
Message string `xorm:"TEXT"` // if task failed, saved the error reason
36+
Message string `xorm:"TEXT"` // if task failed, saved the error reason, it could be a JSON string of TranslatableMessage or a plain message
3937
Created timeutil.TimeStamp `xorm:"created"`
4038
}
4139

@@ -185,14 +183,6 @@ func GetMigratingTask(repoID int64) (*Task, error) {
185183
return &task, nil
186184
}
187185

188-
// HasFinishedMigratingTask returns if a finished migration task exists for the repo.
189-
func HasFinishedMigratingTask(repoID int64) (bool, error) {
190-
return db.GetEngine(db.DefaultContext).
191-
Where("repo_id=? AND type=? AND status=?", repoID, structs.TaskTypeMigrateRepo, structs.TaskStatusFinished).
192-
Table("task").
193-
Exist()
194-
}
195-
196186
// GetMigratingTaskByID returns the migrating task by repo's id
197187
func GetMigratingTaskByID(id, doerID int64) (*Task, *migration.MigrateOptions, error) {
198188
task := Task{
@@ -214,27 +204,6 @@ func GetMigratingTaskByID(id, doerID int64) (*Task, *migration.MigrateOptions, e
214204
return &task, &opts, nil
215205
}
216206

217-
// FindTaskOptions find all tasks
218-
type FindTaskOptions struct {
219-
Status int
220-
}
221-
222-
// ToConds generates conditions for database operation.
223-
func (opts FindTaskOptions) ToConds() builder.Cond {
224-
cond := builder.NewCond()
225-
if opts.Status >= 0 {
226-
cond = cond.And(builder.Eq{"status": opts.Status})
227-
}
228-
return cond
229-
}
230-
231-
// FindTasks find all tasks
232-
func FindTasks(opts FindTaskOptions) ([]*Task, error) {
233-
tasks := make([]*Task, 0, 10)
234-
err := db.GetEngine(db.DefaultContext).Where(opts.ToConds()).Find(&tasks)
235-
return tasks, err
236-
}
237-
238207
// CreateTask creates a task on database
239208
func CreateTask(task *Task) error {
240209
return db.Insert(db.DefaultContext, task)

modules/structs/task.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ package structs
66
// TaskType defines task type
77
type TaskType int
88

9-
// all kinds of task types
10-
const (
11-
TaskTypeMigrateRepo TaskType = iota // migrate repository from external or local disk
12-
)
9+
const TaskTypeMigrateRepo TaskType = iota // migrate repository from external or local disk
1310

1411
// Name returns the task type name
1512
func (taskType TaskType) Name() string {
@@ -25,9 +22,9 @@ type TaskStatus int
2522

2623
// enumerate all the kinds of task status
2724
const (
28-
TaskStatusQueue TaskStatus = iota // 0 task is queue
25+
TaskStatusQueued TaskStatus = iota // 0 task is queued
2926
TaskStatusRunning // 1 task is running
30-
TaskStatusStopped // 2 task is stopped
27+
TaskStatusStopped // 2 task is stopped (never used)
3128
TaskStatusFailed // 3 task is failed
3229
TaskStatusFinished // 4 task is finished
3330
)

options/locale/locale_en-US.ini

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ migrated_from_fake = Migrated From %[1]s
10381038
migrate.migrate = Migrate From %s
10391039
migrate.migrating = Migrating from <b>%s</b> ...
10401040
migrate.migrating_failed = Migrating from <b>%s</b> failed.
1041-
migrate.migrating_failed.error = Error: %s
1041+
migrate.migrating_failed.error = Failed to migrate: %s
10421042
migrate.migrating_failed_no_addr = Migration failed.
10431043
migrate.github.description = Migrate data from github.com or other GitHub instances.
10441044
migrate.git.description = Migrate a repository only from any Git service.
@@ -1055,6 +1055,8 @@ migrate.migrating_labels = Migrating Labels
10551055
migrate.migrating_releases = Migrating Releases
10561056
migrate.migrating_issues = Migrating Issues
10571057
migrate.migrating_pulls = Migrating Pull Requests
1058+
migrate.cancel_migrating_title = Cancel Migrating
1059+
migrate.cancel_migrating_confirm = Do you want to cancel this migration?
10581060

10591061
mirror_from = mirror of
10601062
forked_from = forked from

routers/web/repo/migrate.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"code.gitea.io/gitea/models"
13+
admin_model "code.gitea.io/gitea/models/admin"
1314
"code.gitea.io/gitea/models/db"
1415
repo_model "code.gitea.io/gitea/models/repo"
1516
user_model "code.gitea.io/gitea/models/user"
@@ -257,3 +258,19 @@ func setMigrationContextData(ctx *context.Context, serviceType structs.GitServic
257258
ctx.Data["Services"] = append([]structs.GitServiceType{structs.PlainGitService}, structs.SupportedFullGitService...)
258259
ctx.Data["service"] = serviceType
259260
}
261+
262+
func MigrateCancelPost(ctx *context.Context) {
263+
migratingTask, err := admin_model.GetMigratingTask(ctx.Repo.Repository.ID)
264+
if err != nil {
265+
ctx.ServerError("GetMigratingTask", err)
266+
return
267+
}
268+
if migratingTask.Status == structs.TaskStatusRunning {
269+
taskUpdate := &admin_model.Task{ID: migratingTask.ID, Status: structs.TaskStatusFailed, Message: "canceled"}
270+
if err = taskUpdate.UpdateCols("status", "message"); err != nil {
271+
ctx.ServerError("task.UpdateCols", err)
272+
return
273+
}
274+
}
275+
ctx.Redirect(ctx.Repo.Repository.Link())
276+
}

routers/web/web.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,7 @@ func registerRoutes(m *web.Route) {
936936
addSettingsRunnersRoutes()
937937
addSettingsSecretsRoutes()
938938
}, actions.MustEnableActions)
939+
m.Post("/migrate/cancel", repo.MigrateCancelPost) // this handler must be under "settings", otherwise this incomplete repo can't be accessed
939940
}, ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer))
940941
}, reqSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoAdmin, context.RepoRef())
941942

services/migrations/gitea_uploader.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -923,9 +923,8 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
923923
func (g *GiteaLocalUploader) Rollback() error {
924924
if g.repo != nil && g.repo.ID > 0 {
925925
g.gitRepo.Close()
926-
if err := models.DeleteRepository(g.doer, g.repo.OwnerID, g.repo.ID); err != nil {
927-
return err
928-
}
926+
927+
// do not delete the repository, otherwise the end users won't be able to see the last error message
929928
}
930929
return nil
931930
}

services/task/migrate.go

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"errors"
88
"fmt"
99
"strings"
10+
"time"
1011

11-
"code.gitea.io/gitea/models"
1212
admin_model "code.gitea.io/gitea/models/admin"
1313
"code.gitea.io/gitea/models/db"
1414
repo_model "code.gitea.io/gitea/models/repo"
@@ -28,13 +28,13 @@ import (
2828
func handleCreateError(owner *user_model.User, err error) error {
2929
switch {
3030
case repo_model.IsErrReachLimitOfRepo(err):
31-
return fmt.Errorf("You have already reached your limit of %d repositories", owner.MaxCreationLimit())
31+
return fmt.Errorf("you have already reached your limit of %d repositories", owner.MaxCreationLimit())
3232
case repo_model.IsErrRepoAlreadyExist(err):
33-
return errors.New("The repository name is already used")
33+
return errors.New("the repository name is already used")
3434
case db.IsErrNameReserved(err):
35-
return fmt.Errorf("The repository name '%s' is reserved", err.(db.ErrNameReserved).Name)
35+
return fmt.Errorf("the repository name '%s' is reserved", err.(db.ErrNameReserved).Name)
3636
case db.IsErrNamePatternNotAllowed(err):
37-
return fmt.Errorf("The pattern '%s' is not allowed in a repository name", err.(db.ErrNamePatternNotAllowed).Pattern)
37+
return fmt.Errorf("the pattern '%s' is not allowed in a repository name", err.(db.ErrNamePatternNotAllowed).Pattern)
3838
default:
3939
return err
4040
}
@@ -57,22 +57,17 @@ func runMigrateTask(t *admin_model.Task) (err error) {
5757
log.Error("FinishMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, err)
5858
}
5959

60+
log.Error("runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, err)
61+
6062
t.EndTime = timeutil.TimeStampNow()
6163
t.Status = structs.TaskStatusFailed
6264
t.Message = err.Error()
63-
// Ensure that the repo loaded before we zero out the repo ID from the task - thus ensuring that we can delete it
64-
_ = t.LoadRepo()
6565

66-
t.RepoID = 0
67-
if err := t.UpdateCols("status", "errors", "repo_id", "end_time"); err != nil {
66+
if err := t.UpdateCols("status", "message", "end_time"); err != nil {
6867
log.Error("Task UpdateCols failed: %v", err)
6968
}
7069

71-
if t.Repo != nil {
72-
if errDelete := models.DeleteRepository(t.Doer, t.OwnerID, t.Repo.ID); errDelete != nil {
73-
log.Error("DeleteRepository: %v", errDelete)
74-
}
75-
}
70+
// then, do not delete the repository, otherwise the users won't be able to see the last error
7671
}()
7772

7873
if err = t.LoadRepo(); err != nil {
@@ -100,7 +95,7 @@ func runMigrateTask(t *admin_model.Task) (err error) {
10095
opts.MigrateToRepoID = t.RepoID
10196

10297
pm := process.GetManager()
103-
ctx, _, finished := pm.AddContext(graceful.GetManager().ShutdownContext(), fmt.Sprintf("MigrateTask: %s/%s", t.Owner.Name, opts.RepoName))
98+
ctx, cancel, finished := pm.AddContext(graceful.GetManager().ShutdownContext(), fmt.Sprintf("MigrateTask: %s/%s", t.Owner.Name, opts.RepoName))
10499
defer finished()
105100

106101
t.StartTime = timeutil.TimeStampNow()
@@ -109,6 +104,23 @@ func runMigrateTask(t *admin_model.Task) (err error) {
109104
return
110105
}
111106

107+
// check whether the task should be canceled, this goroutine is also managed by process manager
108+
go func() {
109+
for {
110+
select {
111+
case <-time.After(2 * time.Second):
112+
case <-ctx.Done():
113+
return
114+
}
115+
task, _ := admin_model.GetMigratingTask(t.RepoID)
116+
if task != nil && task.Status != structs.TaskStatusRunning {
117+
log.Debug("MigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] is canceled due to status is not 'running'", t.ID, t.DoerID, t.RepoID, t.OwnerID)
118+
cancel()
119+
return
120+
}
121+
}
122+
}()
123+
112124
t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
113125
message := admin_model.TranslatableMessage{
114126
Format: format,
@@ -118,23 +130,24 @@ func runMigrateTask(t *admin_model.Task) (err error) {
118130
t.Message = string(bs)
119131
_ = t.UpdateCols("message")
120132
})
133+
121134
if err == nil {
122135
log.Trace("Repository migrated [%d]: %s/%s", t.Repo.ID, t.Owner.Name, t.Repo.Name)
123136
return
124137
}
125138

126139
if repo_model.IsErrRepoAlreadyExist(err) {
127-
err = errors.New("The repository name is already used")
140+
err = errors.New("the repository name is already used")
128141
return
129142
}
130143

131144
// remoteAddr may contain credentials, so we sanitize it
132145
err = util.SanitizeErrorCredentialURLs(err)
133146
if strings.Contains(err.Error(), "Authentication failed") ||
134147
strings.Contains(err.Error(), "could not read Username") {
135-
return fmt.Errorf("Authentication failed: %w", err)
148+
return fmt.Errorf("authentication failed: %w", err)
136149
} else if strings.Contains(err.Error(), "fatal:") {
137-
return fmt.Errorf("Migration failed: %w", err)
150+
return fmt.Errorf("migration failed: %w", err)
138151
}
139152

140153
// do not be tempted to coalesce this line with the return

services/task/task.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*adm
9595
DoerID: doer.ID,
9696
OwnerID: u.ID,
9797
Type: structs.TaskTypeMigrateRepo,
98-
Status: structs.TaskStatusQueue,
98+
Status: structs.TaskStatusQueued,
9999
PayloadContent: string(bs),
100100
}
101101

templates/repo/migrate/migrating.tmpl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{{template "base/alert" .}}
88
<div class="home">
99
<div class="ui stackable middle very relaxed page grid">
10-
<div id="repo_migrating" class="sixteen wide center aligned centered column" task="{{.MigrateTask.ID}}">
10+
<div id="repo_migrating" class="sixteen wide center aligned centered column" data-migrating-task-id="{{.MigrateTask.ID}}">
1111
<div>
1212
<img src="{{AssetUrlPrefix}}/img/loading.png">
1313
</div>
@@ -32,10 +32,14 @@
3232
{{end}}
3333
<p id="repo_migrating_failed_error"></p>
3434
</div>
35-
{{if and .Failed .Permission.IsAdmin}}
35+
{{if .Permission.IsAdmin}}
3636
<div class="ui divider"></div>
3737
<div class="item">
38+
{{if .Failed}}
3839
<button class="ui basic red show-modal button" data-modal="#delete-repo-modal">{{.locale.Tr "repo.settings.delete"}}</button>
40+
{{else}}
41+
<button class="ui basic red show-modal button" data-modal="#cancel-repo-modal">{{.locale.Tr "cancel"}}</button>
42+
{{end}}
3943
</div>
4044
{{end}}
4145
</div>
@@ -45,6 +49,7 @@
4549
</div>
4650
</div>
4751
</div>
52+
4853
<div class="ui small modal" id="delete-repo-modal">
4954
<div class="header">
5055
{{.locale.Tr "repo.settings.delete"}}
@@ -78,4 +83,18 @@
7883
</form>
7984
</div>
8085
</div>
86+
87+
<div class="ui g-modal-confirm modal" id="cancel-repo-modal">
88+
<div class="header">
89+
{{.locale.Tr "repo.migrate.cancel_migrating_title"}}
90+
</div>
91+
<form action="{{.Link}}/settings/migrate/cancel" method="post">
92+
{{.CsrfTokenHtml}}
93+
<div class="content">
94+
{{.locale.Tr "repo.migrate.cancel_migrating_confirm"}}
95+
</div>
96+
{{template "base/modal_actions_confirm" .}}
97+
</form>
98+
</div>
99+
81100
{{template "base/footer" .}}

0 commit comments

Comments
 (0)