1
1
mirror of https://github.com/go-gitea/gitea.git synced 2024-09-18 14:52:30 +02:00

Support migration from AWS CodeCommit (#31981)

This PR adds support for migrating repos from [AWS
CodeCommit](https://docs.aws.amazon.com/codecommit/latest/userguide/welcome.html).

The access key ID and secret access key are required to get repository
information and pull requests. And [HTTPS Git
credentials](https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-gc.html)
are required to clone the repository.

<img
src="https://github.com/user-attachments/assets/82ecb2d0-8d43-42b0-b5af-f5347a13b9d0"
width="680" />

The AWS CodeCommit icon is from [AWS Architecture
Icons](https://aws.amazon.com/architecture/icons/).

<img
src="https://github.com/user-attachments/assets/3c44d21f-d753-40f5-9eae-5d3589e0d50d"
width="320" />
This commit is contained in:
Zettat123 2024-09-11 07:49:42 +08:00 committed by GitHub
parent d9a7748cdc
commit def1c9670b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 513 additions and 9 deletions

File diff suppressed because one or more lines are too long

7
go.mod
View File

@ -23,6 +23,9 @@ require (
github.com/PuerkitoBio/goquery v1.9.2
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
github.com/alecthomas/chroma/v2 v2.14.0
github.com/aws/aws-sdk-go v1.43.21
github.com/aws/aws-sdk-go-v2/credentials v1.17.30
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.4.2
github.com/buildkite/terminal-to-html/v3 v3.12.1
@ -146,6 +149,10 @@ require (
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
github.com/aws/smithy-go v1.20.4 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect

18
go.sum
View File

@ -109,6 +109,20 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.43.21 h1:E4S2eX3d2gKJyI/ISrcIrSwXwqjIvCK85gtBMt4sAPE=
github.com/aws/aws-sdk-go v1.43.21/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
github.com/aws/aws-sdk-go-v2/credentials v1.17.30 h1:aau/oYFtibVovr2rDt8FHlU17BTicFEMAi29V1U+L5Q=
github.com/aws/aws-sdk-go-v2/credentials v1.17.30/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1 h1:mOOALIM4JzhYkq3voCBbmZqmyEVEhHsfasMTbVxLkNs=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1/go.mod h1:6zf5j3mIUXKM0s2iz5ttR2Qwq+o47D0jotpAyaKgZRA=
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -504,6 +518,9 @@ github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LF
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jhillyerd/enmime v1.2.0 h1:dIu1IPEymQgoT2dzuB//ttA/xcV40NMPpQtmd4wslHk=
github.com/jhillyerd/enmime v1.2.0/go.mod h1:FRFuUPCLh8PByQv+8xRcLO9QHqaqTqreYhopv5eyk4I=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@ -894,6 +911,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=

View File

@ -38,4 +38,7 @@ type MigrateOptions struct {
ReleaseAssets bool
MigrateToRepoID int64
MirrorInterval string `json:"mirror_interval"`
AWSAccessKeyID string
AWSSecretAccessKey string
}

View File

@ -291,15 +291,16 @@ type GitServiceType int
// enumerate all GitServiceType
const (
NotMigrated GitServiceType = iota // 0 not migrated from external sites
PlainGitService // 1 plain git service
GithubService // 2 github.com
GiteaService // 3 gitea service
GitlabService // 4 gitlab service
GogsService // 5 gogs service
OneDevService // 6 onedev service
GitBucketService // 7 gitbucket service
CodebaseService // 8 codebase service
NotMigrated GitServiceType = iota // 0 not migrated from external sites
PlainGitService // 1 plain git service
GithubService // 2 github.com
GiteaService // 3 gitea service
GitlabService // 4 gitlab service
GogsService // 5 gogs service
OneDevService // 6 onedev service
GitBucketService // 7 gitbucket service
CodebaseService // 8 codebase service
CodeCommitService // 9 codecommit service
)
// Name represents the service type's name
@ -325,6 +326,8 @@ func (gt GitServiceType) Title() string {
return "GitBucket"
case CodebaseService:
return "Codebase"
case CodeCommitService:
return "CodeCommit"
case PlainGitService:
return "Git"
}
@ -361,6 +364,9 @@ type MigrateRepoOptions struct {
PullRequests bool `json:"pull_requests"`
Releases bool `json:"releases"`
MirrorInterval string `json:"mirror_interval"`
AWSAccessKeyID string `json:"aws_access_key_id"`
AWSSecretAccessKey string `json:"aws_secret_access_key"`
}
// TokenAuth represents whether a service type supports token-based auth
@ -382,6 +388,7 @@ var SupportedFullGitService = []GitServiceType{
OneDevService,
GitBucketService,
CodebaseService,
CodeCommitService,
}
// RepoTransfer represents a pending repo transfer

View File

@ -1176,6 +1176,11 @@ 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.gitbucket.description = Migrate data from GitBucket instances.
migrate.codecommit.description = Migrate data from AWS CodeCommit.
migrate.codecommit.aws_access_key_id = AWS Access Key ID
migrate.codecommit.aws_secret_access_key = AWS Secret Access Key
migrate.codecommit.https_git_credentials_username = HTTPS Git Credentials Username
migrate.codecommit.https_git_credentials_password = HTTPS Git Credentials Password
migrate.migrating_git = Migrating Git Data
migrate.migrating_topics = Migrating Topics
migrate.migrating_milestones = Migrating Milestones

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -169,6 +169,10 @@ func Migrate(ctx *context.APIContext) {
opts.PullRequests = false
opts.Releases = false
}
if gitServiceType == api.CodeCommitService {
opts.AWSAccessKeyID = form.AWSAccessKeyID
opts.AWSSecretAccessKey = form.AWSSecretAccessKey
}
repo, err := repo_service.CreateRepositoryDirectly(ctx, ctx.Doer, repoOwner, repo_service.CreateRepoOptions{
Name: opts.RepoName,

View File

@ -231,6 +231,10 @@ func MigratePost(ctx *context.Context) {
opts.PullRequests = false
opts.Releases = false
}
if form.Service == structs.CodeCommitService {
opts.AWSAccessKeyID = form.AWSAccessKeyID
opts.AWSSecretAccessKey = form.AWSSecretAccessKey
}
err = repo_model.CheckCreateRepository(ctx, ctx.Doer, ctxUser, opts.RepoName, false)
if err != nil {

View File

@ -36,6 +36,8 @@ func ToGitServiceType(value string) structs.GitServiceType {
return structs.OneDevService
case "gitbucket":
return structs.GitBucketService
case "codecommit":
return structs.CodeCommitService
default:
return structs.PlainGitService
}

View File

@ -79,6 +79,9 @@ type MigrateRepoForm struct {
PullRequests bool `json:"pull_requests"`
Releases bool `json:"releases"`
MirrorInterval string `json:"mirror_interval"`
AWSAccessKeyID string `json:"aws_access_key_id"`
AWSSecretAccessKey string `json:"aws_secret_access_key"`
}
// Validate validates the fields

View File

@ -0,0 +1,269 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package migrations
import (
"context"
"fmt"
"net/url"
"strconv"
"strings"
git_module "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/structs"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/codecommit"
"github.com/aws/aws-sdk-go-v2/service/codecommit/types"
"github.com/aws/aws-sdk-go/aws"
)
var (
_ base.Downloader = &CodeCommitDownloader{}
_ base.DownloaderFactory = &CodeCommitDownloaderFactory{}
)
func init() {
RegisterDownloaderFactory(&CodeCommitDownloaderFactory{})
}
// CodeCommitDownloaderFactory defines a codecommit downloader factory
type CodeCommitDownloaderFactory struct{}
// New returns a Downloader related to this factory according MigrateOptions
func (c *CodeCommitDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
u, err := url.Parse(opts.CloneAddr)
if err != nil {
return nil, err
}
hostElems := strings.Split(u.Host, ".")
if len(hostElems) != 4 {
return nil, fmt.Errorf("cannot get the region from clone URL")
}
region := hostElems[1]
pathElems := strings.Split(u.Path, "/")
if len(pathElems) == 0 {
return nil, fmt.Errorf("cannot get the repo name from clone URL")
}
repoName := pathElems[len(pathElems)-1]
baseURL := u.Scheme + "://" + u.Host
return NewCodeCommitDownloader(ctx, repoName, baseURL, opts.AWSAccessKeyID, opts.AWSSecretAccessKey, region), nil
}
// GitServiceType returns the type of git service
func (c *CodeCommitDownloaderFactory) GitServiceType() structs.GitServiceType {
return structs.CodeCommitService
}
func NewCodeCommitDownloader(ctx context.Context, repoName, baseURL, accessKeyID, secretAccessKey, region string) *CodeCommitDownloader {
downloader := CodeCommitDownloader{
ctx: ctx,
repoName: repoName,
baseURL: baseURL,
codeCommitClient: codecommit.New(codecommit.Options{
Credentials: credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, ""),
Region: region,
}),
}
return &downloader
}
// CodeCommitDownloader implements a downloader for AWS CodeCommit
type CodeCommitDownloader struct {
base.NullDownloader
ctx context.Context
codeCommitClient *codecommit.Client
repoName string
baseURL string
allPullRequestIDs []string
}
// SetContext set context
func (c *CodeCommitDownloader) SetContext(ctx context.Context) {
c.ctx = ctx
}
// GetRepoInfo returns a repository information
func (c *CodeCommitDownloader) GetRepoInfo() (*base.Repository, error) {
output, err := c.codeCommitClient.GetRepository(c.ctx, &codecommit.GetRepositoryInput{
RepositoryName: aws.String(c.repoName),
})
if err != nil {
return nil, err
}
repoMeta := output.RepositoryMetadata
repo := &base.Repository{
Name: *repoMeta.RepositoryName,
Owner: *repoMeta.AccountId,
IsPrivate: true, // CodeCommit repos are always private
CloneURL: *repoMeta.CloneUrlHttp,
}
if repoMeta.DefaultBranch != nil {
repo.DefaultBranch = *repoMeta.DefaultBranch
}
if repoMeta.RepositoryDescription != nil {
repo.DefaultBranch = *repoMeta.RepositoryDescription
}
return repo, nil
}
// GetComments returns comments of an issue or PR
func (c *CodeCommitDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
var (
nextToken *string
comments []*base.Comment
)
for {
resp, err := c.codeCommitClient.GetCommentsForPullRequest(c.ctx, &codecommit.GetCommentsForPullRequestInput{
NextToken: nextToken,
PullRequestId: aws.String(strconv.FormatInt(commentable.GetForeignIndex(), 10)),
})
if err != nil {
return nil, false, err
}
for _, prComment := range resp.CommentsForPullRequestData {
for _, ccComment := range prComment.Comments {
comment := &base.Comment{
IssueIndex: commentable.GetForeignIndex(),
PosterName: c.getUsernameFromARN(*ccComment.AuthorArn),
Content: *ccComment.Content,
Created: *ccComment.CreationDate,
Updated: *ccComment.LastModifiedDate,
}
comments = append(comments, comment)
}
}
nextToken = resp.NextToken
if nextToken == nil {
break
}
}
return comments, true, nil
}
// GetPullRequests returns pull requests according page and perPage
func (c *CodeCommitDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
allPullRequestIDs, err := c.getAllPullRequestIDs()
if err != nil {
return nil, false, err
}
startIndex := (page - 1) * perPage
endIndex := page * perPage
if endIndex > len(allPullRequestIDs) {
endIndex = len(allPullRequestIDs)
}
batch := allPullRequestIDs[startIndex:endIndex]
prs := make([]*base.PullRequest, 0, len(batch))
for _, id := range batch {
output, err := c.codeCommitClient.GetPullRequest(c.ctx, &codecommit.GetPullRequestInput{
PullRequestId: aws.String(id),
})
if err != nil {
return nil, false, err
}
orig := output.PullRequest
number, err := strconv.ParseInt(*orig.PullRequestId, 10, 64)
if err != nil {
log.Error("CodeCommit pull request id is not a number: %s", *orig.PullRequestId)
continue
}
if len(orig.PullRequestTargets) == 0 {
log.Error("CodeCommit pull request does not contain targets", *orig.PullRequestId)
continue
}
target := orig.PullRequestTargets[0]
pr := &base.PullRequest{
Number: number,
Title: *orig.Title,
PosterName: c.getUsernameFromARN(*orig.AuthorArn),
Content: *orig.Description,
State: "open",
Created: *orig.CreationDate,
Updated: *orig.LastActivityDate,
Merged: target.MergeMetadata.IsMerged,
Head: base.PullRequestBranch{
Ref: strings.TrimPrefix(*target.SourceReference, git_module.BranchPrefix),
SHA: *target.SourceCommit,
RepoName: c.repoName,
},
Base: base.PullRequestBranch{
Ref: strings.TrimPrefix(*target.DestinationReference, git_module.BranchPrefix),
SHA: *target.DestinationCommit,
RepoName: c.repoName,
},
ForeignIndex: number,
}
if orig.PullRequestStatus == types.PullRequestStatusEnumClosed {
pr.State = "closed"
pr.Closed = orig.LastActivityDate
}
_ = CheckAndEnsureSafePR(pr, c.baseURL, c)
prs = append(prs, pr)
}
return prs, len(prs) < perPage, nil
}
// FormatCloneURL add authentication into remote URLs
func (c *CodeCommitDownloader) FormatCloneURL(opts MigrateOptions, remoteAddr string) (string, error) {
u, err := url.Parse(remoteAddr)
if err != nil {
return "", err
}
u.User = url.UserPassword(opts.AuthUsername, opts.AuthPassword)
return u.String(), nil
}
func (c *CodeCommitDownloader) getAllPullRequestIDs() ([]string, error) {
if len(c.allPullRequestIDs) > 0 {
return c.allPullRequestIDs, nil
}
var (
nextToken *string
prIDs []string
)
for {
output, err := c.codeCommitClient.ListPullRequests(c.ctx, &codecommit.ListPullRequestsInput{
RepositoryName: aws.String(c.repoName),
NextToken: nextToken,
})
if err != nil {
return nil, err
}
prIDs = append(prIDs, output.PullRequestIds...)
nextToken = output.NextToken
if nextToken == nil {
break
}
}
c.allPullRequestIDs = prIDs
return c.allPullRequestIDs, nil
}
func (c *CodeCommitDownloader) getUsernameFromARN(arn string) string {
parts := strings.Split(arn, "/")
if len(parts) > 0 {
return parts[len(parts)-1]
}
return ""
}

View File

@ -0,0 +1,117 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" 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">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{ctx.Locale.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">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
<span class="help">
{{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
</span>
</div>
<div class="inline required field {{if .Err_Auth}}error{{end}}">
<label for="aws_access_key_id">{{ctx.Locale.Tr "repo.migrate.codecommit.aws_access_key_id"}}</label>
<input id="aws_access_key_id" name="aws_access_key_id" value="{{.aws_access_key_id}}" required>
</div>
<div class="inline required field {{if .Err_Auth}}error{{end}}">
<label for="aws_secret_access_key">{{ctx.Locale.Tr "repo.migrate.codecommit.aws_secret_access_key"}}</label>
<input id="aws_secret_access_key" name="aws_secret_access_key" type="password" value="{{.aws_secret_access_key}}" required>
</div>
<div class="inline required field {{if .Err_Auth}}error{{end}}">
<label for="auth_username">{{ctx.Locale.Tr "repo.migrate.codecommit.https_git_credentials_username"}}</label>
<input id="auth_username" name="auth_username" value="{{.auth_username}}" required>
</div>
<div class="inline required field {{if .Err_Auth}}error{{end}}">
<label for="auth_password">{{ctx.Locale.Tr "repo.migrate.codecommit.https_git_credentials_password"}}</label>
<input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}" required>
</div>
{{if not .DisableNewPullMirrors}}
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.migrate_options"}}</label>
<div class="ui checkbox">
<input id="mirror" name="mirror" type="checkbox" {{if .mirror}} checked{{end}}>
<label>{{ctx.Locale.Tr "repo.migrate_options_mirror_helper"}}</label>
</div>
</div>
{{end}}
<div id="migrate_items">
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
<div class="ui checkbox">
<input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
</div>
</div>
</div>
<div class="divider"></div>
<div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{ctx.Locale.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}}">
{{ctx.AvatarUtils.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}}">
{{ctx.AvatarUtils.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}}">
{{ctx.AvatarUtils.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">{{ctx.Locale.Tr "repo.repo_name"}}</label>
<input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
</div>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
<div class="ui checkbox">
{{if .IsForcedPrivate}}
<input name="private" type="checkbox" checked disabled>
<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
{{else}}
<input name="private" type="checkbox" {{if .private}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
{{end}}
</div>
</div>
<div class="inline field {{if .Err_Description}}error{{end}}">
<label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
<textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
</div>
<div class="inline field">
<label></label>
<button class="ui primary button">
{{ctx.Locale.Tr "repo.migrate_repo"}}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
{{template "base/footer" .}}

View File

@ -22615,6 +22615,14 @@
"type": "string",
"x-go-name": "AuthUsername"
},
"aws_access_key_id": {
"type": "string",
"x-go-name": "AWSAccessKeyID"
},
"aws_secret_access_key": {
"type": "string",
"x-go-name": "AWSSecretAccessKey"
},
"clone_addr": {
"type": "string",
"x-go-name": "CloneAddr"

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.1 KiB