diff --git a/frontend/cli/cmd_new_migration.go b/frontend/cli/cmd_new_migration.go new file mode 100644 index 000000000..214a92de7 --- /dev/null +++ b/frontend/cli/cmd_new_migration.go @@ -0,0 +1,78 @@ +package main + +import ( + "context" + "fmt" + "net/url" + "os" + "path/filepath" + "strings" + + "github.com/amacneil/dbmate/v2/pkg/dbmate" + + "github.com/block/ftl/internal/log" + "github.com/block/ftl/internal/moduleconfig" + "github.com/block/ftl/internal/watch" +) + +type newSQLMigrationCmd struct { + Datasource string `arg:"" help:"The qualified name of the datasource in the form module.datasource to create the migration for. If the module is not specified FTL will attempt to infer it from the current working directory."` + Name string `arg:"" help:"Name of the migration, this will be included in the migration file name."` +} + +func (i newSQLMigrationCmd) Run(ctx context.Context) error { + + dir, err := os.Getwd() + if err != nil { + return fmt.Errorf("could not get current working directory: %w", err) + } + modules, err := watch.DiscoverModules(ctx, []string{dir}) + var module *moduleconfig.UnvalidatedModuleConfig + parts := strings.Split(i.Datasource, ".") + var dsName string + if len(parts) == 1 && len(modules) == 1 { + module = &modules[0] + dsName = parts[0] + } else if len(parts) == 2 { + for i := range modules { + if modules[i].Module == parts[0] { + module = &modules[i] + break + } + } + dsName = parts[1] + } else { + return fmt.Errorf("invalid datasource %q, must be in the form module.datasource", i.Datasource) + } + if module == nil { + return fmt.Errorf("could not find module %q", parts[0]) + } + + if err != nil { + return fmt.Errorf("could not discover modules: %w", err) + } + migrationDir := module.SQLMigrationDirectory + if migrationDir == "" { + language := module.Language + plugin, err := createLanguagePlugin(ctx, language) + if err != nil { + return fmt.Errorf("could not create plugin for language %q: %w", language, err) + } + defaults, err := plugin.ModuleConfigDefaults(ctx, module.Dir) + if err != nil { + return fmt.Errorf("could not get module config defaults for language %q: %w", language, err) + } + migrationDir = defaults.SQLMigrationDir + } + migrationDir = filepath.Join(module.Dir, migrationDir, dsName) + + logger := log.FromContext(ctx) + logger.Debugf("Creating DBMate SQL migration %s in module %q in %s", i.Name, module.Module, migrationDir) + db := dbmate.New(&url.URL{}) + db.MigrationsDir = []string{migrationDir} + err = db.NewMigration(i.Name) + if err != nil { + return fmt.Errorf("failed to create migration: %w", err) + } + return nil +} diff --git a/frontend/cli/main.go b/frontend/cli/main.go index 5cce3dd1f..be5fb81ed 100644 --- a/frontend/cli/main.go +++ b/frontend/cli/main.go @@ -46,25 +46,26 @@ type InteractiveCLI struct { AdminEndpoint *url.URL `help:"Admin endpoint." env:"FTL_ADMIN_ENDPOINT" default:"http://127.0.0.1:8896"` Trace string `help:"File to write golang runtime/trace output to." hidden:""` - Ping pingCmd `cmd:"" help:"Ping the FTL cluster."` - Status statusCmd `cmd:"" help:"Show FTL status."` - Init initCmd `cmd:"" help:"Initialize a new FTL project."` - Profile profileCmd `cmd:"" help:"Manage profiles."` - New newCmd `cmd:"" help:"Create a new FTL module. See language specific flags with 'ftl new --help'."` - PS psCmd `cmd:"" help:"List deployments."` - Call callCmd `cmd:"" help:"Call an FTL function."` - Bench benchCmd `cmd:"" help:"Benchmark an FTL function."` - Replay replayCmd `cmd:"" help:"Call an FTL function with the same request body as the last invocation."` - Update updateCmd `cmd:"" help:"Update a deployment."` - Kill killCmd `cmd:"" help:"Kill a deployment."` - Schema schemaCmd `cmd:"" help:"FTL schema commands."` - Build buildCmd `cmd:"" help:"Build all modules found in the specified directories."` - Deploy deployCmd `cmd:"" help:"Build and deploy all modules found in the specified directories."` - Download downloadCmd `cmd:"" help:"Download a deployment."` - Secret secretCmd `cmd:"" help:"Manage secrets."` - Config configCmd `cmd:"" help:"Manage configuration."` - Pubsub pubsubCmd `cmd:"" help:"Manage pub/sub."` - Release releaseCmd `cmd:"" help:"Manage releases."` + Ping pingCmd `cmd:"" help:"Ping the FTL cluster."` + Status statusCmd `cmd:"" help:"Show FTL status."` + Init initCmd `cmd:"" help:"Initialize a new FTL project."` + Profile profileCmd `cmd:"" help:"Manage profiles."` + New newCmd `cmd:"" help:"Create a new FTL module. See language specific flags with 'ftl new --help'."` + PS psCmd `cmd:"" help:"List deployments."` + Call callCmd `cmd:"" help:"Call an FTL function."` + Bench benchCmd `cmd:"" help:"Benchmark an FTL function."` + Replay replayCmd `cmd:"" help:"Call an FTL function with the same request body as the last invocation."` + Update updateCmd `cmd:"" help:"Update a deployment."` + Kill killCmd `cmd:"" help:"Kill a deployment."` + Schema schemaCmd `cmd:"" help:"FTL schema commands."` + Build buildCmd `cmd:"" help:"Build all modules found in the specified directories."` + Deploy deployCmd `cmd:"" help:"Build and deploy all modules found in the specified directories."` + Download downloadCmd `cmd:"" help:"Download a deployment."` + Secret secretCmd `cmd:"" help:"Manage secrets."` + Config configCmd `cmd:"" help:"Manage configuration."` + Pubsub pubsubCmd `cmd:"" help:"Manage pub/sub."` + Release releaseCmd `cmd:"" help:"Manage releases."` + NewSQLMigration newSQLMigrationCmd `cmd:"" help:"Create a new SQL Database migration."` } type CLI struct { diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/StaticConfigSource.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/StaticConfigSource.java index c2a3b3532..e53698c42 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/StaticConfigSource.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/StaticConfigSource.java @@ -33,6 +33,6 @@ public String getValue(String propertyName) { @Override public String getName() { - return "Quarkus Static Config Source"; + return "FTL Static Config Source"; } }