feat(upgrade): support upgrade from specific version manually

This commit is contained in:
LinkinStars 2023-05-17 18:13:03 +08:00
parent 8e46fe3ecd
commit acad5b2811
2 changed files with 41 additions and 28 deletions

View File

@ -10,6 +10,7 @@ import (
"github.com/answerdev/answer/internal/install"
"github.com/answerdev/answer/internal/migrations"
"github.com/answerdev/answer/plugin"
"github.com/segmentfault/pacman/log"
"github.com/spf13/cobra"
)
@ -22,10 +23,9 @@ var (
buildWithPlugins []string
// build output path
buildOutput string
// This config is used to upgrade the database from a specific version number manually.
// The version number is calculated with `migrations.go` migrations length.
// If you want to upgrade the database from version 1 to version 2, you can use `answer upgrade -f 1`.
upgradeFromVersionNumber int
// This config is used to upgrade the database from a specific version manually.
// If you want to upgrade the database to version 1.1.0, you can use `answer upgrade -f v1.1.0`.
upgradeVersion string
)
func init() {
@ -39,7 +39,7 @@ func init() {
buildCmd.Flags().StringVarP(&buildOutput, "output", "o", "", "build output path")
upgradeCmd.Flags().IntVarP(&upgradeFromVersionNumber, "from", "f", 0, "upgrade from specific version number that in database version table")
upgradeCmd.Flags().StringVarP(&upgradeVersion, "from", "f", "", "upgrade from specific version, eg: -f v1.1.0")
for _, cmd := range []*cobra.Command{initCmd, checkCmd, runCmd, dumpCmd, upgradeCmd, buildCmd, pluginCmd} {
rootCmd.AddCommand(cmd)
@ -106,6 +106,7 @@ To run answer, use:
Short: "upgrade Answer version",
Long: `upgrade Answer version`,
Run: func(_ *cobra.Command, _ []string) {
log.SetLogger(log.NewStdLogger(os.Stdout))
cli.FormatAllPath(dataDirPath)
cli.InstallI18nBundle(true)
c, err := conf.ReadConfig(cli.GetConfigFilePath())
@ -113,7 +114,7 @@ To run answer, use:
fmt.Println("read config failed: ", err.Error())
return
}
if err = migrations.Migrate(c.Data.Database, c.Data.Cache, upgradeFromVersionNumber); err != nil {
if err = migrations.Migrate(c.Data.Database, c.Data.Cache, upgradeVersion); err != nil {
fmt.Println("migrate failed: ", err.Error())
return
}

View File

@ -9,21 +9,28 @@ import (
"xorm.io/xorm"
)
const minDBVersion = 0 // answer 1.0.0
const minDBVersion = 0
// Migration describes on migration from lower version to high version
type Migration interface {
Version() string
Description() string
Migrate(*xorm.Engine) error
ShouldCleanCache() bool
}
type migration struct {
version string
description string
migrate func(*xorm.Engine) error
shouldCleanCache bool
}
// Version returns the migration's version
func (m *migration) Version() string {
return m.version
}
// Description returns the migration's description
func (m *migration) Description() string {
return m.description
@ -40,8 +47,8 @@ func (m *migration) ShouldCleanCache() bool {
}
// NewMigration creates a new migration
func NewMigration(desc string, fn func(*xorm.Engine) error, shouldCleanCache bool) Migration {
return &migration{description: desc, migrate: fn, shouldCleanCache: shouldCleanCache}
func NewMigration(version, desc string, fn func(*xorm.Engine) error, shouldCleanCache bool) Migration {
return &migration{version: version, description: desc, migrate: fn, shouldCleanCache: shouldCleanCache}
}
// Use noopMigration when there is a migration that has been no-oped
@ -49,20 +56,20 @@ var noopMigration = func(_ *xorm.Engine) error { return nil }
var migrations = []Migration{
// 0->1
NewMigration("this is first version, no operation", noopMigration, false),
NewMigration("add user language", addUserLanguage, false),
NewMigration("add recommend and reserved tag fields", addTagRecommendedAndReserved, false),
NewMigration("add activity timeline", addActivityTimeline, false),
NewMigration("add user role", addRoleFeatures, false),
NewMigration("add theme and private mode", addThemeAndPrivateMode, true),
NewMigration("add new answer notification", addNewAnswerNotification, true),
NewMigration("add plugin", addPlugin, false),
NewMigration("add user pin hide features", addRolePinAndHideFeatures, true),
NewMigration("update accept answer rank", updateAcceptAnswerRank, true),
NewMigration("add login limitations", addLoginLimitations, true),
NewMigration("update user pin hide features", updateRolePinAndHideFeatures, true),
NewMigration("update question post time", updateQuestionPostTime, true),
NewMigration("add gravatar base url", addGravatarBaseURL, false),
NewMigration("v0.0.1", "this is first version, no operation", noopMigration, false),
NewMigration("v0.3.0", "add user language", addUserLanguage, false),
NewMigration("v0.4.1", "add recommend and reserved tag fields", addTagRecommendedAndReserved, false),
NewMigration("v0.5.0", "add activity timeline", addActivityTimeline, false),
NewMigration("v0.6.0", "add user role", addRoleFeatures, false),
NewMigration("v1.0.0", "add theme and private mode", addThemeAndPrivateMode, true),
NewMigration("v1.0.2", "add new answer notification", addNewAnswerNotification, true),
NewMigration("v1.0.5", "add plugin", addPlugin, false),
NewMigration("v1.0.7", "add user pin hide features", addRolePinAndHideFeatures, true),
NewMigration("v1.0.8", "update accept answer rank", updateAcceptAnswerRank, true),
NewMigration("v1.0.9", "add login limitations", addLoginLimitations, true),
NewMigration("v1.1.0-beta.1", "update user pin hide features", updateRolePinAndHideFeatures, true),
NewMigration("v1.1.0-beta.2", "update question post time", updateQuestionPostTime, true),
NewMigration("v1.1.0", "add gravatar base url", addGravatarBaseURL, false),
}
// GetCurrentDBVersion returns the current db version
@ -92,7 +99,7 @@ func ExpectedVersion() int64 {
}
// Migrate database to current version
func Migrate(dbConf *data.Database, cacheConf *data.CacheConf, upgradeFromVersionNumber int) error {
func Migrate(dbConf *data.Database, cacheConf *data.CacheConf, upgradeToSpecificVersion string) error {
cache, cacheCleanup, err := data.NewCache(cacheConf)
if err != nil {
fmt.Println("new check failed:", err.Error())
@ -108,16 +115,21 @@ func Migrate(dbConf *data.Database, cacheConf *data.CacheConf, upgradeFromVersio
return err
}
expectedVersion := ExpectedVersion()
if upgradeFromVersionNumber > 0 {
fmt.Printf("[migrate] user set upgrade from version number %d\n", upgradeFromVersionNumber)
currentDBVersion = int64(upgradeFromVersionNumber)
if len(upgradeToSpecificVersion) > 0 {
fmt.Printf("[migrate] user set upgrade to version: %s\n", upgradeToSpecificVersion)
for i, m := range migrations {
if m.Version() == upgradeToSpecificVersion {
currentDBVersion = int64(i)
break
}
}
}
for currentDBVersion < expectedVersion {
fmt.Printf("[migrate] current db version is %d, try to migrate version %d, latest version is %d\n",
currentDBVersion, currentDBVersion+1, expectedVersion)
migrationFunc := migrations[currentDBVersion]
fmt.Printf("[migrate] try to migrate db version %d, description: %s\n", currentDBVersion+1, migrationFunc.Description())
fmt.Printf("[migrate] try to migrate Answer version %s, description: %s\n", migrationFunc.Version(), migrationFunc.Description())
if err := migrationFunc.Migrate(engine); err != nil {
fmt.Printf("[migrate] migrate to db version %d failed: %s\n", currentDBVersion+1, err.Error())
return err