mirror of
https://github.com/harness/drone.git
synced 2025-05-10 01:49:56 +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/hooks"
|
||||
"github.com/harness/gitness/cli/operations/migrate"
|
||||
"github.com/harness/gitness/cli/operations/user"
|
||||
"github.com/harness/gitness/cli/operations/users"
|
||||
"github.com/harness/gitness/cli/server"
|
||||
@ -29,6 +30,8 @@ func Command() {
|
||||
args := getArguments()
|
||||
|
||||
app := kingpin.New(application, description)
|
||||
|
||||
migrate.Register(app)
|
||||
server.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
|
||||
setupLogger(config)
|
||||
SetupLogger(config)
|
||||
|
||||
// add logger to context
|
||||
log := log.Logger.With().Logger()
|
||||
@ -119,9 +119,8 @@ func (c *command) run(*kingpin.ParseContext) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// helper function configures the global logger from
|
||||
// the loaded configuration.
|
||||
func setupLogger(config *types.Config) {
|
||||
// SetupLogger configures the global logger from the loaded configuration.
|
||||
func SetupLogger(config *types.Config) {
|
||||
// configure the log level
|
||||
switch {
|
||||
case config.Trace:
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
@ -21,8 +22,59 @@ var postgres embed.FS
|
||||
//go:embed sqlite/*.sql
|
||||
var sqlite embed.FS
|
||||
|
||||
const tableName = "migrations"
|
||||
|
||||
// Migrate performs the database migration.
|
||||
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 {
|
||||
log.Trace().Str("version", version).Msg("migration started")
|
||||
return nil
|
||||
@ -38,7 +90,7 @@ func Migrate(ctx context.Context, db *sqlx.DB) error {
|
||||
Before: before,
|
||||
DB: db.DB,
|
||||
FS: sqlite,
|
||||
Table: "migrations",
|
||||
Table: tableName,
|
||||
}
|
||||
|
||||
switch db.DriverName() {
|
||||
@ -51,5 +103,5 @@ func Migrate(ctx context.Context, db *sqlx.DB) error {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -111,6 +121,6 @@ func pingDatabase(ctx context.Context, db *sqlx.DB) error {
|
||||
|
||||
// helper function to setup the database by performing automated
|
||||
// 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)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ var WireSet = wire.NewSet(
|
||||
|
||||
// ProvideDatabase provides a database connection.
|
||||
func ProvideDatabase(ctx context.Context, config *types.Config) (*sqlx.DB, error) {
|
||||
return Connect(
|
||||
return ConnectAndMigrate(
|
||||
ctx,
|
||||
config.Database.Driver,
|
||||
config.Database.Datasource,
|
||||
|
Loading…
Reference in New Issue
Block a user