diff options
Diffstat (limited to '')
24 files changed, 899 insertions, 0 deletions
diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go new file mode 100644 index 0000000..2005789 --- /dev/null +++ b/models/migrations/v1_22/main_test.go @@ -0,0 +1,14 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "testing" + + migration_tests "code.gitea.io/gitea/models/migrations/test" +) + +func TestMain(m *testing.M) { + migration_tests.MainTest(m) +} diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go new file mode 100644 index 0000000..a8ee4a3 --- /dev/null +++ b/models/migrations/v1_22/v280.go @@ -0,0 +1,29 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "xorm.io/xorm" +) + +func RenameUserThemes(x *xorm.Engine) error { + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + if _, err := sess.Exec("UPDATE `user` SET `theme` = 'gitea-light' WHERE `theme` = 'gitea'"); err != nil { + return err + } + if _, err := sess.Exec("UPDATE `user` SET `theme` = 'gitea-dark' WHERE `theme` = 'arc-green'"); err != nil { + return err + } + if _, err := sess.Exec("UPDATE `user` SET `theme` = 'gitea-auto' WHERE `theme` = 'auto'"); err != nil { + return err + } + + return sess.Commit() +} diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go new file mode 100644 index 0000000..fc1866a --- /dev/null +++ b/models/migrations/v1_22/v281.go @@ -0,0 +1,21 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func CreateAuthTokenTable(x *xorm.Engine) error { + type AuthToken struct { + ID string `xorm:"pk"` + TokenHash string + UserID int64 `xorm:"INDEX"` + ExpiresUnix timeutil.TimeStamp `xorm:"INDEX"` + } + + return x.Sync(new(AuthToken)) +} diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go new file mode 100644 index 0000000..baad9e0 --- /dev/null +++ b/models/migrations/v1_22/v282.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "xorm.io/xorm" +) + +func AddIndexToPullAutoMergeDoerID(x *xorm.Engine) error { + type PullAutoMerge struct { + DoerID int64 `xorm:"INDEX NOT NULL"` + } + + return x.Sync(&PullAutoMerge{}) +} diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go new file mode 100644 index 0000000..86946d1 --- /dev/null +++ b/models/migrations/v1_22/v283.go @@ -0,0 +1,38 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "xorm.io/xorm" +) + +func AddCombinedIndexToIssueUser(x *xorm.Engine) error { + type OldIssueUser struct { + IssueID int64 + UID int64 + Cnt int64 + } + + var duplicatedIssueUsers []OldIssueUser + if err := x.SQL("select * from (select issue_id, uid, count(1) as cnt from issue_user group by issue_id, uid) a where a.cnt > 1"). + Find(&duplicatedIssueUsers); err != nil { + return err + } + for _, issueUser := range duplicatedIssueUsers { + var ids []int64 + if err := x.SQL("SELECT id FROM issue_user WHERE issue_id = ? and uid = ? limit ?", issueUser.IssueID, issueUser.UID, issueUser.Cnt-1).Find(&ids); err != nil { + return err + } + if _, err := x.Table("issue_user").In("id", ids).Delete(); err != nil { + return err + } + } + + type IssueUser struct { + UID int64 `xorm:"INDEX unique(uid_to_issue)"` // User ID. + IssueID int64 `xorm:"INDEX unique(uid_to_issue)"` + } + + return x.Sync(&IssueUser{}) +} diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go new file mode 100644 index 0000000..5f6c04a --- /dev/null +++ b/models/migrations/v1_22/v283_test.go @@ -0,0 +1,28 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "testing" + + migration_tests "code.gitea.io/gitea/models/migrations/test" + + "github.com/stretchr/testify/require" +) + +func Test_AddCombinedIndexToIssueUser(t *testing.T) { + type IssueUser struct { // old struct + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"INDEX"` // User ID. + IssueID int64 `xorm:"INDEX"` + IsRead bool + IsMentioned bool + } + + // Prepare and load the testing database + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(IssueUser)) + defer deferable() + + require.NoError(t, AddCombinedIndexToIssueUser(x)) +} diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go new file mode 100644 index 0000000..2b95078 --- /dev/null +++ b/models/migrations/v1_22/v284.go @@ -0,0 +1,18 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint +import ( + "xorm.io/xorm" +) + +func AddIgnoreStaleApprovalsColumnToProtectedBranchTable(x *xorm.Engine) error { + type ProtectedBranch struct { + IgnoreStaleApprovals bool `xorm:"NOT NULL DEFAULT false"` + } + _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreIndices: true, + IgnoreConstrains: true, + }, new(ProtectedBranch)) + return err +} diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go new file mode 100644 index 0000000..a55cc17 --- /dev/null +++ b/models/migrations/v1_22/v285.go @@ -0,0 +1,22 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "time" + + "xorm.io/xorm" +) + +func AddPreviousDurationToActionRun(x *xorm.Engine) error { + type ActionRun struct { + PreviousDuration time.Duration + } + + _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreIndices: true, + IgnoreConstrains: true, + }, &ActionRun{}) + return err +} diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go new file mode 100644 index 0000000..97ff649 --- /dev/null +++ b/models/migrations/v1_22/v286.go @@ -0,0 +1,75 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT +package v1_22 //nolint + +import ( + "fmt" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "xorm.io/xorm" +) + +func expandHashReferencesToSha256(x *xorm.Engine) error { + alteredTables := [][2]string{ + {"commit_status", "context_hash"}, + {"comment", "commit_sha"}, + {"pull_request", "merge_base"}, + {"pull_request", "merged_commit_id"}, + {"review", "commit_id"}, + {"review_state", "commit_sha"}, + {"repo_archiver", "commit_id"}, + {"release", "sha1"}, + {"repo_indexer_status", "commit_sha"}, + } + + db := x.NewSession() + defer db.Close() + + if err := db.Begin(); err != nil { + return err + } + + if !setting.Database.Type.IsSQLite3() { + for _, alts := range alteredTables { + var err error + if setting.Database.Type.IsMySQL() { + _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1])) + } else { + _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1])) + } + if err != nil { + return fmt.Errorf("alter column '%s' of table '%s' failed: %w", alts[1], alts[0], err) + } + } + } + log.Debug("Updated database tables to hold SHA256 git hash references") + + return db.Commit() +} + +func addObjectFormatNameToRepository(x *xorm.Engine) error { + type Repository struct { + ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"` + } + + if _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreIndices: true, + IgnoreConstrains: true, + }, new(Repository)); err != nil { + return err + } + + // Here to catch weird edge-cases where column constraints above are + // not applied by the DB backend + _, err := x.Exec("UPDATE `repository` set `object_format_name` = 'sha1' WHERE `object_format_name` = '' or `object_format_name` IS NULL") + return err +} + +func AdjustDBForSha256(x *xorm.Engine) error { + if err := expandHashReferencesToSha256(x); err != nil { + return err + } + return addObjectFormatNameToRepository(x) +} diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go new file mode 100644 index 0000000..76b00e5 --- /dev/null +++ b/models/migrations/v1_22/v286_test.go @@ -0,0 +1,119 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "testing" + + migration_tests "code.gitea.io/gitea/models/migrations/test" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "xorm.io/xorm" +) + +func PrepareOldRepository(t *testing.T) (*xorm.Engine, func()) { + type Repository struct { // old struct + ID int64 `xorm:"pk autoincr"` + } + + type CommitStatus struct { + ID int64 + ContextHash string `xorm:"char(40) index"` + } + + type RepoArchiver struct { + ID int64 + RepoID int64 `xorm:"index unique(s)"` + Type int `xorm:"unique(s)"` + CommitID string `xorm:"VARCHAR(40) unique(s)"` + } + + type ReviewState struct { + ID int64 + UserID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user)"` + PullID int64 `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"` + CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"` + } + + type Comment struct { + ID int64 + CommitSHA string + } + + type PullRequest struct { + ID int64 + CommitSHA string + MergeBase string + MergedCommitID string + } + + type Release struct { + ID int64 + Sha1 string + } + + type RepoIndexerStatus struct { + ID int64 + CommitSHA string + } + + type Review struct { + ID int64 + CommitID string + } + + // Prepare and load the testing database + return migration_tests.PrepareTestEnv(t, 0, + new(Repository), + new(CommitStatus), + new(RepoArchiver), + new(ReviewState), + new(Review), + new(Comment), + new(PullRequest), + new(Release), + new(RepoIndexerStatus), + ) +} + +func Test_RepositoryFormat(t *testing.T) { + x, deferable := PrepareOldRepository(t) + defer deferable() + + require.NoError(t, AdjustDBForSha256(x)) + + type Repository struct { + ID int64 `xorm:"pk autoincr"` + ObjectFormatName string `xorg:"not null default('sha1')"` + } + + repo := new(Repository) + + // check we have some records to migrate + count, err := x.Count(new(Repository)) + require.NoError(t, err) + assert.EqualValues(t, 4, count) + + repo.ObjectFormatName = "sha256" + _, err = x.Insert(repo) + require.NoError(t, err) + id := repo.ID + + count, err = x.Count(new(Repository)) + require.NoError(t, err) + assert.EqualValues(t, 5, count) + + repo = new(Repository) + ok, err := x.ID(2).Get(repo) + require.NoError(t, err) + assert.True(t, ok) + assert.EqualValues(t, "sha1", repo.ObjectFormatName) + + repo = new(Repository) + ok, err = x.ID(id).Get(repo) + require.NoError(t, err) + assert.True(t, ok) + assert.EqualValues(t, "sha256", repo.ObjectFormatName) +} diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go new file mode 100644 index 0000000..c8b1593 --- /dev/null +++ b/models/migrations/v1_22/v287.go @@ -0,0 +1,46 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "xorm.io/xorm" +) + +type BadgeUnique struct { + ID int64 `xorm:"pk autoincr"` + Slug string `xorm:"UNIQUE"` +} + +func (BadgeUnique) TableName() string { + return "badge" +} + +func UseSlugInsteadOfIDForBadges(x *xorm.Engine) error { + type Badge struct { + Slug string + } + + err := x.Sync(new(Badge)) + if err != nil { + return err + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + _, err = sess.Exec("UPDATE `badge` SET `slug` = `id` Where `slug` IS NULL") + if err != nil { + return err + } + + err = sess.Sync(new(BadgeUnique)) + if err != nil { + return err + } + + return sess.Commit() +} diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go new file mode 100644 index 0000000..7c93bfc --- /dev/null +++ b/models/migrations/v1_22/v288.go @@ -0,0 +1,26 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +type Blocking struct { + ID int64 `xorm:"pk autoincr"` + BlockerID int64 `xorm:"UNIQUE(block)"` + BlockeeID int64 `xorm:"UNIQUE(block)"` + Note string + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` +} + +func (*Blocking) TableName() string { + return "user_blocking" +} + +func AddUserBlockingTable(x *xorm.Engine) error { + return x.Sync(&Blocking{}) +} diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go new file mode 100644 index 0000000..b9941aa --- /dev/null +++ b/models/migrations/v1_22/v289.go @@ -0,0 +1,21 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddDefaultWikiBranch(x *xorm.Engine) error { + type Repository struct { + ID int64 + DefaultWikiBranch string + } + if _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreIndices: true, + IgnoreConstrains: true, + }, &Repository{}); err != nil { + return err + } + _, err := x.Exec("UPDATE `repository` SET default_wiki_branch = 'master' WHERE (default_wiki_branch IS NULL) OR (default_wiki_branch = '')") + return err +} diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go new file mode 100644 index 0000000..e3c58b0 --- /dev/null +++ b/models/migrations/v1_22/v290.go @@ -0,0 +1,46 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "xorm.io/xorm" +) + +// HookTask represents a hook task. +// exact copy of models/webhook/hooktask.go when this migration was created +// - xorm:"-" fields deleted +type HookTask struct { + ID int64 `xorm:"pk autoincr"` + HookID int64 `xorm:"index"` + UUID string `xorm:"unique"` + PayloadContent string `xorm:"LONGTEXT"` + EventType webhook_module.HookEventType + IsDelivered bool + Delivered timeutil.TimeStampNano + + // History info. + IsSucceed bool + RequestContent string `xorm:"LONGTEXT"` + ResponseContent string `xorm:"LONGTEXT"` + + // Version number to allow for smooth version upgrades: + // - Version 1: PayloadContent contains the JSON as send to the URL + // - Version 2: PayloadContent contains the original event + PayloadVersion int `xorm:"DEFAULT 1"` +} + +func AddPayloadVersionToHookTaskTable(x *xorm.Engine) error { + // create missing column + if _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreIndices: true, + IgnoreConstrains: true, + }, new(HookTask)); err != nil { + return err + } + _, err := x.Exec("UPDATE hook_task SET payload_version = 1 WHERE payload_version IS NULL") + return err +} diff --git a/models/migrations/v1_22/v290_test.go b/models/migrations/v1_22/v290_test.go new file mode 100644 index 0000000..ced200f --- /dev/null +++ b/models/migrations/v1_22/v290_test.go @@ -0,0 +1,59 @@ +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "strconv" + "testing" + + migration_tests "code.gitea.io/gitea/models/migrations/test" + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_AddPayloadVersionToHookTaskTable(t *testing.T) { + type HookTaskMigrated HookTask + + // HookTask represents a hook task, as of before the migration + type HookTask struct { + ID int64 `xorm:"pk autoincr"` + HookID int64 `xorm:"index"` + UUID string `xorm:"unique"` + PayloadContent string `xorm:"LONGTEXT"` + EventType webhook_module.HookEventType + IsDelivered bool + Delivered timeutil.TimeStampNano + + // History info. + IsSucceed bool + RequestContent string `xorm:"LONGTEXT"` + ResponseContent string `xorm:"LONGTEXT"` + } + + // Prepare and load the testing database + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(HookTask), new(HookTaskMigrated)) + defer deferable() + if x == nil || t.Failed() { + return + } + + require.NoError(t, AddPayloadVersionToHookTaskTable(x)) + + expected := []HookTaskMigrated{} + require.NoError(t, x.Table("hook_task_migrated").Asc("id").Find(&expected)) + assert.Len(t, expected, 2) + + got := []HookTaskMigrated{} + require.NoError(t, x.Table("hook_task").Asc("id").Find(&got)) + + for i, expected := range expected { + expected, got := expected, got[i] + t.Run(strconv.FormatInt(expected.ID, 10), func(t *testing.T) { + assert.Equal(t, expected.PayloadVersion, got.PayloadVersion) + }) + } +} diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go new file mode 100644 index 0000000..74726fa --- /dev/null +++ b/models/migrations/v1_22/v291.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddCommentIDIndexofAttachment(x *xorm.Engine) error { + type Attachment struct { + CommentID int64 `xorm:"INDEX"` + } + + _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreDropIndices: true, + IgnoreConstrains: true, + }, &Attachment{}) + return err +} diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go new file mode 100644 index 0000000..beca556 --- /dev/null +++ b/models/migrations/v1_22/v292.go @@ -0,0 +1,9 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +// NOTE: noop the original migration has bug which some projects will be skip, so +// these projects will have no default board. +// So that this migration will be skipped and go to v293.go +// This file is a placeholder so that readers can know what happened diff --git a/models/migrations/v1_22/v293.go b/models/migrations/v1_22/v293.go new file mode 100644 index 0000000..53cc719 --- /dev/null +++ b/models/migrations/v1_22/v293.go @@ -0,0 +1,108 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +// CheckProjectColumnsConsistency ensures there is exactly one default board per project present +func CheckProjectColumnsConsistency(x *xorm.Engine) error { + sess := x.NewSession() + defer sess.Close() + + limit := setting.Database.IterateBufferSize + if limit <= 0 { + limit = 50 + } + + type Project struct { + ID int64 + CreatorID int64 + BoardID int64 + } + + type ProjectBoard struct { + ID int64 `xorm:"pk autoincr"` + Title string + Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board + Sorting int8 `xorm:"NOT NULL DEFAULT 0"` + Color string `xorm:"VARCHAR(7)"` + + ProjectID int64 `xorm:"INDEX NOT NULL"` + CreatorID int64 `xorm:"NOT NULL"` + + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` + } + + for { + if err := sess.Begin(); err != nil { + return err + } + + // all these projects without defaults will be fixed in the same loop, so + // we just need to always get projects without defaults until no such project + var projects []*Project + if err := sess.Select("project.id as id, project.creator_id, project_board.id as board_id"). + Join("LEFT", "project_board", "project_board.project_id = project.id AND project_board.`default`=?", true). + Where("project_board.id is NULL OR project_board.id = 0"). + Limit(limit). + Find(&projects); err != nil { + return err + } + + for _, p := range projects { + if _, err := sess.Insert(ProjectBoard{ + ProjectID: p.ID, + Default: true, + Title: "Uncategorized", + CreatorID: p.CreatorID, + }); err != nil { + return err + } + } + if err := sess.Commit(); err != nil { + return err + } + + if len(projects) == 0 { + break + } + } + sess.Close() + + return removeDuplicatedBoardDefault(x) +} + +func removeDuplicatedBoardDefault(x *xorm.Engine) error { + type ProjectInfo struct { + ProjectID int64 + DefaultNum int + } + var projects []ProjectInfo + if err := x.Select("project_id, count(*) AS default_num"). + Table("project_board"). + Where("`default` = ?", true). + GroupBy("project_id"). + Having("count(*) > 1"). + Find(&projects); err != nil { + return err + } + + for _, project := range projects { + if _, err := x.Where("project_id=?", project.ProjectID). + Table("project_board"). + Limit(project.DefaultNum - 1). + Update(map[string]bool{ + "`default`": false, + }); err != nil { + return err + } + } + return nil +} diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go new file mode 100644 index 0000000..85bb464 --- /dev/null +++ b/models/migrations/v1_22/v293_test.go @@ -0,0 +1,45 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + migration_tests "code.gitea.io/gitea/models/migrations/test" + "code.gitea.io/gitea/models/project" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_CheckProjectColumnsConsistency(t *testing.T) { + // Prepare and load the testing database + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(project.Project), new(project.Column)) + defer deferable() + if x == nil || t.Failed() { + return + } + + require.NoError(t, CheckProjectColumnsConsistency(x)) + + // check if default column was added + var defaultColumn project.Column + has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultColumn) + require.NoError(t, err) + assert.True(t, has) + assert.Equal(t, int64(1), defaultColumn.ProjectID) + assert.True(t, defaultColumn.Default) + + // check if multiple defaults, previous were removed and last will be kept + expectDefaultColumn, err := project.GetColumn(db.DefaultContext, 2) + require.NoError(t, err) + assert.Equal(t, int64(2), expectDefaultColumn.ProjectID) + assert.False(t, expectDefaultColumn.Default) + + expectNonDefaultColumn, err := project.GetColumn(db.DefaultContext, 3) + require.NoError(t, err) + assert.Equal(t, int64(2), expectNonDefaultColumn.ProjectID) + assert.True(t, expectNonDefaultColumn.Default) +} diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go new file mode 100644 index 0000000..314b451 --- /dev/null +++ b/models/migrations/v1_22/v294.go @@ -0,0 +1,44 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "xorm.io/xorm" +) + +// AddUniqueIndexForProjectIssue adds unique indexes for project issue table +func AddUniqueIndexForProjectIssue(x *xorm.Engine) error { + // remove possible duplicated records in table project_issue + type result struct { + IssueID int64 + ProjectID int64 + Cnt int + } + var results []result + if err := x.Select("issue_id, project_id, count(*) as cnt"). + Table("project_issue"). + GroupBy("issue_id, project_id"). + Having("count(*) > 1"). + Find(&results); err != nil { + return err + } + for _, r := range results { + var ids []int64 + if err := x.SQL("SELECT id FROM project_issue WHERE issue_id = ? and project_id = ? limit ?", r.IssueID, r.ProjectID, r.Cnt-1).Find(&ids); err != nil { + return err + } + if _, err := x.Table("project_issue").In("id", ids).Delete(); err != nil { + return err + } + } + + // add unique index for project_issue table + type ProjectIssue struct { //revive:disable-line:exported + ID int64 `xorm:"pk autoincr"` + IssueID int64 `xorm:"INDEX unique(s)"` + ProjectID int64 `xorm:"INDEX unique(s)"` + } + + return x.Sync(new(ProjectIssue)) +} diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go new file mode 100644 index 0000000..c465d53 --- /dev/null +++ b/models/migrations/v1_22/v294_test.go @@ -0,0 +1,53 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "slices" + "testing" + + migration_tests "code.gitea.io/gitea/models/migrations/test" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "xorm.io/xorm/schemas" +) + +func Test_AddUniqueIndexForProjectIssue(t *testing.T) { + type ProjectIssue struct { //revive:disable-line:exported + ID int64 `xorm:"pk autoincr"` + IssueID int64 `xorm:"INDEX"` + ProjectID int64 `xorm:"INDEX"` + } + + // Prepare and load the testing database + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ProjectIssue)) + defer deferable() + if x == nil || t.Failed() { + return + } + + cnt, err := x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count() + require.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + require.NoError(t, AddUniqueIndexForProjectIssue(x)) + + cnt, err = x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count() + require.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + tables, err := x.DBMetas() + require.NoError(t, err) + assert.Len(t, tables, 1) + found := false + for _, index := range tables[0].Indexes { + if index.Type == schemas.UniqueType { + found = true + slices.Equal(index.Cols, []string{"project_id", "issue_id"}) + break + } + } + assert.True(t, found) +} diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go new file mode 100644 index 0000000..17bdadb --- /dev/null +++ b/models/migrations/v1_22/v295.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddCommitStatusSummary(x *xorm.Engine) error { + type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` + SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` + State string `xorm:"VARCHAR(7) NOT NULL"` + } + // there is no migrations because if there is no data on this table, it will fall back to get data + // from commit status + return x.Sync2(new(CommitStatusSummary)) +} diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go new file mode 100644 index 0000000..1ecacab --- /dev/null +++ b/models/migrations/v1_22/v296.go @@ -0,0 +1,16 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddCommitStatusSummary2(x *xorm.Engine) error { + type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + TargetURL string `xorm:"TEXT"` + } + // there is no migrations because if there is no data on this table, it will fall back to get data + // from commit status + return x.Sync(new(CommitStatusSummary)) +} diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go new file mode 100644 index 0000000..b9f3b95 --- /dev/null +++ b/models/migrations/v1_22/v298.go @@ -0,0 +1,10 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func DropWronglyCreatedTable(x *xorm.Engine) error { + return x.DropTables("o_auth2_application") +} |