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
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"fmt"
"gitea.dev/modelmigration/base"
"gitea.dev/modules/setting"
)
func FixLanguageStatsToSaveSize(x base.EngineMigration) error {
// LanguageStat see models/repo_language_stats.go
type LanguageStat struct {
Size int64 `xorm:"NOT NULL DEFAULT 0"`
}
// RepoIndexerType specifies the repository indexer type
type RepoIndexerType int
const RepoIndexerTypeStats RepoIndexerType = 1
// RepoIndexerStatus see models/repo_indexer.go
type RepoIndexerStatus struct {
IndexerType RepoIndexerType `xorm:"INDEX(s) NOT NULL DEFAULT 0"`
}
if err := x.Sync(new(LanguageStat)); err != nil {
return fmt.Errorf("Sync: %w", err)
}
_, _ = x.Delete(&RepoIndexerStatus{IndexerType: RepoIndexerTypeStats})
// Delete language stat statuses
truncExpr := "TRUNCATE TABLE"
if setting.Database.Type.IsSQLite3() {
truncExpr = "DELETE FROM"
}
// Delete language stats
if _, err := x.Exec(truncExpr + " language_stat"); err != nil {
return err
}
sess := x.NewSession()
defer sess.Close()
return base.DropTableColumns(sess, "language_stat", "percentage")
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"fmt"
"gitea.dev/modelmigration/base"
)
func AddKeepActivityPrivateUserColumn(x base.EngineMigration) error {
type User struct {
KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"`
}
if err := x.Sync(new(User)); err != nil {
return fmt.Errorf("Sync: %w", err)
}
return nil
}
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/log"
"xorm.io/builder"
)
func SetIsArchivedToFalse(x base.EngineMigration) error {
type Repository struct {
IsArchived bool `xorm:"INDEX"`
}
count, err := x.Where(builder.IsNull{"is_archived"}).Cols("is_archived").Update(&Repository{
IsArchived: false,
})
if err == nil {
log.Debug("Updated %d repositories with is_archived IS NULL", count)
}
return err
}
+50
View File
@@ -0,0 +1,50 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/log"
)
func RecalculateStars(x base.EngineMigration) (err error) {
// because of issue https://github.com/go-gitea/gitea/issues/11949,
// recalculate Stars number for all users to fully fix it.
type User struct {
ID int64 `xorm:"pk autoincr"`
}
const batchSize = 100
sess := x.NewSession()
defer sess.Close()
for start := 0; ; start += batchSize {
users := make([]User, 0, batchSize)
if err := sess.Limit(batchSize, start).Where("type = ?", 0).Cols("id").Find(&users); err != nil {
return err
}
if len(users) == 0 {
break
}
if err := sess.Begin(); err != nil {
return err
}
for _, user := range users {
if _, err := sess.Exec("UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil {
return err
}
}
if err := sess.Commit(); err != nil {
return err
}
}
log.Debug("recalculate Stars number for all user finished")
return err
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/log"
"xorm.io/builder"
)
func UpdateMatrixWebhookHTTPMethod(x base.EngineMigration) error {
matrixHookTaskType := 9 // value comes from the models package
type Webhook struct {
HTTPMethod string
}
cond := builder.Eq{"hook_task_type": matrixHookTaskType}.And(builder.Neq{"http_method": "PUT"})
count, err := x.Where(cond).Cols("http_method").Update(&Webhook{HTTPMethod: "PUT"})
if err == nil {
log.Debug("Updated %d Matrix webhooks with http_method 'PUT'", count)
}
return err
}
+80
View File
@@ -0,0 +1,80 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"fmt"
"gitea.dev/modelmigration/base"
"gitea.dev/modules/setting"
)
func IncreaseLanguageField(x base.EngineMigration) error {
type LanguageStat struct {
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Language string `xorm:"VARCHAR(50) UNIQUE(s) INDEX NOT NULL"`
}
if err := x.Sync(new(LanguageStat)); err != nil {
return err
}
if setting.Database.Type.IsSQLite3() {
// SQLite maps VARCHAR to TEXT without size so we're done
return nil
}
// need to get the correct type for the new column
inferredTable, err := x.TableInfo(new(LanguageStat))
if err != nil {
return err
}
column := inferredTable.GetColumn("language")
sqlType := x.Dialect().SQLType(column)
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
switch {
case setting.Database.Type.IsMySQL():
if _, err := sess.Exec("ALTER TABLE language_stat MODIFY COLUMN language " + sqlType); err != nil {
return err
}
case setting.Database.Type.IsMSSQL():
// Yet again MSSQL just has to be awkward.
// Here we have to drop the constraints first and then rebuild them
constraints := make([]string, 0)
if err := sess.SQL(`SELECT i.name AS Name
FROM sys.indexes i INNER JOIN sys.index_columns ic
ON i.index_id = ic.index_id AND i.object_id = ic.object_id
INNER JOIN sys.tables AS t
ON t.object_id = i.object_id
INNER JOIN sys.columns c
ON t.object_id = c.object_id AND ic.column_id = c.column_id
WHERE t.name = 'language_stat' AND c.name = 'language'`).Find(&constraints); err != nil {
return fmt.Errorf("Find constraints: %w", err)
}
for _, constraint := range constraints {
if _, err := sess.Exec(fmt.Sprintf("DROP INDEX [%s] ON `language_stat`", constraint)); err != nil {
return fmt.Errorf("Drop table `language_stat` constraint `%s`: %w", constraint, err)
}
}
if _, err := sess.Exec("ALTER TABLE language_stat ALTER COLUMN language " + sqlType); err != nil {
return err
}
// Finally restore the constraint
if err := sess.CreateUniques(new(LanguageStat)); err != nil {
return err
}
case setting.Database.Type.IsPostgreSQL():
if _, err := sess.Exec("ALTER TABLE language_stat ALTER COLUMN language TYPE " + sqlType); err != nil {
return err
}
}
return sess.Commit()
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/timeutil"
)
func AddProjectsInfo(x base.EngineMigration) error {
// Create new tables
type (
ProjectType uint8
ProjectBoardType uint8
)
type Project struct {
ID int64 `xorm:"pk autoincr"`
Title string `xorm:"INDEX NOT NULL"`
Description string `xorm:"TEXT"`
RepoID int64 `xorm:"INDEX"`
CreatorID int64 `xorm:"NOT NULL"`
IsClosed bool `xorm:"INDEX"`
BoardType ProjectBoardType
Type ProjectType
ClosedDateUnix timeutil.TimeStamp
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync(new(Project)); err != nil {
return err
}
type Comment struct {
OldProjectID int64
ProjectID int64
}
if err := x.Sync(new(Comment)); err != nil {
return err
}
type Repository struct {
ID int64
NumProjects int `xorm:"NOT NULL DEFAULT 0"`
NumClosedProjects int `xorm:"NOT NULL DEFAULT 0"`
}
if err := x.Sync(new(Repository)); err != nil {
return err
}
// ProjectIssue saves relation from issue to a project
type ProjectIssue struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX"`
ProjectID int64 `xorm:"INDEX"`
ProjectBoardID int64 `xorm:"INDEX"`
}
if err := x.Sync(new(ProjectIssue)); err != nil {
return err
}
type ProjectBoard struct {
ID int64 `xorm:"pk autoincr"`
Title string
Default bool `xorm:"NOT NULL DEFAULT false"`
ProjectID int64 `xorm:"INDEX NOT NULL"`
CreatorID int64 `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
return x.Sync(new(ProjectBoard))
}
+152
View File
@@ -0,0 +1,152 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/timeutil"
)
func CreateReviewsForCodeComments(x base.EngineMigration) error {
// Review
type Review struct {
ID int64 `xorm:"pk autoincr"`
Type int
ReviewerID int64 `xorm:"index"`
OriginalAuthor string
OriginalAuthorID int64
IssueID int64 `xorm:"index"`
Content string `xorm:"TEXT"`
// Official is a review made by an assigned approver (counts towards approval)
Official bool `xorm:"NOT NULL DEFAULT false"`
CommitID string `xorm:"VARCHAR(40)"`
Stale bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
const ReviewTypeComment = 2
// Comment represents a comment in commit and issue page.
type Comment struct {
ID int64 `xorm:"pk autoincr"`
Type int `xorm:"INDEX"`
PosterID int64 `xorm:"INDEX"`
OriginalAuthor string
OriginalAuthorID int64
IssueID int64 `xorm:"INDEX"`
LabelID int64
OldProjectID int64
ProjectID int64
OldMilestoneID int64
MilestoneID int64
AssigneeID int64
RemovedAssignee bool
ResolveDoerID int64
OldTitle string
NewTitle string
OldRef string
NewRef string
DependentIssueID int64
CommitID int64
Line int64 // - previous line / + proposed line
TreePath string
Content string `xorm:"TEXT"`
// Path represents the 4 lines of code cemented by this comment
PatchQuoted string `xorm:"TEXT patch"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
// Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"`
ReviewID int64 `xorm:"index"`
Invalidated bool
// Reference an issue or pull from another comment, issue or PR
// All information is about the origin of the reference
RefRepoID int64 `xorm:"index"` // Repo where the referencing
RefIssueID int64 `xorm:"index"`
RefCommentID int64 `xorm:"index"` // 0 if origin is Issue title or content (or PR's)
RefAction int `xorm:"SMALLINT"` // What happens if RefIssueID resolves
RefIsPull bool
}
if err := x.Sync(new(Review), new(Comment)); err != nil {
return err
}
updateComment := func(comments []*Comment) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
for _, comment := range comments {
review := &Review{
Type: ReviewTypeComment,
ReviewerID: comment.PosterID,
IssueID: comment.IssueID,
Official: false,
CommitID: comment.CommitSHA,
Stale: comment.Invalidated,
OriginalAuthor: comment.OriginalAuthor,
OriginalAuthorID: comment.OriginalAuthorID,
CreatedUnix: comment.CreatedUnix,
UpdatedUnix: comment.CreatedUnix,
}
if _, err := sess.NoAutoTime().Insert(review); err != nil {
return err
}
reviewComment := &Comment{
Type: 22,
PosterID: comment.PosterID,
Content: "",
IssueID: comment.IssueID,
ReviewID: review.ID,
OriginalAuthor: comment.OriginalAuthor,
OriginalAuthorID: comment.OriginalAuthorID,
CreatedUnix: comment.CreatedUnix,
UpdatedUnix: comment.CreatedUnix,
}
if _, err := sess.NoAutoTime().Insert(reviewComment); err != nil {
return err
}
comment.ReviewID = review.ID
if _, err := sess.ID(comment.ID).Cols("review_id").NoAutoTime().Update(comment); err != nil {
return err
}
}
return sess.Commit()
}
start := 0
batchSize := 100
for {
comments := make([]*Comment, 0, batchSize)
if err := x.Where("review_id = 0 and type = 21").Limit(batchSize, start).Find(&comments); err != nil {
return err
}
if err := updateComment(comments); err != nil {
return err
}
start += len(comments)
if len(comments) < batchSize {
break
}
}
return nil
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import "gitea.dev/modelmigration/base"
func PurgeInvalidDependenciesComments(x base.EngineMigration) error {
_, err := x.Exec("DELETE FROM comment WHERE dependent_issue_id != 0 AND dependent_issue_id NOT IN (SELECT id FROM issue)")
return err
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"fmt"
"gitea.dev/modelmigration/base"
"gitea.dev/modules/timeutil"
)
func AddCreatedAndUpdatedToMilestones(x base.EngineMigration) error {
type Milestone struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync(new(Milestone)); err != nil {
return fmt.Errorf("Sync: %w", err)
}
return nil
}
+37
View File
@@ -0,0 +1,37 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/timeutil"
)
func AddPrimaryKeyToRepoTopic(x base.EngineMigration) error {
// Topic represents a topic of repositories
type Topic struct {
ID int64 `xorm:"pk autoincr"`
Name string `xorm:"UNIQUE VARCHAR(25)"`
RepoCount int
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
// RepoTopic represents associated repositories and topics
type RepoTopic struct {
RepoID int64 `xorm:"pk"`
TopicID int64 `xorm:"pk"`
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
_ = base.RecreateTable(sess, &Topic{})
_ = base.RecreateTable(sess, &RepoTopic{})
return sess.Commit()
}
+197
View File
@@ -0,0 +1,197 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"context"
"errors"
"fmt"
"strings"
"gitea.dev/modelmigration/base"
"gitea.dev/modules/log"
"gitea.dev/modules/setting"
"xorm.io/xorm/schemas"
)
func SetDefaultPasswordToArgon2(x base.EngineMigration) error {
switch {
case setting.Database.Type.IsMySQL():
_, err := x.Exec("ALTER TABLE `user` ALTER passwd_hash_algo SET DEFAULT 'argon2';")
return err
case setting.Database.Type.IsPostgreSQL():
_, err := x.Exec("ALTER TABLE `user` ALTER COLUMN passwd_hash_algo SET DEFAULT 'argon2';")
return err
case setting.Database.Type.IsMSSQL():
// need to find the constraint and drop it, then recreate it.
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
res, err := sess.QueryString("SELECT [name] FROM sys.default_constraints WHERE parent_object_id=OBJECT_ID(?) AND COL_NAME(parent_object_id, parent_column_id)=?;", "user", "passwd_hash_algo")
if err != nil {
return err
}
if len(res) > 0 {
constraintName := res[0]["name"]
log.Error("Results of select constraint: %s", constraintName)
_, err := sess.Exec("ALTER TABLE [user] DROP CONSTRAINT " + constraintName)
if err != nil {
return err
}
_, err = sess.Exec("ALTER TABLE [user] ADD CONSTRAINT " + constraintName + " DEFAULT 'argon2' FOR passwd_hash_algo")
if err != nil {
return err
}
} else {
_, err := sess.Exec("ALTER TABLE [user] ADD DEFAULT('argon2') FOR passwd_hash_algo")
if err != nil {
return err
}
}
return sess.Commit()
case setting.Database.Type.IsSQLite3():
// drop through
default:
log.Fatal("Unrecognized DB")
}
tables, err := x.DBMetas()
if err != nil {
return err
}
// Now for SQLite we have to recreate the table
var table *schemas.Table
tableName := "user"
for _, table = range tables {
if table.Name == tableName {
break
}
}
if table == nil || table.Name != tableName {
type User struct {
PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"`
}
return x.Sync(new(User))
}
column := table.GetColumn("passwd_hash_algo")
if column == nil {
type User struct {
PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"`
}
return x.Sync(new(User))
}
tempTableName := "tmp_recreate__user"
column.Default = "'argon2'"
createTableSQL, _, err := x.Dialect().CreateTableSQL(context.Background(), x.DB(), table, tempTableName)
if err != nil {
return err
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if _, err := sess.Exec(createTableSQL); err != nil {
log.Error("Unable to create table %s. Error: %v\n", tempTableName, err, createTableSQL)
return err
}
for _, index := range table.Indexes {
if _, err := sess.Exec(x.Dialect().CreateIndexSQL(tempTableName, index)); err != nil {
log.Error("Unable to create indexes on temporary table %s. Error: %v", tempTableName, err)
return err
}
}
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
return errors.New("no columns in new table")
}
hasID := false
for _, column := range newTableColumns {
hasID = hasID || (column.IsPrimaryKey && column.IsAutoIncrement)
}
sqlStringBuilder := &strings.Builder{}
_, _ = sqlStringBuilder.WriteString("INSERT INTO `")
_, _ = sqlStringBuilder.WriteString(tempTableName)
_, _ = sqlStringBuilder.WriteString("` (`")
_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name)
_, _ = sqlStringBuilder.WriteString("`")
for _, column := range newTableColumns[1:] {
_, _ = sqlStringBuilder.WriteString(", `")
_, _ = sqlStringBuilder.WriteString(column.Name)
_, _ = sqlStringBuilder.WriteString("`")
}
_, _ = sqlStringBuilder.WriteString(")")
_, _ = sqlStringBuilder.WriteString(" SELECT ")
if newTableColumns[0].Default != "" {
_, _ = sqlStringBuilder.WriteString("COALESCE(`")
_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name)
_, _ = sqlStringBuilder.WriteString("`, ")
_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Default)
_, _ = sqlStringBuilder.WriteString(")")
} else {
_, _ = sqlStringBuilder.WriteString("`")
_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name)
_, _ = sqlStringBuilder.WriteString("`")
}
for _, column := range newTableColumns[1:] {
if column.Default != "" {
_, _ = sqlStringBuilder.WriteString(", COALESCE(`")
_, _ = sqlStringBuilder.WriteString(column.Name)
_, _ = sqlStringBuilder.WriteString("`, ")
_, _ = sqlStringBuilder.WriteString(column.Default)
_, _ = sqlStringBuilder.WriteString(")")
} else {
_, _ = sqlStringBuilder.WriteString(", `")
_, _ = sqlStringBuilder.WriteString(column.Name)
_, _ = sqlStringBuilder.WriteString("`")
}
}
_, _ = sqlStringBuilder.WriteString(" FROM `")
_, _ = sqlStringBuilder.WriteString(tableName)
_, _ = sqlStringBuilder.WriteString("`")
if _, err := sess.Exec(sqlStringBuilder.String()); err != nil {
log.Error("Unable to set copy data in to temp table %s. Error: %v", tempTableName, err)
return err
}
// SQLite will drop all the constraints on the old table
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
log.Error("Unable to drop old table %s. Error: %v", tableName, err)
return err
}
for _, index := range table.Indexes {
if _, err := sess.Exec(x.Dialect().DropIndexSQL(tempTableName, index)); err != nil {
log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err)
return err
}
}
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil {
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
return err
}
for _, index := range table.Indexes {
if _, err := sess.Exec(x.Dialect().CreateIndexSQL(tableName, index)); err != nil {
log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err)
return err
}
}
return sess.Commit()
}
+13
View File
@@ -0,0 +1,13 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import "gitea.dev/modelmigration/base"
func AddTrustModelToRepository(x base.EngineMigration) error {
type Repository struct {
TrustModel int
}
return x.Sync(new(Repository))
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import "gitea.dev/modelmigration/base"
func AddTeamReviewRequestSupport(x base.EngineMigration) error {
type Review struct {
ReviewerTeamID int64 `xorm:"NOT NULL DEFAULT 0"`
}
type Comment struct {
AssigneeTeamID int64 `xorm:"NOT NULL DEFAULT 0"`
}
if err := x.Sync(new(Review)); err != nil {
return err
}
return x.Sync(new(Comment))
}
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_13
import (
"gitea.dev/modelmigration/base"
"gitea.dev/modules/timeutil"
)
func AddTimeStamps(x base.EngineMigration) error {
// this will add timestamps where it is useful to have
// Star represents a starred repo by an user.
type Star struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}
if err := x.Sync(new(Star)); err != nil {
return err
}
// Label represents a label of repository for issues.
type Label struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync(new(Label)); err != nil {
return err
}
// Follow represents relations of user and their followers.
type Follow struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}
if err := x.Sync(new(Follow)); err != nil {
return err
}
// Watch is connection request for receiving repository notification.
type Watch struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync(new(Watch)); err != nil {
return err
}
// Collaboration represent the relation between an individual and a repository.
type Collaboration struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
return x.Sync(new(Collaboration))
}