初始提交: Gitea 项目代码
This commit is contained in:
@@ -0,0 +1,510 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// RecreateTables will recreate the tables for the provided beans using the newly provided bean definition and move all data to that new table
|
||||
// WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION
|
||||
func RecreateTables(beans ...any) func(db.EngineMigration) error {
|
||||
return func(x db.EngineMigration) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
sess = sess.StoreEngine("InnoDB")
|
||||
for _, bean := range beans {
|
||||
log.Info("Recreating Table: %s for Bean: %s", x.TableName(bean), reflect.Indirect(reflect.ValueOf(bean)).Type().Name())
|
||||
if err := RecreateTable(sess, bean); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
}
|
||||
|
||||
// RecreateTable will recreate the table using the newly provided bean definition and move all data to that new table
|
||||
// WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION
|
||||
// WARNING: YOU MUST COMMIT THE SESSION AT THE END
|
||||
func RecreateTable(sess db.Session, bean any) error {
|
||||
// TODO: This will not work if there are foreign keys
|
||||
|
||||
tableName := sess.Engine().TableName(bean)
|
||||
tempTableName := "tmp_recreate__" + tableName
|
||||
|
||||
// We need to move the old table away and create a new one with the correct columns
|
||||
// We will need to do this in stages to prevent data loss
|
||||
//
|
||||
// First create the temporary table
|
||||
if err := sess.Table(tempTableName).CreateTable(bean); err != nil {
|
||||
log.Error("Unable to create table %s. Error: %v", tempTableName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Table(tempTableName).CreateUniques(bean); err != nil {
|
||||
log.Error("Unable to create uniques for table %s. Error: %v", tempTableName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Table(tempTableName).CreateIndexes(bean); err != nil {
|
||||
log.Error("Unable to create indexes for table %s. Error: %v", tempTableName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Work out the column names from the bean - these are the columns to select from the old table and install into the new table
|
||||
table, err := sess.Engine().TableInfo(bean)
|
||||
if err != nil {
|
||||
log.Error("Unable to get table info. Error: %v", 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)
|
||||
}
|
||||
|
||||
if hasID && setting.Database.Type.IsMSSQL() {
|
||||
if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` ON", tempTableName)); err != nil {
|
||||
log.Error("Unable to set identity insert for table %s. Error: %v", tempTableName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if hasID && setting.Database.Type.IsMSSQL() {
|
||||
if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` OFF", tempTableName)); err != nil {
|
||||
log.Error("Unable to switch off identity insert for table %s. Error: %v", tempTableName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case setting.Database.Type.IsSQLite3():
|
||||
// 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
|
||||
}
|
||||
|
||||
if err := sess.Table(tempTableName).DropIndexes(bean); 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
|
||||
}
|
||||
|
||||
if err := sess.Table(tableName).CreateIndexes(bean); err != nil {
|
||||
log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Table(tableName).CreateUniques(bean); err != nil {
|
||||
log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
|
||||
return err
|
||||
}
|
||||
case setting.Database.Type.IsMySQL():
|
||||
// MySQL 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
|
||||
}
|
||||
|
||||
if err := sess.Table(tempTableName).DropIndexes(bean); err != nil {
|
||||
log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// SQLite and MySQL will move all the constraints from the temporary table to the new table
|
||||
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
|
||||
}
|
||||
|
||||
if err := sess.Table(tableName).CreateIndexes(bean); err != nil {
|
||||
log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Table(tableName).CreateUniques(bean); err != nil {
|
||||
log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
|
||||
return err
|
||||
}
|
||||
case setting.Database.Type.IsPostgreSQL():
|
||||
var originalSequences []string
|
||||
type sequenceData struct {
|
||||
LastValue int `xorm:"'last_value'"`
|
||||
IsCalled bool `xorm:"'is_called'"`
|
||||
}
|
||||
sequenceMap := map[string]sequenceData{}
|
||||
|
||||
schema := sess.Engine().Dialect().URI().Schema
|
||||
sess.Engine().SetSchema("")
|
||||
if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE ? || '_%' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&originalSequences); err != nil {
|
||||
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
|
||||
return err
|
||||
}
|
||||
sess.Engine().SetSchema(schema)
|
||||
|
||||
for _, sequence := range originalSequences {
|
||||
sequenceData := sequenceData{}
|
||||
if _, err := sess.Table(sequence).Cols("last_value", "is_called").Get(&sequenceData); err != nil {
|
||||
log.Error("Unable to get last_value and is_called from %s. Error: %v", sequence, err)
|
||||
return err
|
||||
}
|
||||
sequenceMap[sequence] = sequenceData
|
||||
}
|
||||
|
||||
// CASCADE causes postgres to drop all the constraints on the old table
|
||||
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s` CASCADE", tableName)); err != nil {
|
||||
log.Error("Unable to drop old table %s. Error: %v", tableName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// CASCADE causes postgres to move all the constraints from the temporary table to the new table
|
||||
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
|
||||
}
|
||||
|
||||
var indices []string
|
||||
sess.Engine().SetSchema("")
|
||||
if err := sess.Table("pg_indexes").Cols("indexname").Where("tablename = ? ", tableName).Find(&indices); err != nil {
|
||||
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
|
||||
return err
|
||||
}
|
||||
sess.Engine().SetSchema(schema)
|
||||
|
||||
for _, index := range indices {
|
||||
newIndexName := strings.Replace(index, "tmp_recreate__", "", 1)
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER INDEX `%s` RENAME TO `%s`", index, newIndexName)); err != nil {
|
||||
log.Error("Unable to rename %s to %s. Error: %v", index, newIndexName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var sequences []string
|
||||
sess.Engine().SetSchema("")
|
||||
if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__' || ? || '_%' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&sequences); err != nil {
|
||||
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
|
||||
return err
|
||||
}
|
||||
sess.Engine().SetSchema(schema)
|
||||
|
||||
for _, sequence := range sequences {
|
||||
newSequenceName := strings.Replace(sequence, "tmp_recreate__", "", 1)
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER SEQUENCE `%s` RENAME TO `%s`", sequence, newSequenceName)); err != nil {
|
||||
log.Error("Unable to rename %s sequence to %s. Error: %v", sequence, newSequenceName, err)
|
||||
return err
|
||||
}
|
||||
val, ok := sequenceMap[newSequenceName]
|
||||
if newSequenceName == tableName+"_id_seq" {
|
||||
if ok && val.LastValue != 0 {
|
||||
if _, err := sess.Exec(fmt.Sprintf("SELECT setval('%s', %d, %t)", newSequenceName, val.LastValue, val.IsCalled)); err != nil {
|
||||
log.Error("Unable to reset %s to %d. Error: %v", newSequenceName, val, err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// We're going to try to guess this
|
||||
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 %s. Error: %v", newSequenceName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if ok {
|
||||
if _, err := sess.Exec(fmt.Sprintf("SELECT setval('%s', %d, %t)", newSequenceName, val.LastValue, val.IsCalled)); err != nil {
|
||||
log.Error("Unable to reset %s to %d. Error: %v", newSequenceName, val, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case setting.Database.Type.IsMSSQL():
|
||||
// MSSQL 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
|
||||
}
|
||||
|
||||
// MSSQL sp_rename will move all the constraints from the temporary table to the new table
|
||||
if _, err := sess.Exec(fmt.Sprintf("sp_rename `%s`,`%s`", tempTableName, tableName)); err != nil {
|
||||
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Fatal("Unrecognized DB")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WARNING: YOU MUST COMMIT THE SESSION AT THE END
|
||||
func DropTableColumns(sess db.Session, tableName string, columnNames ...string) (err error) {
|
||||
if tableName == "" || len(columnNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
// TODO: This will not work if there are foreign keys
|
||||
|
||||
switch {
|
||||
case setting.Database.Type.IsSQLite3():
|
||||
// First drop the indexes on the columns
|
||||
res, errIndex := sess.Query(fmt.Sprintf("PRAGMA index_list(`%s`)", tableName))
|
||||
if errIndex != nil {
|
||||
return errIndex
|
||||
}
|
||||
for _, row := range res {
|
||||
indexName := row["name"]
|
||||
indexRes, err := sess.Query(fmt.Sprintf("PRAGMA index_info(`%s`)", indexName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(indexRes) != 1 {
|
||||
continue
|
||||
}
|
||||
indexColumn := string(indexRes[0]["name"])
|
||||
for _, name := range columnNames {
|
||||
if name == indexColumn {
|
||||
_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s`", indexName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Here we need to get the columns from the original table
|
||||
sql := fmt.Sprintf("SELECT sql FROM sqlite_master WHERE tbl_name='%s' and type='table'", tableName)
|
||||
res, err := sess.Query(sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tableSQL := string(res[0]["sql"])
|
||||
|
||||
// Get the string offset for column definitions: `CREATE TABLE ( column-definitions... )`
|
||||
columnDefinitionsIndex := strings.Index(tableSQL, "(")
|
||||
if columnDefinitionsIndex < 0 {
|
||||
return errors.New("couldn't find column definitions")
|
||||
}
|
||||
|
||||
// Separate out the column definitions
|
||||
tableSQL = tableSQL[columnDefinitionsIndex:]
|
||||
|
||||
// Remove the required columnNames
|
||||
for _, name := range columnNames {
|
||||
tableSQL = regexp.MustCompile(regexp.QuoteMeta("`"+name+"`")+"[^`,)]*?[,)]").ReplaceAllString(tableSQL, "")
|
||||
}
|
||||
|
||||
// Ensure the query is ended properly
|
||||
tableSQL = strings.TrimSpace(tableSQL)
|
||||
if tableSQL[len(tableSQL)-1] != ')' {
|
||||
if tableSQL[len(tableSQL)-1] == ',' {
|
||||
tableSQL = tableSQL[:len(tableSQL)-1]
|
||||
}
|
||||
tableSQL += ")"
|
||||
}
|
||||
|
||||
// Find all the columns in the table
|
||||
columns := regexp.MustCompile("`([^`]*)`").FindAllString(tableSQL, -1)
|
||||
|
||||
tableSQL = fmt.Sprintf("CREATE TABLE `new_%s_new` ", tableName) + tableSQL
|
||||
if _, err := sess.Exec(tableSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now restore the data
|
||||
columnsSeparated := strings.Join(columns, ",")
|
||||
insertSQL := fmt.Sprintf("INSERT INTO `new_%s_new` (%s) SELECT %s FROM %s", tableName, columnsSeparated, columnsSeparated, tableName)
|
||||
if _, err := sess.Exec(insertSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now drop the old table
|
||||
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename the table
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `new_%s_new` RENAME TO `%s`", tableName, tableName)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case setting.Database.Type.IsPostgreSQL():
|
||||
cols := ""
|
||||
for _, col := range columnNames {
|
||||
if cols != "" {
|
||||
cols += ", "
|
||||
}
|
||||
cols += "DROP COLUMN `" + col + "` CASCADE"
|
||||
}
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
|
||||
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
|
||||
}
|
||||
case setting.Database.Type.IsMySQL():
|
||||
// Drop indexes on columns first
|
||||
sql := fmt.Sprintf("SHOW INDEX FROM %s WHERE column_name IN ('%s')", tableName, strings.Join(columnNames, "','"))
|
||||
res, err := sess.Query(sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, index := range res {
|
||||
indexName := index["column_name"]
|
||||
if len(indexName) > 0 {
|
||||
_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now drop the columns
|
||||
cols := ""
|
||||
for _, col := range columnNames {
|
||||
if cols != "" {
|
||||
cols += ", "
|
||||
}
|
||||
cols += "DROP COLUMN `" + col + "`"
|
||||
}
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
|
||||
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
|
||||
}
|
||||
case setting.Database.Type.IsMSSQL():
|
||||
cols := ""
|
||||
for _, col := range columnNames {
|
||||
if cols != "" {
|
||||
cols += ", "
|
||||
}
|
||||
cols += "`" + strings.ToLower(col) + "`"
|
||||
}
|
||||
sql := fmt.Sprintf("SELECT Name FROM sys.default_constraints WHERE parent_object_id = OBJECT_ID('%[1]s') AND parent_column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
|
||||
tableName, strings.ReplaceAll(cols, "`", "'"))
|
||||
constraints := make([]string, 0)
|
||||
if err := sess.SQL(sql).Find(&constraints); err != nil {
|
||||
return fmt.Errorf("find constraints: %w", err)
|
||||
}
|
||||
for _, constraint := range constraints {
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT `%s`", tableName, constraint)); err != nil {
|
||||
return fmt.Errorf("drop table `%s` default constraint `%s`: %w", tableName, constraint, err)
|
||||
}
|
||||
}
|
||||
sql = fmt.Sprintf("SELECT DISTINCT Name FROM sys.indexes INNER JOIN sys.index_columns ON indexes.index_id = index_columns.index_id AND indexes.object_id = index_columns.object_id WHERE indexes.object_id = OBJECT_ID('%[1]s') AND index_columns.column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
|
||||
tableName, strings.ReplaceAll(cols, "`", "'"))
|
||||
constraints = make([]string, 0)
|
||||
if err := sess.SQL(sql).Find(&constraints); err != nil {
|
||||
return fmt.Errorf("find constraints: %w", err)
|
||||
}
|
||||
for _, constraint := range constraints {
|
||||
if _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil {
|
||||
return fmt.Errorf("drop index `%[2]s` on `%[1]s`: %[3]w", tableName, constraint, err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN %s", tableName, cols)); err != nil {
|
||||
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
|
||||
}
|
||||
default:
|
||||
log.Fatal("Unrecognized DB")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ModifyColumn will modify column's type or other property. SQLITE is not supported
|
||||
func ModifyColumn(x db.EngineMigration, tableName string, col *schemas.Column) error {
|
||||
var indexes map[string]*schemas.Index
|
||||
var err error
|
||||
// MSSQL have to remove index at first, otherwise alter column will fail
|
||||
// ref. https://sqlzealots.com/2018/05/09/error-message-the-index-is-dependent-on-column-alter-table-alter-column-failed-because-one-or-more-objects-access-this-column/
|
||||
if x.Dialect().URI().DBType == schemas.MSSQL {
|
||||
indexes, err = x.Dialect().GetIndexes(x.DB(), context.Background(), tableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, index := range indexes {
|
||||
_, err = x.Exec(x.Dialect().DropIndexSQL(tableName, index))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for _, index := range indexes {
|
||||
_, err = x.Exec(x.Dialect().CreateIndexSQL(tableName, index))
|
||||
if err != nil {
|
||||
log.Error("Create index %s on table %s failed: %v", index.Name, tableName, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
alterSQL := x.Dialect().ModifyColumnSQL(tableName, col)
|
||||
if _, err := x.Exec(alterSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/models/migrations/migrationtest"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm/names"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
migrationtest.MainTest(m)
|
||||
}
|
||||
|
||||
func Test_DropTableColumns(t *testing.T) {
|
||||
x, deferable := migrationtest.PrepareTestEnv(t, 0)
|
||||
defer deferable()
|
||||
// FIXME: this logic seems wrong. Need to add an assertion here in the future, but it seems causing failure.
|
||||
if x == nil || t.Failed() {
|
||||
t.Skip("PrepareTestEnv did not yield a usable engine")
|
||||
}
|
||||
|
||||
type DropTest struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
FirstColumn string
|
||||
ToDropColumn string `xorm:"unique"`
|
||||
AnotherColumn int64
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
}
|
||||
|
||||
columns := []string{
|
||||
"first_column",
|
||||
"to_drop_column",
|
||||
"another_column",
|
||||
"created_unix",
|
||||
"updated_unix",
|
||||
}
|
||||
|
||||
x.SetMapper(names.GonicMapper{})
|
||||
|
||||
for i := range columns {
|
||||
if err := x.Sync(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to create DropTest table: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("unable to begin transaction: %v", err)
|
||||
return
|
||||
}
|
||||
if err := DropTableColumns(sess, "drop_test", columns[i:]...); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("Unable to drop columns[%d:]: %s from drop_test: %v", i, columns[i:], err)
|
||||
return
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("unable to commit transaction: %v", err)
|
||||
return
|
||||
}
|
||||
sess.Close()
|
||||
if err := x.DropTables(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to drop table: %v", err)
|
||||
return
|
||||
}
|
||||
for j := range columns[i+1:] {
|
||||
if err := x.Sync(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to create DropTest table: %v", err)
|
||||
return
|
||||
}
|
||||
dropcols := append([]string{columns[i]}, columns[j+i+1:]...)
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("unable to begin transaction: %v", err)
|
||||
return
|
||||
}
|
||||
if err := DropTableColumns(sess, "drop_test", dropcols...); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("Unable to drop columns: %s from drop_test: %v", dropcols, err)
|
||||
return
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("unable to commit transaction: %v", err)
|
||||
return
|
||||
}
|
||||
sess.Close()
|
||||
if err := x.DropTables(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to drop table: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
func HashToken(token, salt string) string {
|
||||
tempHash := pbkdf2.Key([]byte(token), []byte(salt), 10000, 50, sha256.New)
|
||||
return hex.EncodeToString(tempHash)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
-
|
||||
id: 1
|
||||
uid: 1
|
||||
issue_id: 1
|
||||
is_read: true
|
||||
is_mentioned: false
|
||||
|
||||
-
|
||||
id: 2
|
||||
uid: 2
|
||||
issue_id: 1
|
||||
is_read: true
|
||||
is_mentioned: false
|
||||
|
||||
-
|
||||
id: 3
|
||||
uid: 2
|
||||
issue_id: 1 # duplicated with id 2
|
||||
is_read: false
|
||||
is_mentioned: true
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-
|
||||
id: 1
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# for matrix, the access_token has been moved to "header_authorization"
|
||||
-
|
||||
id: 1
|
||||
meta: '{"homeserver_url":"https://matrix.example.com","room_id":"roomID","message_type":1}'
|
||||
header_authorization: "Bearer s3cr3t"
|
||||
-
|
||||
id: 2
|
||||
meta: ''
|
||||
header_authorization: ""
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
# unsafe payload
|
||||
- id: 1
|
||||
hook_id: 1
|
||||
payload_content: '{"homeserver_url":"https://matrix.example.com","room_id":"roomID","access_token":"s3cr3t","message_type":1}'
|
||||
# safe payload
|
||||
- id: 2
|
||||
hook_id: 2
|
||||
payload_content: '{"homeserver_url":"https://matrix.example.com","room_id":"roomID","message_type":1}'
|
||||
@@ -0,0 +1,10 @@
|
||||
# matrix webhook
|
||||
- id: 1
|
||||
type: matrix
|
||||
meta: '{"homeserver_url":"https://matrix.example.com","room_id":"roomID","access_token":"s3cr3t","message_type":1}'
|
||||
header_authorization_encrypted: ''
|
||||
# gitea webhook
|
||||
- id: 2
|
||||
type: gitea
|
||||
meta: ''
|
||||
header_authorization_encrypted: ''
|
||||
@@ -0,0 +1,4 @@
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
index: 1
|
||||
@@ -0,0 +1,11 @@
|
||||
-
|
||||
id: 1
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
|
||||
issue_id: 1
|
||||
release_id: 0
|
||||
|
||||
-
|
||||
id: 2
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12
|
||||
issue_id: 0
|
||||
release_id: 1
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
@@ -0,0 +1,9 @@
|
||||
-
|
||||
id: 1
|
||||
project_id: 1
|
||||
issue_id: 1
|
||||
|
||||
-
|
||||
id: 2
|
||||
project_id: 1
|
||||
issue_id: 1
|
||||
@@ -0,0 +1,23 @@
|
||||
-
|
||||
id: 1
|
||||
title: project without default column
|
||||
owner_id: 2
|
||||
repo_id: 0
|
||||
is_closed: false
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
||||
|
||||
-
|
||||
id: 2
|
||||
title: project with multiple default columns
|
||||
owner_id: 2
|
||||
repo_id: 0
|
||||
is_closed: false
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
||||
@@ -0,0 +1,26 @@
|
||||
-
|
||||
id: 1
|
||||
project_id: 1
|
||||
title: Done
|
||||
creator_id: 2
|
||||
default: false
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 2
|
||||
project_id: 2
|
||||
title: Backlog
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 3
|
||||
project_id: 2
|
||||
title: Uncategorized
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
@@ -0,0 +1,28 @@
|
||||
# Issue_Label 1 should not be deleted
|
||||
-
|
||||
id: 1
|
||||
issue_id: 1
|
||||
label_id: 1
|
||||
|
||||
# Issue_label 2 should be deleted
|
||||
-
|
||||
id: 2
|
||||
issue_id: 5
|
||||
label_id: 99
|
||||
|
||||
# Issue_Label 3 should not be deleted
|
||||
-
|
||||
id: 3
|
||||
issue_id: 2
|
||||
label_id: 1
|
||||
|
||||
# Issue_Label 4 should not be deleted
|
||||
-
|
||||
id: 4
|
||||
issue_id: 2
|
||||
label_id: 4
|
||||
|
||||
-
|
||||
id: 5
|
||||
issue_id: 2
|
||||
label_id: 87
|
||||
@@ -0,0 +1,43 @@
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
org_id: 0
|
||||
name: label1
|
||||
color: '#abcdef'
|
||||
num_issues: 2
|
||||
num_closed_issues: 0
|
||||
|
||||
-
|
||||
id: 2
|
||||
repo_id: 1
|
||||
org_id: 0
|
||||
name: label2
|
||||
color: '#000000'
|
||||
num_issues: 1
|
||||
num_closed_issues: 1
|
||||
-
|
||||
id: 3
|
||||
repo_id: 0
|
||||
org_id: 3
|
||||
name: orglabel3
|
||||
color: '#abcdef'
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
|
||||
-
|
||||
id: 4
|
||||
repo_id: 0
|
||||
org_id: 3
|
||||
name: orglabel4
|
||||
color: '#000000'
|
||||
num_issues: 1
|
||||
num_closed_issues: 0
|
||||
|
||||
-
|
||||
id: 5
|
||||
repo_id: 10
|
||||
org_id: 0
|
||||
name: pull-test-label
|
||||
color: '#000000'
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# type ActionRun struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# RepoID int64 `xorm:"index"`
|
||||
# Index int64
|
||||
# CommitSHA string `xorm:"commit_sha"`
|
||||
# Event string
|
||||
# TriggerEvent string
|
||||
# EventPayload string `xorm:"LONGTEXT"`
|
||||
# }
|
||||
-
|
||||
id: 990
|
||||
repo_id: 100
|
||||
index: 7
|
||||
commit_sha: merge-sha
|
||||
event: pull_request
|
||||
event_payload: '{"pull_request":{"head":{"sha":"sha-shared"}}}'
|
||||
-
|
||||
id: 991
|
||||
repo_id: 100
|
||||
index: 8
|
||||
commit_sha: sha-shared
|
||||
event: push
|
||||
event_payload: '{"head_commit":{"id":"sha-shared"}}'
|
||||
-
|
||||
id: 1991
|
||||
repo_id: 100
|
||||
index: 9
|
||||
commit_sha: sha-other
|
||||
event: release
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# type ActionRunJob struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# RunID int64 `xorm:"index"`
|
||||
# }
|
||||
-
|
||||
id: 997
|
||||
run_id: 990
|
||||
-
|
||||
id: 998
|
||||
run_id: 990
|
||||
-
|
||||
id: 1997
|
||||
run_id: 991
|
||||
-
|
||||
id: 1998
|
||||
run_id: 1991
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
# type CommitStatus struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# RepoID int64 `xorm:"index"`
|
||||
# SHA string
|
||||
# TargetURL string
|
||||
# }
|
||||
-
|
||||
id: 10010
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: /testuser/repo1/actions/runs/7/jobs/0
|
||||
-
|
||||
id: 10011
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: /testuser/repo1/actions/runs/7/jobs/1
|
||||
-
|
||||
id: 10012
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: /testuser/repo1/actions/runs/8/jobs/0
|
||||
-
|
||||
id: 10013
|
||||
repo_id: 100
|
||||
sha: sha-other
|
||||
target_url: /testuser/repo1/actions/runs/9/jobs/0
|
||||
-
|
||||
id: 10014
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: /otheruser/badrepo/actions/runs/7/jobs/0
|
||||
-
|
||||
id: 10015
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: /testuser/repo1/actions/runs/10/jobs/0
|
||||
-
|
||||
id: 10016
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: /testuser/repo1/actions/runs/7/jobs/3
|
||||
-
|
||||
id: 10017
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: https://ci.example.com/build/123
|
||||
-
|
||||
id: 10018
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
target_url: /testuser/repo1/actions/runs/990/jobs/997
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# type CommitStatusSummary struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# RepoID int64 `xorm:"index"`
|
||||
# SHA string `xorm:"VARCHAR(64) NOT NULL"`
|
||||
# State string `xorm:"VARCHAR(7) NOT NULL"`
|
||||
# TargetURL string
|
||||
# }
|
||||
-
|
||||
id: 10020
|
||||
repo_id: 100
|
||||
sha: sha-shared
|
||||
state: pending
|
||||
target_url: /testuser/repo1/actions/runs/7/jobs/0
|
||||
-
|
||||
id: 10021
|
||||
repo_id: 100
|
||||
sha: sha-other
|
||||
state: pending
|
||||
target_url: /testuser/repo1/actions/runs/9/jobs/0
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# type Repository struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# OwnerName string
|
||||
# Name string
|
||||
# }
|
||||
-
|
||||
id: 100
|
||||
owner_name: testuser
|
||||
name: repo1
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
-
|
||||
id: 1
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
|
||||
-
|
||||
id: 2
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
|
||||
-
|
||||
id: 3
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
|
||||
-
|
||||
id: 4
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
|
||||
@@ -0,0 +1,21 @@
|
||||
-
|
||||
id: 1
|
||||
name: "u2fkey-correctly-migrated"
|
||||
user_id: 1
|
||||
raw: 0x05040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf240efe2e213b889daf3fc88e3952e8dd6b4cfd82f1a1212e2ab4b19389455ecf3e67f0aeafc91b9c0d413c9d6215a45177c1d5076358aa6ee20e1b30e3d7467cae2308202bd308201a5a00302010202041e8f8734300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a306e310b300906035504061302534531123010060355040a0c0959756269636f20414231223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e3127302506035504030c1e59756269636f205532462045452053657269616c203531323732323734303059301306072a8648ce3d020106082a8648ce3d03010703420004a879f82338ed1494bac0704bcc7fc663d1b271715976243101c7605115d7c1529e281c1c67322d384b5cd55dd3e9818d5fd85c22af326e0c64fc20afe33f2366a36c306a302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e373013060b2b0601040182e51c0201010404030204303021060b2b0601040182e51c010104041204102fc0579f811347eab116bb5a8db9202a300c0603551d130101ff04023000300d06092a864886f70d01010b050003820101008693ff62df0d5779d4748d7fc8d10227318a8e580e6a3a57c108e94e03c38568b366894fce5624be4a3efd7f34118b3d993743f792a1989160c8fc9ae0b04e3df9ee15e3e88c04fc82a8dcbf5818e108dcc2968577ae79ff662b94734e3dec4597305d73e6e55ee2beb9cd9678ca0935e533eb638f8e26fabb817cda441fbe9831832ae5f6e2ad992f9ebbdb4c62238b8f8d7ab481d6d3263bcdbf9e4a57550370988ad5813440fa032cadb6723cadd8f8d7ba809f75b43cffa0a5b9add14232ef9d9e14812638233c4ca4a873b9f8ac98e32ba19167606e15909fcddb4a2dffbdae4620249f9a6646ac81e4832d1119febfaa731a882da25a77827d46d190173046022100b579338a44c236d3f214b2e150011a08cf251193ecfae2244edb0a5794e9b301022100fab468862c47d98204d437cf2be8c54a5a4ecd1ebb1c61a6c23da7b9c75f6841
|
||||
counter: 0
|
||||
- id: 2
|
||||
name: "u2fkey-incorrectly-migrated"
|
||||
user_id: 1
|
||||
raw: 0x05040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf240efe2e213b889daf3fc88e3952e8dd6b4cfd82f1a1212e2ab4b19389455ecf3e67f0aeafc91b9c0d413c9d6215a45177c1d5076358aa6ee20e1b30e3d7467cae2308202bd308201a5a00302010202041e8f8734300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a306e310b300906035504061302534531123010060355040a0c0959756269636f20414231223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e3127302506035504030c1e59756269636f205532462045452053657269616c203531323732323734303059301306072a8648ce3d020106082a8648ce3d03010703420004a879f82338ed1494bac0704bcc7fc663d1b271715976243101c7605115d7c1529e281c1c67322d384b5cd55dd3e9818d5fd85c22af326e0c64fc20afe33f2366a36c306a302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e373013060b2b0601040182e51c0201010404030204303021060b2b0601040182e51c010104041204102fc0579f811347eab116bb5a8db9202a300c0603551d130101ff04023000300d06092a864886f70d01010b050003820101008693ff62df0d5779d4748d7fc8d10227318a8e580e6a3a57c108e94e03c38568b366894fce5624be4a3efd7f34118b3d993743f792a1989160c8fc9ae0b04e3df9ee15e3e88c04fc82a8dcbf5818e108dcc2968577ae79ff662b94734e3dec4597305d73e6e55ee2beb9cd9678ca0935e533eb638f8e26fabb817cda441fbe9831832ae5f6e2ad992f9ebbdb4c62238b8f8d7ab481d6d3263bcdbf9e4a57550370988ad5813440fa032cadb6723cadd8f8d7ba809f75b43cffa0a5b9add14232ef9d9e14812638233c4ca4a873b9f8ac98e32ba19167606e15909fcddb4a2dffbdae4620249f9a6646ac81e4832d1119febfaa731a882da25a77827d46d190173046022100b579338a44c236d3f214b2e150011a08cf251193ecfae2244edb0a5794e9b301022100fab468862c47d98204d437cf2be8c54a5a4ecd1ebb1c61a6c23da7b9c75f6841
|
||||
counter: 0
|
||||
- id: 3
|
||||
name: "u2fkey-deleted"
|
||||
user_id: 1
|
||||
raw: 0x05040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf240efe2e213b889daf3fc88e3952e8dd6b4cfd82f1a1212e2ab4b19389455ecf3e67f0aeafc91b9c0d413c9d6215a45177c1d5076358aa6ee20e1b30e3d7467cae2308202bd308201a5a00302010202041e8f8734300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a306e310b300906035504061302534531123010060355040a0c0959756269636f20414231223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e3127302506035504030c1e59756269636f205532462045452053657269616c203531323732323734303059301306072a8648ce3d020106082a8648ce3d03010703420004a879f82338ed1494bac0704bcc7fc663d1b271715976243101c7605115d7c1529e281c1c67322d384b5cd55dd3e9818d5fd85c22af326e0c64fc20afe33f2366a36c306a302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e373013060b2b0601040182e51c0201010404030204303021060b2b0601040182e51c010104041204102fc0579f811347eab116bb5a8db9202a300c0603551d130101ff04023000300d06092a864886f70d01010b050003820101008693ff62df0d5779d4748d7fc8d10227318a8e580e6a3a57c108e94e03c38568b366894fce5624be4a3efd7f34118b3d993743f792a1989160c8fc9ae0b04e3df9ee15e3e88c04fc82a8dcbf5818e108dcc2968577ae79ff662b94734e3dec4597305d73e6e55ee2beb9cd9678ca0935e533eb638f8e26fabb817cda441fbe9831832ae5f6e2ad992f9ebbdb4c62238b8f8d7ab481d6d3263bcdbf9e4a57550370988ad5813440fa032cadb6723cadd8f8d7ba809f75b43cffa0a5b9add14232ef9d9e14812638233c4ca4a873b9f8ac98e32ba19167606e15909fcddb4a2dffbdae4620249f9a6646ac81e4832d1119febfaa731a882da25a77827d46d190173046022100b579338a44c236d3f214b2e150011a08cf251193ecfae2244edb0a5794e9b301022100fab468862c47d98204d437cf2be8c54a5a4ecd1ebb1c61a6c23da7b9c75f6841
|
||||
counter: 0
|
||||
- id: 4
|
||||
name: "u2fkey-wrong-user-id"
|
||||
user_id: 2
|
||||
raw: 0x05040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf240efe2e213b889daf3fc88e3952e8dd6b4cfd82f1a1212e2ab4b19389455ecf3e67f0aeafc91b9c0d413c9d6215a45177c1d5076358aa6ee20e1b30e3d7467cae2308202bd308201a5a00302010202041e8f8734300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a306e310b300906035504061302534531123010060355040a0c0959756269636f20414231223020060355040b0c1941757468656e74696361746f72204174746573746174696f6e3127302506035504030c1e59756269636f205532462045452053657269616c203531323732323734303059301306072a8648ce3d020106082a8648ce3d03010703420004a879f82338ed1494bac0704bcc7fc663d1b271715976243101c7605115d7c1529e281c1c67322d384b5cd55dd3e9818d5fd85c22af326e0c64fc20afe33f2366a36c306a302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e373013060b2b0601040182e51c0201010404030204303021060b2b0601040182e51c010104041204102fc0579f811347eab116bb5a8db9202a300c0603551d130101ff04023000300d06092a864886f70d01010b050003820101008693ff62df0d5779d4748d7fc8d10227318a8e580e6a3a57c108e94e03c38568b366894fce5624be4a3efd7f34118b3d993743f792a1989160c8fc9ae0b04e3df9ee15e3e88c04fc82a8dcbf5818e108dcc2968577ae79ff662b94734e3dec4597305d73e6e55ee2beb9cd9678ca0935e533eb638f8e26fabb817cda441fbe9831832ae5f6e2ad992f9ebbdb4c62238b8f8d7ab481d6d3263bcdbf9e4a57550370988ad5813440fa032cadb6723cadd8f8d7ba809f75b43cffa0a5b9add14232ef9d9e14812638233c4ca4a873b9f8ac98e32ba19167606e15909fcddb4a2dffbdae4620249f9a6646ac81e4832d1119febfaa731a882da25a77827d46d190173046022100b579338a44c236d3f214b2e150011a08cf251193ecfae2244edb0a5794e9b301022100fab468862c47d98204d437cf2be8c54a5a4ecd1ebb1c61a6c23da7b9c75f6841
|
||||
counter: 0
|
||||
@@ -0,0 +1,30 @@
|
||||
-
|
||||
id: 1
|
||||
lower_name: "u2fkey-correctly-migrated"
|
||||
name: "u2fkey-correctly-migrated"
|
||||
user_id: 1
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
|
||||
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
|
||||
attestation_type: 'fido-u2f'
|
||||
sign_count: 1
|
||||
clone_warning: false
|
||||
-
|
||||
id: 2
|
||||
lower_name: "u2fkey-incorrectly-migrated"
|
||||
name: "u2fkey-incorrectly-migrated"
|
||||
user_id: 1
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8A"
|
||||
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
|
||||
attestation_type: 'fido-u2f'
|
||||
sign_count: 1
|
||||
clone_warning: false
|
||||
-
|
||||
id: 4
|
||||
lower_name: "u2fkey-wrong-user-id"
|
||||
name: "u2fkey-wrong-user-id"
|
||||
user_id: 1
|
||||
credential_id: "THIS SHOULD CHANGE"
|
||||
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
|
||||
attestation_type: 'fido-u2f'
|
||||
sign_count: 1
|
||||
clone_warning: false
|
||||
@@ -0,0 +1,52 @@
|
||||
# type Comment struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# Type int `xorm:"INDEX"`
|
||||
# IssueID int64 `xorm:"INDEX"`
|
||||
# LabelID int64
|
||||
# }
|
||||
#
|
||||
# we are only interested in type 7
|
||||
#
|
||||
|
||||
-
|
||||
id: 1 # Should remain
|
||||
type: 6
|
||||
issue_id: 1
|
||||
label_id: 0
|
||||
should_remain: true
|
||||
-
|
||||
id: 2 # Should remain
|
||||
type: 7
|
||||
issue_id: 1 # repo_id: 1
|
||||
label_id: 1 # repo_id: 1
|
||||
should_remain: true
|
||||
-
|
||||
id: 3 # Should remain
|
||||
type: 7
|
||||
issue_id: 2 # repo_id: 2 owner_id: 1
|
||||
label_id: 2 # org_id: 1
|
||||
should_remain: true
|
||||
-
|
||||
id: 4 # Should be DELETED
|
||||
type: 7
|
||||
issue_id: 1 # repo_id: 1
|
||||
label_id: 3 # repo_id: 2
|
||||
should_remain: false
|
||||
-
|
||||
id: 5 # Should remain
|
||||
type: 7
|
||||
issue_id: 3 # repo_id: 1
|
||||
label_id: 1 # repo_id: 1
|
||||
should_remain: true
|
||||
-
|
||||
id: 6 # Should be DELETED
|
||||
type: 7
|
||||
issue_id: 3 # repo_id: 1 owner_id: 2
|
||||
label_id: 2 # org_id: 1
|
||||
should_remain: false
|
||||
-
|
||||
id: 7 # Should be DELETED
|
||||
type: 7
|
||||
issue_id: 3 # repo_id: 1 owner_id: 2
|
||||
label_id: 5 # repo_id: 3
|
||||
should_remain: false
|
||||
@@ -0,0 +1,21 @@
|
||||
# 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.
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
index: 1
|
||||
-
|
||||
id: 2
|
||||
repo_id: 2
|
||||
index: 1
|
||||
-
|
||||
id: 3
|
||||
repo_id: 1
|
||||
index: 2
|
||||
-
|
||||
id: 4
|
||||
repo_id: 3
|
||||
index: 1
|
||||
@@ -0,0 +1,35 @@
|
||||
# type IssueLabel struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# IssueID int64 `xorm:"UNIQUE(s)"`
|
||||
# LabelID int64 `xorm:"UNIQUE(s)"`
|
||||
# }
|
||||
-
|
||||
id: 1 # Should remain - matches comment 2
|
||||
issue_id: 1
|
||||
label_id: 1
|
||||
should_remain: true
|
||||
-
|
||||
id: 2 # Should remain
|
||||
issue_id: 2
|
||||
label_id: 2
|
||||
should_remain: true
|
||||
-
|
||||
id: 3 # Should be deleted
|
||||
issue_id: 1
|
||||
label_id: 3
|
||||
should_remain: false
|
||||
-
|
||||
id: 4 # Should remain
|
||||
issue_id: 3
|
||||
label_id: 1
|
||||
should_remain: true
|
||||
-
|
||||
id: 5 # Should be deleted
|
||||
issue_id: 3
|
||||
label_id: 2
|
||||
should_remain: false
|
||||
-
|
||||
id: 6 # Should be deleted
|
||||
issue_id: 3
|
||||
label_id: 5
|
||||
should_remain: false
|
||||
@@ -0,0 +1,25 @@
|
||||
# type Label struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# RepoID int64 `xorm:"INDEX"`
|
||||
# OrgID int64 `xorm:"INDEX"`
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
org_id: 0
|
||||
-
|
||||
id: 2
|
||||
repo_id: 0
|
||||
org_id: 1
|
||||
-
|
||||
id: 3
|
||||
repo_id: 2
|
||||
org_id: 0
|
||||
-
|
||||
id: 4
|
||||
repo_id: 1
|
||||
org_id: 0
|
||||
-
|
||||
id: 5
|
||||
repo_id: 3
|
||||
org_id: 0
|
||||
@@ -0,0 +1,17 @@
|
||||
# type Repository struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# OwnerID int64 `xorm:"UNIQUE(s) index"`
|
||||
# LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
owner_id: 2
|
||||
lower_name: "repo1"
|
||||
-
|
||||
id: 2
|
||||
owner_id: 1
|
||||
lower_name: "repo2"
|
||||
-
|
||||
id: 3
|
||||
owner_id: 2
|
||||
lower_name: "repo3"
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
id: 1
|
||||
context_hash: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
@@ -0,0 +1,5 @@
|
||||
-
|
||||
id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
merge_base: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
merged_commit_id: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
id: 1
|
||||
sha1: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
id: 1
|
||||
commit_id: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
@@ -0,0 +1,11 @@
|
||||
# type Repository struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
-
|
||||
id: 2
|
||||
-
|
||||
id: 3
|
||||
-
|
||||
id: 10
|
||||
@@ -0,0 +1,5 @@
|
||||
-
|
||||
id: 1
|
||||
user_id: 1
|
||||
pull_id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
-
|
||||
id: 1
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
|
||||
-
|
||||
id: 2
|
||||
credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR="
|
||||
-
|
||||
id: 4
|
||||
credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
-
|
||||
id: 1
|
||||
lower_name: "u2fkey-correctly-migrated"
|
||||
name: "u2fkey-correctly-migrated"
|
||||
user_id: 1
|
||||
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
|
||||
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
|
||||
attestation_type: 'fido-u2f'
|
||||
sign_count: 1
|
||||
clone_warning: false
|
||||
-
|
||||
id: 2
|
||||
lower_name: "non-u2f-key"
|
||||
name: "non-u2f-key"
|
||||
user_id: 1
|
||||
credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR"
|
||||
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
|
||||
attestation_type: 'none'
|
||||
sign_count: 1
|
||||
clone_warning: false
|
||||
-
|
||||
id: 4
|
||||
lower_name: "packed-key"
|
||||
name: "packed-key"
|
||||
user_id: 1
|
||||
credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="
|
||||
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
|
||||
attestation_type: 'fido-u2f'
|
||||
sign_count: 1
|
||||
clone_warning: false
|
||||
@@ -0,0 +1,48 @@
|
||||
# type LoginSource struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# Type int
|
||||
# Cfg []byte `xorm:"TEXT"`
|
||||
# Expected []byte `xorm:"TEXT"`
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
type: 1
|
||||
is_actived: false
|
||||
cfg: "{\"Source\":{\"A\":\"string\",\"B\":1}}"
|
||||
expected: "{\"Source\":{\"A\":\"string\",\"B\":1}}"
|
||||
-
|
||||
id: 2
|
||||
type: 2
|
||||
is_actived: true
|
||||
cfg: "{\"Source\":{\"A\":\"string2\",\"B\":2}}"
|
||||
expected: "{\"A\":\"string2\",\"B\":2}"
|
||||
-
|
||||
id: 3
|
||||
type: 3
|
||||
is_actived: false
|
||||
cfg: "{\"Source\":{\"A\":\"string3\",\"B\":3}}"
|
||||
expected: "{\"Source\":{\"A\":\"string3\",\"B\":3}}"
|
||||
-
|
||||
id: 4
|
||||
type: 4
|
||||
is_actived: true
|
||||
cfg: "{\"Source\":{\"A\":\"string4\",\"B\":4}}"
|
||||
expected: "{\"Source\":{\"A\":\"string4\",\"B\":4}}"
|
||||
-
|
||||
id: 5
|
||||
type: 5
|
||||
is_actived: false
|
||||
cfg: "{\"Source\":{\"A\":\"string5\",\"B\":5}}"
|
||||
expected: "{\"A\":\"string5\",\"B\":5}"
|
||||
-
|
||||
id: 6
|
||||
type: 2
|
||||
is_actived: true
|
||||
cfg: "{\"A\":\"string6\",\"B\":6}"
|
||||
expected: "{\"A\":\"string6\",\"B\":6}"
|
||||
-
|
||||
id: 7
|
||||
type: 5
|
||||
is_actived: false
|
||||
cfg: "{\"A\":\"string7\",\"B\":7}"
|
||||
expected: "{\"A\":\"string7\",\"B\":7}"
|
||||
@@ -0,0 +1,4 @@
|
||||
-
|
||||
id: 1
|
||||
description: the badge
|
||||
image_url: https://gitea.com/myimage.png
|
||||
@@ -0,0 +1,19 @@
|
||||
# type Milestone struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# IsClosed bool
|
||||
# NumIssues int
|
||||
# NumClosedIssues int
|
||||
# Completeness int // Percentage(1-100).
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
is_closed: false
|
||||
num_issues: 3
|
||||
num_closed_issues: 1
|
||||
completeness: 33
|
||||
-
|
||||
id: 2
|
||||
is_closed: true
|
||||
num_issues: 5
|
||||
num_closed_issues: 5
|
||||
completeness: 100
|
||||
@@ -0,0 +1,25 @@
|
||||
# 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.
|
||||
# MilestoneID int64 `xorm:"INDEX"`
|
||||
# IsClosed bool `xorm:"INDEX"`
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
index: 1
|
||||
milestone_id: 1
|
||||
is_closed: false
|
||||
-
|
||||
id: 2
|
||||
repo_id: 1
|
||||
index: 2
|
||||
milestone_id: 1
|
||||
is_closed: true
|
||||
-
|
||||
id: 4
|
||||
repo_id: 1
|
||||
index: 3
|
||||
milestone_id: 1
|
||||
is_closed: false
|
||||
@@ -0,0 +1,19 @@
|
||||
# type Milestone struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# IsClosed bool
|
||||
# NumIssues int
|
||||
# NumClosedIssues int
|
||||
# Completeness int // Percentage(1-100).
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
is_closed: false
|
||||
num_issues: 4
|
||||
num_closed_issues: 2
|
||||
completeness: 50
|
||||
-
|
||||
id: 2
|
||||
is_closed: true
|
||||
num_issues: 5
|
||||
num_closed_issues: 5
|
||||
completeness: 100
|
||||
@@ -0,0 +1,548 @@
|
||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/migrations/v1_10"
|
||||
"gitea.dev/models/migrations/v1_11"
|
||||
"gitea.dev/models/migrations/v1_12"
|
||||
"gitea.dev/models/migrations/v1_13"
|
||||
"gitea.dev/models/migrations/v1_14"
|
||||
"gitea.dev/models/migrations/v1_15"
|
||||
"gitea.dev/models/migrations/v1_16"
|
||||
"gitea.dev/models/migrations/v1_17"
|
||||
"gitea.dev/models/migrations/v1_18"
|
||||
"gitea.dev/models/migrations/v1_19"
|
||||
"gitea.dev/models/migrations/v1_20"
|
||||
"gitea.dev/models/migrations/v1_21"
|
||||
"gitea.dev/models/migrations/v1_22"
|
||||
"gitea.dev/models/migrations/v1_23"
|
||||
"gitea.dev/models/migrations/v1_24"
|
||||
"gitea.dev/models/migrations/v1_25"
|
||||
"gitea.dev/models/migrations/v1_26"
|
||||
"gitea.dev/models/migrations/v1_27"
|
||||
"gitea.dev/models/migrations/v1_6"
|
||||
"gitea.dev/models/migrations/v1_7"
|
||||
"gitea.dev/models/migrations/v1_8"
|
||||
"gitea.dev/models/migrations/v1_9"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"xorm.io/xorm/names"
|
||||
)
|
||||
|
||||
const minDBVersion = 70 // Gitea 1.5.3
|
||||
|
||||
type migration struct {
|
||||
idNumber int64 // DB version is "the last migration's idNumber" + 1
|
||||
description string
|
||||
migrate func(context.Context, db.EngineMigration) error
|
||||
}
|
||||
|
||||
// newMigration creates a new migration
|
||||
func newMigration[T func(db.EngineMigration) error | func(context.Context, db.EngineMigration) error](idNumber int64, desc string, fn T) *migration {
|
||||
m := &migration{idNumber: idNumber, description: desc}
|
||||
var ok bool
|
||||
if m.migrate, ok = any(fn).(func(context.Context, db.EngineMigration) error); !ok {
|
||||
m.migrate = func(ctx context.Context, x db.EngineMigration) error {
|
||||
return any(fn).(func(db.EngineMigration) error)(x)
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Migrate executes the migration
|
||||
func (m *migration) Migrate(ctx context.Context, x db.EngineMigration) error {
|
||||
return m.migrate(ctx, x)
|
||||
}
|
||||
|
||||
// Version describes the version table. Should have only one row with id==1
|
||||
type Version struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Version int64 // DB version is "the last migration's idNumber" + 1
|
||||
}
|
||||
|
||||
// Use noopMigration when there is a migration that has been no-oped
|
||||
var noopMigration = func(_ db.EngineMigration) error { return nil }
|
||||
|
||||
var preparedMigrations []*migration
|
||||
|
||||
// This is a sequence of migrations. Add new migrations to the bottom of the list.
|
||||
// If you want to "retire" a migration, remove it from the top of the list and
|
||||
// update minDBVersion accordingly
|
||||
func prepareMigrationTasks() []*migration {
|
||||
if preparedMigrations != nil {
|
||||
return preparedMigrations
|
||||
}
|
||||
preparedMigrations = []*migration{
|
||||
// Gitea 1.5.0 ends at database version 69
|
||||
|
||||
newMigration(70, "add issue_dependencies", v1_6.AddIssueDependencies),
|
||||
newMigration(71, "protect each scratch token", v1_6.AddScratchHash),
|
||||
newMigration(72, "add review", v1_6.AddReview),
|
||||
|
||||
// Gitea 1.6.0 ends at database version 73
|
||||
|
||||
newMigration(73, "add must_change_password column for users table", v1_7.AddMustChangePassword),
|
||||
newMigration(74, "add approval whitelists to protected branches", v1_7.AddApprovalWhitelistsToProtectedBranches),
|
||||
newMigration(75, "clear nonused data which not deleted when user was deleted", v1_7.ClearNonusedData),
|
||||
|
||||
// Gitea 1.7.0 ends at database version 76
|
||||
|
||||
newMigration(76, "add pull request rebase with merge commit", v1_8.AddPullRequestRebaseWithMerge),
|
||||
newMigration(77, "add theme to users", v1_8.AddUserDefaultTheme),
|
||||
newMigration(78, "rename repo is_bare to repo is_empty", v1_8.RenameRepoIsBareToIsEmpty),
|
||||
newMigration(79, "add can close issues via commit in any branch", v1_8.AddCanCloseIssuesViaCommitInAnyBranch),
|
||||
newMigration(80, "add is locked to issues", v1_8.AddIsLockedToIssues),
|
||||
newMigration(81, "update U2F counter type", v1_8.ChangeU2FCounterType),
|
||||
|
||||
// Gitea 1.8.0 ends at database version 82
|
||||
|
||||
newMigration(82, "hot fix for wrong release sha1 on release table", v1_9.FixReleaseSha1OnReleaseTable),
|
||||
newMigration(83, "add uploader id for table attachment", v1_9.AddUploaderIDForAttachment),
|
||||
newMigration(84, "add table to store original imported gpg keys", v1_9.AddGPGKeyImport),
|
||||
newMigration(85, "hash application token", v1_9.HashAppToken),
|
||||
newMigration(86, "add http method to webhook", v1_9.AddHTTPMethodToWebhook),
|
||||
newMigration(87, "add avatar field to repository", v1_9.AddAvatarFieldToRepository),
|
||||
|
||||
// Gitea 1.9.0 ends at database version 88
|
||||
|
||||
newMigration(88, "add commit status context field to commit_status", v1_10.AddCommitStatusContext),
|
||||
newMigration(89, "add original author/url migration info to issues, comments, and repo ", v1_10.AddOriginalMigrationInfo),
|
||||
newMigration(90, "change length of some repository columns", v1_10.ChangeSomeColumnsLengthOfRepo),
|
||||
newMigration(91, "add index on owner_id of repository and type, review_id of comment", v1_10.AddIndexOnRepositoryAndComment),
|
||||
newMigration(92, "remove orphaned repository index statuses", v1_10.RemoveLingeringIndexStatus),
|
||||
newMigration(93, "add email notification enabled preference to user", v1_10.AddEmailNotificationEnabledToUser),
|
||||
newMigration(94, "add enable_status_check, status_check_contexts to protected_branch", v1_10.AddStatusCheckColumnsForProtectedBranches),
|
||||
newMigration(95, "add table columns for cross referencing issues", v1_10.AddCrossReferenceColumns),
|
||||
newMigration(96, "delete orphaned attachments", v1_10.DeleteOrphanedAttachments),
|
||||
newMigration(97, "add repo_admin_change_team_access to user", v1_10.AddRepoAdminChangeTeamAccessColumnForUser),
|
||||
newMigration(98, "add original author name and id on migrated release", v1_10.AddOriginalAuthorOnMigratedReleases),
|
||||
newMigration(99, "add task table and status column for repository table", v1_10.AddTaskTable),
|
||||
newMigration(100, "update migration repositories' service type", v1_10.UpdateMigrationServiceTypes),
|
||||
newMigration(101, "change length of some external login users columns", v1_10.ChangeSomeColumnsLengthOfExternalLoginUser),
|
||||
|
||||
// Gitea 1.10.0 ends at database version 102
|
||||
|
||||
newMigration(102, "update migration repositories' service type", v1_11.DropColumnHeadUserNameOnPullRequest),
|
||||
newMigration(103, "Add WhitelistDeployKeys to protected branch", v1_11.AddWhitelistDeployKeysToBranches),
|
||||
newMigration(104, "remove unnecessary columns from label", v1_11.RemoveLabelUneededCols),
|
||||
newMigration(105, "add includes_all_repositories to teams", v1_11.AddTeamIncludesAllRepositories),
|
||||
newMigration(106, "add column `mode` to table watch", v1_11.AddModeColumnToWatch),
|
||||
newMigration(107, "Add template options to repository", v1_11.AddTemplateToRepo),
|
||||
newMigration(108, "Add comment_id on table notification", v1_11.AddCommentIDOnNotification),
|
||||
newMigration(109, "add can_create_org_repo to team", v1_11.AddCanCreateOrgRepoColumnForTeam),
|
||||
newMigration(110, "change review content type to text", v1_11.ChangeReviewContentToText),
|
||||
newMigration(111, "update branch protection for can push and whitelist enable", v1_11.AddBranchProtectionCanPushAndEnableWhitelist),
|
||||
newMigration(112, "remove release attachments which repository deleted", v1_11.RemoveAttachmentMissedRepo),
|
||||
newMigration(113, "new feature: change target branch of pull requests", v1_11.FeatureChangeTargetBranch),
|
||||
newMigration(114, "Remove authentication credentials from stored URL", v1_11.SanitizeOriginalURL),
|
||||
newMigration(115, "add user_id prefix to existing user avatar name", v1_11.RenameExistingUserAvatarName),
|
||||
newMigration(116, "Extend TrackedTimes", v1_11.ExtendTrackedTimes),
|
||||
|
||||
// Gitea 1.11.0 ends at database version 117
|
||||
|
||||
newMigration(117, "Add block on rejected reviews branch protection", v1_12.AddBlockOnRejectedReviews),
|
||||
newMigration(118, "Add commit id and stale to reviews", v1_12.AddReviewCommitAndStale),
|
||||
newMigration(119, "Fix migrated repositories' git service type", v1_12.FixMigratedRepositoryServiceType),
|
||||
newMigration(120, "Add owner_name on table repository", v1_12.AddOwnerNameOnRepository),
|
||||
newMigration(121, "add is_restricted column for users table", v1_12.AddIsRestricted),
|
||||
newMigration(122, "Add Require Signed Commits to ProtectedBranch", v1_12.AddRequireSignedCommits),
|
||||
newMigration(123, "Add original information for reactions", v1_12.AddReactionOriginals),
|
||||
newMigration(124, "Add columns to user and repository", v1_12.AddUserRepoMissingColumns),
|
||||
newMigration(125, "Add some columns on review for migration", v1_12.AddReviewMigrateInfo),
|
||||
newMigration(126, "Fix topic repository count", v1_12.FixTopicRepositoryCount),
|
||||
newMigration(127, "add repository code language statistics", v1_12.AddLanguageStats),
|
||||
newMigration(128, "fix merge base for pull requests", v1_12.FixMergeBase),
|
||||
newMigration(129, "remove dependencies from deleted repositories", v1_12.PurgeUnusedDependencies),
|
||||
newMigration(130, "Expand webhooks for more granularity", v1_12.ExpandWebhooks),
|
||||
newMigration(131, "Add IsSystemWebhook column to webhooks table", v1_12.AddSystemWebhookColumn),
|
||||
newMigration(132, "Add Branch Protection Protected Files Column", v1_12.AddBranchProtectionProtectedFilesColumn),
|
||||
newMigration(133, "Add EmailHash Table", v1_12.AddEmailHashTable),
|
||||
newMigration(134, "Refix merge base for merged pull requests", v1_12.RefixMergeBase),
|
||||
newMigration(135, "Add OrgID column to Labels table", v1_12.AddOrgIDLabelColumn),
|
||||
newMigration(136, "Add CommitsAhead and CommitsBehind Column to PullRequest Table", v1_12.AddCommitDivergenceToPulls),
|
||||
newMigration(137, "Add Branch Protection Block Outdated Branch", v1_12.AddBlockOnOutdatedBranch),
|
||||
newMigration(138, "Add ResolveDoerID to Comment table", v1_12.AddResolveDoerIDCommentColumn),
|
||||
newMigration(139, "prepend refs/heads/ to issue refs", v1_12.PrependRefsHeadsToIssueRefs),
|
||||
|
||||
// Gitea 1.12.0 ends at database version 140
|
||||
|
||||
newMigration(140, "Save detected language file size to database instead of percent", v1_13.FixLanguageStatsToSaveSize),
|
||||
newMigration(141, "Add KeepActivityPrivate to User table", v1_13.AddKeepActivityPrivateUserColumn),
|
||||
newMigration(142, "Ensure Repository.IsArchived is not null", v1_13.SetIsArchivedToFalse),
|
||||
newMigration(143, "recalculate Stars number for all user", v1_13.RecalculateStars),
|
||||
newMigration(144, "update Matrix Webhook http method to 'PUT'", v1_13.UpdateMatrixWebhookHTTPMethod),
|
||||
newMigration(145, "Increase Language field to 50 in LanguageStats", v1_13.IncreaseLanguageField),
|
||||
newMigration(146, "Add projects info to repository table", v1_13.AddProjectsInfo),
|
||||
newMigration(147, "create review for 0 review id code comments", v1_13.CreateReviewsForCodeComments),
|
||||
newMigration(148, "remove issue dependency comments who refer to non existing issues", v1_13.PurgeInvalidDependenciesComments),
|
||||
newMigration(149, "Add Created and Updated to Milestone table", v1_13.AddCreatedAndUpdatedToMilestones),
|
||||
newMigration(150, "add primary key to repo_topic", v1_13.AddPrimaryKeyToRepoTopic),
|
||||
newMigration(151, "set default password algorithm to Argon2", v1_13.SetDefaultPasswordToArgon2),
|
||||
newMigration(152, "add TrustModel field to Repository", v1_13.AddTrustModelToRepository),
|
||||
newMigration(153, "add Team review request support", v1_13.AddTeamReviewRequestSupport),
|
||||
newMigration(154, "add timestamps to Star, Label, Follow, Watch and Collaboration", v1_13.AddTimeStamps),
|
||||
|
||||
// Gitea 1.13.0 ends at database version 155
|
||||
|
||||
newMigration(155, "add changed_protected_files column for pull_request table", v1_14.AddChangedProtectedFilesPullRequestColumn),
|
||||
newMigration(156, "fix publisher ID for tag releases", v1_14.FixPublisherIDforTagReleases),
|
||||
newMigration(157, "ensure repo topics are up-to-date", v1_14.FixRepoTopics),
|
||||
newMigration(158, "code comment replies should have the commitID of the review they are replying to", v1_14.UpdateCodeCommentReplies),
|
||||
newMigration(159, "update reactions constraint", v1_14.UpdateReactionConstraint),
|
||||
newMigration(160, "Add block on official review requests branch protection", v1_14.AddBlockOnOfficialReviewRequests),
|
||||
newMigration(161, "Convert task type from int to string", v1_14.ConvertTaskTypeToString),
|
||||
newMigration(162, "Convert webhook task type from int to string", v1_14.ConvertWebhookTaskTypeToString),
|
||||
newMigration(163, "Convert topic name from 25 to 50", v1_14.ConvertTopicNameFrom25To50),
|
||||
newMigration(164, "Add scope and nonce columns to oauth2_grant table", v1_14.AddScopeAndNonceColumnsToOAuth2Grant),
|
||||
newMigration(165, "Convert hook task type from char(16) to varchar(16) and trim the column", v1_14.ConvertHookTaskTypeToVarcharAndTrim),
|
||||
newMigration(166, "Where Password is Valid with Empty String delete it", v1_14.RecalculateUserEmptyPWD),
|
||||
newMigration(167, "Add user redirect", v1_14.AddUserRedirect),
|
||||
newMigration(168, "Recreate user table to fix default values", v1_14.RecreateUserTableToFixDefaultValues),
|
||||
newMigration(169, "Update DeleteBranch comments to set the old_ref to the commit_sha", v1_14.CommentTypeDeleteBranchUseOldRef),
|
||||
newMigration(170, "Add Dismissed to Review table", v1_14.AddDismissedReviewColumn),
|
||||
newMigration(171, "Add Sorting to ProjectBoard table", v1_14.AddSortingColToProjectBoard),
|
||||
newMigration(172, "Add sessions table for go-chi/session", v1_14.AddSessionTable),
|
||||
newMigration(173, "Add time_id column to Comment", v1_14.AddTimeIDCommentColumn),
|
||||
newMigration(174, "Create repo transfer table", v1_14.AddRepoTransfer),
|
||||
newMigration(175, "Fix Postgres ID Sequences broken by recreate-table", v1_14.FixPostgresIDSequences),
|
||||
newMigration(176, "Remove invalid labels from comments", v1_14.RemoveInvalidLabels),
|
||||
newMigration(177, "Delete orphaned IssueLabels", v1_14.DeleteOrphanedIssueLabels),
|
||||
|
||||
// Gitea 1.14.0 ends at database version 178
|
||||
|
||||
newMigration(178, "Add LFS columns to Mirror", v1_15.AddLFSMirrorColumns),
|
||||
newMigration(179, "Convert avatar url to text", v1_15.ConvertAvatarURLToText),
|
||||
newMigration(180, "Delete credentials from past migrations", v1_15.DeleteMigrationCredentials),
|
||||
newMigration(181, "Always save primary email on email address table", v1_15.AddPrimaryEmail2EmailAddress),
|
||||
newMigration(182, "Add issue resource index table", v1_15.AddIssueResourceIndexTable),
|
||||
newMigration(183, "Create PushMirror table", v1_15.CreatePushMirrorTable),
|
||||
newMigration(184, "Rename Task errors to message", v1_15.RenameTaskErrorsToMessage),
|
||||
newMigration(185, "Add new table repo_archiver", v1_15.AddRepoArchiver),
|
||||
newMigration(186, "Create protected tag table", v1_15.CreateProtectedTagTable),
|
||||
newMigration(187, "Drop unneeded webhook related columns", v1_15.DropWebhookColumns),
|
||||
newMigration(188, "Add key is verified to gpg key", v1_15.AddKeyIsVerified),
|
||||
|
||||
// Gitea 1.15.0 ends at database version 189
|
||||
|
||||
newMigration(189, "Unwrap ldap.Sources", v1_16.UnwrapLDAPSourceCfg),
|
||||
newMigration(190, "Add agit flow pull request support", v1_16.AddAgitFlowPullRequest),
|
||||
newMigration(191, "Alter issue/comment table TEXT fields to LONGTEXT", v1_16.AlterIssueAndCommentTextFieldsToLongText),
|
||||
newMigration(192, "RecreateIssueResourceIndexTable to have a primary key instead of an unique index", v1_16.RecreateIssueResourceIndexTable),
|
||||
newMigration(193, "Add repo id column for attachment table", v1_16.AddRepoIDForAttachment),
|
||||
newMigration(194, "Add Branch Protection Unprotected Files Column", v1_16.AddBranchProtectionUnprotectedFilesColumn),
|
||||
newMigration(195, "Add table commit_status_index", v1_16.AddTableCommitStatusIndex),
|
||||
newMigration(196, "Add Color to ProjectBoard table", v1_16.AddColorColToProjectBoard),
|
||||
newMigration(197, "Add renamed_branch table", v1_16.AddRenamedBranchTable),
|
||||
newMigration(198, "Add issue content history table", v1_16.AddTableIssueContentHistory),
|
||||
newMigration(199, "No-op (remote version is using AppState now)", noopMigration),
|
||||
newMigration(200, "Add table app_state", v1_16.AddTableAppState),
|
||||
newMigration(201, "Drop table remote_version (if exists)", v1_16.DropTableRemoteVersion),
|
||||
newMigration(202, "Create key/value table for user settings", v1_16.CreateUserSettingsTable),
|
||||
newMigration(203, "Add Sorting to ProjectIssue table", v1_16.AddProjectIssueSorting),
|
||||
newMigration(204, "Add key is verified to ssh key", v1_16.AddSSHKeyIsVerified),
|
||||
newMigration(205, "Migrate to higher varchar on user struct", v1_16.MigrateUserPasswordSalt),
|
||||
newMigration(206, "Add authorize column to team_unit table", v1_16.AddAuthorizeColForTeamUnit),
|
||||
newMigration(207, "Add webauthn table and migrate u2f data to webauthn - NO-OPED", v1_16.AddWebAuthnCred),
|
||||
newMigration(208, "Use base32.HexEncoding instead of base64 encoding for cred ID as it is case insensitive - NO-OPED", v1_16.UseBase32HexForCredIDInWebAuthnCredential),
|
||||
newMigration(209, "Increase WebAuthentication CredentialID size to 410 - NO-OPED", v1_16.IncreaseCredentialIDTo410),
|
||||
newMigration(210, "v208 was completely broken - remigrate", v1_16.RemigrateU2FCredentials),
|
||||
|
||||
// Gitea 1.16.2 ends at database version 211
|
||||
|
||||
newMigration(211, "Create ForeignReference table", v1_17.CreateForeignReferenceTable),
|
||||
newMigration(212, "Add package tables", v1_17.AddPackageTables),
|
||||
newMigration(213, "Add allow edits from maintainers to PullRequest table", v1_17.AddAllowMaintainerEdit),
|
||||
newMigration(214, "Add auto merge table", v1_17.AddAutoMergeTable),
|
||||
newMigration(215, "allow to view files in PRs", v1_17.AddReviewViewedFiles),
|
||||
newMigration(216, "No-op (Improve Action table indices v1)", noopMigration),
|
||||
newMigration(217, "Alter hook_task table TEXT fields to LONGTEXT", v1_17.AlterHookTaskTextFieldsToLongText),
|
||||
newMigration(218, "Improve Action table indices v2", v1_17.ImproveActionTableIndices),
|
||||
newMigration(219, "Add sync_on_commit column to push_mirror table", v1_17.AddSyncOnCommitColForPushMirror),
|
||||
newMigration(220, "Add container repository property", v1_17.AddContainerRepositoryProperty),
|
||||
newMigration(221, "Store WebAuthentication CredentialID as bytes and increase size to at least 1024", v1_17.StoreWebauthnCredentialIDAsBytes),
|
||||
newMigration(222, "Drop old CredentialID column", v1_17.DropOldCredentialIDColumn),
|
||||
newMigration(223, "Rename CredentialIDBytes column to CredentialID", v1_17.RenameCredentialIDBytes),
|
||||
|
||||
// Gitea 1.17.0 ends at database version 224
|
||||
|
||||
newMigration(224, "Add badges to users", v1_18.CreateUserBadgesTable),
|
||||
newMigration(225, "Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", v1_18.AlterPublicGPGKeyContentFieldsToMediumText),
|
||||
newMigration(226, "Conan and generic packages do not need to be semantically versioned", v1_18.FixPackageSemverField),
|
||||
newMigration(227, "Create key/value table for system settings", v1_18.CreateSystemSettingsTable),
|
||||
newMigration(228, "Add TeamInvite table", v1_18.AddTeamInviteTable),
|
||||
newMigration(229, "Update counts of all open milestones", v1_18.UpdateOpenMilestoneCounts),
|
||||
newMigration(230, "Add ConfidentialClient column (default true) to OAuth2Application table", v1_18.AddConfidentialClientColumnToOAuth2ApplicationTable),
|
||||
|
||||
// Gitea 1.18.0 ends at database version 231
|
||||
|
||||
newMigration(231, "Add index for hook_task", v1_19.AddIndexForHookTask),
|
||||
newMigration(232, "Alter package_version.metadata_json to LONGTEXT", v1_19.AlterPackageVersionMetadataToLongText),
|
||||
newMigration(233, "Add header_authorization_encrypted column to webhook table", v1_19.AddHeaderAuthorizationEncryptedColWebhook),
|
||||
newMigration(234, "Add package cleanup rule table", v1_19.CreatePackageCleanupRuleTable),
|
||||
newMigration(235, "Add index for access_token", v1_19.AddIndexForAccessToken),
|
||||
newMigration(236, "Create secrets table", v1_19.CreateSecretsTable),
|
||||
newMigration(237, "Drop ForeignReference table", v1_19.DropForeignReferenceTable),
|
||||
newMigration(238, "Add updated unix to LFSMetaObject", v1_19.AddUpdatedUnixToLFSMetaObject),
|
||||
newMigration(239, "Add scope for access_token", v1_19.AddScopeForAccessTokens),
|
||||
newMigration(240, "Add actions tables", v1_19.AddActionsTables),
|
||||
newMigration(241, "Add card_type column to project table", v1_19.AddCardTypeToProjectTable),
|
||||
newMigration(242, "Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText),
|
||||
newMigration(243, "Add exclusive label", v1_19.AddExclusiveLabel),
|
||||
|
||||
// Gitea 1.19.0 ends at database version 244
|
||||
|
||||
newMigration(244, "Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),
|
||||
newMigration(245, "Rename Webhook org_id to owner_id", v1_20.RenameWebhookOrgToOwner),
|
||||
newMigration(246, "Add missed column owner_id for project table", v1_20.AddNewColumnForProject),
|
||||
newMigration(247, "Fix incorrect project type", v1_20.FixIncorrectProjectType),
|
||||
newMigration(248, "Add version column to action_runner table", v1_20.AddVersionToActionRunner),
|
||||
newMigration(249, "Improve Action table indices v3", v1_20.ImproveActionTableIndices),
|
||||
newMigration(250, "Change Container Metadata", v1_20.ChangeContainerMetadataMultiArch),
|
||||
newMigration(251, "Fix incorrect owner team unit access mode", v1_20.FixIncorrectOwnerTeamUnitAccessMode),
|
||||
newMigration(252, "Fix incorrect admin team unit access mode", v1_20.FixIncorrectAdminTeamUnitAccessMode),
|
||||
newMigration(253, "Fix ExternalTracker and ExternalWiki accessMode in owner and admin team", v1_20.FixExternalTrackerAndExternalWikiAccessModeInOwnerAndAdminTeam),
|
||||
newMigration(254, "Add ActionTaskOutput table", v1_20.AddActionTaskOutputTable),
|
||||
newMigration(255, "Add ArchivedUnix Column", v1_20.AddArchivedUnixToRepository),
|
||||
newMigration(256, "Add is_internal column to package", v1_20.AddIsInternalColumnToPackage),
|
||||
newMigration(257, "Add Actions Artifact table", v1_20.CreateActionArtifactTable),
|
||||
newMigration(258, "Add PinOrder Column", v1_20.AddPinOrderToIssue),
|
||||
newMigration(259, "Convert scoped access tokens", v1_20.ConvertScopedAccessTokens),
|
||||
|
||||
// Gitea 1.20.0 ends at database version 260
|
||||
|
||||
newMigration(260, "Drop custom_labels column of action_runner table", v1_21.DropCustomLabelsColumnOfActionRunner),
|
||||
newMigration(261, "Add variable table", v1_21.CreateVariableTable),
|
||||
newMigration(262, "Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
|
||||
newMigration(263, "Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
|
||||
newMigration(264, "Add branch table", v1_21.AddBranchTable),
|
||||
newMigration(265, "Alter Actions Artifact table", v1_21.AlterActionArtifactTable),
|
||||
newMigration(266, "Reduce commit status", v1_21.ReduceCommitStatus),
|
||||
newMigration(267, "Add action_tasks_version table", v1_21.CreateActionTasksVersionTable),
|
||||
newMigration(268, "Update Action Ref", v1_21.UpdateActionsRefIndex),
|
||||
newMigration(269, "Drop deleted branch table", v1_21.DropDeletedBranchTable),
|
||||
newMigration(270, "Fix PackageProperty typo", v1_21.FixPackagePropertyTypo),
|
||||
newMigration(271, "Allow archiving labels", v1_21.AddArchivedUnixColumInLabelTable),
|
||||
newMigration(272, "Add Version to ActionRun table", v1_21.AddVersionToActionRunTable),
|
||||
newMigration(273, "Add Action Schedule Table", v1_21.AddActionScheduleTable),
|
||||
newMigration(274, "Add Actions artifacts expiration date", v1_21.AddExpiredUnixColumnInActionArtifactTable),
|
||||
newMigration(275, "Add ScheduleID for ActionRun", v1_21.AddScheduleIDForActionRun),
|
||||
newMigration(276, "Add RemoteAddress to mirrors", v1_21.AddRemoteAddressToMirrors),
|
||||
newMigration(277, "Add Index to issue_user.issue_id", v1_21.AddIndexToIssueUserIssueID),
|
||||
newMigration(278, "Add Index to comment.dependent_issue_id", v1_21.AddIndexToCommentDependentIssueID),
|
||||
newMigration(279, "Add Index to action.user_id", v1_21.AddIndexToActionUserID),
|
||||
|
||||
// Gitea 1.21.0 ends at database version 280
|
||||
|
||||
newMigration(280, "Rename user themes", v1_22.RenameUserThemes),
|
||||
newMigration(281, "Add auth_token table", v1_22.CreateAuthTokenTable),
|
||||
newMigration(282, "Add Index to pull_auto_merge.doer_id", v1_22.AddIndexToPullAutoMergeDoerID),
|
||||
newMigration(283, "Add combined Index to issue_user.uid and issue_id", v1_22.AddCombinedIndexToIssueUser),
|
||||
newMigration(284, "Add ignore stale approval column on branch table", v1_22.AddIgnoreStaleApprovalsColumnToProtectedBranchTable),
|
||||
newMigration(285, "Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun),
|
||||
newMigration(286, "Add support for SHA256 git repositories", v1_22.AdjustDBForSha256),
|
||||
newMigration(287, "Use Slug instead of ID for Badges", v1_22.UseSlugInsteadOfIDForBadges),
|
||||
newMigration(288, "Add user_blocking table", v1_22.AddUserBlockingTable),
|
||||
newMigration(289, "Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch),
|
||||
newMigration(290, "Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable),
|
||||
newMigration(291, "Add Index to attachment.comment_id", v1_22.AddCommentIDIndexofAttachment),
|
||||
newMigration(292, "Ensure every project has exactly one default column - No Op", noopMigration),
|
||||
newMigration(293, "Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
|
||||
|
||||
// Gitea 1.22.0-rc0 ends at database version 294
|
||||
|
||||
newMigration(294, "Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue),
|
||||
newMigration(295, "Add commit status summary table", v1_22.AddCommitStatusSummary),
|
||||
newMigration(296, "Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2),
|
||||
newMigration(297, "Add everyone_access_mode for repo_unit", v1_22.AddRepoUnitEveryoneAccessMode),
|
||||
newMigration(298, "Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
|
||||
|
||||
// Gitea 1.22.0-rc1 ends at migration ID number 298 (database version 299)
|
||||
|
||||
newMigration(299, "Add content version to issue and comment table", v1_23.AddContentVersionToIssueAndComment),
|
||||
newMigration(300, "Add force-push branch protection support", v1_23.AddForcePushBranchProtection),
|
||||
newMigration(301, "Add skip_secondary_authorization option to oauth2 application table", v1_23.AddSkipSecondaryAuthColumnToOAuth2ApplicationTable),
|
||||
newMigration(302, "Add index to action_task stopped log_expired", v1_23.AddIndexToActionTaskStoppedLogExpired),
|
||||
newMigration(303, "Add metadata column for comment table", v1_23.AddCommentMetaDataColumn),
|
||||
newMigration(304, "Add index for release sha1", v1_23.AddIndexForReleaseSha1),
|
||||
newMigration(305, "Add Repository Licenses", v1_23.AddRepositoryLicenses),
|
||||
newMigration(306, "Add BlockAdminMergeOverride to ProtectedBranch", v1_23.AddBlockAdminMergeOverrideBranchProtection),
|
||||
newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate),
|
||||
newMigration(308, "Add index(user_id, is_deleted) for action table", v1_23.AddNewIndexForUserDashboard),
|
||||
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
|
||||
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
|
||||
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
|
||||
// Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
|
||||
|
||||
newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
|
||||
newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin),
|
||||
newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
|
||||
newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
|
||||
newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables),
|
||||
newMigration(317, "Add new index for action for heatmap", v1_24.AddNewIndexForUserDashboard),
|
||||
newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
|
||||
newMigration(319, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable),
|
||||
newMigration(320, "Migrate two_factor_policy to login_source table", v1_24.MigrateSkipTwoFactor),
|
||||
// Gitea 1.24.0 ends at migration ID number 320 (database version 321)
|
||||
|
||||
newMigration(321, "Use LONGTEXT for some columns and fix review_state.updated_files column", v1_25.UseLongTextInSomeColumnsAndFixBugs),
|
||||
newMigration(322, "Extend comment tree_path length limit", v1_25.ExtendCommentTreePathLength),
|
||||
// Gitea 1.25.0 ends at migration ID number 322 (database version 323)
|
||||
|
||||
newMigration(323, "Add support for actions concurrency", v1_26.AddActionsConcurrency),
|
||||
newMigration(324, "Fix closed milestone completeness for milestones with no issues", v1_26.FixClosedMilestoneCompleteness),
|
||||
newMigration(325, "Fix missed repo_id when migrate attachments", v1_26.FixMissedRepoIDWhenMigrateAttachments),
|
||||
newMigration(326, "Partially migrate commit status target URL to use run ID and job ID", v1_26.FixCommitStatusTargetURLToUseRunAndJobID),
|
||||
newMigration(327, "Add disabled state to action runners", v1_26.AddDisabledToActionRunner),
|
||||
newMigration(328, "Add TokenPermissions column to ActionRunJob", v1_26.AddTokenPermissionsToActionRunJob),
|
||||
newMigration(329, "Add unique constraint for user badge", v1_26.AddUniqueIndexForUserBadge),
|
||||
newMigration(330, "Add name column to webhook", v1_26.AddNameToWebhook),
|
||||
// Gitea 1.26.0 ends at migration ID number 330 (database version 331)
|
||||
|
||||
newMigration(331, "Add ActionRunAttempt model and related action fields", v1_27.AddActionRunAttemptModel),
|
||||
newMigration(332, "Add last_sync_unix to mirror", v1_27.AddLastSyncUnixToMirror),
|
||||
newMigration(333, "Add bypass allowlist to branch protection", v1_27.AddBranchProtectionBypassAllowlist),
|
||||
newMigration(334, "Add cancelling support to action runners", v1_27.AddCancellingSupportToActionRunner),
|
||||
newMigration(335, "Add reusable workflow fields and action_run_attempt_job_id_index table for ActionRunJob", v1_27.AddReusableWorkflowFieldsToActionRunJob),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
func GetCurrentDBVersion(x db.EngineMigration) (int64, error) {
|
||||
if err := x.Sync(new(Version)); err != nil {
|
||||
return -1, fmt.Errorf("sync: %w", err)
|
||||
}
|
||||
|
||||
currentVersion := &Version{ID: 1}
|
||||
has, err := x.Get(currentVersion)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("get: %w", err)
|
||||
}
|
||||
if !has {
|
||||
return -1, nil
|
||||
}
|
||||
return currentVersion.Version, nil
|
||||
}
|
||||
|
||||
func calcDBVersion(migrations []*migration) int64 {
|
||||
dbVer := int64(minDBVersion + len(migrations))
|
||||
if migrations[0].idNumber != minDBVersion {
|
||||
panic("migrations should start at minDBVersion")
|
||||
}
|
||||
if dbVer != migrations[len(migrations)-1].idNumber+1 {
|
||||
panic("migrations are not in order")
|
||||
}
|
||||
return dbVer
|
||||
}
|
||||
|
||||
// ExpectedDBVersion returns the expected db version
|
||||
func ExpectedDBVersion() int64 {
|
||||
return calcDBVersion(prepareMigrationTasks())
|
||||
}
|
||||
|
||||
// EnsureUpToDate will check if the db is at the correct version
|
||||
func EnsureUpToDate(ctx context.Context, x db.EngineMigration) error {
|
||||
currentDB, err := GetCurrentDBVersion(x)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if currentDB < 0 {
|
||||
return errors.New("database has not been initialized")
|
||||
}
|
||||
|
||||
if minDBVersion > currentDB {
|
||||
return fmt.Errorf("DB version %d (<= %d) is too old for auto-migration. Upgrade to Gitea 1.6.4 first then upgrade to this version", currentDB, minDBVersion)
|
||||
}
|
||||
|
||||
expectedDB := ExpectedDBVersion()
|
||||
|
||||
if currentDB != expectedDB {
|
||||
return fmt.Errorf(`current database version %d is not equal to the expected version %d. Please run "gitea [--config /path/to/app.ini] migrate" to update the database version`, currentDB, expectedDB)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPendingMigrations(curDBVer int64, migrations []*migration) []*migration {
|
||||
return migrations[curDBVer-minDBVersion:]
|
||||
}
|
||||
|
||||
func migrationIDNumberToDBVersion(idNumber int64) int64 {
|
||||
return idNumber + 1
|
||||
}
|
||||
|
||||
// Migrate database to current version
|
||||
func Migrate(ctx context.Context, x db.EngineMigration) error {
|
||||
migrations := prepareMigrationTasks()
|
||||
maxDBVer := calcDBVersion(migrations)
|
||||
|
||||
// Set a new clean the default mapper to GonicMapper as that is the default for Gitea.
|
||||
x.SetMapper(names.GonicMapper{})
|
||||
if err := x.Sync(new(Version)); err != nil {
|
||||
return fmt.Errorf("sync: %w", err)
|
||||
}
|
||||
|
||||
currentVersion := &Version{ID: 1}
|
||||
has, err := x.Get(currentVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get: %w", err)
|
||||
} else if !has {
|
||||
// If the version record does not exist, it is a fresh installation, and we can skip all migrations.
|
||||
// XORM model framework will create all tables when initializing.
|
||||
currentVersion.ID = 0
|
||||
currentVersion.Version = maxDBVer
|
||||
if _, err = x.Insert(currentVersion); err != nil {
|
||||
return fmt.Errorf("insert: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
curDBVer := currentVersion.Version
|
||||
// Outdated Gitea database version is not supported
|
||||
if curDBVer < minDBVersion {
|
||||
log.Fatal(`Gitea no longer supports auto-migration from your previously installed version.
|
||||
Please try upgrading to a lower version first (suggested v1.6.4), then upgrade to this version.`)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Downgrading Gitea's database version not supported
|
||||
if maxDBVer < curDBVer {
|
||||
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", curDBVer, maxDBVer)
|
||||
msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)."
|
||||
if !setting.IsProd {
|
||||
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", maxDBVer)
|
||||
}
|
||||
log.Fatal("Migration Error: %s", msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Some migration tasks depend on the git command
|
||||
if err = git.InitSimple(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Migrate
|
||||
for _, m := range getPendingMigrations(curDBVer, migrations) {
|
||||
log.Info("Migration[%d]: %s", m.idNumber, m.description)
|
||||
// Reset the mapper between each migration - migrations are not supposed to depend on each other
|
||||
x.SetMapper(names.GonicMapper{})
|
||||
if err = m.Migrate(ctx, x); err != nil {
|
||||
return fmt.Errorf("migration[%d]: %s failed: %w", m.idNumber, m.description, err)
|
||||
}
|
||||
currentVersion.Version = migrationIDNumberToDBVersion(m.idNumber)
|
||||
if _, err = x.ID(1).Update(currentVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMigrations(t *testing.T) {
|
||||
defer test.MockVariableValue(&preparedMigrations)()
|
||||
preparedMigrations = []*migration{
|
||||
{idNumber: 70},
|
||||
{idNumber: 71},
|
||||
}
|
||||
assert.EqualValues(t, 72, calcDBVersion(preparedMigrations))
|
||||
assert.EqualValues(t, 72, ExpectedDBVersion())
|
||||
|
||||
assert.EqualValues(t, 71, migrationIDNumberToDBVersion(70))
|
||||
|
||||
assert.Equal(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations))
|
||||
assert.Equal(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations))
|
||||
assert.Equal(t, []*migration{}, getPendingMigrations(72, preparedMigrations))
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package migrationtest
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/testlogger"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// PrepareTestEnv prepares the test environment and reset the database. The skip parameter should usually be 0.
|
||||
// Provide models to be sync'd with the database - in particular any models you expect fixtures to be loaded from.
|
||||
//
|
||||
// fixtures in `models/migrations/fixtures/<TestName>` will be loaded automatically
|
||||
func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (db.EngineMigration, func()) {
|
||||
t.Helper()
|
||||
ourSkip := 2
|
||||
ourSkip += skip
|
||||
deferFn := testlogger.PrintCurrentTest(t, ourSkip)
|
||||
giteaRoot := setting.GetGiteaTestSourceRoot()
|
||||
require.NoError(t, unittest.SyncDirs(filepath.Join(giteaRoot, "tests/gitea-repositories-meta"), setting.RepoRootPath))
|
||||
|
||||
cleanup, err := unittest.ResetTestDatabase()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to reset database: %v", err)
|
||||
return nil, deferFn
|
||||
}
|
||||
{
|
||||
oldDefer := deferFn
|
||||
deferFn = func() {
|
||||
cleanup()
|
||||
oldDefer()
|
||||
}
|
||||
}
|
||||
|
||||
err = db.InitEngine(t.Context())
|
||||
if !assert.NoError(t, err) {
|
||||
return nil, deferFn
|
||||
}
|
||||
x := unittest.GetXORMEngine()
|
||||
{
|
||||
oldDefer := deferFn
|
||||
deferFn = func() {
|
||||
_ = x.Close()
|
||||
oldDefer()
|
||||
}
|
||||
}
|
||||
|
||||
if len(syncModels) > 0 {
|
||||
if err := x.Sync(syncModels...); err != nil {
|
||||
t.Errorf("error during sync: %v", err)
|
||||
return x, deferFn
|
||||
}
|
||||
}
|
||||
|
||||
fixturesDir := filepath.Join(giteaRoot, "models", "migrations", "fixtures", t.Name())
|
||||
|
||||
if _, err := os.Stat(fixturesDir); err == nil {
|
||||
t.Logf("initializing fixtures from: %s", fixturesDir)
|
||||
if err := unittest.InitFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: fixturesDir,
|
||||
}); err != nil {
|
||||
t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err)
|
||||
return x, deferFn
|
||||
}
|
||||
if err := unittest.LoadFixtures(); err != nil {
|
||||
t.Errorf("error whilst loading fixtures from %s: %v", fixturesDir, err)
|
||||
return x, deferFn
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
t.Errorf("unexpected error whilst checking for existence of fixtures: %v", err)
|
||||
} else {
|
||||
t.Logf("no fixtures found in: %s", fixturesDir)
|
||||
}
|
||||
|
||||
return x, deferFn
|
||||
}
|
||||
|
||||
func LoadTableSchemasMap(t *testing.T, x db.EngineMigration) map[string]*schemas.Table {
|
||||
tables, err := x.DBMetas()
|
||||
require.NoError(t, err)
|
||||
tableMap := make(map[string]*schemas.Table)
|
||||
for _, table := range tables {
|
||||
tableMap[table.Name] = table
|
||||
}
|
||||
return tableMap
|
||||
}
|
||||
|
||||
func mainTest(m *testing.M) int {
|
||||
testlogger.Init()
|
||||
err := setting.PrepareIntegrationTestConfig()
|
||||
if err != nil {
|
||||
return testlogger.MainErrorf("Unable to prepare integration test config: %v", err)
|
||||
}
|
||||
setting.SetupGiteaTestEnv()
|
||||
|
||||
if err = git.InitFull(); err != nil {
|
||||
return testlogger.MainErrorf("Unable to InitFull: %v", err)
|
||||
}
|
||||
setting.Database.SlowQueryThreshold = 0
|
||||
setting.LoadDBSetting()
|
||||
setting.InitLoggersForTest()
|
||||
return m.Run()
|
||||
}
|
||||
|
||||
func MainTest(m *testing.M) {
|
||||
os.Exit(mainTest(m))
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func UpdateMigrationServiceTypes(x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
ID int64
|
||||
OriginalServiceType int `xorm:"index default(0)"`
|
||||
OriginalURL string `xorm:"VARCHAR(2048)"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var last int
|
||||
const batchSize = 50
|
||||
for {
|
||||
results := make([]Repository, 0, batchSize)
|
||||
err := x.Where("original_url <> '' AND original_url IS NOT NULL").
|
||||
And("original_service_type = 0 OR original_service_type IS NULL").
|
||||
OrderBy("id").
|
||||
Limit(batchSize, last).
|
||||
Find(&results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
break
|
||||
}
|
||||
last += len(results)
|
||||
|
||||
const PlainGitService = 1 // 1 plain git service
|
||||
const GithubService = 2 // 2 github.com
|
||||
|
||||
for _, res := range results {
|
||||
u, err := url.Parse(res.OriginalURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serviceType := PlainGitService
|
||||
if strings.EqualFold(u.Host, "github.com") {
|
||||
serviceType = GithubService
|
||||
}
|
||||
_, err = x.Exec("UPDATE repository SET original_service_type = ? WHERE id = ?", serviceType, res.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ExternalLoginUser struct {
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
RawData map[string]any `xorm:"TEXT JSON"`
|
||||
Provider string `xorm:"index VARCHAR(25)"`
|
||||
Email string
|
||||
Name string
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
Description string
|
||||
AvatarURL string
|
||||
Location string
|
||||
AccessToken string
|
||||
AccessTokenSecret string
|
||||
RefreshToken string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
return x.Sync(new(ExternalLoginUser))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func ChangeSomeColumnsLengthOfExternalLoginUser(x db.EngineMigration) error {
|
||||
type ExternalLoginUser struct {
|
||||
AccessToken string `xorm:"TEXT"`
|
||||
AccessTokenSecret string `xorm:"TEXT"`
|
||||
RefreshToken string `xorm:"TEXT"`
|
||||
}
|
||||
|
||||
return x.Sync(new(ExternalLoginUser))
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func hashContext(context string) string {
|
||||
return fmt.Sprintf("%x", sha1.Sum([]byte(context)))
|
||||
}
|
||||
|
||||
func AddCommitStatusContext(x db.EngineMigration) error {
|
||||
type CommitStatus struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
ContextHash string `xorm:"char(40) index"`
|
||||
Context string `xorm:"TEXT"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(CommitStatus)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
start := 0
|
||||
for {
|
||||
statuses := make([]*CommitStatus, 0, 100)
|
||||
err := sess.OrderBy("id").Limit(100, start).Find(&statuses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(statuses) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, status := range statuses {
|
||||
status.ContextHash = hashContext(status.Context)
|
||||
if _, err := sess.ID(status.ID).Cols("context_hash").Update(status); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(statuses) < 100 {
|
||||
break
|
||||
}
|
||||
|
||||
start += len(statuses)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddOriginalMigrationInfo(x db.EngineMigration) error {
|
||||
// Issue see models/issue.go
|
||||
type Issue struct {
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Issue)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Issue see models/issue_comment.go
|
||||
type Comment struct {
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Comment)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Issue see models/repo.go
|
||||
type Repository struct {
|
||||
OriginalURL string
|
||||
}
|
||||
|
||||
return x.Sync(new(Repository))
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func ChangeSomeColumnsLengthOfRepo(x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Description string `xorm:"TEXT"`
|
||||
Website string `xorm:"VARCHAR(2048)"`
|
||||
OriginalURL string `xorm:"VARCHAR(2048)"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Repository))
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddIndexOnRepositoryAndComment(x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type int `xorm:"index"`
|
||||
ReviewID int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Comment))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func RemoveLingeringIndexStatus(x db.EngineMigration) error {
|
||||
_, err := x.Exec(builder.Delete(builder.NotIn("`repo_id`", builder.Select("`id`").From("`repository`"))).From("`repo_indexer_status`"))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddEmailNotificationEnabledToUser(x db.EngineMigration) error {
|
||||
// User see models/user.go
|
||||
type User struct {
|
||||
EmailNotificationsPreference string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'"`
|
||||
}
|
||||
|
||||
return x.Sync(new(User))
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddStatusCheckColumnsForProtectedBranches(x db.EngineMigration) error {
|
||||
type ProtectedBranch struct {
|
||||
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
|
||||
StatusCheckContexts []string `xorm:"JSON TEXT"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(ProtectedBranch)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := x.Cols("enable_status_check", "status_check_contexts").Update(&ProtectedBranch{
|
||||
EnableStatusCheck: false,
|
||||
StatusCheckContexts: []string{},
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddCrossReferenceColumns(x db.EngineMigration) error {
|
||||
// Comment see models/comment.go
|
||||
type Comment struct {
|
||||
RefRepoID int64 `xorm:"index"`
|
||||
RefIssueID int64 `xorm:"index"`
|
||||
RefCommentID int64 `xorm:"index"`
|
||||
RefAction int64 `xorm:"SMALLINT"`
|
||||
RefIsPull bool
|
||||
}
|
||||
|
||||
return x.Sync(new(Comment))
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
func DeleteOrphanedAttachments(x db.EngineMigration) error {
|
||||
type Attachment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UUID string `xorm:"uuid UNIQUE"`
|
||||
IssueID int64 `xorm:"INDEX"`
|
||||
ReleaseID int64 `xorm:"INDEX"`
|
||||
CommentID int64
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
limit := setting.Database.IterateBufferSize
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
for {
|
||||
attachments := make([]Attachment, 0, limit)
|
||||
if err := sess.Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").
|
||||
Cols("id, uuid").Limit(limit).
|
||||
Asc("id").
|
||||
Find(&attachments); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(attachments) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ids := make([]int64, 0, limit)
|
||||
for _, attachment := range attachments {
|
||||
ids = append(ids, attachment.ID)
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
if _, err := sess.In("id", ids).Delete(new(Attachment)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, attachment := range attachments {
|
||||
uuid := attachment.UUID
|
||||
if err := util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(attachments) < limit {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddRepoAdminChangeTeamAccessColumnForUser(x db.EngineMigration) error {
|
||||
type User struct {
|
||||
RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync(new(User))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddOriginalAuthorOnMigratedReleases(x db.EngineMigration) error {
|
||||
type Release struct {
|
||||
ID int64
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Release))
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_10
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/timeutil"
|
||||
)
|
||||
|
||||
func AddTaskTable(x db.EngineMigration) error {
|
||||
// TaskType defines task type
|
||||
type TaskType int
|
||||
|
||||
// TaskStatus defines task status
|
||||
type TaskStatus int
|
||||
|
||||
type Task struct {
|
||||
ID int64
|
||||
DoerID int64 `xorm:"index"` // operator
|
||||
OwnerID int64 `xorm:"index"` // repo owner id, when creating, the repoID maybe zero
|
||||
RepoID int64 `xorm:"index"`
|
||||
Type TaskType
|
||||
Status TaskStatus `xorm:"index"`
|
||||
StartTime timeutil.TimeStamp
|
||||
EndTime timeutil.TimeStamp
|
||||
PayloadContent string `xorm:"TEXT"`
|
||||
Errors string `xorm:"TEXT"` // if task failed, saved the error reason
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
Status int `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Task), new(Repository))
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/migrations/base"
|
||||
)
|
||||
|
||||
func DropColumnHeadUserNameOnPullRequest(x db.EngineMigration) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := base.DropTableColumns(sess, "pull_request", "head_user_name"); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddWhitelistDeployKeysToBranches(x db.EngineMigration) error {
|
||||
type ProtectedBranch struct {
|
||||
ID int64
|
||||
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync(new(ProtectedBranch))
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/migrations/base"
|
||||
)
|
||||
|
||||
func RemoveLabelUneededCols(x db.EngineMigration) error {
|
||||
// Make sure the columns exist before dropping them
|
||||
type Label struct {
|
||||
QueryString string
|
||||
IsSelected bool
|
||||
}
|
||||
if err := x.Sync(new(Label)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := base.DropTableColumns(sess, "label", "query_string"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := base.DropTableColumns(sess, "label", "is_selected"); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddTeamIncludesAllRepositories(x db.EngineMigration) error {
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Team)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?",
|
||||
true, "Owners")
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
// RepoWatchMode specifies what kind of watch the user has on a repository
|
||||
type RepoWatchMode int8
|
||||
|
||||
// Watch is connection request for receiving repository notification.
|
||||
type Watch struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"`
|
||||
}
|
||||
|
||||
func AddModeColumnToWatch(x db.EngineMigration) error {
|
||||
if err := x.Sync(new(Watch)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := x.Exec("UPDATE `watch` SET `mode` = 1")
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddTemplateToRepo(x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
IsTemplate bool `xorm:"INDEX NOT NULL DEFAULT false"`
|
||||
TemplateID int64 `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Repository))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddCommentIDOnNotification(x db.EngineMigration) error {
|
||||
type Notification struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CommentID int64
|
||||
}
|
||||
|
||||
return x.Sync(new(Notification))
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddCanCreateOrgRepoColumnForTeam(x db.EngineMigration) error {
|
||||
type Team struct {
|
||||
CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Team))
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
func ChangeReviewContentToText(x db.EngineMigration) error {
|
||||
switch x.Dialect().URI().DBType {
|
||||
case schemas.MYSQL:
|
||||
_, err := x.Exec("ALTER TABLE review MODIFY COLUMN content TEXT")
|
||||
return err
|
||||
case schemas.ORACLE:
|
||||
_, err := x.Exec("ALTER TABLE review MODIFY content TEXT")
|
||||
return err
|
||||
case schemas.MSSQL:
|
||||
_, err := x.Exec("ALTER TABLE review ALTER COLUMN content TEXT")
|
||||
return err
|
||||
case schemas.POSTGRES:
|
||||
_, err := x.Exec("ALTER TABLE review ALTER COLUMN content TYPE TEXT")
|
||||
return err
|
||||
default:
|
||||
// SQLite doesn't support ALTER COLUMN, and it seem to already make String to _TEXT_ default so no migration needed
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func AddBranchProtectionCanPushAndEnableWhitelist(x db.EngineMigration) error {
|
||||
type ProtectedBranch struct {
|
||||
CanPush bool `xorm:"NOT NULL DEFAULT false"`
|
||||
EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"`
|
||||
ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||
ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type int
|
||||
|
||||
// Permissions
|
||||
IsAdmin bool
|
||||
IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
|
||||
Visibility int `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
type Review struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Official bool `xorm:"NOT NULL DEFAULT false"`
|
||||
|
||||
ReviewerID int64 `xorm:"index"`
|
||||
IssueID int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(ProtectedBranch)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Review)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
// ReviewTypeApprove approves changes
|
||||
ReviewTypeApprove int = 1
|
||||
// ReviewTypeReject gives feedback blocking merge
|
||||
ReviewTypeReject int = 3
|
||||
|
||||
// VisibleTypePublic Visible for everyone
|
||||
VisibleTypePublic int = 0
|
||||
// VisibleTypePrivate Visible only for organization's members
|
||||
VisibleTypePrivate int = 2
|
||||
|
||||
// unit.UnitTypeCode is unit type code
|
||||
UnitTypeCode int = 1
|
||||
|
||||
// AccessModeNone no access
|
||||
AccessModeNone int = 0
|
||||
// AccessModeRead read access
|
||||
AccessModeRead int = 1
|
||||
// AccessModeWrite write access
|
||||
AccessModeWrite int = 2
|
||||
// AccessModeOwner owner access
|
||||
AccessModeOwner int = 4
|
||||
)
|
||||
|
||||
// Repository represents a git repository.
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"UNIQUE(s) index"`
|
||||
|
||||
IsPrivate bool `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
||||
BaseRepoID int64 `xorm:"INDEX"`
|
||||
BaseBranch string
|
||||
}
|
||||
|
||||
// RepoUnit describes all units of a repository
|
||||
type RepoUnit struct {
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type int `xorm:"INDEX(s)"`
|
||||
}
|
||||
|
||||
type Permission struct {
|
||||
AccessMode int
|
||||
Units []*RepoUnit
|
||||
UnitsMode map[int]int
|
||||
}
|
||||
|
||||
type TeamUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
||||
UID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
type Collaboration struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Mode int `xorm:"DEFAULT 2 NOT NULL"`
|
||||
}
|
||||
|
||||
type Access struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 `xorm:"UNIQUE(s)"`
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
Mode int
|
||||
}
|
||||
|
||||
type TeamUnit struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
||||
Type int `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
// Team represents a organization team.
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
Authorize int
|
||||
}
|
||||
|
||||
// getUserRepoPermission static function based on issues_model.IsOfficialReviewer at 5d78792385
|
||||
getUserRepoPermission := func(sess db.Session, repo *Repository, user *User) (Permission, error) {
|
||||
var perm Permission
|
||||
|
||||
repoOwner := new(User)
|
||||
has, err := sess.ID(repo.OwnerID).Get(repoOwner)
|
||||
if err != nil || !has {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
// Prevent strangers from checking out public repo of private organization
|
||||
// Allow user if they are collaborator of a repo within a private organization but not a member of the organization itself
|
||||
hasOrgVisible := true
|
||||
// Not SignedUser
|
||||
if user == nil {
|
||||
hasOrgVisible = repoOwner.Visibility == VisibleTypePublic
|
||||
} else if !user.IsAdmin {
|
||||
hasMemberWithUserID, err := sess.
|
||||
Where("uid=?", user.ID).
|
||||
And("org_id=?", repoOwner.ID).
|
||||
Table("org_user").
|
||||
Exist()
|
||||
if err != nil {
|
||||
hasOrgVisible = false
|
||||
}
|
||||
if (repoOwner.Visibility == VisibleTypePrivate || user.IsRestricted) && !hasMemberWithUserID {
|
||||
hasOrgVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
isCollaborator, err := sess.Get(&Collaboration{RepoID: repo.ID, UserID: user.ID})
|
||||
if err != nil {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
if repoOwner.Type == 1 && !hasOrgVisible && !isCollaborator {
|
||||
perm.AccessMode = AccessModeNone
|
||||
return perm, err
|
||||
}
|
||||
|
||||
var units []*RepoUnit
|
||||
if err := sess.Where("repo_id = ?", repo.ID).Find(&units); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
perm.Units = units
|
||||
|
||||
// anonymous visit public repo
|
||||
if user == nil {
|
||||
perm.AccessMode = AccessModeRead
|
||||
return perm, err
|
||||
}
|
||||
|
||||
// Admin or the owner has super access to the repository
|
||||
if user.IsAdmin || user.ID == repo.OwnerID {
|
||||
perm.AccessMode = AccessModeOwner
|
||||
return perm, err
|
||||
}
|
||||
|
||||
accessLevel := func(user *User, repo *Repository) (int, error) {
|
||||
mode := AccessModeNone
|
||||
var userID int64
|
||||
restricted := false
|
||||
|
||||
if user != nil {
|
||||
userID = user.ID
|
||||
restricted = user.IsRestricted
|
||||
}
|
||||
|
||||
if !restricted && !repo.IsPrivate {
|
||||
mode = AccessModeRead
|
||||
}
|
||||
|
||||
if userID == 0 {
|
||||
return mode, nil
|
||||
}
|
||||
|
||||
if userID == repo.OwnerID {
|
||||
return AccessModeOwner, nil
|
||||
}
|
||||
|
||||
a := &Access{UserID: userID, RepoID: repo.ID}
|
||||
if has, err := sess.Get(a); !has || err != nil {
|
||||
return mode, err
|
||||
}
|
||||
return a.Mode, nil
|
||||
}
|
||||
|
||||
// plain user
|
||||
perm.AccessMode, err = accessLevel(user, repo)
|
||||
if err != nil {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
// If Owner is no Org
|
||||
if repoOwner.Type != 1 {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
perm.UnitsMode = make(map[int]int)
|
||||
|
||||
// Collaborators on organization
|
||||
if isCollaborator {
|
||||
for _, u := range units {
|
||||
perm.UnitsMode[u.Type] = perm.AccessMode
|
||||
}
|
||||
}
|
||||
|
||||
// get units mode from teams
|
||||
var teams []*Team
|
||||
err = sess.
|
||||
Join("INNER", "team_user", "team_user.team_id = team.id").
|
||||
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
||||
Where("team.org_id = ?", repo.OwnerID).
|
||||
And("team_user.uid=?", user.ID).
|
||||
And("team_repo.repo_id=?", repo.ID).
|
||||
Find(&teams)
|
||||
if err != nil {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
// if user in an owner team
|
||||
for _, team := range teams {
|
||||
if team.Authorize >= AccessModeOwner {
|
||||
perm.AccessMode = AccessModeOwner
|
||||
perm.UnitsMode = nil
|
||||
return perm, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, u := range units {
|
||||
var found bool
|
||||
for _, team := range teams {
|
||||
var teamU []*TeamUnit
|
||||
var unitEnabled bool
|
||||
err = sess.Where("team_id = ?", team.ID).Find(&teamU)
|
||||
|
||||
for _, tu := range teamU {
|
||||
if tu.Type == u.Type {
|
||||
unitEnabled = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if unitEnabled {
|
||||
m := perm.UnitsMode[u.Type]
|
||||
if m < team.Authorize {
|
||||
perm.UnitsMode[u.Type] = team.Authorize
|
||||
}
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
|
||||
if !found && !repo.IsPrivate && !user.IsRestricted {
|
||||
if _, ok := perm.UnitsMode[u.Type]; !ok {
|
||||
perm.UnitsMode[u.Type] = AccessModeRead
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove no permission units
|
||||
perm.Units = make([]*RepoUnit, 0, len(units))
|
||||
for t := range perm.UnitsMode {
|
||||
for _, u := range units {
|
||||
if u.Type == t {
|
||||
perm.Units = append(perm.Units, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return perm, err
|
||||
}
|
||||
|
||||
// isOfficialReviewer static function based on 5d78792385
|
||||
isOfficialReviewer := func(sess db.Session, issueID int64, reviewer *User) (bool, error) {
|
||||
pr := new(PullRequest)
|
||||
has, err := sess.ID(issueID).Get(pr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if !has {
|
||||
return false, fmt.Errorf("PullRequest for issueID %d not exist", issueID)
|
||||
}
|
||||
|
||||
baseRepo := new(Repository)
|
||||
has, err = sess.ID(pr.BaseRepoID).Get(baseRepo)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if !has {
|
||||
return false, fmt.Errorf("baseRepo with id %d not exist", pr.BaseRepoID)
|
||||
}
|
||||
protectedBranch := new(ProtectedBranch)
|
||||
has, err = sess.Where("repo_id=? AND branch_name=?", baseRepo.ID, pr.BaseBranch).Get(protectedBranch)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !has {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if !protectedBranch.EnableApprovalsWhitelist {
|
||||
perm, err := getUserRepoPermission(sess, baseRepo, reviewer)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(perm.UnitsMode) == 0 {
|
||||
for _, u := range perm.Units {
|
||||
if u.Type == UnitTypeCode {
|
||||
return AccessModeWrite <= perm.AccessMode, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
return AccessModeWrite <= perm.UnitsMode[UnitTypeCode], nil
|
||||
}
|
||||
if slices.Contains(protectedBranch.ApprovalsWhitelistUserIDs, reviewer.ID) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// isUserInTeams
|
||||
return sess.Where("uid=?", reviewer.ID).In("team_id", protectedBranch.ApprovalsWhitelistTeamIDs).Exist(new(TeamUser))
|
||||
}
|
||||
|
||||
if _, err := x.Exec("UPDATE `protected_branch` SET `enable_whitelist` = ? WHERE enable_whitelist IS NULL", false); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := x.Exec("UPDATE `protected_branch` SET `can_push` = `enable_whitelist`"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := x.Exec("UPDATE `protected_branch` SET `enable_approvals_whitelist` = ? WHERE `required_approvals` > ?", true, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pageSize int64 = 20
|
||||
qresult, err := x.QueryInterface("SELECT max(id) as max_id FROM issue")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var totalIssues int64
|
||||
totalIssues, ok := qresult[0]["max_id"].(int64)
|
||||
if !ok {
|
||||
// If there are no issues at all we ignore it
|
||||
return nil
|
||||
}
|
||||
totalPages := totalIssues / pageSize
|
||||
|
||||
executeBody := func(page, pageSize int64) error {
|
||||
// Find latest review of each user in each pull request, and set official field if appropriate
|
||||
reviews := []*Review{}
|
||||
|
||||
if err := x.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id > ? AND issue_id <= ? AND type in (?, ?) GROUP BY issue_id, reviewer_id)",
|
||||
page*pageSize, (page+1)*pageSize, ReviewTypeApprove, ReviewTypeReject).
|
||||
Find(&reviews); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(reviews) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var updated int
|
||||
for _, review := range reviews {
|
||||
reviewer := new(User)
|
||||
has, err := sess.ID(review.ReviewerID).Get(reviewer)
|
||||
if err != nil || !has {
|
||||
// Error might occur if user doesn't exist, ignore it.
|
||||
continue
|
||||
}
|
||||
|
||||
official, err := isOfficialReviewer(sess, review.IssueID, reviewer)
|
||||
if err != nil {
|
||||
// Branch might not be protected or other error, ignore it.
|
||||
continue
|
||||
}
|
||||
review.Official = official
|
||||
updated++
|
||||
if _, err := sess.ID(review.ID).Cols("official").Update(review); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if updated > 0 {
|
||||
return sess.Commit()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var page int64
|
||||
for page = 0; page <= totalPages; page++ {
|
||||
if err := executeBody(page, pageSize); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func RemoveAttachmentMissedRepo(x db.EngineMigration) error {
|
||||
type Attachment struct {
|
||||
UUID string `xorm:"uuid"`
|
||||
}
|
||||
var start int
|
||||
attachments := make([]*Attachment, 0, 50)
|
||||
for {
|
||||
err := x.Select("uuid").Where(builder.NotIn("release_id", builder.Select("id").From("`release`"))).
|
||||
And("release_id > 0").
|
||||
OrderBy("id").Limit(50, start).Find(&attachments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(attachments); i++ {
|
||||
uuid := attachments[i].UUID
|
||||
if err = util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
|
||||
log.Warn("Unable to remove attachment file by UUID %s: %v", uuid, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(attachments) < 50 {
|
||||
break
|
||||
}
|
||||
start += 50
|
||||
attachments = attachments[:0]
|
||||
}
|
||||
|
||||
_, err := x.Exec("DELETE FROM attachment WHERE release_id > 0 AND release_id NOT IN (SELECT id FROM `release`)")
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func FeatureChangeTargetBranch(x db.EngineMigration) error {
|
||||
type Comment struct {
|
||||
OldRef string
|
||||
NewRef string
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Comment)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func SanitizeOriginalURL(x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
ID int64
|
||||
OriginalURL string `xorm:"VARCHAR(2048)"`
|
||||
}
|
||||
|
||||
var last int
|
||||
const batchSize = 50
|
||||
for {
|
||||
results := make([]Repository, 0, batchSize)
|
||||
err := x.Where("original_url <> '' AND original_url IS NOT NULL").
|
||||
And("original_service_type = 0 OR original_service_type IS NULL").
|
||||
OrderBy("id").
|
||||
Limit(batchSize, last).
|
||||
Find(&results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
break
|
||||
}
|
||||
last += len(results)
|
||||
|
||||
for _, res := range results {
|
||||
u, err := url.Parse(res.OriginalURL)
|
||||
if err != nil {
|
||||
// it is ok to continue here, we only care about fixing URLs that we can read
|
||||
continue
|
||||
}
|
||||
u.User = nil
|
||||
originalURL := u.String()
|
||||
_, err = x.Exec("UPDATE repository SET original_url = ? WHERE id = ?", originalURL, res.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
func RenameExistingUserAvatarName(x db.EngineMigration) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
type User struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
LowerName string `xorm:"UNIQUE NOT NULL"`
|
||||
Avatar string
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
count, err := x.Count(new(User))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("%d User Avatar(s) to migrate ...", count)
|
||||
|
||||
deleteList := make(container.Set[string])
|
||||
start := 0
|
||||
migrated := 0
|
||||
for {
|
||||
if err := sess.Begin(); err != nil {
|
||||
return fmt.Errorf("session.Begin: %w", err)
|
||||
}
|
||||
users := make([]*User, 0, 50)
|
||||
if err := sess.Table("user").Asc("id").Limit(50, start).Find(&users); err != nil {
|
||||
return fmt.Errorf("select users from id [%d]: %w", start, err)
|
||||
}
|
||||
if len(users) == 0 {
|
||||
_ = sess.Rollback()
|
||||
break
|
||||
}
|
||||
|
||||
log.Info("select users [%d - %d]", start, start+len(users))
|
||||
start += 50
|
||||
|
||||
for _, user := range users {
|
||||
oldAvatar := user.Avatar
|
||||
|
||||
if stat, err := os.Stat(filepath.Join(setting.Avatar.Storage.Path, oldAvatar)); err != nil || !stat.Mode().IsRegular() {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("Error: \"%s\" is not a regular file", oldAvatar)
|
||||
}
|
||||
log.Warn("[user: %s] os.Stat: %v", user.LowerName, err)
|
||||
// avatar doesn't exist in the storage
|
||||
// no need to move avatar and update database
|
||||
// we can just skip this
|
||||
continue
|
||||
}
|
||||
|
||||
newAvatar, err := copyOldAvatarToNewLocation(user.ID, oldAvatar)
|
||||
if err != nil {
|
||||
_ = sess.Rollback()
|
||||
return fmt.Errorf("[user: %s] %w", user.LowerName, err)
|
||||
} else if newAvatar == oldAvatar {
|
||||
continue
|
||||
}
|
||||
|
||||
user.Avatar = newAvatar
|
||||
if _, err := sess.ID(user.ID).Cols("avatar").Update(user); err != nil {
|
||||
_ = sess.Rollback()
|
||||
return fmt.Errorf("[user: %s] user table update: %w", user.LowerName, err)
|
||||
}
|
||||
|
||||
deleteList.Add(filepath.Join(setting.Avatar.Storage.Path, oldAvatar))
|
||||
migrated++
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Info(
|
||||
"%d/%d (%2.0f%%) User Avatar(s) migrated (%d old avatars to be deleted) in %d batches. %d Remaining ...",
|
||||
migrated,
|
||||
count,
|
||||
float64(migrated)/float64(count)*100,
|
||||
len(deleteList),
|
||||
int(math.Ceil(float64(migrated)/float64(50))),
|
||||
count-int64(migrated))
|
||||
default:
|
||||
}
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
_ = sess.Rollback()
|
||||
return fmt.Errorf("commit session: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
deleteCount := len(deleteList)
|
||||
log.Info("Deleting %d old avatars ...", deleteCount)
|
||||
i := 0
|
||||
for file := range deleteList {
|
||||
if err := util.Remove(file); err != nil {
|
||||
log.Warn("util.Remove: %v", err)
|
||||
}
|
||||
i++
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Info(
|
||||
"%d/%d (%2.0f%%) Old User Avatar(s) deleted. %d Remaining ...",
|
||||
i,
|
||||
deleteCount,
|
||||
float64(i)/float64(deleteCount)*100,
|
||||
deleteCount-i)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Completed migrating %d User Avatar(s) and deleting %d Old Avatars", count, deleteCount)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation
|
||||
// and returns newAvatar location
|
||||
func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) {
|
||||
fr, err := os.Open(filepath.Join(setting.Avatar.Storage.Path, oldAvatar))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("os.Open: %w", err)
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
data, err := io.ReadAll(fr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("io.ReadAll: %w", err)
|
||||
}
|
||||
|
||||
newAvatar := fmt.Sprintf("%x", md5.Sum(fmt.Appendf(nil, "%d-%x", userID, md5.Sum(data))))
|
||||
if newAvatar == oldAvatar {
|
||||
return newAvatar, nil
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(setting.Avatar.Storage.Path, newAvatar), data, 0o666); err != nil {
|
||||
return "", fmt.Errorf("os.WriteFile: %w", err)
|
||||
}
|
||||
|
||||
return newAvatar, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_11
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func ExtendTrackedTimes(x db.EngineMigration) error {
|
||||
type TrackedTime struct {
|
||||
Time int64 `xorm:"NOT NULL"`
|
||||
Deleted bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := sess.Exec("DELETE FROM tracked_time WHERE time IS NULL"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sess.Sync(new(TrackedTime)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddBlockOnRejectedReviews(x db.EngineMigration) error {
|
||||
type ProtectedBranch struct {
|
||||
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync(new(ProtectedBranch))
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddReviewCommitAndStale(x db.EngineMigration) error {
|
||||
type Review struct {
|
||||
CommitID string `xorm:"VARCHAR(40)"`
|
||||
Stale bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
type ProtectedBranch struct {
|
||||
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
// Old reviews will have commit ID set to "" and not stale
|
||||
if err := x.Sync(new(Review)); err != nil {
|
||||
return err
|
||||
}
|
||||
return x.Sync(new(ProtectedBranch))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func FixMigratedRepositoryServiceType(x db.EngineMigration) error {
|
||||
// structs.GithubService:
|
||||
// GithubService = 2
|
||||
_, err := x.Exec("UPDATE repository SET original_service_type = ? WHERE original_url LIKE 'https://github.com/%'", 2)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddOwnerNameOnRepository(x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
OwnerName string
|
||||
}
|
||||
if err := x.Sync(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := x.Exec("UPDATE repository SET owner_name = (SELECT name FROM `user` WHERE `user`.id = repository.owner_id)")
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddIsRestricted(x db.EngineMigration) error {
|
||||
// User see models/user.go
|
||||
type User struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync(new(User))
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddRequireSignedCommits(x db.EngineMigration) error {
|
||||
type ProtectedBranch struct {
|
||||
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync(new(ProtectedBranch))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddReactionOriginals(x db.EngineMigration) error {
|
||||
type Reaction struct {
|
||||
OriginalAuthorID int64 `xorm:"INDEX NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthor string
|
||||
}
|
||||
|
||||
return x.Sync(new(Reaction))
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddUserRepoMissingColumns(x db.EngineMigration) error {
|
||||
type VisibleType int
|
||||
type User struct {
|
||||
PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'pbkdf2'"`
|
||||
Visibility VisibleType `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
IsArchived bool `xorm:"INDEX"`
|
||||
Topics []string `xorm:"TEXT JSON"`
|
||||
}
|
||||
|
||||
return x.Sync(new(User), new(Repository))
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func AddReviewMigrateInfo(x db.EngineMigration) error {
|
||||
type Review struct {
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Review)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func FixTopicRepositoryCount(x db.EngineMigration) error {
|
||||
_, err := x.Exec(builder.Delete(builder.NotIn("`repo_id`", builder.Select("`id`").From("`repository`"))).From("`repo_topic`"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = x.Exec(builder.Update(
|
||||
builder.Eq{
|
||||
"`repo_count`": builder.Select("count(*)").From("`repo_topic`").Where(builder.Eq{
|
||||
"`repo_topic`.`topic_id`": builder.Expr("`topic`.`id`"),
|
||||
}),
|
||||
}).From("`topic`").Where(builder.Eq{"'1'": "1"}))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/timeutil"
|
||||
)
|
||||
|
||||
func AddLanguageStats(x db.EngineMigration) error {
|
||||
// LanguageStat see models/repo_language_stats.go
|
||||
type LanguageStat struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
CommitID string
|
||||
IsPrimary bool
|
||||
Language string `xorm:"VARCHAR(30) UNIQUE(s) INDEX NOT NULL"`
|
||||
Percentage float32 `xorm:"NUMERIC(5,2) NOT NULL DEFAULT 0"`
|
||||
Color string `xorm:"-"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||
}
|
||||
|
||||
type RepoIndexerType int
|
||||
|
||||
// RepoIndexerStatus see models/repo_stats_indexer.go
|
||||
type RepoIndexerStatus struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
CommitSha string `xorm:"VARCHAR(40)"`
|
||||
IndexerType RepoIndexerType `xorm:"INDEX(s) NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(LanguageStat)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
if err := x.Sync(new(RepoIndexerStatus)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
func FixMergeBase(ctx context.Context, x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"UNIQUE(s) index"`
|
||||
OwnerName string
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Index int64
|
||||
HeadRepoID int64 `xorm:"INDEX"`
|
||||
BaseRepoID int64 `xorm:"INDEX"`
|
||||
HeadBranch string
|
||||
BaseBranch string
|
||||
MergeBase string `xorm:"VARCHAR(40)"`
|
||||
|
||||
HasMerged bool `xorm:"INDEX"`
|
||||
MergedCommitID string `xorm:"VARCHAR(40)"`
|
||||
}
|
||||
|
||||
limit := setting.Database.IterateBufferSize
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
count, err := x.Count(new(PullRequest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("%d Pull Request(s) to migrate ...", count)
|
||||
|
||||
i := 0
|
||||
start := 0
|
||||
for {
|
||||
prs := make([]PullRequest, 0, 50)
|
||||
if err := x.Limit(limit, start).Asc("id").Find(&prs); err != nil {
|
||||
return fmt.Errorf("Find: %w", err)
|
||||
}
|
||||
if len(prs) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
start += 50
|
||||
for _, pr := range prs {
|
||||
baseRepo := &Repository{ID: pr.BaseRepoID}
|
||||
has, err := x.Table("repository").Get(baseRepo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to get base repo %d %w", pr.BaseRepoID, err)
|
||||
}
|
||||
if !has {
|
||||
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
|
||||
continue
|
||||
}
|
||||
userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName))
|
||||
repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git")
|
||||
|
||||
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||
|
||||
if !pr.HasMerged {
|
||||
var err error
|
||||
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).WithDir(repoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
var err2 error
|
||||
pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).WithDir(repoPath).RunStdString(ctx)
|
||||
if err2 != nil {
|
||||
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
}
|
||||
parents := strings.Split(strings.TrimSpace(parentsString), " ")
|
||||
if len(parents) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
refs := append([]string{}, parents[1:]...)
|
||||
refs = append(refs, gitRefName)
|
||||
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
|
||||
|
||||
pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
pr.MergeBase = strings.TrimSpace(pr.MergeBase)
|
||||
x.ID(pr.ID).Cols("merge_base").Update(pr)
|
||||
i++
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Info("%d/%d (%2.0f%%) Pull Request(s) migrated in %d batches. %d PRs Remaining ...", i, count, float64(i)/float64(count)*100, int(math.Ceil(float64(i)/float64(limit))), count-int64(i))
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Info("Completed migrating %d Pull Request(s) in: %d batches", count, int(math.Ceil(float64(i)/float64(limit))))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func PurgeUnusedDependencies(x db.EngineMigration) error {
|
||||
if _, err := x.Exec("DELETE FROM issue_dependency WHERE issue_id NOT IN (SELECT id FROM issue)"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := x.Exec("DELETE FROM issue_dependency WHERE dependency_id NOT IN (SELECT id FROM issue)")
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
func ExpandWebhooks(x db.EngineMigration) error {
|
||||
type HookEvents struct {
|
||||
Create bool `json:"create"`
|
||||
Delete bool `json:"delete"`
|
||||
Fork bool `json:"fork"`
|
||||
Issues bool `json:"issues"`
|
||||
IssueAssign bool `json:"issue_assign"`
|
||||
IssueLabel bool `json:"issue_label"`
|
||||
IssueMilestone bool `json:"issue_milestone"`
|
||||
IssueComment bool `json:"issue_comment"`
|
||||
Push bool `json:"push"`
|
||||
PullRequest bool `json:"pull_request"`
|
||||
PullRequestAssign bool `json:"pull_request_assign"`
|
||||
PullRequestLabel bool `json:"pull_request_label"`
|
||||
PullRequestMilestone bool `json:"pull_request_milestone"`
|
||||
PullRequestComment bool `json:"pull_request_comment"`
|
||||
PullRequestReview bool `json:"pull_request_review"`
|
||||
PullRequestSync bool `json:"pull_request_sync"`
|
||||
Repository bool `json:"repository"`
|
||||
Release bool `json:"release"`
|
||||
}
|
||||
|
||||
type HookEvent struct {
|
||||
PushOnly bool `json:"push_only"`
|
||||
SendEverything bool `json:"send_everything"`
|
||||
ChooseEvents bool `json:"choose_events"`
|
||||
BranchFilter string `json:"branch_filter"`
|
||||
|
||||
HookEvents `json:"events"`
|
||||
}
|
||||
|
||||
type Webhook struct {
|
||||
ID int64
|
||||
Events string
|
||||
}
|
||||
|
||||
var bytes []byte
|
||||
var last int
|
||||
batchSize := setting.Database.IterateBufferSize
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
for {
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
results := make([]Webhook, 0, batchSize)
|
||||
err := x.OrderBy("id").
|
||||
Limit(batchSize, last).
|
||||
Find(&results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
break
|
||||
}
|
||||
last += len(results)
|
||||
|
||||
for _, res := range results {
|
||||
var events HookEvent
|
||||
if err = json.Unmarshal([]byte(res.Events), &events); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !events.ChooseEvents {
|
||||
continue
|
||||
}
|
||||
|
||||
if events.Issues {
|
||||
events.IssueAssign = true
|
||||
events.IssueLabel = true
|
||||
events.IssueMilestone = true
|
||||
events.IssueComment = true
|
||||
}
|
||||
|
||||
if events.PullRequest {
|
||||
events.PullRequestAssign = true
|
||||
events.PullRequestLabel = true
|
||||
events.PullRequestMilestone = true
|
||||
events.PullRequestComment = true
|
||||
events.PullRequestReview = true
|
||||
events.PullRequestSync = true
|
||||
}
|
||||
|
||||
if bytes, err = json.Marshal(&events); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = sess.Exec("UPDATE webhook SET events = ? WHERE id = ?", string(bytes), res.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func AddSystemWebhookColumn(x db.EngineMigration) error {
|
||||
type Webhook struct {
|
||||
IsSystemWebhook bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Webhook)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func AddBranchProtectionProtectedFilesColumn(x db.EngineMigration) error {
|
||||
type ProtectedBranch struct {
|
||||
ProtectedFilePatterns string `xorm:"TEXT"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(ProtectedBranch)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddEmailHashTable(x db.EngineMigration) error {
|
||||
// EmailHash represents a pre-generated hash map
|
||||
type EmailHash struct {
|
||||
Hash string `xorm:"pk varchar(32)"`
|
||||
Email string `xorm:"UNIQUE NOT NULL"`
|
||||
}
|
||||
return x.Sync(new(EmailHash))
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
func RefixMergeBase(ctx context.Context, x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"UNIQUE(s) index"`
|
||||
OwnerName string
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Index int64
|
||||
HeadRepoID int64 `xorm:"INDEX"`
|
||||
BaseRepoID int64 `xorm:"INDEX"`
|
||||
HeadBranch string
|
||||
BaseBranch string
|
||||
MergeBase string `xorm:"VARCHAR(40)"`
|
||||
|
||||
HasMerged bool `xorm:"INDEX"`
|
||||
MergedCommitID string `xorm:"VARCHAR(40)"`
|
||||
}
|
||||
|
||||
limit := setting.Database.IterateBufferSize
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
count, err := x.Where("has_merged = ?", true).Count(new(PullRequest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("%d Merged Pull Request(s) to migrate ...", count)
|
||||
|
||||
i := 0
|
||||
start := 0
|
||||
for {
|
||||
prs := make([]PullRequest, 0, 50)
|
||||
if err := x.Limit(limit, start).Asc("id").Where("has_merged = ?", true).Find(&prs); err != nil {
|
||||
return fmt.Errorf("Find: %w", err)
|
||||
}
|
||||
if len(prs) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
start += 50
|
||||
for _, pr := range prs {
|
||||
baseRepo := &Repository{ID: pr.BaseRepoID}
|
||||
has, err := x.Table("repository").Get(baseRepo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to get base repo %d %w", pr.BaseRepoID, err)
|
||||
}
|
||||
if !has {
|
||||
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
|
||||
continue
|
||||
}
|
||||
userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName))
|
||||
repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git")
|
||||
|
||||
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||
|
||||
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
}
|
||||
parents := strings.Split(strings.TrimSpace(parentsString), " ")
|
||||
if len(parents) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
// we should recalculate
|
||||
refs := append([]string{}, parents[1:]...)
|
||||
refs = append(refs, gitRefName)
|
||||
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
|
||||
|
||||
pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
}
|
||||
pr.MergeBase = strings.TrimSpace(pr.MergeBase)
|
||||
x.ID(pr.ID).Cols("merge_base").Update(pr)
|
||||
i++
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Info("%d/%d (%2.0f%%) Pull Request(s) migrated in %d batches. %d PRs Remaining ...", i, count, float64(i)/float64(count)*100, int(math.Ceil(float64(i)/float64(limit))), count-int64(i))
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Completed migrating %d Pull Request(s) in: %d batches", count, int(math.Ceil(float64(i)/float64(limit))))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func AddOrgIDLabelColumn(x db.EngineMigration) error {
|
||||
type Label struct {
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Label)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/modules/gitrepo"
|
||||
"gitea.dev/modules/graceful"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
func AddCommitDivergenceToPulls(x db.EngineMigration) error {
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"UNIQUE(s) index"`
|
||||
OwnerName string
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IssueID int64 `xorm:"INDEX"`
|
||||
Index int64
|
||||
|
||||
CommitsAhead int
|
||||
CommitsBehind int
|
||||
|
||||
BaseRepoID int64 `xorm:"INDEX"`
|
||||
BaseBranch string
|
||||
|
||||
HasMerged bool `xorm:"INDEX"`
|
||||
MergedCommitID string `xorm:"VARCHAR(40)"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(PullRequest)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
|
||||
last := 0
|
||||
migrated := 0
|
||||
|
||||
batchSize := setting.Database.IterateBufferSize
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
count, err := sess.Where("has_merged = ?", false).Count(new(PullRequest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("%d Unmerged Pull Request(s) to migrate ...", count)
|
||||
|
||||
for {
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
results := make([]*PullRequest, 0, batchSize)
|
||||
err := sess.Where("has_merged = ?", false).OrderBy("id").Limit(batchSize, last).Find(&results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
break
|
||||
}
|
||||
last += batchSize
|
||||
|
||||
for _, pr := range results {
|
||||
baseRepo := &Repository{ID: pr.BaseRepoID}
|
||||
has, err := x.Table("repository").Get(baseRepo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to get base repo %d %w", pr.BaseRepoID, err)
|
||||
}
|
||||
if !has {
|
||||
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
|
||||
continue
|
||||
}
|
||||
repoStore := repo_model.StorageRepo(repo_model.RelativePath(baseRepo.OwnerName, baseRepo.Name))
|
||||
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||
divergence, err := gitrepo.GetDivergingCommits(graceful.GetManager().HammerContext(), repoStore, pr.BaseBranch, gitRefName)
|
||||
if err != nil {
|
||||
log.Warn("Could not recalculate Divergence for pull: %d", pr.ID)
|
||||
pr.CommitsAhead = 0
|
||||
pr.CommitsBehind = 0
|
||||
}
|
||||
pr.CommitsAhead = divergence.Ahead
|
||||
pr.CommitsBehind = divergence.Behind
|
||||
|
||||
if _, err = sess.ID(pr.ID).Cols("commits_ahead", "commits_behind").Update(pr); err != nil {
|
||||
return fmt.Errorf("Update Cols: %w", err)
|
||||
}
|
||||
migrated++
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Info(
|
||||
"%d/%d (%2.0f%%) Pull Request(s) migrated in %d batches. %d PRs Remaining ...",
|
||||
migrated,
|
||||
count,
|
||||
float64(migrated)/float64(count)*100,
|
||||
int(math.Ceil(float64(migrated)/float64(batchSize))),
|
||||
count-int64(migrated))
|
||||
default:
|
||||
}
|
||||
}
|
||||
log.Info("Completed migrating %d Pull Request(s) in: %d batches", count, int(math.Ceil(float64(migrated)/float64(batchSize))))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import "gitea.dev/models/db"
|
||||
|
||||
func AddBlockOnOutdatedBranch(x db.EngineMigration) error {
|
||||
type ProtectedBranch struct {
|
||||
BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
return x.Sync(new(ProtectedBranch))
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
)
|
||||
|
||||
func AddResolveDoerIDCommentColumn(x db.EngineMigration) error {
|
||||
type Comment struct {
|
||||
ResolveDoerID int64
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Comment)); err != nil {
|
||||
return fmt.Errorf("Sync: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_12
|
||||
|
||||
import (
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
func PrependRefsHeadsToIssueRefs(x db.EngineMigration) error {
|
||||
var query string
|
||||
|
||||
switch {
|
||||
case setting.Database.Type.IsMSSQL():
|
||||
query = "UPDATE `issue` SET `ref` = 'refs/heads/' + `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'"
|
||||
case setting.Database.Type.IsMySQL():
|
||||
query = "UPDATE `issue` SET `ref` = CONCAT('refs/heads/', `ref`) WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%';"
|
||||
default:
|
||||
query = "UPDATE `issue` SET `ref` = 'refs/heads/' || `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'"
|
||||
}
|
||||
|
||||
_, err := x.Exec(query)
|
||||
return err
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user