diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 51e175fba..3e0b5496d 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -293,6 +293,7 @@ const ( OneDevService // 6 onedev service GitBucketService // 7 gitbucket service CodebaseService // 8 codebase service + ForgejoService // 9 forgejo service ) // Name represents the service type's name @@ -318,6 +319,8 @@ func (gt GitServiceType) Title() string { return "GitBucket" case CodebaseService: return "Codebase" + case ForgejoService: + return "Forgejo" case PlainGitService: return "Git" } @@ -359,7 +362,7 @@ type MigrateRepoOptions struct { // TokenAuth represents whether a service type supports token-based auth func (gt GitServiceType) TokenAuth() bool { switch gt { - case GithubService, GiteaService, GitlabService: + case GithubService, GiteaService, GitlabService, ForgejoService: return true } return false @@ -370,6 +373,7 @@ func (gt GitServiceType) TokenAuth() bool { var SupportedFullGitService = []GitServiceType{ GithubService, GitlabService, + ForgejoService, GiteaService, GogsService, OneDevService, diff --git a/public/assets/img/svg/gitea-forgejo.svg b/public/assets/img/svg/gitea-forgejo.svg new file mode 100644 index 000000000..ef617c00f --- /dev/null +++ b/public/assets/img/svg/gitea-forgejo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/services/convert/utils.go b/services/convert/utils.go index cdce60831..fe35fd2da 100644 --- a/services/convert/utils.go +++ b/services/convert/utils.go @@ -36,6 +36,8 @@ func ToGitServiceType(value string) structs.GitServiceType { return structs.OneDevService case "gitbucket": return structs.GitBucketService + case "forgejo": + return structs.ForgejoService default: return structs.PlainGitService } diff --git a/services/convert/utils_test.go b/services/convert/utils_test.go index 1ac03a309..b464d8bb6 100644 --- a/services/convert/utils_test.go +++ b/services/convert/utils_test.go @@ -28,6 +28,8 @@ func TestToGitServiceType(t *testing.T) { typ: "gitlab", enum: 4, }, { typ: "gogs", enum: 5, + }, { + typ: "forgejo", enum: 9, }, { typ: "trash", enum: 1, }} diff --git a/services/migrations/forgejo_downloader.go b/services/migrations/forgejo_downloader.go new file mode 100644 index 000000000..25dbb6ec5 --- /dev/null +++ b/services/migrations/forgejo_downloader.go @@ -0,0 +1,20 @@ +// Copyright 2023 The Forgejo Authors +// SPDX-License-Identifier: MIT + +package migrations + +import ( + "code.gitea.io/gitea/modules/structs" +) + +func init() { + RegisterDownloaderFactory(&ForgejoDownloaderFactory{}) +} + +type ForgejoDownloaderFactory struct { + GiteaDownloaderFactory +} + +func (f *ForgejoDownloaderFactory) GitServiceType() structs.GitServiceType { + return structs.ForgejoService +} diff --git a/services/migrations/forgejo_downloader_test.go b/services/migrations/forgejo_downloader_test.go new file mode 100644 index 000000000..5bd37551c --- /dev/null +++ b/services/migrations/forgejo_downloader_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Forgejo Authors +// SPDX-License-Identifier: MIT + +package migrations + +import ( + "testing" + + "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/require" +) + +func TestForgejoDownload(t *testing.T) { + require.NotNil(t, getFactoryFromServiceType(structs.ForgejoService)) +} diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go index 0b83f3b4a..ae164a7ad 100644 --- a/services/migrations/migrate.go +++ b/services/migrations/migrate.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" ) @@ -139,19 +140,25 @@ func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName str return uploader.repo, nil } +func getFactoryFromServiceType(serviceType structs.GitServiceType) base.DownloaderFactory { + for _, factory := range factories { + if factory.GitServiceType() == serviceType { + return factory + } + } + return nil +} + func newDownloader(ctx context.Context, ownerName string, opts base.MigrateOptions) (base.Downloader, error) { var ( downloader base.Downloader err error ) - for _, factory := range factories { - if factory.GitServiceType() == opts.GitServiceType { - downloader, err = factory.New(ctx, opts) - if err != nil { - return nil, err - } - break + if factory := getFactoryFromServiceType(opts.GitServiceType); factory != nil { + downloader, err = factory.New(ctx, opts) + if err != nil { + return nil, err } } diff --git a/templates/repo/migrate/forgejo.tmpl b/templates/repo/migrate/forgejo.tmpl new file mode 100644 index 000000000..3caadbee1 --- /dev/null +++ b/templates/repo/migrate/forgejo.tmpl @@ -0,0 +1 @@ +{{template "repo/migrate/gitea" .}} diff --git a/tests/integration/migrate_test.go b/tests/integration/migrate_test.go index 2f44de8a4..44fc890ab 100644 --- a/tests/integration/migrate_test.go +++ b/tests/integration/migrate_test.go @@ -4,6 +4,7 @@ package integration import ( + "context" "fmt" "net/http" "net/url" @@ -20,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/migrations" + "code.gitea.io/gitea/services/repository" "github.com/stretchr/testify/assert" ) @@ -51,7 +53,7 @@ func TestMigrateLocalPath(t *testing.T) { setting.ImportLocalPaths = old } -func TestMigrateGiteaForm(t *testing.T) { +func TestMigrate(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { AllowLocalNetworks := setting.Migrations.AllowLocalNetworks setting.Migrations.AllowLocalNetworks = true @@ -71,34 +73,45 @@ func TestMigrateGiteaForm(t *testing.T) { session := loginUser(t, ownerName) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeReadMisc) - // Step 0: verify the repo is available - req := NewRequestf(t, "GET", fmt.Sprintf("/%s/%s", ownerName, repoName)) - _ = session.MakeRequest(t, req, http.StatusOK) - // Step 1: get the Gitea migration form - req = NewRequestf(t, "GET", "/repo/migrate/?service_type=%d", structs.GiteaService) - resp := session.MakeRequest(t, req, http.StatusOK) - // Step 2: load the form - htmlDoc := NewHTMLParser(t, resp.Body) - link, exists := htmlDoc.doc.Find(`form.ui.form[action^="/repo/migrate"]`).Attr("action") - assert.True(t, exists, "The template has changed") - // Step 4: submit the migration to only migrate issues - migratedRepoName := "otherrepo" - req = NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": htmlDoc.GetCSRF(), - "service": fmt.Sprintf("%d", structs.GiteaService), - "clone_addr": fmt.Sprintf("%s%s/%s", u, ownerName, repoName), - "auth_token": token, - "issues": "on", - "repo_name": migratedRepoName, - "description": "", - "uid": fmt.Sprintf("%d", repoOwner.ID), - }) - resp = session.MakeRequest(t, req, http.StatusSeeOther) - // Step 5: a redirection displays the migrated repository - loc := resp.Header().Get("Location") - assert.EqualValues(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), loc) - // Step 6: check the repo was created - unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName}) + for _, s := range []struct { + svc structs.GitServiceType + }{ + {svc: structs.GiteaService}, + {svc: structs.ForgejoService}, + } { + // Step 0: verify the repo is available + req := NewRequestf(t, "GET", fmt.Sprintf("/%s/%s", ownerName, repoName)) + _ = session.MakeRequest(t, req, http.StatusOK) + // Step 1: get the Gitea migration form + req = NewRequestf(t, "GET", "/repo/migrate/?service_type=%d", s.svc) + resp := session.MakeRequest(t, req, http.StatusOK) + // Step 2: load the form + htmlDoc := NewHTMLParser(t, resp.Body) + link, exists := htmlDoc.doc.Find(`form.ui.form[action^="/repo/migrate"]`).Attr("action") + assert.True(t, exists, "The template has changed") + // Step 4: submit the migration to only migrate issues + migratedRepoName := "otherrepo" + req = NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": htmlDoc.GetCSRF(), + "service": fmt.Sprintf("%d", s.svc), + "clone_addr": fmt.Sprintf("%s%s/%s", u, ownerName, repoName), + "auth_token": token, + "issues": "on", + "repo_name": migratedRepoName, + "description": "", + "uid": fmt.Sprintf("%d", repoOwner.ID), + }) + resp = session.MakeRequest(t, req, http.StatusSeeOther) + // Step 5: a redirection displays the migrated repository + loc := resp.Header().Get("Location") + assert.EqualValues(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), loc) + // Step 6: check the repo was created + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName}) + + // Step 7: delete the repository, so we can test with other services + err := repository.DeleteRepository(context.Background(), repoOwner, repo, false) + assert.NoError(t, err) + } }) } diff --git a/web_src/svg/gitea-forgejo.svg b/web_src/svg/gitea-forgejo.svg new file mode 100644 index 000000000..e00e5963c --- /dev/null +++ b/web_src/svg/gitea-forgejo.svg @@ -0,0 +1,9 @@ +