diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2019-12-15 01:30:01 +0800 |
---|---|---|
committer | Antoine GIRARD <sapk@users.noreply.github.com> | 2019-12-14 18:30:01 +0100 |
commit | 7b2ff82d209c01be8082eaa9b888f93a00850d4d (patch) | |
tree | e3876a3f5c4b204b97bb0b611493c27453204a2c /modules/repository/repo.go | |
parent | 2904abdc22da36a7110d0b69af0fd45cdb74ebf6 (diff) | |
download | gitea-7b2ff82d209c01be8082eaa9b888f93a00850d4d.tar.gz gitea-7b2ff82d209c01be8082eaa9b888f93a00850d4d.zip |
Move some repository methods from models to modules/repository (#9353)
* Move some repository methods from models to modules/repository
* fix test
Diffstat (limited to 'modules/repository/repo.go')
-rw-r--r-- | modules/repository/repo.go | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/modules/repository/repo.go b/modules/repository/repo.go new file mode 100644 index 0000000000..ea526a1e30 --- /dev/null +++ b/modules/repository/repo.go @@ -0,0 +1,223 @@ +// 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 repository + +import ( + "fmt" + "os" + "path" + "strings" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/timeutil" + "gopkg.in/ini.v1" +) + +/* + 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 "" +} + +// MigrateRepositoryGitData starts migrating git related data after created migrating repository +func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opts api.MigrateRepoOption) (*models.Repository, error) { + repoPath := models.RepoPath(u.Name, opts.RepoName) + + 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 + + var err error + if err = os.RemoveAll(repoPath); err != nil { + return repo, fmt.Errorf("Failed to remove %s: %v", repoPath, err) + } + + if err = git.Clone(opts.CloneAddr, repoPath, git.CloneRepoOptions{ + Mirror: true, + Quiet: true, + Timeout: migrateTimeout, + }); err != nil { + return repo, fmt.Errorf("Clone: %v", err) + } + + if opts.Wiki { + wikiPath := models.WikiPath(u.Name, opts.RepoName) + wikiRemotePath := wikiRemoteURL(opts.CloneAddr) + 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) + } + } + } + } + + gitRepo, err := git.OpenRepository(repoPath) + if err != nil { + return repo, fmt.Errorf("OpenRepository: %v", err) + } + defer gitRepo.Close() + + repo.IsEmpty, err = gitRepo.IsEmpty() + if err != nil { + return repo, fmt.Errorf("git.IsEmpty: %v", err) + } + + if !opts.Releases && !repo.IsEmpty { + // Try to get HEAD branch and set it as default branch. + 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("Failed to synchronize tags to releases for repository: %v", err) + } + } + + if err = repo.UpdateSize(); err != nil { + log.Error("Failed to update size for repository: %v", err) + } + + if opts.Mirror { + if err = models.InsertMirror(&models.Mirror{ + RepoID: repo.ID, + Interval: setting.Mirror.DefaultInterval, + EnablePrune: true, + NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), + }); err != nil { + return repo, fmt.Errorf("InsertOne: %v", err) + } + + repo.IsMirror = true + err = models.UpdateRepository(repo, false) + } else { + repo, err = CleanUpMigrateInfo(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 +} + +// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. +func CleanUpMigrateInfo(repo *models.Repository) (*models.Repository, error) { + repoPath := repo.RepoPath() + if err := models.CreateDelegateHooks(repoPath); err != nil { + return repo, fmt.Errorf("createDelegateHooks: %v", err) + } + if repo.HasWiki() { + if err := models.CreateDelegateHooks(repo.WikiPath()); err != nil { + return repo, fmt.Errorf("createDelegateHooks.(wiki): %v", err) + } + } + + _, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath) + if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + return repo, fmt.Errorf("CleanUpMigrateInfo: %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, models.UpdateRepository(repo, false) +} + +// SyncReleasesWithTags synchronizes release table with repository tags +func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) error { + existingRelTags := make(map[string]struct{}) + opts := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true} + for page := 1; ; page++ { + rels, err := models.GetReleasesByRepoID(repo.ID, opts, page, 100) + if err != nil { + return fmt.Errorf("GetReleasesByRepoID: %v", err) + } + if len(rels) == 0 { + break + } + for _, rel := range rels { + if rel.IsDraft { + continue + } + commitID, err := gitRepo.GetTagCommitID(rel.TagName) + if err != nil && !git.IsErrNotExist(err) { + return fmt.Errorf("GetTagCommitID: %v", err) + } + if git.IsErrNotExist(err) || commitID != rel.Sha1 { + if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil { + return fmt.Errorf("PushUpdateDeleteTag: %v", err) + } + } else { + existingRelTags[strings.ToLower(rel.TagName)] = struct{}{} + } + } + } + tags, err := gitRepo.GetTags() + if err != nil { + return fmt.Errorf("GetTags: %v", err) + } + for _, tagName := range tags { + if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok { + if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil { + return fmt.Errorf("pushUpdateAddTag: %v", err) + } + } + } + return nil +} |