diff --git a/models/action.go b/models/action.go index ca6bfaf66634d..e2a67a19a928a 100644 --- a/models/action.go +++ b/models/action.go @@ -30,26 +30,29 @@ type ActionType int // Possible action types. const ( - ActionCreateRepo ActionType = iota + 1 // 1 - ActionRenameRepo // 2 - ActionStarRepo // 3 - ActionWatchRepo // 4 - ActionCommitRepo // 5 - ActionCreateIssue // 6 - ActionCreatePullRequest // 7 - ActionTransferRepo // 8 - ActionPushTag // 9 - ActionCommentIssue // 10 - ActionMergePullRequest // 11 - ActionCloseIssue // 12 - ActionReopenIssue // 13 - ActionClosePullRequest // 14 - ActionReopenPullRequest // 15 - ActionDeleteTag // 16 - ActionDeleteBranch // 17 - ActionMirrorSyncPush // 18 - ActionMirrorSyncCreate // 19 - ActionMirrorSyncDelete // 20 + ActionCreateRepo ActionType = iota + 1 // 1 + ActionRenameRepo // 2 + ActionStarRepo // 3 + ActionWatchRepo // 4 + ActionCommitRepo // 5 + ActionCreateIssue // 6 + ActionCreatePullRequest // 7 + ActionTransferRepo // 8 + ActionPushTag // 9 + ActionCommentIssue // 10 + ActionMergePullRequest // 11 + ActionCloseIssue // 12 + ActionReopenIssue // 13 + ActionClosePullRequest // 14 + ActionReopenPullRequest // 15 + ActionDeleteTag // 16 + ActionDeleteBranch // 17 + ActionMirrorSyncPush // 18 + ActionMirrorSyncCreate // 19 + ActionMirrorSyncDelete // 20 + ActionMigrationStarted // 21 + ActionMigrationSuccessful // 22 + ActionMigrationFailure // 23 ) var ( diff --git a/models/release_test.go b/models/release_test.go index de34109e8c88b..45e4741b75420 100644 --- a/models/release_test.go +++ b/models/release_test.go @@ -108,9 +108,25 @@ func TestRelease_MirrorDelete(t *testing.T) { IsMirror: true, RemoteAddr: repoPath, } - mirror, err := MigrateRepository(user, user, migrationOptions) + + out := make(chan bool) + defer close(out) + + mirror, err := MigrateRepository(user, user, migrationOptions, func(err error) string { + if err != nil { + out <- false + return err.Error() + } + out <- true + return repoPath + }) + assert.NoError(t, err) + // Now we have to wait til the migration is reported. + success := <-out + assert.True(t, success) + gitRepo, err := git.OpenRepository(repoPath) assert.NoError(t, err) diff --git a/models/repo.go b/models/repo.go index 848a76fe883b4..d9cdf8e2443cd 100644 --- a/models/repo.go +++ b/models/repo.go @@ -36,7 +36,6 @@ import ( "github.com/go-xorm/builder" "github.com/go-xorm/xorm" version "github.com/mcuadros/go-version" - ini "gopkg.in/ini.v1" ) var repoWorkingPool = sync.NewExclusivePool() @@ -868,162 +867,6 @@ func (repo *Repository) CloneLink() (cl *CloneLink) { return repo.cloneLink(x, false) } -// MigrateRepoOptions contains the repository migrate options -type MigrateRepoOptions struct { - Name string - Description string - IsPrivate bool - IsMirror bool - RemoteAddr string -} - -/* - GitHub, GitLab, Gogs: *.wiki.git - BitBucket: *.git/wiki -*/ -var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"} - -// wikiRemoteURL returns accessible repository URL for wiki if exists. -// Otherwise, it returns an empty string. -func wikiRemoteURL(remote string) string { - remote = strings.TrimSuffix(remote, ".git") - for _, suffix := range commonWikiURLSuffixes { - wikiURL := remote + suffix - if git.IsRepoURLAccessible(wikiURL) { - return wikiURL - } - } - return "" -} - -// MigrateRepository migrates a existing repository from other project hosting. -func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, error) { - repo, err := CreateRepository(doer, u, CreateRepoOptions{ - Name: opts.Name, - Description: opts.Description, - IsPrivate: opts.IsPrivate, - IsMirror: opts.IsMirror, - }) - if err != nil { - return nil, err - } - - repoPath := RepoPath(u.Name, opts.Name) - wikiPath := WikiPath(u.Name, opts.Name) - - if u.IsOrganization() { - t, err := u.GetOwnerTeam() - if err != nil { - return nil, err - } - repo.NumWatches = t.NumMembers - } else { - repo.NumWatches = 1 - } - - migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second - - if err := os.RemoveAll(repoPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %v", repoPath, err) - } - - if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{ - Mirror: true, - Quiet: true, - Timeout: migrateTimeout, - }); err != nil { - return repo, fmt.Errorf("Clone: %v", err) - } - - wikiRemotePath := wikiRemoteURL(opts.RemoteAddr) - if len(wikiRemotePath) > 0 { - if err := os.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err) - } - - if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{ - Mirror: true, - Quiet: true, - Timeout: migrateTimeout, - Branch: "master", - }); err != nil { - log.Warn("Clone wiki: %v", err) - if err := os.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err) - } - } - } - - // Check if repository is empty. - _, stderr, err := com.ExecCmdDir(repoPath, "git", "log", "-1") - if err != nil { - if strings.Contains(stderr, "fatal: bad default revision 'HEAD'") { - repo.IsEmpty = true - } else { - return repo, fmt.Errorf("check empty: %v - %s", err, stderr) - } - } - - if !repo.IsEmpty { - // Try to get HEAD branch and set it as default branch. - gitRepo, err := git.OpenRepository(repoPath) - if err != nil { - return repo, fmt.Errorf("OpenRepository: %v", err) - } - headBranch, err := gitRepo.GetHEADBranch() - if err != nil { - return repo, fmt.Errorf("GetHEADBranch: %v", err) - } - if headBranch != nil { - repo.DefaultBranch = headBranch.Name - } - - if err = SyncReleasesWithTags(repo, gitRepo); err != nil { - log.Error(4, "Failed to synchronize tags to releases for repository: %v", err) - } - } - - if err = repo.UpdateSize(); err != nil { - log.Error(4, "Failed to update size for repository: %v", err) - } - - if opts.IsMirror { - if _, err = x.InsertOne(&Mirror{ - RepoID: repo.ID, - Interval: setting.Mirror.DefaultInterval, - EnablePrune: true, - NextUpdateUnix: util.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), - }); err != nil { - return repo, fmt.Errorf("InsertOne: %v", err) - } - - repo.IsMirror = true - err = UpdateRepository(repo, false) - } else { - repo, err = CleanUpMigrateInfo(repo) - } - - if err != nil && !repo.IsEmpty { - UpdateRepoIndexer(repo) - } - - return repo, err -} - -// cleanUpMigrateGitConfig removes mirror info which prevents "push --all". -// This also removes possible user credentials. -func cleanUpMigrateGitConfig(configPath string) error { - cfg, err := ini.Load(configPath) - if err != nil { - return fmt.Errorf("open config file: %v", err) - } - cfg.DeleteSection("remote \"origin\"") - if err = cfg.SaveToIndent(configPath, "\t"); err != nil { - return fmt.Errorf("save config file: %v", err) - } - return nil -} - // createDelegateHooks creates all the hooks scripts for the repo func createDelegateHooks(repoPath string) (err error) { var ( @@ -1063,30 +906,6 @@ func createDelegateHooks(repoPath string) (err error) { return nil } -// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. -func CleanUpMigrateInfo(repo *Repository) (*Repository, error) { - repoPath := repo.RepoPath() - if err := createDelegateHooks(repoPath); err != nil { - return repo, fmt.Errorf("createDelegateHooks: %v", err) - } - if repo.HasWiki() { - if err := createDelegateHooks(repo.WikiPath()); err != nil { - return repo, fmt.Errorf("createDelegateHooks.(wiki): %v", err) - } - } - - if err := cleanUpMigrateGitConfig(repo.GitConfigPath()); err != nil { - return repo, fmt.Errorf("cleanUpMigrateGitConfig: %v", err) - } - if repo.HasWiki() { - if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil { - return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err) - } - } - - return repo, UpdateRepository(repo, false) -} - // initRepoCommit temporarily changes with work directory. func initRepoCommit(tmpPath string, sig *git.Signature) (err error) { var stderr string @@ -1121,6 +940,7 @@ type CreateRepoOptions struct { IsPrivate bool IsMirror bool AutoInit bool + NoWatchers bool } func getRepoInitFile(tp, name string) ([]byte, error) { @@ -1274,7 +1094,7 @@ func IsUsableRepoName(name string) error { return isUsableName(reservedRepoNames, reservedRepoPatterns, name) } -func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err error) { +func createRepository(e *xorm.Session, doer, u *User, repo *Repository, noWatchers bool) (err error) { if err = IsUsableRepoName(repo.Name); err != nil { return err } @@ -1360,10 +1180,12 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err return fmt.Errorf("watchRepo: %v", err) } } - if err = newRepoAction(e, doer, repo); err != nil { - return fmt.Errorf("newRepoAction: %v", err) - } + if !noWatchers { + if err = newRepoAction(e, doer, repo); err != nil { + return fmt.Errorf("newRepoAction: %v", err) + } + } return nil } @@ -1390,7 +1212,7 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err return nil, err } - if err = createRepository(sess, doer, u, repo); err != nil { + if err = createRepository(sess, doer, u, repo, opts.NoWatchers); err != nil { return nil, err } @@ -2422,7 +2244,7 @@ func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *R return nil, err } - if err = createRepository(sess, doer, u, repo); err != nil { + if err = createRepository(sess, doer, u, repo, true); err != nil { return nil, err } diff --git a/models/repo_migrate.go b/models/repo_migrate.go new file mode 100644 index 0000000000000..f57ef85302147 --- /dev/null +++ b/models/repo_migrate.go @@ -0,0 +1,367 @@ +// Copyright 2019 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 models + +import ( + "fmt" + "os" + "path" + "strings" + "time" + + "code.gitea.io/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + "github.com/Unknwon/com" + ini "gopkg.in/ini.v1" +) + +// MigrateRepoOptions contains the repository migrate options +type MigrateRepoOptions struct { + Name string + Description string + IsPrivate bool + IsMirror bool + RemoteAddr string +} + +/* + GitHub, GitLab, Gogs: *.wiki.git + BitBucket: *.git/wiki +*/ +var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"} + +// wikiRemoteURL returns accessible repository URL for wiki if exists. +// Otherwise, it returns an empty string. +func wikiRemoteURL(remote string) string { + remote = strings.TrimSuffix(remote, ".git") + for _, suffix := range commonWikiURLSuffixes { + wikiURL := remote + suffix + if git.IsRepoURLAccessible(wikiURL) { + return wikiURL + } + } + return "" +} + +func sanitizeRepoPath(path string) string { + return strings.TrimPrefix(path, setting.RepoRootPath) +} + +func doMigration(doer, u *User, repo *Repository, opts MigrateRepoOptions, callback func(error) string) { + repoPath := RepoPath(u.Name, opts.Name) + wikiPath := WikiPath(u.Name, opts.Name) + timeNow := time.Now().Unix() + repoPathTmp := fmt.Sprintf("%s.migration-%d", repoPath, timeNow) + wikiPathTmp := fmt.Sprintf("%s.migration-%d", wikiPath, timeNow) + repoPathToRemove := fmt.Sprintf("%s.toremove-%d", wikiPath, timeNow) + wikiPathToRemove := fmt.Sprintf("%s.toremove-%d", wikiPath, timeNow) + + repoWorkingPool.CheckIn(com.ToStr(repo.ID)) + defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) + + failedMigration := func(err error) { + RemoveAllWithNotice("Unable to remove temporary wiki path in clean-up for migration", wikiPathTmp) + + RemoveAllWithNotice("Unable to remove temporary repo path in clean-up for migration", repoPathTmp) + + var checkRepo Repository + has, err := x.ID(repo.ID).Get(&checkRepo) + if err != nil { + log.Error(3, "Migration Failed: Repository is missing, can't notify: %v", err) + callback(fmt.Errorf("Migration Failed: Target repository is missing, can't notify: %v", err)) + return + } + if !has { + log.Warn("Migration Failed: Target repository is missing, can't notify.") + callback(fmt.Errorf("Migration Failed: Target repository is missing, can't notify")) + return + } + + repo.IsEmpty = true + if _, err := x.ID(repo.ID).AllCols().Update(repo); err != nil { + log.Error(3, "Couldn't set repo to bare:", err) + } + + NotifyWatchers(&Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: ActionMigrationFailure, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + Content: callback(err), + }) + } + + defer func() { + if err := recover(); err != nil { + log.Error(3, "PANIC: Migration failed with panic: %v", err) + + // fail the migration + failedMigration(fmt.Errorf("Migration failed: %v", err)) + } + }() + + NotifyWatchers(&Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: ActionMigrationStarted, + RepoID: repo.ID, + Repo: repo, + Content: util.SanitizeURLCredentials(opts.RemoteAddr, true), + IsPrivate: repo.IsPrivate, + }) + repo.IsEmpty = true + repo.IsArchived = true + if _, err := x.ID(repo.ID).AllCols().Update(repo); err != nil { + failedMigration(err) + return + } + + if u.IsOrganization() { + t, err := u.GetOwnerTeam() + if err != nil { + failedMigration(err) + return + } + repo.NumWatches = t.NumMembers + } else { + repo.NumWatches = 1 + } + + migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second + + if err := git.Clone(opts.RemoteAddr, repoPathTmp, git.CloneRepoOptions{ + Mirror: true, + Quiet: true, + Timeout: migrateTimeout, + }); err != nil { + failedMigration(fmt.Errorf("Clone: %v", err)) + return + + } + + wikiRemotePath := wikiRemoteURL(opts.RemoteAddr) + wikiAvailable := false + if len(wikiRemotePath) > 0 { + wikiAvailable = true + if err := git.Clone(wikiRemotePath, wikiPathTmp, git.CloneRepoOptions{ + Mirror: true, + Quiet: true, + Timeout: migrateTimeout, + Branch: "master", + }); err != nil { + wikiAvailable = false + log.Warn("Clone wiki: %v", err) + if err := os.RemoveAll(wikiPathTmp); err != nil { + log.Error(2, "Migration Failed: unable remove migrated empty wiki repo %s: %v", wikiPathTmp, err) + failedMigration(fmt.Errorf("Failed to remove migrated empty wiki repo %s", sanitizeRepoPath(wikiPathTmp))) + return + } + } + } + + // Check if repository should be empty. + repo.IsEmpty = false + _, stderr, err := com.ExecCmdDir(repoPathTmp, "git", "log", "-1") + if err != nil { + if strings.Contains(stderr, "fatal: bad default revision 'HEAD'") { + repo.IsEmpty = true + } else { + failedMigration(fmt.Errorf("check empty: %v - %s", err, stderr)) + return + } + } + + // OK now we're ready to actually begin + // We need to check if the repo still exists + refreshedRepo := Repository{ID: repo.ID} + has, err := x.Get(&refreshedRepo) + if err != nil { + failedMigration(err) + } + if !has { + failedMigration(fmt.Errorf("Clone completed but repository missing")) + } + + if err := os.Rename(repoPath, repoPathToRemove); err != nil && !os.IsNotExist(err) { + log.Error(2, "Migration Failed: unable rename temporary migrated repo %s to %s: %v", repoPath, repoPathToRemove, err) + failedMigration(fmt.Errorf("Failed to rename temporary migrated repo %s to %s", sanitizeRepoPath(repoPath), sanitizeRepoPath(repoPathToRemove))) + return + } + + if err := os.Rename(repoPathTmp, repoPath); err != nil { + log.Error(2, "Migration Failed: unable rename temporary migrated repo %s to %s: %v", repoPathTmp, repoPath, err) + failedMigration(fmt.Errorf("Failed to rename temporary migrated repo %s to %s", sanitizeRepoPath(repoPathTmp), sanitizeRepoPath(repoPath))) + return + } + + RemoveAllWithNotice("Unable to remove placeholder repository", repoPathToRemove) + + if wikiAvailable { + if err := os.Rename(wikiPath, wikiPathToRemove); err != nil && !os.IsNotExist(err) { + log.Error(2, "Migration Failed: unable rename temporary migrated repo %s to %s: %v", wikiPath, wikiPathToRemove, err) + failedMigration(fmt.Errorf("Failed to rename temporary migrated repo %s to %s", sanitizeRepoPath(wikiPath), sanitizeRepoPath(wikiPathToRemove))) + return + } + + if err := os.Rename(wikiPathTmp, wikiPath); err != nil { + log.Error(2, "Migration Failed: unable rename temporary migrated repo %s to %s: %v", wikiPathTmp, wikiPath, err) + failedMigration(fmt.Errorf("Failed to rename temporary migrated repo %s to %s", sanitizeRepoPath(wikiPathTmp), sanitizeRepoPath(wikiPath))) + return + } + RemoveAllWithNotice("Unable to remove placeholder wiki repository", wikiPathToRemove) + } + + if !repo.IsEmpty { + // Try to get HEAD branch and set it as default branch. + gitRepo, err := git.OpenRepository(repoPath) + if err != nil { + failedMigration(fmt.Errorf("OpenRepository: %v", err)) + return + } + headBranch, err := gitRepo.GetHEADBranch() + if err != nil { + failedMigration(fmt.Errorf("GetHEADBranch: %v", err)) + return + } + if headBranch != nil { + repo.DefaultBranch = headBranch.Name + } + + if err = SyncReleasesWithTags(repo, gitRepo); err != nil { + log.Error(4, "Failed to synchronize tags to releases for repository: %v", err) + } + } + + if err = repo.UpdateSize(); err != nil { + log.Error(4, "Failed to update size for repository: %v", err) + } + + if opts.IsMirror { + if _, err = x.InsertOne(&Mirror{ + RepoID: repo.ID, + Interval: setting.Mirror.DefaultInterval, + EnablePrune: true, + NextUpdateUnix: util.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), + }); err != nil { + failedMigration(fmt.Errorf("InsertOne: %v", err)) + return + } + + repo.IsMirror = true + err = UpdateRepository(repo, false) + } else { + repo, err = CleanUpMigrateInfo(repo) + } + + if err != nil { + if !repo.IsEmpty { + UpdateRepoIndexer(repo) + } + failedMigration(err) + return + } + + repo.IsArchived = false + if _, err := x.ID(repo.ID).AllCols().Update(repo); err != nil { + failedMigration(err) + return + } + + NotifyWatchers(&Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: ActionMigrationSuccessful, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + Content: callback(nil), + }) + + NotifyWatchers(&Action{ + ActUserID: doer.ID, + ActUser: doer, + OpType: ActionCreateRepo, + RepoID: repo.ID, + Repo: repo, + IsPrivate: repo.IsPrivate, + }) +} + +// MigrateRepository migrates a existing repository from other project hosting. +func MigrateRepository(doer, u *User, opts MigrateRepoOptions, messageConverter func(error) string) (*Repository, error) { + repo, err := CreateRepository(doer, u, CreateRepoOptions{ + Name: opts.Name, + Description: opts.Description, + IsPrivate: opts.IsPrivate, + IsMirror: opts.IsMirror, + NoWatchers: true, + }) + if err != nil { + return nil, err + } + + env, ok := os.LookupEnv("GIT_TERMINAL_PROMPT=0") + os.Setenv("GIT_TERMINAL_PROMPT", "0") + if _, err = git.NewCommand("ls-remote", "-h", opts.RemoteAddr).RunTimeout(1 * time.Minute); err != nil { + if ok { + os.Setenv("GIT_TERMINAL_PROMPT", env) + } else { + os.Unsetenv("GIT_TERMINAL_PROMPT") + } + return repo, fmt.Errorf("Clone: %v", err) + } + if ok { + os.Setenv("GIT_TERMINAL_PROMPT", env) + } else { + os.Unsetenv("GIT_TERMINAL_PROMPT") + } + + // OK if we succeeded above then we know that the clone should start... + go doMigration(doer, u, repo, opts, messageConverter) + + return repo, err +} + +// cleanUpMigrateGitConfig removes mirror info which prevents "push --all". +// This also removes possible user credentials. +func cleanUpMigrateGitConfig(configPath string) error { + cfg, err := ini.Load(configPath) + if err != nil { + return fmt.Errorf("open config file: %v", err) + } + cfg.DeleteSection("remote \"origin\"") + if err = cfg.SaveToIndent(configPath, "\t"); err != nil { + return fmt.Errorf("save config file: %v", err) + } + return nil +} + +// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. +func CleanUpMigrateInfo(repo *Repository) (*Repository, error) { + repoPath := repo.RepoPath() + if err := createDelegateHooks(repoPath); err != nil { + return repo, fmt.Errorf("createDelegateHooks: %v", err) + } + if repo.HasWiki() { + if err := createDelegateHooks(repo.WikiPath()); err != nil { + return repo, fmt.Errorf("createDelegateHooks.(wiki): %v", err) + } + } + + if err := cleanUpMigrateGitConfig(repo.GitConfigPath()); err != nil { + return repo, fmt.Errorf("cleanUpMigrateGitConfig: %v", err) + } + if repo.HasWiki() { + if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil { + return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err) + } + } + + return repo, UpdateRepository(repo, false) +} diff --git a/modules/templates/helper.go b/modules/templates/helper.go index ce077d1a928e7..fbb0af370b9fe 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -414,7 +414,7 @@ func ActionIcon(opType models.ActionType) string { return "issue-closed" case models.ActionReopenIssue, models.ActionReopenPullRequest: return "issue-reopened" - case models.ActionMirrorSyncPush, models.ActionMirrorSyncCreate, models.ActionMirrorSyncDelete: + case models.ActionMirrorSyncPush, models.ActionMirrorSyncCreate, models.ActionMirrorSyncDelete, models.ActionMigrationStarted, models.ActionMigrationSuccessful, models.ActionMigrationFailure: return "repo-clone" default: return "invalid type" diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 1e708b17624c6..e12914c37d549 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1717,9 +1717,13 @@ push_tag = pushed tag %[2]s to %[3]s delete_tag = deleted tag %[2]s from %[3]s delete_branch = deleted branch %[2]s from %[3]s compare_commits = Compare %d commits + mirror_sync_push = synced commits to %[3]s at %[4]s from mirror mirror_sync_create = synced new reference %[2]s to %[3]s from mirror mirror_sync_delete = synced and deleted reference %[2]s at %[3]s from mirror +migration_started = started a migration from %s to %s. +migration_successful = successfully migrated from %s to %s +migration_failure = unsuccessfully migrated to %s. You should delete this repository. The error was: %s [tool] ago = %s ago diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index d78700c6b0b58..cde40172091a7 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -398,6 +398,11 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { IsPrivate: form.Private || setting.Repository.ForcePrivate, IsMirror: form.Mirror, RemoteAddr: remoteAddr, + }, func(err error) string { + if err != nil { + return util.URLSanitizedError(err, remoteAddr).Error() + } + return util.SanitizeURLCredentials(remoteAddr, true) }) if err != nil { err = util.URLSanitizedError(err, remoteAddr) @@ -410,7 +415,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { return } - log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName) + log.Trace("Repository migration started: %s/%s", ctxUser.Name, form.RepoName) ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin)) } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 960961a5e569c..963964aead935 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -249,10 +249,22 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { IsPrivate: form.Private || setting.Repository.ForcePrivate, IsMirror: form.Mirror, RemoteAddr: remoteAddr, + }, func(err error) string { + if err != nil { + err = util.URLSanitizedError(err, remoteAddr) + if strings.Contains(err.Error(), "Authentication failed") || + strings.Contains(err.Error(), "could not read Username") { + return ctx.Tr("form.auth_failed", err.Error()) + } else if strings.Contains(err.Error(), "fatal:") { + return ctx.Tr("repo.migrate.failed", err.Error()) + } + return err.Error() + } + return util.SanitizeURLCredentials(remoteAddr, true) }) if err == nil { - log.Trace("Repository migrated [%d]: %s/%s", repo.ID, ctxUser.Name, form.RepoName) - ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + form.RepoName) + log.Trace("Repository migration started [%d]: %s/%s", repo.ID, ctxUser.Name, form.RepoName) + ctx.Redirect(setting.AppURL) return } diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index 849ad7fb2f28f..0c1ef02efa35b 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -57,6 +57,12 @@ {{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} {{else if eq .GetOpType 20}} {{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} + {{else if eq .GetOpType 21}} + {{$.i18n.Tr "action.migration_started" .GetContent .GetRepoLink .ShortRepoPath | Str2html}} + {{else if eq .GetOpType 22}} + {{$.i18n.Tr "action.migration_successful" .GetContent .GetRepoLink .ShortRepoPath | Str2html}} + {{else if eq .GetOpType 23}} + {{$.i18n.Tr "action.migration_failure" .GetRepoLink .ShortRepoPath .GetContent | Str2html}} {{end}}

{{if or (eq .GetOpType 5) (eq .GetOpType 18)}}