repo.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "sync"
  13. "time"
  14. "unicode/utf8"
  15. "github.com/Unknwon/cae/zip"
  16. "github.com/Unknwon/com"
  17. "github.com/gogits/git"
  18. "github.com/gogits/gogs/modules/base"
  19. "github.com/gogits/gogs/modules/log"
  20. )
  21. type Repository struct {
  22. Id int64
  23. OwnerId int64 `xorm:"unique(s)"`
  24. ForkId int64
  25. LowerName string `xorm:"unique(s) index not null"`
  26. Name string `xorm:"index not null"`
  27. Description string
  28. Private bool
  29. NumWatchs int
  30. NumStars int
  31. NumForks int
  32. Created time.Time `xorm:"created"`
  33. Updated time.Time `xorm:"updated"`
  34. }
  35. type Star struct {
  36. Id int64
  37. RepoId int64
  38. UserId int64
  39. Created time.Time `xorm:"created"`
  40. }
  41. var (
  42. gitInitLocker = sync.Mutex{}
  43. LanguageIgns, Licenses []string
  44. )
  45. var (
  46. ErrRepoAlreadyExist = errors.New("Repository already exist")
  47. ErrRepoNotExist = errors.New("Repository does not exist")
  48. )
  49. func init() {
  50. LanguageIgns = strings.Split(base.Cfg.MustValue("repository", "LANG_IGNS"), "|")
  51. Licenses = strings.Split(base.Cfg.MustValue("repository", "LICENSES"), "|")
  52. zip.Verbose = false
  53. }
  54. // check if repository is exist
  55. func IsRepositoryExist(user *User, repoName string) (bool, error) {
  56. repo := Repository{OwnerId: user.Id}
  57. has, err := orm.Where("lower_name = ?", strings.ToLower(repoName)).Get(&repo)
  58. if err != nil {
  59. return has, err
  60. }
  61. s, err := os.Stat(RepoPath(user.Name, repoName))
  62. if err != nil {
  63. return false, nil // Error simply means does not exist, but we don't want to show up.
  64. }
  65. return s.IsDir(), nil
  66. }
  67. // CreateRepository creates a repository for given user or orgnaziation.
  68. func CreateRepository(user *User, repoName, desc, repoLang, license string, private bool, initReadme bool) (*Repository, error) {
  69. isExist, err := IsRepositoryExist(user, repoName)
  70. if err != nil {
  71. return nil, err
  72. } else if isExist {
  73. return nil, ErrRepoAlreadyExist
  74. }
  75. repo := &Repository{
  76. OwnerId: user.Id,
  77. Name: repoName,
  78. LowerName: strings.ToLower(repoName),
  79. Description: desc,
  80. Private: private,
  81. }
  82. f := RepoPath(user.Name, repoName)
  83. if err = initRepository(f, user, repo, initReadme, repoLang, license); err != nil {
  84. return nil, err
  85. }
  86. session := orm.NewSession()
  87. defer session.Close()
  88. session.Begin()
  89. if _, err = session.Insert(repo); err != nil {
  90. if err2 := os.RemoveAll(f); err2 != nil {
  91. return nil, errors.New(fmt.Sprintf(
  92. "delete repo directory %s/%s failed", user.Name, repoName))
  93. }
  94. session.Rollback()
  95. return nil, err
  96. }
  97. access := Access{
  98. UserName: user.Name,
  99. RepoName: repo.Name,
  100. Mode: AU_WRITABLE,
  101. }
  102. if _, err = session.Insert(&access); err != nil {
  103. session.Rollback()
  104. if err2 := os.RemoveAll(f); err2 != nil {
  105. return nil, errors.New(fmt.Sprintf(
  106. "delete repo directory %s/%s failed", user.Name, repoName))
  107. }
  108. return nil, err
  109. }
  110. if _, err = session.Exec("update user set num_repos = num_repos + 1 where id = ?", user.Id); err != nil {
  111. session.Rollback()
  112. if err2 := os.RemoveAll(f); err2 != nil {
  113. return nil, errors.New(fmt.Sprintf(
  114. "delete repo directory %s/%s failed", user.Name, repoName))
  115. }
  116. return nil, err
  117. }
  118. if err = session.Commit(); err != nil {
  119. session.Rollback()
  120. if err2 := os.RemoveAll(f); err2 != nil {
  121. return nil, errors.New(fmt.Sprintf(
  122. "delete repo directory %s/%s failed", user.Name, repoName))
  123. }
  124. return nil, err
  125. }
  126. return repo, NewRepoAction(user, repo)
  127. return nil, nil
  128. }
  129. // extractGitBareZip extracts git-bare.zip to repository path.
  130. func extractGitBareZip(repoPath string) error {
  131. z, err := zip.Open("conf/content/git-bare.zip")
  132. if err != nil {
  133. fmt.Println("shi?")
  134. return err
  135. }
  136. defer z.Close()
  137. return z.ExtractTo(repoPath)
  138. }
  139. // initRepoCommit temporarily changes with work directory.
  140. func initRepoCommit(tmpPath string, sig *git.Signature) error {
  141. gitInitLocker.Lock()
  142. defer gitInitLocker.Unlock()
  143. // Change work directory.
  144. curPath, err := os.Getwd()
  145. if err != nil {
  146. return err
  147. } else if err = os.Chdir(tmpPath); err != nil {
  148. return err
  149. }
  150. defer os.Chdir(curPath)
  151. if _, _, err := com.ExecCmd("git", "add", "--all"); err != nil {
  152. return err
  153. }
  154. if _, _, err := com.ExecCmd("git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
  155. "-m", "Init commit"); err != nil {
  156. return err
  157. }
  158. if _, _, err := com.ExecCmd("git", "push", "origin", "master"); err != nil {
  159. return err
  160. }
  161. return nil
  162. }
  163. // InitRepository initializes README and .gitignore if needed.
  164. func initRepository(f string, user *User, repo *Repository, initReadme bool, repoLang, license string) error {
  165. repoPath := RepoPath(user.Name, repo.Name)
  166. // Create bare new repository.
  167. if err := extractGitBareZip(repoPath); err != nil {
  168. return err
  169. }
  170. // Initialize repository according to user's choice.
  171. fileName := map[string]string{}
  172. if initReadme {
  173. fileName["readme"] = "README.md"
  174. }
  175. if repoLang != "" {
  176. fileName["gitign"] = ".gitignore"
  177. }
  178. if license != "" {
  179. fileName["license"] = "LICENSE"
  180. }
  181. // Clone to temprory path and do the init commit.
  182. tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()))
  183. os.MkdirAll(tmpDir, os.ModePerm)
  184. if _, _, err := com.ExecCmd("git", "clone", repoPath, tmpDir); err != nil {
  185. return err
  186. }
  187. // README
  188. if initReadme {
  189. defaultReadme := repo.Name + "\n" + strings.Repeat("=",
  190. utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description
  191. if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]),
  192. []byte(defaultReadme), 0644); err != nil {
  193. return err
  194. }
  195. }
  196. // .gitignore
  197. if repoLang != "" {
  198. filePath := "conf/gitignore/" + repoLang
  199. if com.IsFile(filePath) {
  200. if _, err := com.Copy(filePath,
  201. filepath.Join(tmpDir, fileName["gitign"])); err != nil {
  202. return err
  203. }
  204. }
  205. }
  206. // LICENSE
  207. if license != "" {
  208. filePath := "conf/license/" + license
  209. if com.IsFile(filePath) {
  210. if _, err := com.Copy(filePath,
  211. filepath.Join(tmpDir, fileName["license"])); err != nil {
  212. return err
  213. }
  214. }
  215. }
  216. // Apply changes and commit.
  217. if err := initRepoCommit(tmpDir, user.NewGitSig()); err != nil {
  218. return err
  219. }
  220. return nil
  221. }
  222. func GetRepositoryByName(user *User, repoName string) (*Repository, error) {
  223. repo := &Repository{
  224. OwnerId: user.Id,
  225. LowerName: strings.ToLower(repoName),
  226. }
  227. has, err := orm.Get(repo)
  228. if err != nil {
  229. return nil, err
  230. } else if !has {
  231. return nil, ErrRepoNotExist
  232. }
  233. return repo, err
  234. }
  235. func GetRepositoryById(id int64) (repo *Repository, err error) {
  236. has, err := orm.Id(id).Get(repo)
  237. if err != nil {
  238. return nil, err
  239. } else if !has {
  240. return nil, ErrRepoNotExist
  241. }
  242. return repo, err
  243. }
  244. // GetRepositories returns the list of repositories of given user.
  245. func GetRepositories(user *User) ([]Repository, error) {
  246. repos := make([]Repository, 0, 10)
  247. err := orm.Desc("updated").Find(&repos, &Repository{OwnerId: user.Id})
  248. return repos, err
  249. }
  250. func GetRepositoryCount(user *User) (int64, error) {
  251. return orm.Count(&Repository{OwnerId: user.Id})
  252. }
  253. func StarReposiory(user *User, repoName string) error {
  254. return nil
  255. }
  256. func UnStarRepository() {
  257. }
  258. func WatchRepository() {
  259. }
  260. func UnWatchRepository() {
  261. }
  262. func ForkRepository(reposName string, userId int64) {
  263. }
  264. func RepoPath(userName, repoName string) string {
  265. return filepath.Join(UserPath(userName), repoName+".git")
  266. }
  267. // DeleteRepository deletes a repository for a user or orgnaztion.
  268. func DeleteRepository(userId, repoId int64, userName string) (err error) {
  269. repo := &Repository{Id: repoId, OwnerId: userId}
  270. has, err := orm.Get(repo)
  271. if err != nil {
  272. return err
  273. } else if !has {
  274. return ErrRepoNotExist
  275. }
  276. session := orm.NewSession()
  277. if err = session.Begin(); err != nil {
  278. return err
  279. }
  280. if _, err = session.Delete(&Repository{Id: repoId}); err != nil {
  281. session.Rollback()
  282. return err
  283. }
  284. if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil {
  285. session.Rollback()
  286. return err
  287. }
  288. if _, err = session.Exec("update user set num_repos = num_repos - 1 where id = ?", userId); err != nil {
  289. session.Rollback()
  290. return err
  291. }
  292. if err = session.Commit(); err != nil {
  293. session.Rollback()
  294. return err
  295. }
  296. if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
  297. // TODO: log and delete manully
  298. log.Error("delete repo %s/%s failed", userName, repo.Name)
  299. return err
  300. }
  301. return nil
  302. }