Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add migrate from Codebase #16768

Merged
merged 11 commits into from
Dec 2, 2021
655 changes: 655 additions & 0 deletions modules/migrations/codebase.go

Large diffs are not rendered by default.

154 changes: 154 additions & 0 deletions modules/migrations/codebase_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"context"
"fmt"
"net/url"
"os"
"testing"
"time"

"code.gitea.io/gitea/modules/migrations/base"

"github.com/stretchr/testify/assert"
)

func TestCodebaseDownloadRepo(t *testing.T) {
// Skip tests if Codebase token is not found
cloneUser := os.Getenv("CODEBASE_CLONE_USER")
clonePassword := os.Getenv("CODEBASE_CLONE_PASSWORD")
apiUser := os.Getenv("CODEBASE_API_USER")
apiPassword := os.Getenv("CODEBASE_API_TOKEN")
if apiUser == "" || apiPassword == "" {
t.Skip("skipped test because a CODEBASE_ variable was not in the environment")
}

cloneAddr := "https://gitea-test.codebasehq.com/gitea-test/test.git"
u, _ := url.Parse(cloneAddr)
if cloneUser != "" {
u.User = url.UserPassword(cloneUser, clonePassword)
}

factory := &CodebaseDownloaderFactory{}
downloader, err := factory.New(context.Background(), base.MigrateOptions{
CloneAddr: u.String(),
AuthUsername: apiUser,
AuthPassword: apiPassword,
})
if err != nil {
t.Fatal(fmt.Sprintf("Error creating Codebase downloader: %v", err))
}
repo, err := downloader.GetRepoInfo()
assert.NoError(t, err)
assertRepositoryEqual(t, &base.Repository{
Name: "test",
Owner: "",
Description: "Repository Description",
CloneURL: "git@codebasehq.com:gitea-test/gitea-test/test.git",
OriginalURL: cloneAddr,
}, repo)

milestones, err := downloader.GetMilestones()
assert.NoError(t, err)
assertMilestonesEqual(t, []*base.Milestone{
{
Title: "Milestone1",
Deadline: timePtr(time.Date(2021, time.September, 16, 0, 0, 0, 0, time.UTC)),
},
{
Title: "Milestone2",
Deadline: timePtr(time.Date(2021, time.September, 17, 0, 0, 0, 0, time.UTC)),
Closed: timePtr(time.Date(2021, time.September, 17, 0, 0, 0, 0, time.UTC)),
State: "closed",
},
}, milestones)

labels, err := downloader.GetLabels()
assert.NoError(t, err)
assert.Len(t, labels, 4)

issues, isEnd, err := downloader.GetIssues(1, 2)
assert.NoError(t, err)
assert.True(t, isEnd)
assertIssuesEqual(t, []*base.Issue{
{
Number: 2,
Title: "Open Ticket",
Content: "Open Ticket Message",
PosterName: "gitea-test-43",
PosterEmail: "gitea-codebase@smack.email",
State: "open",
Created: time.Date(2021, time.September, 26, 19, 19, 14, 0, time.UTC),
Updated: time.Date(2021, time.September, 26, 19, 19, 34, 0, time.UTC),
Labels: []*base.Label{
{
Name: "Feature",
},
},
},
{
Number: 1,
Title: "Closed Ticket",
Content: "Closed Ticket Message",
PosterName: "gitea-test-43",
PosterEmail: "gitea-codebase@smack.email",
State: "closed",
Milestone: "Milestone1",
Created: time.Date(2021, time.September, 26, 19, 18, 33, 0, time.UTC),
Updated: time.Date(2021, time.September, 26, 19, 18, 55, 0, time.UTC),
Labels: []*base.Label{
{
Name: "Bug",
},
},
},
}, issues)

comments, _, err := downloader.GetComments(base.GetCommentOptions{
Context: issues[0].Context,
})
assert.NoError(t, err)
assertCommentsEqual(t, []*base.Comment{
{
IssueIndex: 2,
PosterName: "gitea-test-43",
PosterEmail: "gitea-codebase@smack.email",
Created: time.Date(2021, time.September, 26, 19, 19, 34, 0, time.UTC),
Updated: time.Date(2021, time.September, 26, 19, 19, 34, 0, time.UTC),
Content: "open comment",
},
}, comments)

prs, _, err := downloader.GetPullRequests(1, 1)
assert.NoError(t, err)
assertPullRequestsEqual(t, []*base.PullRequest{
{
Number: 3,
Title: "Readme Change",
Content: "Merge Request comment",
PosterName: "gitea-test-43",
PosterEmail: "gitea-codebase@smack.email",
State: "open",
Created: time.Date(2021, time.September, 26, 20, 25, 47, 0, time.UTC),
Updated: time.Date(2021, time.September, 26, 20, 25, 47, 0, time.UTC),
Head: base.PullRequestBranch{
Ref: "readme-mr",
SHA: "1287f206b888d4d13540e0a8e1c07458f5420059",
RepoName: "test",
},
Base: base.PullRequestBranch{
Ref: "master",
SHA: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
RepoName: "test",
},
},
}, prs)

rvs, err := downloader.GetReviews(prs[0].Context)
assert.NoError(t, err)
assert.Empty(t, rvs)
}
1 change: 1 addition & 0 deletions modules/migrations/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func assertTimePtrEqual(t *testing.T, expected, actual *time.Time) {
if expected == nil {
assert.Nil(t, actual)
} else {
assert.NotNil(t, actual)
assertTimeEqual(t, *expected, *actual)
}
}
Expand Down
4 changes: 4 additions & 0 deletions modules/structs/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ const (
GitlabService // 4 gitlab service
GogsService // 5 gogs service
OneDevService // 6 onedev service
CodebaseService // 7 codebase service
)

// Name represents the service type's name
Expand All @@ -270,6 +271,8 @@ func (gt GitServiceType) Title() string {
return "Gogs"
case OneDevService:
return "OneDev"
case CodebaseService:
return "Codebase"
case PlainGitService:
return "Git"
}
Expand Down Expand Up @@ -326,5 +329,6 @@ var (
GiteaService,
GogsService,
OneDevService,
CodebaseService,
}
)
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,7 @@ migrate.gitlab.description = Migrate data from gitlab.com or other GitLab instan
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.
migrate.codebase.description = Migrate data from codebasehq.com.
migrate.migrating_git = Migrating Git Data
migrate.migrating_topics = Migrating Topics
migrate.migrating_milestones = Migrating Milestones
Expand Down
1 change: 1 addition & 0 deletions public/img/svg/gitea-codebase.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
117 changes: 117 additions & 0 deletions templates/repo/migrate/codebase.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{{template "base/head" .}}
<div class="page-content repository new migrate">
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{.i18n.Tr "repo.migrate.migrate" .service.Title}}
<input id="service_type" type="hidden" name="service" value="{{.service}}">
</h3>
<div class="ui attached segment">
{{template "base/alert" .}}
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
<label for="clone_addr">{{.i18n.Tr "repo.migrate.clone_address"}}</label>
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
<span class="help">
{{.i18n.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{.i18n.Tr "repo.migrate.clone_local_path"}}{{end}}
</span>
</div>

<div class="inline field {{if .Err_Auth}}error{{end}}">
<label for="auth_username">{{.i18n.Tr "username"}}</label>
<input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
</div>
<input class="fake" type="password">
<div class="inline field {{if .Err_Auth}}error{{end}}">
<label for="auth_password">{{.i18n.Tr "password"}}</label>
<input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
</div>

{{template "repo/migrate/options" .}}

<div id="migrate_items">
<div class="inline field">
<label>{{.i18n.Tr "repo.migrate_items"}}</label>
<div class="ui checkbox">
<input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
<label>{{.i18n.Tr "repo.migrate_items_milestones" | Safe}}</label>
</div>
<div class="ui checkbox">
<input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
<label>{{.i18n.Tr "repo.migrate_items_labels" | Safe}}</label>
</div>
</div>
<div class="inline field">
<label></label>
<div class="ui checkbox">
<input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
<label>{{.i18n.Tr "repo.migrate_items_issues" | Safe}}</label>
</div>
<div class="ui checkbox">
<input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
<label>{{.i18n.Tr "repo.migrate_items_merge_requests" | Safe}}</label>
</div>
</div>
</div>

<div class="ui divider"></div>

<div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{.i18n.Tr "repo.owner"}}</label>
<div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
{{avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
</div>
{{range .Orgs}}
<div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
{{avatar . 28 "mini"}}
<span class="truncated-item-name">{{.ShortName 40}}</span>
</div>
{{end}}
</div>
</div>
</div>

<div class="inline required field {{if .Err_RepoName}}error{{end}}">
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
<input id="repo_name" name="repo_name" value="{{.repo_name}}" required>
</div>
<div class="inline field">
<label>{{.i18n.Tr "repo.visibility"}}</label>
<div class="ui checkbox">
{{if .IsForcedPrivate}}
<input name="private" type="checkbox" checked readonly>
<label>{{.i18n.Tr "repo.visibility_helper_forced" | Safe}}</label>
{{else}}
<input name="private" type="checkbox" {{if .private}}checked{{end}}>
<label>{{.i18n.Tr "repo.visibility_helper" | Safe}}</label>
{{end}}
</div>
</div>
<div class="inline field {{if .Err_Description}}error{{end}}">
<label for="description">{{.i18n.Tr "repo.repo_desc"}}</label>
<textarea id="description" name="description">{{.description}}</textarea>
</div>

<div class="inline field">
<label></label>
<button class="ui green button">
{{.i18n.Tr "repo.migrate_repo"}}
</button>
<a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a>
</div>
</div>
</form>
</div>
</div>
</div>
{{template "base/footer" .}}
Loading