2023-06-03 10:41:16 +02:00
// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgejo_migrations //nolint:revive
import (
"context"
"fmt"
"os"
2023-08-08 23:55:25 +02:00
"code.gitea.io/gitea/models/forgejo/semver"
2023-08-08 23:52:37 +02:00
forgejo_v1_20 "code.gitea.io/gitea/models/forgejo_migrations/v1_20"
2023-12-20 21:44:55 +01:00
forgejo_v1_22 "code.gitea.io/gitea/models/forgejo_migrations/v1_22"
2023-06-03 10:41:16 +02:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"xorm.io/xorm"
"xorm.io/xorm/names"
)
// ForgejoVersion describes the Forgejo version table. Should have only one row with id = 1.
type ForgejoVersion struct {
ID int64 ` xorm:"pk autoincr" `
Version int64
}
type Migration struct {
description string
migrate func ( * xorm . Engine ) error
}
// NewMigration creates a new migration.
func NewMigration ( desc string , fn func ( * xorm . Engine ) error ) * Migration {
return & Migration { desc , fn }
}
// This is a sequence of additional Forgejo migrations.
// Add new migrations to the bottom of the list.
2023-08-08 23:52:37 +02:00
var migrations = [ ] * Migration {
2023-08-15 01:06:13 +02:00
// v0 -> v1
NewMigration ( "Add Forgejo Blocked Users table" , forgejo_v1_20 . AddForgejoBlockedUser ) ,
2023-08-08 23:52:37 +02:00
// v1 -> v2
NewMigration ( "create the forgejo_sem_ver table" , forgejo_v1_20 . CreateSemVerTable ) ,
2023-11-22 17:26:21 +01:00
// v2 -> v3
NewMigration ( "create the forgejo_auth_token table" , forgejo_v1_20 . CreateAuthorizationTokenTable ) ,
2023-12-20 21:44:55 +01:00
// v3 -> v4
NewMigration ( "Add default_permissions to repo_unit" , forgejo_v1_22 . AddDefaultPermissionsToRepoUnit ) ,
[FEAT] Repository flags
This implements "repository flags", a way for instance administrators to
assign custom flags to repositories. The idea is that custom templates
can look at these flags, and display banners based on them, Forgejo does
not provide anything built on top of it, just the foundation. The
feature is optional, and disabled by default. To enable it, set
`[repository].ENABLE_FLAGS = true`.
On the UI side, instance administrators will see a new "Manage flags"
tab on repositories, and a list of enabled tags (if any) on the
repository home page. The "Manage flags" page allows them to remove
existing flags, or add any new ones that are listed in
`[repository].SETTABLE_FLAGS`.
The model does not enforce that only the `SETTABLE_FLAGS` are present.
If the setting is changed, old flags may remain present in the database,
and anything that uses them, will still work. The repository flag
management page will allow an instance administrator to remove them, but
not set them, once removed.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
(cherry picked from commit ba735ce2228f8dd7ca105e94b9baa1be058ebe37)
(cherry picked from commit f09f6e029b4fb2714b86cd32dc19255078ecc0ee)
(cherry picked from commit 2f8b0414892f6099f519bda63a9e0fbc8ba6cfc7)
2024-01-04 14:28:19 +01:00
// v4 -> v5
NewMigration ( "create the forgejo_repo_flag table" , forgejo_v1_22 . CreateRepoFlagTable ) ,
Allow changing the repo Wiki branch to main
Previously, the repo wiki was hardcoded to use `master` as its branch,
this change makes it possible to use `main` (or something else, governed
by `[repository].DEFAULT_BRANCH`, a setting that already exists and
defaults to `main`).
The way it is done is that a new column is added to the `repository`
table: `wiki_branch`. The migration will make existing repositories
default to `master`, for compatibility's sake, even if they don't have a
Wiki (because it's easier to do that). Newly created repositories will
default to `[repository].DEFAULT_BRANCH` instead.
The Wiki service was updated to use the branch name stored in the
database, and fall back to the default if it is empty.
Old repositories with Wikis using the older `master` branch will have
the option to do a one-time transition to `main`, available via the
repository settings in the "Danger Zone". This option will only be
available for repositories that have the internal wiki enabled, it is
not empty, and the wiki branch is not `[repository].DEFAULT_BRANCH`.
When migrating a repository with a Wiki, Forgejo will use the same
branch name for the wiki as the source repository did. If that's not the
same as the default, the option to normalize it will be available after
the migration's done.
Additionally, the `/api/v1/{owner}/{repo}` endpoint was updated: it will
now include the wiki branch name in `GET` requests, and allow changing
the wiki branch via `PATCH`.
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-01-30 12:18:53 +01:00
// v5 -> v6
NewMigration ( "Add wiki_branch to repository" , forgejo_v1_22 . AddWikiBranchToRepository ) ,
2023-08-08 23:52:37 +02:00
}
2023-06-03 10:41:16 +02:00
// GetCurrentDBVersion returns the current Forgejo database version.
func GetCurrentDBVersion ( x * xorm . Engine ) ( int64 , error ) {
if err := x . Sync ( new ( ForgejoVersion ) ) ; err != nil {
return - 1 , fmt . Errorf ( "sync: %w" , err )
}
currentVersion := & ForgejoVersion { 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
}
// ExpectedVersion returns the expected Forgejo database version.
func ExpectedVersion ( ) int64 {
return int64 ( len ( migrations ) )
}
// EnsureUpToDate will check if the Forgejo database is at the correct version.
func EnsureUpToDate ( x * xorm . Engine ) error {
currentDB , err := GetCurrentDBVersion ( x )
if err != nil {
return err
}
if currentDB < 0 {
return fmt . Errorf ( "database has not been initialized" )
}
expected := ExpectedVersion ( )
if currentDB != expected {
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 )
}
return nil
}
// Migrate Forgejo database to current version.
func Migrate ( x * xorm . Engine ) error {
// Set a new clean the default mapper to GonicMapper as that is the default for .
x . SetMapper ( names . GonicMapper { } )
if err := x . Sync ( new ( ForgejoVersion ) ) ; err != nil {
return fmt . Errorf ( "sync: %w" , err )
}
currentVersion := & ForgejoVersion { 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 we think
// it is a fresh installation and we can skip all migrations.
currentVersion . ID = 0
currentVersion . Version = ExpectedVersion ( )
if _ , err = x . InsertOne ( currentVersion ) ; err != nil {
return fmt . Errorf ( "insert: %w" , err )
}
}
v := currentVersion . Version
// Downgrading Forgejo's database version not supported
if v > ExpectedVersion ( ) {
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 ( ) )
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)."
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 forgejo_version SET version=%d WHERE id=1;" , ExpectedVersion ( ) )
}
_ , _ = fmt . Fprintln ( os . Stderr , msg )
log . Fatal ( msg )
return nil
}
// Some migration tasks depend on the git command
if git . DefaultContext == nil {
if err = git . InitSimple ( context . Background ( ) ) ; err != nil {
return err
}
}
// Migrate
for i , m := range migrations [ v : ] {
log . Info ( "Migration[%d]: %s" , v + int64 ( i ) , 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 ( x ) ; err != nil {
return fmt . Errorf ( "migration[%d]: %s failed: %w" , v + int64 ( i ) , m . description , err )
}
currentVersion . Version = v + int64 ( i ) + 1
if _ , err = x . ID ( 1 ) . Update ( currentVersion ) ; err != nil {
return err
}
}
2023-08-08 23:55:25 +02:00
if err := x . Sync ( new ( semver . ForgejoSemVer ) ) ; err != nil {
return fmt . Errorf ( "sync: %w" , err )
}
return semver . SetVersionStringWithEngine ( x , setting . ForgejoVersion )
2023-06-03 10:41:16 +02:00
}