From 59233846542aa573a5a029d72f0fdb6f6185113e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 2 Sep 2020 00:01:51 +0200 Subject: [PATCH 01/10] use different structs for MigrateRepoOptions on UI and API --- modules/auth/repo_form.go | 9 ++-- modules/convert/utils.go | 19 +++++++ modules/structs/repo.go | 24 +++++++++ routers/api/v1/api.go | 2 +- routers/api/v1/repo/migrate.go | 12 ++--- routers/api/v1/swagger/options.go | 3 ++ routers/repo/repo.go | 4 +- templates/swagger/v1_json.tmpl | 85 ++++++++++++++++++++++++++++++- 8 files changed, 141 insertions(+), 17 deletions(-) diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index b3fead7da97ac..a25d647f8279e 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -84,9 +84,8 @@ func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) bi // and returns composed URL with needed username and password. // It also checks if given user has permission when remote address // is actually a local path. -func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) { - remoteAddr := strings.TrimSpace(f.CloneAddr) - +func ParseRemoteAddr(remoteAddr, authUsername, authPassword string, user *models.User) (string, error) { + remoteAddr = strings.TrimSpace(remoteAddr) // Remote address can be HTTP/HTTPS/Git URL or local path. if strings.HasPrefix(remoteAddr, "http://") || strings.HasPrefix(remoteAddr, "https://") || @@ -95,8 +94,8 @@ func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) { if err != nil { return "", models.ErrInvalidCloneAddr{IsURLError: true} } - if len(f.AuthUsername)+len(f.AuthPassword) > 0 { - u.User = url.UserPassword(f.AuthUsername, f.AuthPassword) + if len(authUsername)+len(authPassword) > 0 { + u.User = url.UserPassword(authUsername, authPassword) } remoteAddr = u.String() } else if !user.CanImportLocal() { diff --git a/modules/convert/utils.go b/modules/convert/utils.go index ddb8a8820d42a..6f3449973ee33 100644 --- a/modules/convert/utils.go +++ b/modules/convert/utils.go @@ -5,7 +5,10 @@ package convert import ( + "strings" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" ) // ToCorrectPageSize makes sure page size is in allowed range. @@ -17,3 +20,19 @@ func ToCorrectPageSize(size int) int { } return size } + +// ToGitServiceType return GitServiceType based on string +func ToGitServiceType(value string) structs.GitServiceType { + switch strings.ToLower(value) { + case "github": + return structs.GithubService + case "gitea": + return structs.GiteaService + case "gitlab": + return structs.GitlabService + case "gogs": + return structs.GogsService + default: + return structs.PlainGitService + } +} diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 808d2ffbc8ed0..e1d07408eb2b6 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -235,6 +235,30 @@ func (gt GitServiceType) Title() string { return "" } +// MigrateRepoOptions options for migrating repository's +type MigrateRepoOptions struct { + // required: true + CloneAddr string `json:"clone_addr" binding:"Required"` + // enum: git,github,gitea,gitlab + Service string `json:"service"` + AuthUsername string `json:"auth_username"` + AuthPassword string `json:"auth_password"` + AuthToken string `json:"auth_token"` + // required: true + UID int64 `json:"uid" binding:"Required"` + // required: true + RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` + Mirror bool `json:"mirror"` + Private bool `json:"private"` + Description string `json:"description" binding:"MaxSize(255)"` + Wiki bool `json:"wiki"` + Milestones bool `json:"milestones"` + Labels bool `json:"labels"` + Issues bool `json:"issues"` + PullRequests bool `json:"pull_requests"` + Releases bool `json:"releases"` +} + // TokenAuth represents whether a service type supports token-based auth func (gt GitServiceType) TokenAuth() bool { switch gt { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 027c6abc97797..63eff357f19f0 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -635,7 +635,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/issues/search", repo.SearchIssues) - m.Post("/migrate", reqToken(), bind(auth.MigrateRepoForm{}), repo.Migrate) + m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) m.Group("/:username/:reponame", func() { m.Combo("").Get(reqAnyRepoReader(), repo.Get). diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index fa4b7366e89a5..ca822602b549b 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -9,12 +9,12 @@ import ( "errors" "fmt" "net/http" - "net/url" "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/migrations" @@ -26,7 +26,7 @@ import ( ) // Migrate migrate remote git repository to gitea -func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { +func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { // swagger:operation POST /repos/migrate repository repoMigrate // --- // summary: Migrate a remote git repository @@ -87,7 +87,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { } } - remoteAddr, err := form.ParseRemoteAddr(ctx.User) + remoteAddr, err := auth.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword, ctx.User) if err != nil { if models.IsErrInvalidCloneAddr(err) { addrErr := err.(models.ErrInvalidCloneAddr) @@ -107,11 +107,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { return } - var gitServiceType = api.PlainGitService - u, err := url.Parse(remoteAddr) - if err == nil && strings.EqualFold(u.Host, "github.com") { - gitServiceType = api.GithubService - } + gitServiceType := convert.ToGitServiceType(form.Service) if form.Mirror && setting.Repository.DisableMirrors { ctx.Error(http.StatusForbidden, "MirrorsGlobalDisabled", fmt.Errorf("the site administrator has disabled mirrors")) diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index d9ef05c335991..ced6589e48b01 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -149,4 +149,7 @@ type swaggerParameterBodies struct { // in:body SubmitPullReviewOptions api.SubmitPullReviewOptions + + // in:body + MigrateRepoOptions api.MigrateRepoOptions } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 5fc081a6f615d..8bdca432bb777 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -299,7 +299,7 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam ctx.Data["Err_RepoName"] = true ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) default: - remoteAddr, _ := form.ParseRemoteAddr(owner) + remoteAddr, _ := auth.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword, owner) err = util.URLSanitizedError(err, remoteAddr) if strings.Contains(err.Error(), "Authentication failed") || strings.Contains(err.Error(), "Bad credentials") || @@ -333,7 +333,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { return } - remoteAddr, err := form.ParseRemoteAddr(ctx.User) + remoteAddr, err := auth.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword, ctx.User) if err != nil { if models.IsErrInvalidCloneAddr(err) { ctx.Data["Err_CloneAddr"] = true diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 86dd817edef82..84010ecb58eb5 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13557,6 +13557,89 @@ }, "x-go-package": "code.gitea.io/gitea/modules/auth" }, + "MigrateRepoOptions": { + "description": "MigrateRepoOptions options for migrating repository's", + "type": "object", + "required": [ + "clone_addr", + "uid", + "repo_name" + ], + "properties": { + "auth_password": { + "type": "string", + "x-go-name": "AuthPassword" + }, + "auth_token": { + "type": "string", + "x-go-name": "AuthToken" + }, + "auth_username": { + "type": "string", + "x-go-name": "AuthUsername" + }, + "clone_addr": { + "type": "string", + "x-go-name": "CloneAddr" + }, + "description": { + "type": "string", + "x-go-name": "Description" + }, + "issues": { + "type": "boolean", + "x-go-name": "Issues" + }, + "labels": { + "type": "boolean", + "x-go-name": "Labels" + }, + "milestones": { + "type": "boolean", + "x-go-name": "Milestones" + }, + "mirror": { + "type": "boolean", + "x-go-name": "Mirror" + }, + "private": { + "type": "boolean", + "x-go-name": "Private" + }, + "pull_requests": { + "type": "boolean", + "x-go-name": "PullRequests" + }, + "releases": { + "type": "boolean", + "x-go-name": "Releases" + }, + "repo_name": { + "type": "string", + "x-go-name": "RepoName" + }, + "service": { + "type": "string", + "enum": [ + "git", + "github", + "gitea", + "gitlab" + ], + "x-go-name": "Service" + }, + "uid": { + "type": "integer", + "format": "int64", + "x-go-name": "UID" + }, + "wiki": { + "type": "boolean", + "x-go-name": "Wiki" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "Milestone": { "description": "Milestone milestone is a collection of issues on one repository", "type": "object", @@ -15725,7 +15808,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/SubmitPullReviewOptions" + "$ref": "#/definitions/MigrateRepoOptions" } }, "redirect": { From c0705ba32f8c612b1baa2086644ec448033a9c7e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 2 Sep 2020 00:22:36 +0200 Subject: [PATCH 02/10] Fix TokenAuth and rename UID to an understandable Name --- modules/structs/repo.go | 10 ++++++---- routers/api/v1/repo/migrate.go | 11 +++++++---- templates/swagger/v1_json.tmpl | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index e1d07408eb2b6..c33c14d0f4967 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -239,15 +239,17 @@ func (gt GitServiceType) Title() string { type MigrateRepoOptions struct { // required: true CloneAddr string `json:"clone_addr" binding:"Required"` + // Owner of the new created repo, if not set current user is used + RepoOwner int64 `json:"uid"` + // required: true + RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` + // enum: git,github,gitea,gitlab Service string `json:"service"` AuthUsername string `json:"auth_username"` AuthPassword string `json:"auth_password"` AuthToken string `json:"auth_token"` - // required: true - UID int64 `json:"uid" binding:"Required"` - // required: true - RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` + Mirror bool `json:"mirror"` Private bool `json:"private"` Description string `json:"description" binding:"MaxSize(255)"` diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index ca822602b549b..420666a592a4e 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -48,10 +48,12 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { // "$ref": "#/responses/validationError" ctxUser := ctx.User - // Not equal means context user is an organization, - // or is another user/organization if current user is admin. - if form.UID != ctxUser.ID { - org, err := models.GetUserByID(form.UID) + if form.RepoOwner == 0 { + form.RepoOwner = ctx.User.ID + } else if form.RepoOwner != ctxUser.ID { + // Not equal means context user is an organization, + // or is another user/organization if current user is admin. + org, err := models.GetUserByID(form.RepoOwner) if err != nil { if models.IsErrUserNotExist(err) { ctx.Error(http.StatusUnprocessableEntity, "", err) @@ -122,6 +124,7 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { Mirror: form.Mirror, AuthUsername: form.AuthUsername, AuthPassword: form.AuthPassword, + AuthToken: form.AuthToken, Wiki: form.Wiki, Issues: form.Issues, Milestones: form.Milestones, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 84010ecb58eb5..ea1ad0940691a 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13562,7 +13562,6 @@ "type": "object", "required": [ "clone_addr", - "uid", "repo_name" ], "properties": { @@ -13629,9 +13628,10 @@ "x-go-name": "Service" }, "uid": { + "description": "Owner of the new created repo, if not set current user is used", "type": "integer", "format": "int64", - "x-go-name": "UID" + "x-go-name": "RepoOwner" }, "wiki": { "type": "boolean", From e0fad58a673de0a866733f3a22e28ff661eefe9a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 2 Sep 2020 00:32:01 +0200 Subject: [PATCH 03/10] fix swagger doc --- modules/structs/repo.go | 2 +- routers/api/v1/repo/migrate.go | 2 +- templates/swagger/v1_json.tmpl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index c33c14d0f4967..1e841d53d98a5 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -239,7 +239,7 @@ func (gt GitServiceType) Title() string { type MigrateRepoOptions struct { // required: true CloneAddr string `json:"clone_addr" binding:"Required"` - // Owner of the new created repo, if not set current user is used + // ID of repo owner, if not set current user is used RepoOwner int64 `json:"uid"` // required: true RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 420666a592a4e..ea2de2af648ab 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -38,7 +38,7 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { // - name: body // in: body // schema: - // "$ref": "#/definitions/MigrateRepoForm" + // "$ref": "#/definitions/MigrateRepoOptions" // responses: // "201": // "$ref": "#/responses/Repository" diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index ea1ad0940691a..dacf0fda97535 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1798,7 +1798,7 @@ "name": "body", "in": "body", "schema": { - "$ref": "#/definitions/MigrateRepoForm" + "$ref": "#/definitions/MigrateRepoOptions" } } ], @@ -13628,7 +13628,7 @@ "x-go-name": "Service" }, "uid": { - "description": "Owner of the new created repo, if not set current user is used", + "description": "ID of repo owner, if not set current user is used", "type": "integer", "format": "int64", "x-go-name": "RepoOwner" From d3a210804d332e5c48870ee8392f03aec6d86a65 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 2 Sep 2020 00:41:26 +0200 Subject: [PATCH 04/10] simplify & mk redable --- routers/api/v1/repo/migrate.go | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index ea2de2af648ab..522792b6470f7 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -47,12 +47,11 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { // "422": // "$ref": "#/responses/validationError" - ctxUser := ctx.User - if form.RepoOwner == 0 { - form.RepoOwner = ctx.User.ID - } else if form.RepoOwner != ctxUser.ID { - // Not equal means context user is an organization, - // or is another user/organization if current user is admin. + repoOwner := ctx.User + + // Not equal means context user is an organization, + // or is another user/organization if current user is admin. + if form.RepoOwner != 0 && form.RepoOwner != repoOwner.ID { org, err := models.GetUserByID(form.RepoOwner) if err != nil { if models.IsErrUserNotExist(err) { @@ -62,7 +61,7 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { } return } - ctxUser = org + repoOwner = org } if ctx.HasError() { @@ -71,14 +70,14 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { } if !ctx.User.IsAdmin { - if !ctxUser.IsOrganization() && ctx.User.ID != ctxUser.ID { + if !repoOwner.IsOrganization() && ctx.User.ID != repoOwner.ID { ctx.Error(http.StatusForbidden, "", "Given user is not an organization.") return } - if ctxUser.IsOrganization() { + if repoOwner.IsOrganization() { // Check ownership of organization. - isOwner, err := ctxUser.IsOwnedBy(ctx.User.ID) + isOwner, err := repoOwner.IsOwnedBy(ctx.User.ID) if err != nil { ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) return @@ -143,7 +142,7 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { opts.Releases = false } - repo, err := repo_module.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{ + repo, err := repo_module.CreateRepository(ctx.User, repoOwner, models.CreateRepoOptions{ Name: opts.RepoName, Description: opts.Description, OriginalURL: form.CloneAddr, @@ -153,7 +152,7 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { Status: models.RepositoryBeingMigrated, }) if err != nil { - handleMigrateError(ctx, ctxUser, remoteAddr, err) + handleMigrateError(ctx, repoOwner, remoteAddr, err) return } @@ -170,24 +169,24 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { if err == nil { repo.Status = models.RepositoryReady if err := models.UpdateRepositoryCols(repo, "status"); err == nil { - notification.NotifyMigrateRepository(ctx.User, ctxUser, repo) + notification.NotifyMigrateRepository(ctx.User, repoOwner, repo) return } } if repo != nil { - if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil { + if errDelete := models.DeleteRepository(ctx.User, repoOwner.ID, repo.ID); errDelete != nil { log.Error("DeleteRepository: %v", errDelete) } } }() - if _, err = migrations.MigrateRepository(graceful.GetManager().HammerContext(), ctx.User, ctxUser.Name, opts); err != nil { - handleMigrateError(ctx, ctxUser, remoteAddr, err) + if _, err = migrations.MigrateRepository(graceful.GetManager().HammerContext(), ctx.User, repoOwner.Name, opts); err != nil { + handleMigrateError(ctx, repoOwner, remoteAddr, err) return } - log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName) + log.Trace("Repository migrated: %s/%s", repoOwner.Name, form.RepoName) ctx.JSON(http.StatusCreated, repo.APIFormat(models.AccessModeAdmin)) } From f66c2e025ebcb7906904636e454140a119f51867 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 2 Sep 2020 01:13:44 +0200 Subject: [PATCH 05/10] R E F A C T O R: migration has now internal 3 structs to store its options: * the Options for WebUI: modules/auth/repo_form.go * the Options for API: modules/structs/repo.go * the option struct with after validation for internal prossessing: modules/migrations/base/options.go --- integrations/api_repo_test.go | 8 ++++---- models/task.go | 5 +++-- modules/migrations/base/options.go | 25 ++++++++++++++++++++++++- modules/migrations/gitea.go | 2 +- modules/migrations/gitea_test.go | 3 ++- modules/repository/repo.go | 4 ++-- modules/structs/repo.go | 26 -------------------------- modules/task/migrate.go | 3 ++- services/mirror/mirror_test.go | 4 ++-- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index 9d3599102a3b2..d000696540097 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -316,9 +316,9 @@ func TestAPIRepoMigrate(t *testing.T) { user := models.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User) session := loginUser(t, user.Name) token := getTokenForLoggedInUser(t, session) - req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOption{ + req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ CloneAddr: testCase.cloneURL, - UID: int(testCase.userID), + RepoOwner: testCase.userID, RepoName: testCase.repoName, }) resp := MakeRequest(t, req, NoExpectedStatus) @@ -360,9 +360,9 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { cloneURL := "https://github.com/go-gitea/test_repo.git" req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+httpContext.Token, - &api.MigrateRepoOption{ + &api.MigrateRepoOptions{ CloneAddr: cloneURL, - UID: int(userID), + RepoOwner: userID, RepoName: httpContext.Reponame, }) resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict) diff --git a/models/task.go b/models/task.go index f4fce058c056a..43cb2d4d9a633 100644 --- a/models/task.go +++ b/models/task.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" + migration "code.gitea.io/gitea/modules/migrations/base" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -101,9 +102,9 @@ func (task *Task) UpdateCols(cols ...string) error { } // MigrateConfig returns task config when migrate repository -func (task *Task) MigrateConfig() (*structs.MigrateRepoOption, error) { +func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) { if task.Type == structs.TaskTypeMigrateRepo { - var opts structs.MigrateRepoOption + var opts migration.MigrateOptions err := json.Unmarshal([]byte(task.PayloadContent), &opts) if err != nil { return nil, err diff --git a/modules/migrations/base/options.go b/modules/migrations/base/options.go index 2d180b61d955a..de201cedea82f 100644 --- a/modules/migrations/base/options.go +++ b/modules/migrations/base/options.go @@ -8,4 +8,27 @@ package base import "code.gitea.io/gitea/modules/structs" // MigrateOptions defines the way a repository gets migrated -type MigrateOptions = structs.MigrateRepoOption +type MigrateOptions struct { + // required: true + CloneAddr string `json:"clone_addr" binding:"Required"` + AuthUsername string `json:"auth_username"` + AuthPassword string `json:"auth_password"` + AuthToken string `json:"auth_token"` + // required: true + UID int `json:"uid" binding:"Required"` + // required: true + RepoName string `json:"repo_name" binding:"Required"` + Mirror bool `json:"mirror"` + Private bool `json:"private"` + Description string `json:"description"` + OriginalURL string + GitServiceType structs.GitServiceType + Wiki bool + Issues bool + Milestones bool + Labels bool + Releases bool + Comments bool + PullRequests bool + MigrateToRepoID int64 +} diff --git a/modules/migrations/gitea.go b/modules/migrations/gitea.go index 082ddcd5fb0e8..b70ad7b0ce001 100644 --- a/modules/migrations/gitea.go +++ b/modules/migrations/gitea.go @@ -123,7 +123,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate return err } - r, err = repository.MigrateRepositoryGitData(g.doer, owner, r, structs.MigrateRepoOption{ + r, err = repository.MigrateRepositoryGitData(g.doer, owner, r, base.MigrateOptions{ RepoName: g.repoName, Description: repo.Description, OriginalURL: repo.OriginalURL, diff --git a/modules/migrations/gitea_test.go b/modules/migrations/gitea_test.go index 62c8f71322618..2dbd8ffd446ef 100644 --- a/modules/migrations/gitea_test.go +++ b/modules/migrations/gitea_test.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/graceful" + "code.gitea.io/gitea/modules/migrations/base" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -32,7 +33,7 @@ func TestGiteaUploadRepo(t *testing.T) { uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName) ) - err := migrateRepository(downloader, uploader, structs.MigrateRepoOption{ + err := migrateRepository(downloader, uploader, base.MigrateOptions{ CloneAddr: "https://github.com/go-xorm/builder", RepoName: repoName, AuthUsername: "", diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 2d5551d9875c1..36e9ed49c16bc 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -13,8 +13,8 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + migration "code.gitea.io/gitea/modules/migrations/base" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -41,7 +41,7 @@ func WikiRemoteURL(remote string) string { } // MigrateRepositoryGitData starts migrating git related data after created migrating repository -func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opts api.MigrateRepoOption) (*models.Repository, error) { +func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opts migration.MigrateOptions) (*models.Repository, error) { repoPath := models.RepoPath(u.Name, opts.RepoName) if u.IsOrganization() { diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 1e841d53d98a5..6d8607164ac83 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -278,29 +278,3 @@ var ( GitlabService, } ) - -// MigrateRepoOption options for migrating a repository from an external service -type MigrateRepoOption struct { - // required: true - CloneAddr string `json:"clone_addr" binding:"Required"` - AuthUsername string `json:"auth_username"` - AuthPassword string `json:"auth_password"` - AuthToken string `json:"auth_token"` - // required: true - UID int `json:"uid" binding:"Required"` - // required: true - RepoName string `json:"repo_name" binding:"Required"` - Mirror bool `json:"mirror"` - Private bool `json:"private"` - Description string `json:"description"` - OriginalURL string - GitServiceType GitServiceType - Wiki bool - Issues bool - Milestones bool - Labels bool - Releases bool - Comments bool - PullRequests bool - MigrateToRepoID int64 -} diff --git a/modules/task/migrate.go b/modules/task/migrate.go index d3b4fa45f01ab..9d6c8bf733213 100644 --- a/modules/task/migrate.go +++ b/modules/task/migrate.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/migrations" + migration "code.gitea.io/gitea/modules/migrations/base" "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -89,7 +90,7 @@ func runMigrateTask(t *models.Task) (err error) { return err } - var opts *structs.MigrateRepoOption + var opts *migration.MigrateOptions opts, err = t.MigrateConfig() if err != nil { return err diff --git a/services/mirror/mirror_test.go b/services/mirror/mirror_test.go index 25e499ad788a5..79e18885b3383 100644 --- a/services/mirror/mirror_test.go +++ b/services/mirror/mirror_test.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" + migration "code.gitea.io/gitea/modules/migrations/base" "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/structs" release_service "code.gitea.io/gitea/services/release" "github.com/stretchr/testify/assert" @@ -28,7 +28,7 @@ func TestRelease_MirrorDelete(t *testing.T) { repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) repoPath := models.RepoPath(user.Name, repo.Name) - opts := structs.MigrateRepoOption{ + opts := migration.MigrateOptions{ RepoName: "test_mirror", Description: "Test mirror", Private: false, From cbbdb93c91b5af3336b1d5b1345658e0c76c48f6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 2 Sep 2020 06:44:44 +0200 Subject: [PATCH 06/10] Copyright Header --- modules/convert/utils.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/convert/utils.go b/modules/convert/utils.go index 6f3449973ee33..69de306689e04 100644 --- a/modules/convert/utils.go +++ b/modules/convert/utils.go @@ -1,3 +1,4 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. From ed459bb03c552f79160ba4834fa33448870253a4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 5 Sep 2020 01:58:52 +0200 Subject: [PATCH 07/10] Deprecate UID - add RepoOwner --- integrations/api_repo_test.go | 12 ++++++------ modules/structs/repo.go | 6 ++++-- routers/api/v1/repo/migrate.go | 32 ++++++++++++++++++-------------- templates/swagger/v1_json.tmpl | 9 +++++++-- 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index d000696540097..c8afa73ae6816 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -317,9 +317,9 @@ func TestAPIRepoMigrate(t *testing.T) { session := loginUser(t, user.Name) token := getTokenForLoggedInUser(t, session) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ - CloneAddr: testCase.cloneURL, - RepoOwner: testCase.userID, - RepoName: testCase.repoName, + CloneAddr: testCase.cloneURL, + RepoOwnerID: testCase.userID, + RepoName: testCase.repoName, }) resp := MakeRequest(t, req, NoExpectedStatus) if resp.Code == http.StatusUnprocessableEntity { @@ -361,9 +361,9 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+httpContext.Token, &api.MigrateRepoOptions{ - CloneAddr: cloneURL, - RepoOwner: userID, - RepoName: httpContext.Reponame, + CloneAddr: cloneURL, + RepoOwnerID: userID, + RepoName: httpContext.Reponame, }) resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict) respJSON := map[string]string{} diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 6d8607164ac83..2e6e0cd9bdee2 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -239,8 +239,10 @@ func (gt GitServiceType) Title() string { type MigrateRepoOptions struct { // required: true CloneAddr string `json:"clone_addr" binding:"Required"` - // ID of repo owner, if not set current user is used - RepoOwner int64 `json:"uid"` + // deprecated (only for backwards compatibility) + RepoOwnerID int64 `json:"uid"` + // Name of User or Organisation who will own Repo after migration + RepoOwner string `json:"repo_owner"` // required: true RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 522792b6470f7..019d82031cb70 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -47,21 +47,25 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { // "422": // "$ref": "#/responses/validationError" - repoOwner := ctx.User - - // Not equal means context user is an organization, - // or is another user/organization if current user is admin. - if form.RepoOwner != 0 && form.RepoOwner != repoOwner.ID { - org, err := models.GetUserByID(form.RepoOwner) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Error(http.StatusUnprocessableEntity, "", err) - } else { - ctx.Error(http.StatusInternalServerError, "GetUserByID", err) - } - return + //get repoOwner + var ( + repoOwner *models.User + err error + ) + if len(form.RepoOwner) != 0 { + repoOwner, err = models.GetUserByName(form.RepoOwner) + } else if form.RepoOwnerID != 0 { + repoOwner, err = models.GetUserByID(form.RepoOwnerID) + } else { + repoOwner = ctx.User + } + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Error(http.StatusUnprocessableEntity, "", err) + } else { + ctx.Error(http.StatusInternalServerError, "GetUser", err) } - repoOwner = org + return } if ctx.HasError() { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index dacf0fda97535..5424f5b140efa 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13617,6 +13617,11 @@ "type": "string", "x-go-name": "RepoName" }, + "repo_owner": { + "description": "Name of User or Organisation who will own Repo after migration", + "type": "string", + "x-go-name": "RepoOwner" + }, "service": { "type": "string", "enum": [ @@ -13628,10 +13633,10 @@ "x-go-name": "Service" }, "uid": { - "description": "ID of repo owner, if not set current user is used", + "description": "deprecated (only for backwards compatibility)", "type": "integer", "format": "int64", - "x-go-name": "RepoOwner" + "x-go-name": "RepoOwnerID" }, "wiki": { "type": "boolean", From 0e706149237f6196c9fd6711ca8533dedf207339 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 10 Sep 2020 09:45:54 +0200 Subject: [PATCH 08/10] adopt repo.go -> migrate.go --- routers/repo/migrate.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/repo/migrate.go b/routers/repo/migrate.go index 497f2ce36f840..34060aabde5ab 100644 --- a/routers/repo/migrate.go +++ b/routers/repo/migrate.go @@ -74,7 +74,7 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam ctx.Data["Err_RepoName"] = true ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) default: - remoteAddr, _ := form.ParseRemoteAddr(owner) + remoteAddr, _ := auth.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword, owner) err = util.URLSanitizedError(err, remoteAddr) if strings.Contains(err.Error(), "Authentication failed") || strings.Contains(err.Error(), "Bad credentials") || @@ -108,7 +108,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { return } - remoteAddr, err := form.ParseRemoteAddr(ctx.User) + remoteAddr, err := auth.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword, ctx.User) if err != nil { if models.IsErrInvalidCloneAddr(err) { ctx.Data["Err_CloneAddr"] = true From 6e68fb1f87f6d9eee7e5e1d9598e4ca037386632 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 10 Sep 2020 22:53:05 +0200 Subject: [PATCH 09/10] add comment about each struct purpose --- modules/auth/repo_form.go | 1 + modules/migrations/base/options.go | 1 + modules/structs/repo.go | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index a25d647f8279e..3ad57085b0533 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -53,6 +53,7 @@ func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) bin } // MigrateRepoForm form for migrating repository +// this is used to interact with web ui type MigrateRepoForm struct { // required: true CloneAddr string `json:"clone_addr" binding:"Required"` diff --git a/modules/migrations/base/options.go b/modules/migrations/base/options.go index de201cedea82f..dbc40b138aadc 100644 --- a/modules/migrations/base/options.go +++ b/modules/migrations/base/options.go @@ -8,6 +8,7 @@ package base import "code.gitea.io/gitea/modules/structs" // MigrateOptions defines the way a repository gets migrated +// this is for internal usage by migrations module and func who interact with it type MigrateOptions struct { // required: true CloneAddr string `json:"clone_addr" binding:"Required"` diff --git a/modules/structs/repo.go b/modules/structs/repo.go index b74518024c311..c57702b2821b6 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -227,6 +227,7 @@ func (gt GitServiceType) Title() string { } // MigrateRepoOptions options for migrating repository's +// this is used to interact with api v1 type MigrateRepoOptions struct { // required: true CloneAddr string `json:"clone_addr" binding:"Required"` From 02ffa9073738ba9dcef91b600d8bad02743cb93d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 10 Sep 2020 23:20:53 +0200 Subject: [PATCH 10/10] lint --- templates/swagger/v1_json.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e884370f82c09..ac65b3ce17bb3 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13522,7 +13522,7 @@ "x-go-package": "code.gitea.io/gitea/modules/auth" }, "MigrateRepoForm": { - "description": "MigrateRepoForm form for migrating repository", + "description": "MigrateRepoForm form for migrating repository\nthis is used to interact with web ui", "type": "object", "required": [ "clone_addr", @@ -13600,7 +13600,7 @@ "x-go-package": "code.gitea.io/gitea/modules/auth" }, "MigrateRepoOptions": { - "description": "MigrateRepoOptions options for migrating repository's", + "description": "MigrateRepoOptions options for migrating repository's\nthis is used to interact with api v1", "type": "object", "required": [ "clone_addr",