mirror of
https://github.com/harness/drone.git
synced 2025-05-11 14:40:05 +08:00
CLI migrate command (#304)
This commit is contained in:
parent
eabf098f96
commit
efcd415603
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/harness/gitness/cli/operations/account"
|
"github.com/harness/gitness/cli/operations/account"
|
||||||
"github.com/harness/gitness/cli/operations/hooks"
|
"github.com/harness/gitness/cli/operations/hooks"
|
||||||
|
"github.com/harness/gitness/cli/operations/migrate"
|
||||||
"github.com/harness/gitness/cli/operations/user"
|
"github.com/harness/gitness/cli/operations/user"
|
||||||
"github.com/harness/gitness/cli/operations/users"
|
"github.com/harness/gitness/cli/operations/users"
|
||||||
"github.com/harness/gitness/cli/server"
|
"github.com/harness/gitness/cli/server"
|
||||||
@ -29,6 +30,8 @@ func Command() {
|
|||||||
args := getArguments()
|
args := getArguments()
|
||||||
|
|
||||||
app := kingpin.New(application, description)
|
app := kingpin.New(application, description)
|
||||||
|
|
||||||
|
migrate.Register(app)
|
||||||
server.Register(app)
|
server.Register(app)
|
||||||
|
|
||||||
user.Register(app)
|
user.Register(app)
|
||||||
|
49
cli/operations/migrate/current.go
Normal file
49
cli/operations/migrate/current.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package migrate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/store/database/migrate"
|
||||||
|
|
||||||
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commandCurrent struct {
|
||||||
|
envfile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *commandCurrent) run(*kingpin.ParseContext) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
db, err := getDB(ctx, c.envfile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
version, err := migrate.Current(ctx, db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(version)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerCurrent(app *kingpin.CmdClause) {
|
||||||
|
c := &commandCurrent{}
|
||||||
|
|
||||||
|
cmd := app.Command("current", "display the current version of the database").
|
||||||
|
Action(c.run)
|
||||||
|
|
||||||
|
cmd.Arg("envfile", "load the environment variable file").
|
||||||
|
Default("").
|
||||||
|
StringVar(&c.envfile)
|
||||||
|
}
|
40
cli/operations/migrate/migrate.go
Normal file
40
cli/operations/migrate/migrate.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package migrate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/cli/server"
|
||||||
|
"github.com/harness/gitness/internal/store/database"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDB(ctx context.Context, envfile string) (*sqlx.DB, error) {
|
||||||
|
_ = godotenv.Load(envfile)
|
||||||
|
|
||||||
|
config, err := server.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load configuration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := database.Connect(ctx, config.Database.Driver, config.Database.Datasource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create database handle: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the server command.
|
||||||
|
func Register(app *kingpin.Application) {
|
||||||
|
cmd := app.Command("migrate", "database migration tool")
|
||||||
|
registerCurrent(cmd)
|
||||||
|
registerTo(cmd)
|
||||||
|
}
|
46
cli/operations/migrate/to.go
Normal file
46
cli/operations/migrate/to.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package migrate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/store/database/migrate"
|
||||||
|
|
||||||
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commandTo struct {
|
||||||
|
envfile string
|
||||||
|
version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *commandTo) run(k *kingpin.ParseContext) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
db, err := getDB(ctx, c.envfile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrate.To(ctx, db, c.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerTo(app *kingpin.CmdClause) {
|
||||||
|
c := &commandTo{}
|
||||||
|
|
||||||
|
cmd := app.Command("to", "migrates the database to the provided version").
|
||||||
|
Action(c.run)
|
||||||
|
|
||||||
|
cmd.Arg("version", "database version to migrate to").
|
||||||
|
Required().
|
||||||
|
StringVar(&c.version)
|
||||||
|
|
||||||
|
cmd.Arg("envfile", "load the environment variable file").
|
||||||
|
Default("").
|
||||||
|
StringVar(&c.envfile)
|
||||||
|
}
|
@ -50,7 +50,7 @@ func (c *command) run(*kingpin.ParseContext) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configure the log level
|
// configure the log level
|
||||||
setupLogger(config)
|
SetupLogger(config)
|
||||||
|
|
||||||
// add logger to context
|
// add logger to context
|
||||||
log := log.Logger.With().Logger()
|
log := log.Logger.With().Logger()
|
||||||
@ -119,9 +119,8 @@ func (c *command) run(*kingpin.ParseContext) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper function configures the global logger from
|
// SetupLogger configures the global logger from the loaded configuration.
|
||||||
// the loaded configuration.
|
func SetupLogger(config *types.Config) {
|
||||||
func setupLogger(config *types.Config) {
|
|
||||||
// configure the log level
|
// configure the log level
|
||||||
switch {
|
switch {
|
||||||
case config.Trace:
|
case config.Trace:
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"embed"
|
"embed"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
@ -21,8 +22,59 @@ var postgres embed.FS
|
|||||||
//go:embed sqlite/*.sql
|
//go:embed sqlite/*.sql
|
||||||
var sqlite embed.FS
|
var sqlite embed.FS
|
||||||
|
|
||||||
|
const tableName = "migrations"
|
||||||
|
|
||||||
// Migrate performs the database migration.
|
// Migrate performs the database migration.
|
||||||
func Migrate(ctx context.Context, db *sqlx.DB) error {
|
func Migrate(ctx context.Context, db *sqlx.DB) error {
|
||||||
|
opts := getMigrator(db)
|
||||||
|
return migrate.New(opts).MigrateUp(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// To performs the database migration to the specific version.
|
||||||
|
func To(ctx context.Context, db *sqlx.DB, version string) error {
|
||||||
|
opts := getMigrator(db)
|
||||||
|
return migrate.New(opts).MigrateTo(ctx, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the current version ID (the latest migration applied) of the database.
|
||||||
|
func Current(ctx context.Context, db *sqlx.DB) (string, error) {
|
||||||
|
var (
|
||||||
|
query string
|
||||||
|
migrationTableCount int
|
||||||
|
)
|
||||||
|
|
||||||
|
switch db.DriverName() {
|
||||||
|
case "sqlite3":
|
||||||
|
query = `
|
||||||
|
SELECT count(*)
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE name = ? and type = 'table'`
|
||||||
|
default:
|
||||||
|
query = `
|
||||||
|
SELECT count(*)
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_name = ? and table_schema = 'public'`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.QueryRowContext(ctx, query, tableName).Scan(&migrationTableCount); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to check migration table existence: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if migrationTableCount == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var version string
|
||||||
|
|
||||||
|
query = "select version from " + tableName + " limit 1"
|
||||||
|
if err := db.QueryRowContext(ctx, query).Scan(&version); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read current DB version from migration table: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMigrator(db *sqlx.DB) migrate.Options {
|
||||||
before := func(_ context.Context, _ *sql.Tx, version string) error {
|
before := func(_ context.Context, _ *sql.Tx, version string) error {
|
||||||
log.Trace().Str("version", version).Msg("migration started")
|
log.Trace().Str("version", version).Msg("migration started")
|
||||||
return nil
|
return nil
|
||||||
@ -38,7 +90,7 @@ func Migrate(ctx context.Context, db *sqlx.DB) error {
|
|||||||
Before: before,
|
Before: before,
|
||||||
DB: db.DB,
|
DB: db.DB,
|
||||||
FS: sqlite,
|
FS: sqlite,
|
||||||
Table: "migrations",
|
Table: tableName,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch db.DriverName() {
|
switch db.DriverName() {
|
||||||
@ -51,5 +103,5 @@ func Migrate(ctx context.Context, db *sqlx.DB) error {
|
|||||||
opts.FS = folder
|
opts.FS = folder
|
||||||
}
|
}
|
||||||
|
|
||||||
return migrate.New(opts).MigrateUp(ctx)
|
return opts
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,17 @@ func Connect(ctx context.Context, driver string, datasource string) (*sqlx.DB, e
|
|||||||
return nil, fmt.Errorf("failed to ping the db: %w", err)
|
return nil, fmt.Errorf("failed to ping the db: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = setupDatabase(ctx, dbx); err != nil {
|
return dbx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectAndMigrate creates the database handle and migrates the database.
|
||||||
|
func ConnectAndMigrate(ctx context.Context, driver string, datasource string) (*sqlx.DB, error) {
|
||||||
|
dbx, err := Connect(ctx, driver, datasource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = migrateDatabase(ctx, dbx); err != nil {
|
||||||
return nil, fmt.Errorf("failed to setup the db: %w", err)
|
return nil, fmt.Errorf("failed to setup the db: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +121,6 @@ func pingDatabase(ctx context.Context, db *sqlx.DB) error {
|
|||||||
|
|
||||||
// helper function to setup the database by performing automated
|
// helper function to setup the database by performing automated
|
||||||
// database migration steps.
|
// database migration steps.
|
||||||
func setupDatabase(ctx context.Context, db *sqlx.DB) error {
|
func migrateDatabase(ctx context.Context, db *sqlx.DB) error {
|
||||||
return migrate.Migrate(ctx, db)
|
return migrate.Migrate(ctx, db)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ var WireSet = wire.NewSet(
|
|||||||
|
|
||||||
// ProvideDatabase provides a database connection.
|
// ProvideDatabase provides a database connection.
|
||||||
func ProvideDatabase(ctx context.Context, config *types.Config) (*sqlx.DB, error) {
|
func ProvideDatabase(ctx context.Context, config *types.Config) (*sqlx.DB, error) {
|
||||||
return Connect(
|
return ConnectAndMigrate(
|
||||||
ctx,
|
ctx,
|
||||||
config.Database.Driver,
|
config.Database.Driver,
|
||||||
config.Database.Datasource,
|
config.Database.Datasource,
|
||||||
|
Loading…
Reference in New Issue
Block a user