diff options
author | Meaglith Ma <genedna@gmail.com> | 2014-04-23 12:29:53 +0800 |
---|---|---|
committer | Meaglith Ma <genedna@gmail.com> | 2014-04-23 12:29:53 +0800 |
commit | ee7bfe2ebe3a453beff5e8d4c1436061d321acfe (patch) | |
tree | 02575fe703fdcfaa2bd6450b852734d9305c9ce6 /models/repo.go | |
parent | b270b34c98b10b0e4807048890e883b6b06a6461 (diff) | |
parent | f0cdf30134e62be6bf5924735a6145769e495cfc (diff) |
Add memcached and redis Docker supported
Diffstat (limited to 'models/repo.go')
-rw-r--r-- | models/repo.go | 302 |
1 files changed, 250 insertions, 52 deletions
diff --git a/models/repo.go b/models/repo.go index e8ebce92..19fe6e28 100644 --- a/models/repo.go +++ b/models/repo.go @@ -30,7 +30,8 @@ var ( ErrRepoNotExist = errors.New("Repository does not exist") ErrRepoFileNotExist = errors.New("Target Repo file does not exist") ErrRepoNameIllegal = errors.New("Repository name contains illegal characters") - ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded") + ErrRepoFileNotLoaded = errors.New("repo file not loaded") + ErrMirrorNotExist = errors.New("Mirror does not exist") ) var ( @@ -65,6 +66,7 @@ func NewRepoContext() { type Repository struct { Id int64 OwnerId int64 `xorm:"unique(s)"` + Owner *User `xorm:"-"` ForkId int64 LowerName string `xorm:"unique(s) index not null"` Name string `xorm:"index not null"` @@ -74,11 +76,14 @@ type Repository struct { NumStars int NumForks int NumIssues int - NumReleases int `xorm:"NOT NULL"` NumClosedIssues int NumOpenIssues int `xorm:"-"` + NumTags int `xorm:"-"` IsPrivate bool + IsMirror bool IsBare bool + IsGoget bool + DefaultBranch string Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` } @@ -117,13 +122,133 @@ func IsLegalName(repoName string) bool { return true } +// Mirror represents a mirror information of repository. +type Mirror struct { + Id int64 + RepoId int64 + RepoName string // <user name>/<repo name> + Interval int // Hour. + Updated time.Time `xorm:"UPDATED"` + NextUpdate time.Time +} + +func GetMirror(repoId int64) (*Mirror, error) { + m := &Mirror{RepoId: repoId} + has, err := orm.Get(m) + if err != nil { + return nil, err + } else if !has { + return nil, ErrMirrorNotExist + } + return m, nil +} + +func UpdateMirror(m *Mirror) error { + _, err := orm.Id(m.Id).Update(m) + return err +} + +// MirrorUpdate checks and updates mirror repositories. +func MirrorUpdate() { + if err := orm.Iterate(new(Mirror), func(idx int, bean interface{}) error { + m := bean.(*Mirror) + if m.NextUpdate.After(time.Now()) { + return nil + } + + repoPath := filepath.Join(base.RepoRootPath, m.RepoName+".git") + _, stderr, err := com.ExecCmdDir(repoPath, "git", "remote", "update") + if err != nil { + return err + } else if strings.Contains(stderr, "fatal:") { + return errors.New(stderr) + } else if err = git.UnpackRefs(repoPath); err != nil { + return err + } + + m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) + return UpdateMirror(m) + }); err != nil { + log.Error("repo.MirrorUpdate: %v", err) + } +} + +// MirrorRepository creates a mirror repository from source. +func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) error { + _, stderr, err := com.ExecCmd("git", "clone", "--mirror", url, repoPath) + if err != nil { + return err + } else if strings.Contains(stderr, "fatal:") { + return errors.New(stderr) + } + + if _, err = orm.InsertOne(&Mirror{ + RepoId: repoId, + RepoName: strings.ToLower(userName + "/" + repoName), + Interval: 24, + NextUpdate: time.Now().Add(24 * time.Hour), + }); err != nil { + return err + } + + return git.UnpackRefs(repoPath) +} + +// MigrateRepository migrates a existing repository from other project hosting. +func MigrateRepository(user *User, name, desc string, private, mirror bool, url string) (*Repository, error) { + repo, err := CreateRepository(user, name, desc, "", "", private, mirror, false) + if err != nil { + return nil, err + } + + // Clone to temprory path and do the init commit. + tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond())) + os.MkdirAll(tmpDir, os.ModePerm) + + repoPath := RepoPath(user.Name, name) + + repo.IsBare = false + if mirror { + if err = MirrorRepository(repo.Id, user.Name, repo.Name, repoPath, url); err != nil { + return repo, err + } + repo.IsMirror = true + return repo, UpdateRepository(repo) + } + + // Clone from local repository. + _, stderr, err := com.ExecCmd("git", "clone", repoPath, tmpDir) + if err != nil { + return repo, err + } else if strings.Contains(stderr, "fatal:") { + return repo, errors.New("git clone: " + stderr) + } + + // Pull data from source. + _, stderr, err = com.ExecCmdDir(tmpDir, "git", "pull", url) + if err != nil { + return repo, err + } else if strings.Contains(stderr, "fatal:") { + return repo, errors.New("git pull: " + stderr) + } + + // Push data to local repository. + if _, stderr, err = com.ExecCmdDir(tmpDir, "git", "push", "origin", "master"); err != nil { + return repo, err + } else if strings.Contains(stderr, "fatal:") { + return repo, errors.New("git push: " + stderr) + } + + return repo, UpdateRepository(repo) +} + // CreateRepository creates a repository for given user or orgnaziation. -func CreateRepository(user *User, repoName, desc, repoLang, license string, private bool, initReadme bool) (*Repository, error) { - if !IsLegalName(repoName) { +func CreateRepository(user *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) { + if !IsLegalName(name) { return nil, ErrRepoNameIllegal } - isExist, err := IsRepositoryExist(user, repoName) + isExist, err := IsRepositoryExist(user, name) if err != nil { return nil, err } else if isExist { @@ -131,18 +256,16 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv } repo := &Repository{ - OwnerId: user.Id, - Name: repoName, - LowerName: strings.ToLower(repoName), - Description: desc, - IsPrivate: private, - IsBare: repoLang == "" && license == "" && !initReadme, + OwnerId: user.Id, + Name: name, + LowerName: strings.ToLower(name), + Description: desc, + IsPrivate: private, + IsBare: lang == "" && license == "" && !initReadme, + DefaultBranch: "master", } + repoPath := RepoPath(user.Name, repo.Name) - repoPath := RepoPath(user.Name, repoName) - if err = initRepository(repoPath, user, repo, initReadme, repoLang, license); err != nil { - return nil, err - } sess := orm.NewSession() defer sess.Close() sess.Begin() @@ -151,23 +274,27 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv if err2 := os.RemoveAll(repoPath); err2 != nil { log.Error("repo.CreateRepository(repo): %v", err) return nil, errors.New(fmt.Sprintf( - "delete repo directory %s/%s failed(1): %v", user.Name, repoName, err2)) + "delete repo directory %s/%s failed(1): %v", user.Name, repo.Name, err2)) } sess.Rollback() return nil, err } + mode := AU_WRITABLE + if mirror { + mode = AU_READABLE + } access := Access{ UserName: user.LowerName, RepoName: strings.ToLower(path.Join(user.Name, repo.Name)), - Mode: AU_WRITABLE, + Mode: mode, } if _, err = sess.Insert(&access); err != nil { sess.Rollback() if err2 := os.RemoveAll(repoPath); err2 != nil { log.Error("repo.CreateRepository(access): %v", err) return nil, errors.New(fmt.Sprintf( - "delete repo directory %s/%s failed(2): %v", user.Name, repoName, err2)) + "delete repo directory %s/%s failed(2): %v", user.Name, repo.Name, err2)) } return nil, err } @@ -178,7 +305,7 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv if err2 := os.RemoveAll(repoPath); err2 != nil { log.Error("repo.CreateRepository(repo count): %v", err) return nil, errors.New(fmt.Sprintf( - "delete repo directory %s/%s failed(3): %v", user.Name, repoName, err2)) + "delete repo directory %s/%s failed(3): %v", user.Name, repo.Name, err2)) } return nil, err } @@ -188,25 +315,36 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv if err2 := os.RemoveAll(repoPath); err2 != nil { log.Error("repo.CreateRepository(commit): %v", err) return nil, errors.New(fmt.Sprintf( - "delete repo directory %s/%s failed(3): %v", user.Name, repoName, err2)) + "delete repo directory %s/%s failed(3): %v", user.Name, repo.Name, err2)) } return nil, err } - c := exec.Command("git", "update-server-info") - c.Dir = repoPath - if err = c.Run(); err != nil { - log.Error("repo.CreateRepository(exec update-server-info): %v", err) - } - - if err = NewRepoAction(user, repo); err != nil { - log.Error("repo.CreateRepository(NewRepoAction): %v", err) + if !repo.IsPrivate { + if err = NewRepoAction(user, repo); err != nil { + log.Error("repo.CreateRepository(NewRepoAction): %v", err) + } } if err = WatchRepo(user.Id, repo.Id, true); err != nil { log.Error("repo.CreateRepository(WatchRepo): %v", err) } + // No need for init for mirror. + if mirror { + return repo, nil + } + + if err = initRepository(repoPath, user, repo, initReadme, lang, license); err != nil { + return nil, err + } + + c := exec.Command("git", "update-server-info") + c.Dir = repoPath + if err = c.Run(); err != nil { + log.Error("repo.CreateRepository(exec update-server-info): %v", err) + } + return repo, nil } @@ -227,24 +365,21 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) { var stderr string if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "add", "--all"); err != nil { return err - } - if len(stderr) > 0 { - log.Trace("stderr(1): %s", stderr) + } else if strings.Contains(stderr, "fatal:") { + return errors.New("git add: " + stderr) } if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", "Init commit"); err != nil { return err - } - if len(stderr) > 0 { - log.Trace("stderr(2): %s", stderr) + } else if strings.Contains(stderr, "fatal:") { + return errors.New("git commit: " + stderr) } if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "push", "origin", "master"); err != nil { return err - } - if len(stderr) > 0 { - log.Trace("stderr(3): %s", stderr) + } else if strings.Contains(stderr, "fatal:") { + return errors.New("git push: " + stderr) } return nil } @@ -260,6 +395,13 @@ func createHookUpdate(hookPath, content string) error { return err } +// SetRepoEnvs sets environment variables for command update. +func SetRepoEnvs(userId int64, userName, repoName string) { + os.Setenv("userId", base.ToStr(userId)) + os.Setenv("userName", userName) + os.Setenv("repoName", repoName) +} + // InitRepository initializes README and .gitignore if needed. func initRepository(f string, user *User, repo *Repository, initReadme bool, repoLang, license string) error { repoPath := RepoPath(user.Name, repo.Name) @@ -271,7 +413,7 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep // hook/post-update if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"), - fmt.Sprintf("#!/usr/bin/env bash\n%s update $1 $2 $3\n", + fmt.Sprintf("#!/usr/bin/env %s\n%s update $1 $2 $3\n", base.ScriptType, strings.Replace(appPath, "\\", "/", -1))); err != nil { return err } @@ -292,8 +434,11 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond())) os.MkdirAll(tmpDir, os.ModePerm) - if _, _, err := com.ExecCmd("git", "clone", repoPath, tmpDir); err != nil { + _, stderr, err := com.ExecCmd("git", "clone", repoPath, tmpDir) + if err != nil { return err + } else if strings.Contains(stderr, "fatal:") { + return errors.New("git clone: " + stderr) } // README @@ -310,7 +455,7 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep if repoLang != "" { filePath := "conf/gitignore/" + repoLang if com.IsFile(filePath) { - if _, err := com.Copy(filePath, + if err := com.Copy(filePath, filepath.Join(tmpDir, fileName["gitign"])); err != nil { return err } @@ -321,7 +466,7 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep if license != "" { filePath := "conf/license/" + license if com.IsFile(filePath) { - if _, err := com.Copy(filePath, + if err := com.Copy(filePath, filepath.Join(tmpDir, fileName["license"])); err != nil { return err } @@ -332,6 +477,8 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep return nil } + SetRepoEnvs(user.Id, user.Name, repo.Name) + // Apply changes and commit. return initRepoCommit(tmpDir, user.NewGitSig()) } @@ -365,6 +512,7 @@ func GetRepos(num, offset int) ([]UserRepo, error) { return urepos, nil } +// RepoPath returns repository path by given user and repository name. func RepoPath(userName, repoName string) string { return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".git") } @@ -381,45 +529,62 @@ func TransferOwnership(user *User, newOwner string, repo *Repository) (err error if err = orm.Find(&accesses, &Access{RepoName: user.LowerName + "/" + repo.LowerName}); err != nil { return err } + + sess := orm.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + for i := range accesses { accesses[i].RepoName = newUser.LowerName + "/" + repo.LowerName if accesses[i].UserName == user.LowerName { accesses[i].UserName = newUser.LowerName } - if err = UpdateAccess(&accesses[i]); err != nil { + if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { return err } } // Update repository. repo.OwnerId = newUser.Id - if _, err := orm.Id(repo.Id).Update(repo); err != nil { + if _, err := sess.Id(repo.Id).Update(repo); err != nil { + sess.Rollback() return err } // Update user repository number. rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" - if _, err = orm.Exec(rawSql, newUser.Id); err != nil { + if _, err = sess.Exec(rawSql, newUser.Id); err != nil { + sess.Rollback() return err } rawSql = "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" - if _, err = orm.Exec(rawSql, user.Id); err != nil { + if _, err = sess.Exec(rawSql, user.Id); err != nil { + sess.Rollback() return err } // Add watch of new owner to repository. if !IsWatching(newUser.Id, repo.Id) { if err = WatchRepo(newUser.Id, repo.Id, true); err != nil { + sess.Rollback() return err } } if err = TransferRepoAction(user, newUser, repo); err != nil { + sess.Rollback() return err } // Change repository directory name. - return os.Rename(RepoPath(user.Name, repo.Name), RepoPath(newUser.Name, repo.Name)) + if err = os.Rename(RepoPath(user.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil { + sess.Rollback() + return err + } + + return sess.Commit() } // ChangeRepositoryName changes all corresponding setting from old repository name to new one. @@ -429,15 +594,27 @@ func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) if err = orm.Find(&accesses, &Access{RepoName: strings.ToLower(userName + "/" + oldRepoName)}); err != nil { return err } + + sess := orm.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + for i := range accesses { accesses[i].RepoName = userName + "/" + newRepoName - if err = UpdateAccess(&accesses[i]); err != nil { + if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { return err } } // Change repository directory name. - return os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)) + if err = os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)); err != nil { + sess.Rollback() + return err + } + + return sess.Commit() } func UpdateRepository(repo *Repository) error { @@ -476,8 +653,7 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) { sess.Rollback() return err } - rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" - if _, err = sess.Exec(rawSql, userId); err != nil { + if _, err := sess.Delete(&Action{RepoId: repo.Id}); err != nil { sess.Rollback() return err } @@ -485,6 +661,16 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) { sess.Rollback() return err } + if _, err = sess.Delete(&Mirror{RepoId: repoId}); err != nil { + sess.Rollback() + return err + } + + rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" + if _, err = sess.Exec(rawSql, userId); err != nil { + sess.Rollback() + return err + } if err = sess.Commit(); err != nil { sess.Rollback() return err @@ -525,12 +711,24 @@ func GetRepositoryById(id int64) (*Repository, error) { } // GetRepositories returns the list of repositories of given user. -func GetRepositories(user *User) ([]Repository, error) { +func GetRepositories(user *User, private bool) ([]Repository, error) { repos := make([]Repository, 0, 10) - err := orm.Desc("updated").Find(&repos, &Repository{OwnerId: user.Id}) + sess := orm.Desc("updated") + if !private { + sess.Where("is_private=?", false) + } + + err := sess.Find(&repos, &Repository{OwnerId: user.Id}) + return repos, err +} + +// GetRecentUpdatedRepositories returns the list of repositories that are recently updated. +func GetRecentUpdatedRepositories() (repos []*Repository, err error) { + err = orm.Where("is_private=?", false).Limit(5).Desc("updated").Find(&repos) return repos, err } +// GetRepositoryCount returns the total number of repositories of user. func GetRepositoryCount(user *User) (int64, error) { return orm.Count(&Repository{OwnerId: user.Id}) } |