refactor: prepare to decouple the "model migration" package and "models" package (#38533)

Migrations should never use model structs directly, because the model
structs can be different in different releases. e.g. if one migration uses
"User" model, it works in the early releases, then one day, when the
User model changes, the migration breaks because it will use the
new (incorrect) User model, it should only use the old User model.

The same to "modules/structs".

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
wxiaoguang
2026-07-20 03:42:02 +00:00
committed by GitHub
co-authored by delvh
parent 775e3bdb34
commit fff32e9469
378 changed files with 708 additions and 721 deletions
+14
View File
@@ -0,0 +1,14 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"testing"
"gitea.dev/modelmigration/migrationtest"
)
func TestMain(m *testing.M) {
migrationtest.MainTest(m)
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import "gitea.dev/modelmigration/base"
func CreateUserBadgesTable(x base.EngineMigration) error {
type Badge struct {
ID int64 `xorm:"pk autoincr"`
Description string
ImageURL string
}
type userBadge struct {
ID int64 `xorm:"pk autoincr"`
BadgeID int64
UserID int64 `xorm:"INDEX"`
}
if err := x.Sync(new(Badge)); err != nil {
return err
}
return x.Sync(new(userBadge))
}
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/setting"
)
func AlterPublicGPGKeyContentFieldsToMediumText(x base.EngineMigration) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if setting.Database.Type.IsMySQL() {
if _, err := sess.Exec("ALTER TABLE `gpg_key` CHANGE `content` `content` MEDIUMTEXT"); err != nil {
return err
}
if _, err := sess.Exec("ALTER TABLE `public_key` CHANGE `content` `content` MEDIUMTEXT"); err != nil {
return err
}
}
return sess.Commit()
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"gitea.dev/modelmigration/base"
"xorm.io/builder"
)
func FixPackageSemverField(x base.EngineMigration) error {
_, err := x.Exec(builder.Update(builder.Eq{"semver_compatible": false}).From("`package`").Where(builder.In("`type`", "conan", "generic")))
return err
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/timeutil"
)
type SystemSetting struct {
ID int64 `xorm:"pk autoincr"`
SettingKey string `xorm:"varchar(255) unique"` // ensure key is always lowercase
SettingValue string `xorm:"text"`
Version int `xorm:"version"` // prevent to override
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
}
func CreateSystemSettingsTable(x base.EngineMigration) error {
return x.Sync(new(SystemSetting))
}
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/timeutil"
)
func AddTeamInviteTable(x base.EngineMigration) error {
type TeamInvite struct {
ID int64 `xorm:"pk autoincr"`
Token string `xorm:"UNIQUE(token) INDEX NOT NULL DEFAULT ''"`
InviterID int64 `xorm:"NOT NULL DEFAULT 0"`
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
TeamID int64 `xorm:"UNIQUE(team_mail) INDEX NOT NULL DEFAULT 0"`
Email string `xorm:"UNIQUE(team_mail) NOT NULL DEFAULT ''"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
return x.Sync(new(TeamInvite))
}
+47
View File
@@ -0,0 +1,47 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"fmt"
"gitea.dev/modelmigration/base"
"gitea.dev/models/issues"
"xorm.io/builder"
)
func UpdateOpenMilestoneCounts(x base.EngineMigration) error {
var openMilestoneIDs []int64
err := x.Table("milestone").Select("id").Where(builder.Neq{"is_closed": 1}).Find(&openMilestoneIDs)
if err != nil {
return fmt.Errorf("error selecting open milestone IDs: %w", err)
}
for _, id := range openMilestoneIDs {
_, err := x.ID(id).
Cols("num_issues", "num_closed_issues").
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
builder.Eq{"milestone_id": id},
)).
SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
builder.Eq{
"milestone_id": id,
"is_closed": true,
},
)).
Update(&issues.Milestone{})
if err != nil {
return fmt.Errorf("error updating issue counts in milestone %d: %w", id, err)
}
_, err = x.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
id,
)
if err != nil {
return fmt.Errorf("error setting completeness on milestone %d: %w", id, err)
}
}
return nil
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"testing"
"gitea.dev/modelmigration/migrationtest"
"gitea.dev/models/issues"
"github.com/stretchr/testify/assert"
)
func Test_UpdateOpenMilestoneCounts(t *testing.T) {
type ExpectedMilestone issues.Milestone
// Prepare and load the testing database
x, deferable := migrationtest.PrepareTestEnv(t, 0, new(issues.Milestone), new(ExpectedMilestone), new(issues.Issue))
defer deferable()
if x == nil || t.Failed() {
return
}
if err := UpdateOpenMilestoneCounts(x); err != nil {
assert.NoError(t, err)
return
}
expected := []ExpectedMilestone{}
if err := x.Table("expected_milestone").Asc("id").Find(&expected); !assert.NoError(t, err) {
return
}
got := []issues.Milestone{}
if err := x.Table("milestone").Asc("id").Find(&got); !assert.NoError(t, err) {
return
}
for i, e := range expected {
got := got[i]
assert.Equal(t, e.ID, got.ID)
assert.Equal(t, e.NumIssues, got.NumIssues)
assert.Equal(t, e.NumClosedIssues, got.NumClosedIssues)
}
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import "gitea.dev/modelmigration/base"
// AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
func AddConfidentialClientColumnToOAuth2ApplicationTable(x base.EngineMigration) error {
type oauth2Application struct {
ID int64
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
}
return x.Sync(new(oauth2Application))
}
+47
View File
@@ -0,0 +1,47 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_18
import (
"testing"
"gitea.dev/modelmigration/migrationtest"
"github.com/stretchr/testify/assert"
)
func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
// premigration
type oauth2Application struct {
ID int64
}
// Prepare and load the testing database
x, deferable := migrationtest.PrepareTestEnv(t, 0, new(oauth2Application))
defer deferable()
if x == nil || t.Failed() {
return
}
if err := AddConfidentialClientColumnToOAuth2ApplicationTable(x); err != nil {
assert.NoError(t, err)
return
}
// postmigration
type ExpectedOAuth2Application struct {
ID int64
ConfidentialClient bool
}
got := []ExpectedOAuth2Application{}
if err := x.Table("oauth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
return
}
assert.NotEmpty(t, got)
for _, e := range got {
assert.True(t, e.ConfidentialClient)
}
}