mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Split migrations folder (#21549)
There are too many files in `models/migrations` folder so that I split them into sub folders.
This commit is contained in:
15
models/migrations/v1_14/main_test.go
Normal file
15
models/migrations/v1_14/main_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
base.MainTest(m)
|
||||
}
|
22
models/migrations/v1_14/v155.go
Normal file
22
models/migrations/v1_14/v155.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddChangedProtectedFilesPullRequestColumn(x *xorm.Engine) error {
|
||||
type PullRequest struct {
|
||||
ChangedProtectedFiles []string `xorm:"TEXT JSON"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(PullRequest)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
178
models/migrations/v1_14/v156.go
Normal file
178
models/migrations/v1_14/v156.go
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Copy paste from models/repo.go because we cannot import models package
|
||||
func repoPath(userName, repoName string) string {
|
||||
return filepath.Join(userPath(userName), strings.ToLower(repoName)+".git")
|
||||
}
|
||||
|
||||
func userPath(userName string) string {
|
||||
return filepath.Join(setting.RepoRootPath, strings.ToLower(userName))
|
||||
}
|
||||
|
||||
func FixPublisherIDforTagReleases(x *xorm.Engine) error {
|
||||
type Release struct {
|
||||
ID int64
|
||||
RepoID int64
|
||||
Sha1 string
|
||||
TagName string
|
||||
PublisherID int64
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
ID int64
|
||||
OwnerID int64
|
||||
OwnerName string
|
||||
Name string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
const batchSize = 100
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
var (
|
||||
repo *Repository
|
||||
gitRepo *git.Repository
|
||||
user *User
|
||||
)
|
||||
defer func() {
|
||||
if gitRepo != nil {
|
||||
gitRepo.Close()
|
||||
}
|
||||
}()
|
||||
for start := 0; ; start += batchSize {
|
||||
releases := make([]*Release, 0, batchSize)
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Limit(batchSize, start).
|
||||
Where("publisher_id = 0 OR publisher_id is null").
|
||||
Asc("repo_id", "id").Where("is_tag=?", true).
|
||||
Find(&releases); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(releases) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, release := range releases {
|
||||
if repo == nil || repo.ID != release.RepoID {
|
||||
if gitRepo != nil {
|
||||
gitRepo.Close()
|
||||
gitRepo = nil
|
||||
}
|
||||
repo = new(Repository)
|
||||
has, err := sess.ID(release.RepoID).Get(repo)
|
||||
if err != nil {
|
||||
log.Error("Error whilst loading repository[%d] for release[%d] with tag name %s. Error: %v", release.RepoID, release.ID, release.TagName, err)
|
||||
return err
|
||||
} else if !has {
|
||||
log.Warn("Release[%d] is orphaned and refers to non-existing repository %d", release.ID, release.RepoID)
|
||||
log.Warn("This release should be deleted")
|
||||
continue
|
||||
}
|
||||
|
||||
if repo.OwnerName == "" {
|
||||
// v120.go migration may not have been run correctly - we'll just replicate it here
|
||||
// because this appears to be a common-ish problem.
|
||||
if _, err := sess.Exec("UPDATE repository SET owner_name = (SELECT name FROM `user` WHERE `user`.id = repository.owner_id)"); err != nil {
|
||||
log.Error("Error whilst updating repository[%d] owner name", repo.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := sess.ID(release.RepoID).Get(repo); err != nil {
|
||||
log.Error("Error whilst loading repository[%d] for release[%d] with tag name %s. Error: %v", release.RepoID, release.ID, release.TagName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
gitRepo, err = git.OpenRepository(git.DefaultContext, repoPath(repo.OwnerName, repo.Name))
|
||||
if err != nil {
|
||||
log.Error("Error whilst opening git repo for [%d]%s/%s. Error: %v", repo.ID, repo.OwnerName, repo.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
commit, err := gitRepo.GetTagCommit(release.TagName)
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
log.Warn("Unable to find commit %s for Tag: %s in [%d]%s/%s. Cannot update publisher ID.", err.(git.ErrNotExist).ID, release.TagName, repo.ID, repo.OwnerName, repo.Name)
|
||||
continue
|
||||
}
|
||||
log.Error("Error whilst getting commit for Tag: %s in [%d]%s/%s. Error: %v", release.TagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
return fmt.Errorf("GetTagCommit: %w", err)
|
||||
}
|
||||
|
||||
if commit.Author.Email == "" {
|
||||
log.Warn("Tag: %s in Repo[%d]%s/%s does not have a tagger.", release.TagName, repo.ID, repo.OwnerName, repo.Name)
|
||||
commit, err = gitRepo.GetCommit(commit.ID.String())
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
log.Warn("Unable to find commit %s for Tag: %s in [%d]%s/%s. Cannot update publisher ID.", err.(git.ErrNotExist).ID, release.TagName, repo.ID, repo.OwnerName, repo.Name)
|
||||
continue
|
||||
}
|
||||
log.Error("Error whilst getting commit for Tag: %s in [%d]%s/%s. Error: %v", release.TagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
return fmt.Errorf("GetCommit: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if commit.Author.Email == "" {
|
||||
log.Warn("Tag: %s in Repo[%d]%s/%s does not have a Tagger and its underlying commit does not have an Author either!", release.TagName, repo.ID, repo.OwnerName, repo.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if user == nil || !strings.EqualFold(user.Email, commit.Author.Email) {
|
||||
user = new(User)
|
||||
_, err = sess.Where("email=?", commit.Author.Email).Get(user)
|
||||
if err != nil {
|
||||
log.Error("Error whilst getting commit author by email: %s for Tag: %s in [%d]%s/%s. Error: %v", commit.Author.Email, release.TagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
user.Email = commit.Author.Email
|
||||
}
|
||||
|
||||
if user.ID <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
release.PublisherID = user.ID
|
||||
if _, err := sess.ID(release.ID).Cols("publisher_id").Update(release); err != nil {
|
||||
log.Error("Error whilst updating publisher[%d] for release[%d] with tag name %s. Error: %v", release.PublisherID, release.ID, release.TagName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if gitRepo != nil {
|
||||
gitRepo.Close()
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
67
models/migrations/v1_14/v157.go
Normal file
67
models/migrations/v1_14/v157.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func FixRepoTopics(x *xorm.Engine) error {
|
||||
type Topic struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Name string `xorm:"UNIQUE VARCHAR(25)"`
|
||||
RepoCount int
|
||||
}
|
||||
|
||||
type RepoTopic struct {
|
||||
RepoID int64 `xorm:"pk"`
|
||||
TopicID int64 `xorm:"pk"`
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Topics []string `xorm:"TEXT JSON"`
|
||||
}
|
||||
|
||||
const batchSize = 100
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
repos := make([]*Repository, 0, batchSize)
|
||||
topics := make([]string, 0, batchSize)
|
||||
for start := 0; ; start += batchSize {
|
||||
repos = repos[:0]
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Limit(batchSize, start).Find(&repos); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(repos) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
topics = topics[:0]
|
||||
if err := sess.Select("name").Table("topic").
|
||||
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
|
||||
Where("repo_topic.repo_id = ?", repo.ID).Desc("topic.repo_count").Find(&topics); err != nil {
|
||||
return err
|
||||
}
|
||||
repo.Topics = topics
|
||||
if _, err := sess.ID(repo.ID).Cols("topics").Update(repo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
112
models/migrations/v1_14/v158.go
Normal file
112
models/migrations/v1_14/v158.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func UpdateCodeCommentReplies(x *xorm.Engine) error {
|
||||
type Comment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CommitSHA string `xorm:"VARCHAR(40)"`
|
||||
Patch string `xorm:"TEXT patch"`
|
||||
Invalidated bool
|
||||
|
||||
// Not extracted but used in the below query
|
||||
Type int `xorm:"INDEX"`
|
||||
Line int64 // - previous line / + proposed line
|
||||
TreePath string
|
||||
ReviewID int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Comment)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlSelect := `SELECT comment.id as id, first.commit_sha as commit_sha, first.patch as patch, first.invalidated as invalidated`
|
||||
sqlTail := ` FROM comment INNER JOIN (
|
||||
SELECT C.id, C.review_id, C.line, C.tree_path, C.patch, C.commit_sha, C.invalidated
|
||||
FROM comment AS C
|
||||
WHERE C.type = 21
|
||||
AND C.created_unix =
|
||||
(SELECT MIN(comment.created_unix)
|
||||
FROM comment
|
||||
WHERE comment.review_id = C.review_id
|
||||
AND comment.type = 21
|
||||
AND comment.line = C.line
|
||||
AND comment.tree_path = C.tree_path)
|
||||
) AS first
|
||||
ON comment.review_id = first.review_id
|
||||
AND comment.tree_path = first.tree_path AND comment.line = first.line
|
||||
WHERE comment.type = 21
|
||||
AND comment.id != first.id
|
||||
AND comment.commit_sha != first.commit_sha`
|
||||
|
||||
var (
|
||||
sqlCmd string
|
||||
start = 0
|
||||
batchSize = 100
|
||||
sess = x.NewSession()
|
||||
)
|
||||
defer sess.Close()
|
||||
for {
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if setting.Database.UseMSSQL {
|
||||
if _, err := sess.Exec(sqlSelect + " INTO #temp_comments" + sqlTail); err != nil {
|
||||
log.Error("unable to create temporary table")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
comments := make([]*Comment, 0, batchSize)
|
||||
|
||||
switch {
|
||||
case setting.Database.UseMySQL:
|
||||
sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + ", " + strconv.Itoa(start)
|
||||
case setting.Database.UsePostgreSQL:
|
||||
fallthrough
|
||||
case setting.Database.UseSQLite3:
|
||||
sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + " OFFSET " + strconv.Itoa(start)
|
||||
case setting.Database.UseMSSQL:
|
||||
sqlCmd = "SELECT TOP " + strconv.Itoa(batchSize) + " * FROM #temp_comments WHERE " +
|
||||
"(id NOT IN ( SELECT TOP " + strconv.Itoa(start) + " id FROM #temp_comments ORDER BY id )) ORDER BY id"
|
||||
default:
|
||||
return fmt.Errorf("Unsupported database type")
|
||||
}
|
||||
|
||||
if err := sess.SQL(sqlCmd).Find(&comments); err != nil {
|
||||
log.Error("failed to select: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, comment := range comments {
|
||||
if _, err := sess.Table("comment").ID(comment.ID).Cols("commit_sha", "patch", "invalidated").Update(comment); err != nil {
|
||||
log.Error("failed to update comment[%d]: %v %v", comment.ID, comment, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
start += len(comments)
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(comments) < batchSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
39
models/migrations/v1_14/v159.go
Normal file
39
models/migrations/v1_14/v159.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func UpdateReactionConstraint(x *xorm.Engine) error {
|
||||
// Reaction represents a reactions on issues and comments.
|
||||
type Reaction struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
|
||||
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
|
||||
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
|
||||
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
|
||||
OriginalAuthorID int64 `xorm:"INDEX UNIQUE(s) NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthor string `xorm:"INDEX UNIQUE(s)"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := base.RecreateTable(sess, &Reaction{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
17
models/migrations/v1_14/v160.go
Normal file
17
models/migrations/v1_14/v160.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddBlockOnOfficialReviewRequests(x *xorm.Engine) error {
|
||||
type ProtectedBranch struct {
|
||||
BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync2(new(ProtectedBranch))
|
||||
}
|
74
models/migrations/v1_14/v161.go
Normal file
74
models/migrations/v1_14/v161.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func ConvertTaskTypeToString(x *xorm.Engine) error {
|
||||
const (
|
||||
GOGS int = iota + 1
|
||||
SLACK
|
||||
GITEA
|
||||
DISCORD
|
||||
DINGTALK
|
||||
TELEGRAM
|
||||
MSTEAMS
|
||||
FEISHU
|
||||
MATRIX
|
||||
WECHATWORK
|
||||
)
|
||||
|
||||
hookTaskTypes := map[int]string{
|
||||
GITEA: "gitea",
|
||||
GOGS: "gogs",
|
||||
SLACK: "slack",
|
||||
DISCORD: "discord",
|
||||
DINGTALK: "dingtalk",
|
||||
TELEGRAM: "telegram",
|
||||
MSTEAMS: "msteams",
|
||||
FEISHU: "feishu",
|
||||
MATRIX: "matrix",
|
||||
WECHATWORK: "wechatwork",
|
||||
}
|
||||
|
||||
type HookTask struct {
|
||||
Typ string `xorm:"VARCHAR(16) index"`
|
||||
}
|
||||
if err := x.Sync2(new(HookTask)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// to keep the migration could be rerun
|
||||
exist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "hook_task", "type")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, s := range hookTaskTypes {
|
||||
if _, err := x.Exec("UPDATE hook_task set typ = ? where `type`=?", s, i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := base.DropTableColumns(sess, "hook_task", "type"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
63
models/migrations/v1_14/v162.go
Normal file
63
models/migrations/v1_14/v162.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func ConvertWebhookTaskTypeToString(x *xorm.Engine) error {
|
||||
const (
|
||||
GOGS int = iota + 1
|
||||
SLACK
|
||||
GITEA
|
||||
DISCORD
|
||||
DINGTALK
|
||||
TELEGRAM
|
||||
MSTEAMS
|
||||
FEISHU
|
||||
MATRIX
|
||||
WECHATWORK
|
||||
)
|
||||
|
||||
hookTaskTypes := map[int]string{
|
||||
GITEA: "gitea",
|
||||
GOGS: "gogs",
|
||||
SLACK: "slack",
|
||||
DISCORD: "discord",
|
||||
DINGTALK: "dingtalk",
|
||||
TELEGRAM: "telegram",
|
||||
MSTEAMS: "msteams",
|
||||
FEISHU: "feishu",
|
||||
MATRIX: "matrix",
|
||||
WECHATWORK: "wechatwork",
|
||||
}
|
||||
|
||||
type Webhook struct {
|
||||
Type string `xorm:"char(16) index"`
|
||||
}
|
||||
if err := x.Sync2(new(Webhook)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, s := range hookTaskTypes {
|
||||
if _, err := x.Exec("UPDATE webhook set type = ? where hook_task_type=?", s, i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := base.DropTableColumns(sess, "webhook", "hook_task_type"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
36
models/migrations/v1_14/v163.go
Normal file
36
models/migrations/v1_14/v163.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func ConvertTopicNameFrom25To50(x *xorm.Engine) error {
|
||||
type Topic struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Name string `xorm:"UNIQUE VARCHAR(50)"`
|
||||
RepoCount int
|
||||
CreatedUnix int64 `xorm:"INDEX created"`
|
||||
UpdatedUnix int64 `xorm:"INDEX updated"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Topic)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := base.RecreateTable(sess, new(Topic)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
38
models/migrations/v1_14/v164.go
Normal file
38
models/migrations/v1_14/v164.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// OAuth2Grant here is a snapshot of models.OAuth2Grant for this version
|
||||
// of the database, as it does not appear to have been added as a part
|
||||
// of a previous migration.
|
||||
type OAuth2Grant struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 `xorm:"INDEX unique(user_application)"`
|
||||
ApplicationID int64 `xorm:"INDEX unique(user_application)"`
|
||||
Counter int64 `xorm:"NOT NULL DEFAULT 1"`
|
||||
Scope string `xorm:"TEXT"`
|
||||
Nonce string `xorm:"TEXT"`
|
||||
CreatedUnix int64 `xorm:"created"`
|
||||
UpdatedUnix int64 `xorm:"updated"`
|
||||
}
|
||||
|
||||
// TableName sets the database table name to be the correct one, as the
|
||||
// autogenerated table name for this struct is "o_auth2_grant".
|
||||
func (grant *OAuth2Grant) TableName() string {
|
||||
return "oauth2_grant"
|
||||
}
|
||||
|
||||
func AddScopeAndNonceColumnsToOAuth2Grant(x *xorm.Engine) error {
|
||||
if err := x.Sync2(new(OAuth2Grant)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
70
models/migrations/v1_14/v165.go
Normal file
70
models/migrations/v1_14/v165.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error {
|
||||
dbType := x.Dialect().URI().DBType
|
||||
if dbType == schemas.SQLITE { // For SQLITE, varchar or char will always be represented as TEXT
|
||||
return nil
|
||||
}
|
||||
|
||||
type HookTask struct {
|
||||
Typ string `xorm:"VARCHAR(16) index"`
|
||||
}
|
||||
|
||||
if err := base.ModifyColumn(x, "hook_task", &schemas.Column{
|
||||
Name: "typ",
|
||||
SQLType: schemas.SQLType{
|
||||
Name: "VARCHAR",
|
||||
},
|
||||
Length: 16,
|
||||
Nullable: true, // To keep compatible as nullable
|
||||
DefaultIsEmpty: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hookTaskTrimSQL string
|
||||
if dbType == schemas.MSSQL {
|
||||
hookTaskTrimSQL = "UPDATE hook_task SET typ = RTRIM(LTRIM(typ))"
|
||||
} else {
|
||||
hookTaskTrimSQL = "UPDATE hook_task SET typ = TRIM(typ)"
|
||||
}
|
||||
if _, err := x.Exec(hookTaskTrimSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Webhook struct {
|
||||
Type string `xorm:"VARCHAR(16) index"`
|
||||
}
|
||||
|
||||
if err := base.ModifyColumn(x, "webhook", &schemas.Column{
|
||||
Name: "type",
|
||||
SQLType: schemas.SQLType{
|
||||
Name: "VARCHAR",
|
||||
},
|
||||
Length: 16,
|
||||
Nullable: true, // To keep compatible as nullable
|
||||
DefaultIsEmpty: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var webhookTrimSQL string
|
||||
if dbType == schemas.MSSQL {
|
||||
webhookTrimSQL = "UPDATE webhook SET type = RTRIM(LTRIM(type))"
|
||||
} else {
|
||||
webhookTrimSQL = "UPDATE webhook SET type = TRIM(type)"
|
||||
}
|
||||
_, err := x.Exec(webhookTrimSQL)
|
||||
return err
|
||||
}
|
113
models/migrations/v1_14/v166.go
Normal file
113
models/migrations/v1_14/v166.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func RecalculateUserEmptyPWD(x *xorm.Engine) (err error) {
|
||||
const (
|
||||
algoBcrypt = "bcrypt"
|
||||
algoScrypt = "scrypt"
|
||||
algoArgon2 = "argon2"
|
||||
algoPbkdf2 = "pbkdf2"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Passwd string `xorm:"NOT NULL"`
|
||||
PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"`
|
||||
MustChangePassword bool `xorm:"NOT NULL DEFAULT false"`
|
||||
LoginType int
|
||||
LoginName string
|
||||
Type int
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
}
|
||||
|
||||
// hashPassword hash password based on algo and salt
|
||||
// state 461406070c
|
||||
hashPassword := func(passwd, salt, algo string) string {
|
||||
var tempPasswd []byte
|
||||
|
||||
switch algo {
|
||||
case algoBcrypt:
|
||||
tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
|
||||
return string(tempPasswd)
|
||||
case algoScrypt:
|
||||
tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50)
|
||||
case algoArgon2:
|
||||
tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50)
|
||||
case algoPbkdf2:
|
||||
fallthrough
|
||||
default:
|
||||
tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", tempPasswd)
|
||||
}
|
||||
|
||||
// ValidatePassword checks if given password matches the one belongs to the user.
|
||||
// state 461406070c, changed since it's not necessary to be time constant
|
||||
ValidatePassword := func(u *User, passwd string) bool {
|
||||
tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo)
|
||||
|
||||
if u.PasswdHashAlgo != algoBcrypt && u.Passwd == tempHash {
|
||||
return true
|
||||
}
|
||||
if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
const batchSize = 100
|
||||
|
||||
for start := 0; ; start += batchSize {
|
||||
users := make([]*User, 0, batchSize)
|
||||
if err = sess.Limit(batchSize, start).Where(builder.Neq{"passwd": ""}, 0).Find(&users); err != nil {
|
||||
return
|
||||
}
|
||||
if len(users) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if err = sess.Begin(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if ValidatePassword(user, "") {
|
||||
user.Passwd = ""
|
||||
user.Salt = ""
|
||||
user.PasswdHashAlgo = ""
|
||||
if _, err = sess.ID(user.ID).Cols("passwd", "salt", "passwd_hash_algo").Update(user); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// delete salt and algo where password is empty
|
||||
_, err = sess.Where(builder.Eq{"passwd": ""}.And(builder.Neq{"salt": ""}.Or(builder.Neq{"passwd_hash_algo": ""}))).
|
||||
Cols("salt", "passwd_hash_algo").Update(&User{})
|
||||
|
||||
return err
|
||||
}
|
24
models/migrations/v1_14/v167.go
Normal file
24
models/migrations/v1_14/v167.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddUserRedirect(x *xorm.Engine) (err error) {
|
||||
type UserRedirect struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
RedirectUserID int64
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(UserRedirect)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
11
models/migrations/v1_14/v168.go
Normal file
11
models/migrations/v1_14/v168.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func RecreateUserTableToFixDefaultValues(_ *xorm.Engine) error {
|
||||
return nil
|
||||
}
|
14
models/migrations/v1_14/v169.go
Normal file
14
models/migrations/v1_14/v169.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func CommentTypeDeleteBranchUseOldRef(x *xorm.Engine) error {
|
||||
_, err := x.Exec("UPDATE comment SET old_ref = commit_sha, commit_sha = '' WHERE type = 11")
|
||||
return err
|
||||
}
|
22
models/migrations/v1_14/v170.go
Normal file
22
models/migrations/v1_14/v170.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddDismissedReviewColumn(x *xorm.Engine) error {
|
||||
type Review struct {
|
||||
Dismissed bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Review)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
22
models/migrations/v1_14/v171.go
Normal file
22
models/migrations/v1_14/v171.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddSortingColToProjectBoard(x *xorm.Engine) error {
|
||||
type ProjectBoard struct {
|
||||
Sorting int8 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(ProjectBoard)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
20
models/migrations/v1_14/v172.go
Normal file
20
models/migrations/v1_14/v172.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2020 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddSessionTable(x *xorm.Engine) error {
|
||||
type Session struct {
|
||||
Key string `xorm:"pk CHAR(16)"`
|
||||
Data []byte `xorm:"BLOB"`
|
||||
Expiry timeutil.TimeStamp
|
||||
}
|
||||
return x.Sync2(new(Session))
|
||||
}
|
22
models/migrations/v1_14/v173.go
Normal file
22
models/migrations/v1_14/v173.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddTimeIDCommentColumn(x *xorm.Engine) error {
|
||||
type Comment struct {
|
||||
TimeID int64
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Comment)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
35
models/migrations/v1_14/v174.go
Normal file
35
models/migrations/v1_14/v174.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddRepoTransfer(x *xorm.Engine) error {
|
||||
type RepoTransfer struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
DoerID int64
|
||||
RecipientID int64
|
||||
RepoID int64
|
||||
TeamIDs []int64
|
||||
CreatedUnix int64 `xorm:"INDEX NOT NULL created"`
|
||||
UpdatedUnix int64 `xorm:"INDEX NOT NULL updated"`
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Sync2(new(RepoTransfer)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
54
models/migrations/v1_14/v175.go
Normal file
54
models/migrations/v1_14/v175.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func FixPostgresIDSequences(x *xorm.Engine) error {
|
||||
if !setting.Database.UsePostgreSQL {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sequences []string
|
||||
schema := sess.Engine().Dialect().URI().Schema
|
||||
|
||||
sess.Engine().SetSchema("")
|
||||
if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__%_id_seq%' AND sequence_catalog = ?", setting.Database.Name).Find(&sequences); err != nil {
|
||||
log.Error("Unable to find sequences: %v", err)
|
||||
return err
|
||||
}
|
||||
sess.Engine().SetSchema(schema)
|
||||
|
||||
sequenceRegexp := regexp.MustCompile(`tmp_recreate__(\w+)_id_seq.*`)
|
||||
|
||||
for _, sequence := range sequences {
|
||||
tableName := sequenceRegexp.FindStringSubmatch(sequence)[1]
|
||||
newSequenceName := tableName + "_id_seq"
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER SEQUENCE `%s` RENAME TO `%s`", sequence, newSequenceName)); err != nil {
|
||||
log.Error("Unable to rename %s to %s. Error: %v", sequence, newSequenceName, err)
|
||||
return err
|
||||
}
|
||||
if _, err := sess.Exec(fmt.Sprintf("SELECT setval('%s', COALESCE((SELECT MAX(id)+1 FROM `%s`), 1), false)", newSequenceName, tableName)); err != nil {
|
||||
log.Error("Unable to reset sequence %s for %s. Error: %v", newSequenceName, tableName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
77
models/migrations/v1_14/v176.go
Normal file
77
models/migrations/v1_14/v176.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// RemoveInvalidLabels looks through the database to look for comments and issue_labels
|
||||
// that refer to labels do not belong to the repository or organization that repository
|
||||
// that the issue is in
|
||||
func RemoveInvalidLabels(x *xorm.Engine) error {
|
||||
type Comment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type int `xorm:"INDEX"`
|
||||
IssueID int64 `xorm:"INDEX"`
|
||||
LabelID int64
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"`
|
||||
Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository.
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"UNIQUE(s) index"`
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
type Label struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
type IssueLabel struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IssueID int64 `xorm:"UNIQUE(s)"`
|
||||
LabelID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Comment), new(Issue), new(Repository), new(Label), new(IssueLabel)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := x.Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
|
||||
SELECT il_too.id FROM (
|
||||
SELECT il_too_too.id
|
||||
FROM issue_label AS il_too_too
|
||||
INNER JOIN label ON il_too_too.label_id = label.id
|
||||
INNER JOIN issue on issue.id = il_too_too.issue_id
|
||||
INNER JOIN repository on repository.id = issue.repo_id
|
||||
WHERE
|
||||
(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
|
||||
) AS il_too )`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := x.Exec(`DELETE FROM comment WHERE comment.id IN (
|
||||
SELECT il_too.id FROM (
|
||||
SELECT com.id
|
||||
FROM comment AS com
|
||||
INNER JOIN label ON com.label_id = label.id
|
||||
INNER JOIN issue on issue.id = com.issue_id
|
||||
INNER JOIN repository on repository.id = issue.repo_id
|
||||
WHERE
|
||||
com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
|
||||
) AS il_too)`, 7); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
129
models/migrations/v1_14/v176_test.go
Normal file
129
models/migrations/v1_14/v176_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_RemoveInvalidLabels(t *testing.T) {
|
||||
// Models used by the migration
|
||||
type Comment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type int `xorm:"INDEX"`
|
||||
IssueID int64 `xorm:"INDEX"`
|
||||
LabelID int64
|
||||
ShouldRemain bool // <- Flag for testing the migration
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"`
|
||||
Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository.
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"UNIQUE(s) index"`
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
type Label struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
type IssueLabel struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IssueID int64 `xorm:"UNIQUE(s)"`
|
||||
LabelID int64 `xorm:"UNIQUE(s)"`
|
||||
ShouldRemain bool // <- Flag for testing the migration
|
||||
}
|
||||
|
||||
// load and prepare the test database
|
||||
x, deferable := base.PrepareTestEnv(t, 0, new(Comment), new(Issue), new(Repository), new(IssueLabel), new(Label))
|
||||
if x == nil || t.Failed() {
|
||||
defer deferable()
|
||||
return
|
||||
}
|
||||
defer deferable()
|
||||
|
||||
var issueLabels []*IssueLabel
|
||||
ilPreMigration := map[int64]*IssueLabel{}
|
||||
ilPostMigration := map[int64]*IssueLabel{}
|
||||
|
||||
var comments []*Comment
|
||||
comPreMigration := map[int64]*Comment{}
|
||||
comPostMigration := map[int64]*Comment{}
|
||||
|
||||
// Get pre migration values
|
||||
if err := x.Find(&issueLabels); err != nil {
|
||||
t.Errorf("Unable to find issueLabels: %v", err)
|
||||
return
|
||||
}
|
||||
for _, issueLabel := range issueLabels {
|
||||
ilPreMigration[issueLabel.ID] = issueLabel
|
||||
}
|
||||
if err := x.Find(&comments); err != nil {
|
||||
t.Errorf("Unable to find comments: %v", err)
|
||||
return
|
||||
}
|
||||
for _, comment := range comments {
|
||||
comPreMigration[comment.ID] = comment
|
||||
}
|
||||
|
||||
// Run the migration
|
||||
if err := RemoveInvalidLabels(x); err != nil {
|
||||
t.Errorf("unable to RemoveInvalidLabels: %v", err)
|
||||
}
|
||||
|
||||
// Get the post migration values
|
||||
issueLabels = issueLabels[:0]
|
||||
if err := x.Find(&issueLabels); err != nil {
|
||||
t.Errorf("Unable to find issueLabels: %v", err)
|
||||
return
|
||||
}
|
||||
for _, issueLabel := range issueLabels {
|
||||
ilPostMigration[issueLabel.ID] = issueLabel
|
||||
}
|
||||
comments = comments[:0]
|
||||
if err := x.Find(&comments); err != nil {
|
||||
t.Errorf("Unable to find comments: %v", err)
|
||||
return
|
||||
}
|
||||
for _, comment := range comments {
|
||||
comPostMigration[comment.ID] = comment
|
||||
}
|
||||
|
||||
// Finally test results of the migration
|
||||
for id, comment := range comPreMigration {
|
||||
post, ok := comPostMigration[id]
|
||||
if ok {
|
||||
if !comment.ShouldRemain {
|
||||
t.Errorf("Comment[%d] remained but should have been deleted", id)
|
||||
}
|
||||
assert.Equal(t, comment, post)
|
||||
} else if comment.ShouldRemain {
|
||||
t.Errorf("Comment[%d] was deleted but should have remained", id)
|
||||
}
|
||||
}
|
||||
|
||||
for id, il := range ilPreMigration {
|
||||
post, ok := ilPostMigration[id]
|
||||
if ok {
|
||||
if !il.ShouldRemain {
|
||||
t.Errorf("IssueLabel[%d] remained but should have been deleted", id)
|
||||
}
|
||||
assert.Equal(t, il, post)
|
||||
} else if il.ShouldRemain {
|
||||
t.Errorf("IssueLabel[%d] was deleted but should have remained", id)
|
||||
}
|
||||
}
|
||||
}
|
43
models/migrations/v1_14/v177.go
Normal file
43
models/migrations/v1_14/v177.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// DeleteOrphanedIssueLabels looks through the database for issue_labels where the label no longer exists and deletes them.
|
||||
func DeleteOrphanedIssueLabels(x *xorm.Engine) error {
|
||||
type IssueLabel struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IssueID int64 `xorm:"UNIQUE(s)"`
|
||||
LabelID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Sync2(new(IssueLabel)); err != nil {
|
||||
return fmt.Errorf("Sync2: %w", err)
|
||||
}
|
||||
|
||||
if _, err := sess.Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
|
||||
SELECT ill.id FROM (
|
||||
SELECT il.id
|
||||
FROM issue_label AS il
|
||||
LEFT JOIN label ON il.label_id = label.id
|
||||
WHERE
|
||||
label.id IS NULL
|
||||
) AS ill)`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
89
models/migrations/v1_14/v177_test.go
Normal file
89
models/migrations/v1_14/v177_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2021 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 v1_14 //nolint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_DeleteOrphanedIssueLabels(t *testing.T) {
|
||||
// Create the models used in the migration
|
||||
type IssueLabel struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IssueID int64 `xorm:"UNIQUE(s)"`
|
||||
LabelID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
type Label struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Description string
|
||||
Color string `xorm:"VARCHAR(7)"`
|
||||
NumIssues int
|
||||
NumClosedIssues int
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
}
|
||||
|
||||
// Prepare and load the testing database
|
||||
x, deferable := base.PrepareTestEnv(t, 0, new(IssueLabel), new(Label))
|
||||
if x == nil || t.Failed() {
|
||||
defer deferable()
|
||||
return
|
||||
}
|
||||
defer deferable()
|
||||
|
||||
var issueLabels []*IssueLabel
|
||||
preMigration := map[int64]*IssueLabel{}
|
||||
postMigration := map[int64]*IssueLabel{}
|
||||
|
||||
// Load issue labels that exist in the database pre-migration
|
||||
if err := x.Find(&issueLabels); err != nil {
|
||||
assert.NoError(t, err)
|
||||
return
|
||||
}
|
||||
for _, issueLabel := range issueLabels {
|
||||
preMigration[issueLabel.ID] = issueLabel
|
||||
}
|
||||
|
||||
// Run the migration
|
||||
if err := DeleteOrphanedIssueLabels(x); err != nil {
|
||||
assert.NoError(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Load the remaining issue-labels
|
||||
issueLabels = issueLabels[:0]
|
||||
if err := x.Find(&issueLabels); err != nil {
|
||||
assert.NoError(t, err)
|
||||
return
|
||||
}
|
||||
for _, issueLabel := range issueLabels {
|
||||
postMigration[issueLabel.ID] = issueLabel
|
||||
}
|
||||
|
||||
// Now test what is left
|
||||
if _, ok := postMigration[2]; ok {
|
||||
t.Errorf("Orphaned Label[2] survived the migration")
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := postMigration[5]; ok {
|
||||
t.Errorf("Orphaned Label[5] survived the migration")
|
||||
return
|
||||
}
|
||||
|
||||
for id, post := range postMigration {
|
||||
pre := preMigration[id]
|
||||
assert.Equal(t, pre, post, "migration changed issueLabel %d", id)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user