migrations.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright 2015 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 migrations
  5. import (
  6. "errors"
  7. "fmt"
  8. "strings"
  9. "time"
  10. "github.com/Unknwon/com"
  11. "github.com/go-xorm/xorm"
  12. "github.com/gogits/gogs/modules/log"
  13. "github.com/gogits/gogs/modules/setting"
  14. )
  15. const _DB_VER = 1
  16. type Migration interface {
  17. Description() string
  18. Migrate(*xorm.Engine) error
  19. }
  20. type migration struct {
  21. description string
  22. migrate func(*xorm.Engine) error
  23. }
  24. func NewMigration(desc string, fn func(*xorm.Engine) error) Migration {
  25. return &migration{desc, fn}
  26. }
  27. func (m *migration) Description() string {
  28. return m.description
  29. }
  30. func (m *migration) Migrate(x *xorm.Engine) error {
  31. return m.migrate(x)
  32. }
  33. // The version table. Should have only one row with id==1
  34. type Version struct {
  35. Id int64
  36. Version int64
  37. }
  38. // This is a sequence of migrations. Add new migrations to the bottom of the list.
  39. // If you want to "retire" a migration, replace it with "expiredMigration"
  40. var migrations = []Migration{
  41. NewMigration("generate collaboration from access", accessToCollaboration),
  42. }
  43. // Migrate database to current version
  44. func Migrate(x *xorm.Engine) error {
  45. if err := x.Sync(new(Version)); err != nil {
  46. return fmt.Errorf("sync: %v", err)
  47. }
  48. currentVersion := &Version{Id: 1}
  49. has, err := x.Get(currentVersion)
  50. if err != nil {
  51. return fmt.Errorf("get: %v", err)
  52. } else if !has {
  53. needsMigration, err := x.IsTableExist("user")
  54. if err != nil {
  55. return err
  56. }
  57. if needsMigration {
  58. isEmpty, err := x.IsTableEmpty("user")
  59. if err != nil {
  60. return err
  61. }
  62. needsMigration = !isEmpty
  63. }
  64. if !needsMigration {
  65. currentVersion.Version = int64(len(migrations))
  66. }
  67. if _, err = x.InsertOne(currentVersion); err != nil {
  68. return fmt.Errorf("insert: %v", err)
  69. }
  70. }
  71. v := currentVersion.Version
  72. for i, m := range migrations[v:] {
  73. log.Info("Migration: %s", m.Description())
  74. if err = m.Migrate(x); err != nil {
  75. return fmt.Errorf("do migrate: %v", err)
  76. }
  77. currentVersion.Version = v + int64(i) + 1
  78. if _, err = x.Id(1).Update(currentVersion); err != nil {
  79. return err
  80. }
  81. }
  82. return nil
  83. }
  84. func expiredMigration(x *xorm.Engine) error {
  85. return errors.New("You are migrating from a too old gogs version")
  86. }
  87. func accessToCollaboration(x *xorm.Engine) error {
  88. type Collaboration struct {
  89. ID int64 `xorm:"pk autoincr"`
  90. RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
  91. UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
  92. Created time.Time
  93. }
  94. x.Sync(new(Collaboration))
  95. results, err := x.Query("SELECT u.id AS `uid`, a.repo_name AS `repo`, a.mode AS `mode`, a.created as `created` FROM `access` a JOIN `user` u ON a.user_name=u.lower_name")
  96. if err != nil {
  97. return err
  98. }
  99. sess := x.NewSession()
  100. defer sess.Close()
  101. if err = sess.Begin(); err != nil {
  102. return err
  103. }
  104. offset := strings.Split(time.Now().String(), " ")[2]
  105. for _, result := range results {
  106. mode := com.StrTo(result["mode"]).MustInt64()
  107. // Collaborators must have write access.
  108. if mode < 2 {
  109. continue
  110. }
  111. userID := com.StrTo(result["uid"]).MustInt64()
  112. repoRefName := string(result["repo"])
  113. var created time.Time
  114. switch {
  115. case setting.UseSQLite3:
  116. created, _ = time.Parse(time.RFC3339, string(result["created"]))
  117. case setting.UseMySQL:
  118. created, _ = time.Parse("2006-01-02 15:04:05-0700", string(result["created"])+offset)
  119. case setting.UsePostgreSQL:
  120. created, _ = time.Parse("2006-01-02T15:04:05Z-0700", string(result["created"])+offset)
  121. }
  122. // find owner of repository
  123. parts := strings.SplitN(repoRefName, "/", 2)
  124. ownerName := parts[0]
  125. repoName := parts[1]
  126. results, err := sess.Query("SELECT u.id as `uid`, ou.uid as `memberid` FROM `user` u LEFT JOIN org_user ou ON ou.org_id=u.id WHERE u.lower_name=?", ownerName)
  127. if err != nil {
  128. sess.Rollback()
  129. return err
  130. }
  131. if len(results) < 1 {
  132. continue
  133. }
  134. ownerID := com.StrTo(results[0]["uid"]).MustInt64()
  135. if ownerID == userID {
  136. continue
  137. }
  138. // test if user is member of owning organization
  139. isMember := false
  140. for _, member := range results {
  141. memberID := com.StrTo(member["memberid"]).MustInt64()
  142. // We can skip all cases that a user is member of the owning organization
  143. if memberID == userID {
  144. isMember = true
  145. }
  146. }
  147. if isMember {
  148. continue
  149. }
  150. results, err = sess.Query("SELECT id FROM `repository` WHERE owner_id=? AND lower_name=?", ownerID, repoName)
  151. if err != nil {
  152. return err
  153. } else if len(results) < 1 {
  154. continue
  155. }
  156. collaboration := &Collaboration{
  157. UserID: userID,
  158. RepoID: com.StrTo(results[0]["id"]).MustInt64(),
  159. }
  160. has, err := sess.Get(collaboration)
  161. if err != nil {
  162. sess.Rollback()
  163. return err
  164. } else if has {
  165. continue
  166. }
  167. collaboration.Created = created
  168. if _, err = sess.InsertOne(collaboration); err != nil {
  169. sess.Rollback()
  170. return err
  171. }
  172. }
  173. return sess.Commit()
  174. }