From 63b6e7155193124e27865225feb1a59878fc31fa Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 28 Apr 2026 00:42:34 +0200 Subject: [PATCH] Drop v332 migration, keep project_board.sorting as int8 Per review feedback, the 127-column cap is intentional (maxProjectColumns is 20), so the DB schema is left as-is and no migration is needed. Reverts the Column.Sorting widening to match. Co-Authored-By: Claude (Opus 4.7) --- models/migrations/migrations.go | 1 - models/migrations/v1_27/v332.go | 42 ------------------------------- models/project/column.go | 7 +++--- models/project/column_test.go | 6 ++--- routers/api/v1/repo/project.go | 2 +- services/convert/project.go | 2 +- services/forms/repo_form.go | 2 +- tests/integration/project_test.go | 6 ++--- 8 files changed, 13 insertions(+), 55 deletions(-) delete mode 100644 models/migrations/v1_27/v332.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index d3522772d2..c3a8f08b5d 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -409,7 +409,6 @@ func prepareMigrationTasks() []*migration { // 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, "Widen project_board.sorting from int8 to int", v1_27.WidenProjectBoardSorting), } return preparedMigrations } diff --git a/models/migrations/v1_27/v332.go b/models/migrations/v1_27/v332.go deleted file mode 100644 index fa10341241..0000000000 --- a/models/migrations/v1_27/v332.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2026 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_27 - -import ( - "code.gitea.io/gitea/models/migrations/base" - "code.gitea.io/gitea/modules/setting" - - "xorm.io/xorm" - "xorm.io/xorm/schemas" -) - -// WidenProjectBoardSorting changes project_board.sorting from int8 to int so the -// API can stop truncating sort values and the column count is no longer capped at -// 127. DefaultIsEmpty: true is required because MSSQL's ALTER COLUMN rejects an -// inline DEFAULT, and MySQL's MODIFY COLUMN drops any DEFAULT not restated, so the -// default is reapplied for MySQL afterwards. Postgres and MSSQL keep the existing -// DEFAULT constraint independently of the type change. -func WidenProjectBoardSorting(x *xorm.Engine) error { - // SQLite uses type affinity rather than strict types: a column declared TINYINT - // already stores any 64-bit int, so the widening is a no-op. Updating the - // declared type would require recreating the table (no ALTER COLUMN in SQLite) - // for no behavioral gain. - if setting.Database.Type.IsSQLite3() { - return nil - } - if err := base.ModifyColumn(x, "project_board", &schemas.Column{ - Name: "sorting", - SQLType: schemas.SQLType{Name: "INT"}, - Nullable: false, - DefaultIsEmpty: true, - }); err != nil { - return err - } - if setting.Database.Type.IsMySQL() { - if _, err := x.Exec("ALTER TABLE `project_board` ALTER `sorting` SET DEFAULT 0"); err != nil { - return err - } - } - return nil -} diff --git a/models/project/column.go b/models/project/column.go index d55baf84d5..9c9abb4599 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -42,7 +42,7 @@ type Column struct { ID int64 `xorm:"pk autoincr"` Title string Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific column will be assigned to this column - Sorting int `xorm:"NOT NULL DEFAULT 0"` + Sorting int8 `xorm:"NOT NULL DEFAULT 0"` Color string `xorm:"VARCHAR(7)"` ProjectID int64 `xorm:"INDEX NOT NULL"` @@ -128,7 +128,8 @@ func createDefaultColumnsForProject(ctx context.Context, project *Project) error }) } -// maxProjectColumns is the maximum number of columns allowed in a project. +// maxProjectColumns max columns allowed in a project, this should not bigger than 127 +// because sorting is int8 in database const maxProjectColumns = 20 // NewColumn adds a new project column to a given project @@ -148,7 +149,7 @@ func NewColumn(ctx context.Context, column *Column) error { if res.ColumnCount >= maxProjectColumns { return errors.New("NewBoard: maximum number of columns reached") } - column.Sorting = int(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0)) + column.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0)) _, err := db.GetEngine(ctx).Insert(column) return err } diff --git a/models/project/column_test.go b/models/project/column_test.go index b32d2a335a..d619698965 100644 --- a/models/project/column_test.go +++ b/models/project/column_test.go @@ -83,9 +83,9 @@ func Test_MoveColumnsOnProject(t *testing.T) { columns, err := GetProjectColumns(t.Context(), project1.ID, db.ListOptionsAll) assert.NoError(t, err) assert.Len(t, columns, 3) - assert.Equal(t, 0, columns[0].Sorting) // even if there is no default sorting, the code should also work - assert.Equal(t, 0, columns[1].Sorting) - assert.Equal(t, 0, columns[2].Sorting) + assert.EqualValues(t, 0, columns[0].Sorting) // even if there is no default sorting, the code should also work + assert.EqualValues(t, 0, columns[1].Sorting) + assert.EqualValues(t, 0, columns[2].Sorting) err = MoveColumnsOnProject(t.Context(), project1, map[int64]int64{ 0: columns[1].ID, diff --git a/routers/api/v1/repo/project.go b/routers/api/v1/repo/project.go index ac323fefb8..1ba67b1ff3 100644 --- a/routers/api/v1/repo/project.go +++ b/routers/api/v1/repo/project.go @@ -574,7 +574,7 @@ func EditProjectColumn(ctx *context.APIContext) { column.Color = *form.Color } if form.Sorting != nil { - column.Sorting = *form.Sorting + column.Sorting = int8(*form.Sorting) } if err := project_model.UpdateColumn(ctx, column); err != nil { diff --git a/services/convert/project.go b/services/convert/project.go index 5b012925d1..cd6109fd19 100644 --- a/services/convert/project.go +++ b/services/convert/project.go @@ -150,7 +150,7 @@ func toProjectColumn(ctx context.Context, column *project_model.Column, doer *us ID: column.ID, Title: column.Title, Default: column.Default, - Sorting: column.Sorting, + Sorting: int(column.Sorting), Color: column.Color, ProjectID: column.ProjectID, NumIssues: column.NumIssues, diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index aa76e82d4e..d8e019f860 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -469,7 +469,7 @@ type CreateProjectForm struct { // EditProjectColumnForm is a form for editing a project column type EditProjectColumnForm struct { Title string `binding:"Required;MaxSize(100)"` - Sorting int + Sorting int8 Color string `binding:"MaxSize(7)"` } diff --git a/tests/integration/project_test.go b/tests/integration/project_test.go index d823b85648..00764bf883 100644 --- a/tests/integration/project_test.go +++ b/tests/integration/project_test.go @@ -64,9 +64,9 @@ func TestMoveRepoProjectColumns(t *testing.T) { columns, err := project_model.GetProjectColumns(t.Context(), project1.ID, db.ListOptionsAll) assert.NoError(t, err) assert.Len(t, columns, 3) - assert.Equal(t, 0, columns[0].Sorting) - assert.Equal(t, 1, columns[1].Sorting) - assert.Equal(t, 2, columns[2].Sorting) + assert.EqualValues(t, 0, columns[0].Sorting) + assert.EqualValues(t, 1, columns[1].Sorting) + assert.EqualValues(t, 2, columns[2].Sorting) sess := loginUser(t, "user1") req := NewRequest(t, "GET", fmt.Sprintf("/%s/projects/%d", repo2.FullName(), project1.ID))