Skip to content

Commit 1dfa826

Browse files
GustedGusted
authored andcommitted
[DB] Forgejo database migrations
- Implements https://codeberg.org/forgejo/discussions/issues/32#issuecomment-918737 - Allows to add Forgejo-specific migrations that don't interfere with Gitea's migration logic. Please do note that we cannot liberally add migrations for Gitea tables, as they might do their own migrations in a future version on that table, and that could undo our migrations. Luckily, we don't have a scenario where that's needed and thus not taken into account. Co-authored-by: Gusted <[email protected]> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/795 (cherry picked from commit 8ee32978c0af1f8f71679c87f695df2b90b617c8) (cherry picked from commit c240b34f595a7a9763f7b748052ac98f9f18954d) (cherry picked from commit 03936c649243a0a29701393d58e63e33064c7461) (cherry picked from commit a20ed852f8b6d28872c05d688bffe5c6976bfa03)
1 parent 61d5008 commit 1dfa826

File tree

2 files changed

+143
-1
lines changed

2 files changed

+143
-1
lines changed

models/forgejo_migrations/migrate.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2023 The Forgejo Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package forgejo_migrations //nolint:revive
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"os"
10+
11+
"code.gitea.io/gitea/modules/git"
12+
"code.gitea.io/gitea/modules/log"
13+
"code.gitea.io/gitea/modules/setting"
14+
15+
"xorm.io/xorm"
16+
"xorm.io/xorm/names"
17+
)
18+
19+
// ForgejoVersion describes the Forgejo version table. Should have only one row with id = 1.
20+
type ForgejoVersion struct {
21+
ID int64 `xorm:"pk autoincr"`
22+
Version int64
23+
}
24+
25+
type Migration struct {
26+
description string
27+
migrate func(*xorm.Engine) error
28+
}
29+
30+
// NewMigration creates a new migration.
31+
func NewMigration(desc string, fn func(*xorm.Engine) error) *Migration {
32+
return &Migration{desc, fn}
33+
}
34+
35+
// This is a sequence of additional Forgejo migrations.
36+
// Add new migrations to the bottom of the list.
37+
var migrations = []*Migration{}
38+
39+
// GetCurrentDBVersion returns the current Forgejo database version.
40+
func GetCurrentDBVersion(x *xorm.Engine) (int64, error) {
41+
if err := x.Sync(new(ForgejoVersion)); err != nil {
42+
return -1, fmt.Errorf("sync: %w", err)
43+
}
44+
45+
currentVersion := &ForgejoVersion{ID: 1}
46+
has, err := x.Get(currentVersion)
47+
if err != nil {
48+
return -1, fmt.Errorf("get: %w", err)
49+
}
50+
if !has {
51+
return -1, nil
52+
}
53+
return currentVersion.Version, nil
54+
}
55+
56+
// ExpectedVersion returns the expected Forgejo database version.
57+
func ExpectedVersion() int64 {
58+
return int64(len(migrations))
59+
}
60+
61+
// EnsureUpToDate will check if the Forgejo database is at the correct version.
62+
func EnsureUpToDate(x *xorm.Engine) error {
63+
currentDB, err := GetCurrentDBVersion(x)
64+
if err != nil {
65+
return err
66+
}
67+
68+
if currentDB < 0 {
69+
return fmt.Errorf("database has not been initialized")
70+
}
71+
72+
expected := ExpectedVersion()
73+
74+
if currentDB != expected {
75+
return fmt.Errorf(`current Forgejo database version %d is not equal to the expected version %d. Please run "forgejo [--config /path/to/app.ini] migrate" to update the database version`, currentDB, expected)
76+
}
77+
78+
return nil
79+
}
80+
81+
// Migrate Forgejo database to current version.
82+
func Migrate(x *xorm.Engine) error {
83+
// Set a new clean the default mapper to GonicMapper as that is the default for .
84+
x.SetMapper(names.GonicMapper{})
85+
if err := x.Sync(new(ForgejoVersion)); err != nil {
86+
return fmt.Errorf("sync: %w", err)
87+
}
88+
89+
currentVersion := &ForgejoVersion{ID: 1}
90+
has, err := x.Get(currentVersion)
91+
if err != nil {
92+
return fmt.Errorf("get: %w", err)
93+
} else if !has {
94+
// If the version record does not exist we think
95+
// it is a fresh installation and we can skip all migrations.
96+
currentVersion.ID = 0
97+
currentVersion.Version = ExpectedVersion()
98+
99+
if _, err = x.InsertOne(currentVersion); err != nil {
100+
return fmt.Errorf("insert: %w", err)
101+
}
102+
}
103+
104+
v := currentVersion.Version
105+
106+
// Downgrading Forgejo's database version not supported
107+
if v > ExpectedVersion() {
108+
msg := fmt.Sprintf("Your Forgejo database (migration version: %d) is for a newer version of Forgejo, you cannot use the newer database for this old Forgejo release (%d).", v, ExpectedVersion())
109+
msg += "\nForgejo will exit to keep your database safe and unchanged. Please use the correct Forgejo release, do not change the migration version manually (incorrect manual operation may cause data loss)."
110+
if !setting.IsProd {
111+
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 forgejo_version SET version=%d WHERE id=1;", ExpectedVersion())
112+
}
113+
_, _ = fmt.Fprintln(os.Stderr, msg)
114+
log.Fatal(msg)
115+
return nil
116+
}
117+
118+
// Some migration tasks depend on the git command
119+
if git.DefaultContext == nil {
120+
if err = git.InitSimple(context.Background()); err != nil {
121+
return err
122+
}
123+
}
124+
125+
// Migrate
126+
for i, m := range migrations[v:] {
127+
log.Info("Migration[%d]: %s", v+int64(i), m.description)
128+
// Reset the mapper between each migration - migrations are not supposed to depend on each other
129+
x.SetMapper(names.GonicMapper{})
130+
if err = m.migrate(x); err != nil {
131+
return fmt.Errorf("migration[%d]: %s failed: %w", v+int64(i), m.description, err)
132+
}
133+
currentVersion.Version = v + int64(i) + 1
134+
if _, err = x.ID(1).Update(currentVersion); err != nil {
135+
return err
136+
}
137+
}
138+
return nil
139+
}

models/migrations/migrations.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"os"
1111

12+
"code.gitea.io/gitea/models/forgejo_migrations"
1213
"code.gitea.io/gitea/models/migrations/v1_10"
1314
"code.gitea.io/gitea/models/migrations/v1_11"
1415
"code.gitea.io/gitea/models/migrations/v1_12"
@@ -616,5 +617,7 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
616617
return err
617618
}
618619
}
619-
return nil
620+
621+
// Execute Forgejo specific migrations.
622+
return forgejo_migrations.Migrate(x)
620623
}

0 commit comments

Comments
 (0)