diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 3974c4db3a..0e4081da8e 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -287,6 +287,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
@@ -312,6 +313,8 @@ func (gt GitServiceType) Title() string {
return "GitBucket"
case CodebaseService:
return "Codebase"
+ case ForgejoService:
+ return "Forgejo"
case PlainGitService:
return "Git"
}
@@ -353,7 +356,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
@@ -364,6 +367,7 @@ func (gt GitServiceType) TokenAuth() bool {
var SupportedFullGitService = []GitServiceType{
GithubService,
GitlabService,
+ ForgejoService,
GiteaService,
GogsService,
OneDevService,
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 6207b531bd..b5fc124d19 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1100,6 +1100,7 @@ migrate.migrating_failed_no_addr = Migration failed.
migrate.github.description = Migrate data from github.com or other GitHub instances.
migrate.git.description = Migrate a repository only from any Git service.
migrate.gitlab.description = Migrate data from gitlab.com or other GitLab instances.
+migrate.forgejo.description = Migrate data from codeberg.org or other Forgejo instances.
migrate.gitea.description = Migrate data from gitea.com or other Gitea instances.
migrate.gogs.description = Migrate data from notabug.org or other Gogs instances.
migrate.onedev.description = Migrate data from code.onedev.io or other OneDev instances.
diff --git a/public/assets/img/svg/gitea-forgejo.svg b/public/assets/img/svg/gitea-forgejo.svg
new file mode 100644
index 0000000000..ef617c00f3
--- /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 cdce60831c..fe35fd2dac 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 1ac03a3097..b464d8bb68 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 0000000000..25dbb6ec51
--- /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 0000000000..5bd37551cc
--- /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 0b83f3b4a3..ae164a7add 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 0000000000..3caadbee15
--- /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 f25329f66b..c4dd431594 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"
@@ -18,6 +19,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"
)
@@ -49,7 +51,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
@@ -69,33 +71,44 @@ 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 0000000000..e00e5963cf
--- /dev/null
+++ b/web_src/svg/gitea-forgejo.svg
@@ -0,0 +1,9 @@
+