diff --git a/cmd/init.go b/cmd/init.go index 2f7c202f..06a1c19c 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -84,13 +84,14 @@ func initFlags() { // Register the commandline flags. f.StringSlice("config", []string{"config.toml"}, "path to one or more config files (will be merged in order)") - f.Bool("install", false, "run first time installation") + f.Bool("install", false, "setup database (first time)") + f.Bool("idempotent", false, "make --install run only if the databse isn't already setup") f.Bool("upgrade", false, "upgrade database to the current version") f.Bool("version", false, "current version of the build") f.Bool("new-config", false, "generate sample config file") f.String("static-dir", "", "(optional) path to directory with static files") f.String("i18n-dir", "", "(optional) path to directory with i18n language files") - f.Bool("yes", false, "assume 'yes' to prompts, eg: during --install") + f.Bool("yes", false, "assume 'yes' to prompts during --install/upgrade") if err := f.Parse(os.Args[1:]); err != nil { lo.Fatalf("error loading flags: %v", err) } diff --git a/cmd/install.go b/cmd/install.go index 2699aad5..0596b0d1 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -17,13 +17,17 @@ import ( // install runs the first time setup of creating and // migrating the database and creating the super user. -func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) { +func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt, idempotent bool) { qMap, _ := initQueries(queryFilePath, db, fs, false) fmt.Println("") - fmt.Println("** first time installation **") - fmt.Printf("** IMPORTANT: This will wipe existing listmonk tables and types in the DB '%s' **", - ko.String("db.database")) + if !idempotent { + fmt.Println("** first time installation **") + fmt.Printf("** IMPORTANT: This will wipe existing listmonk tables and types in the DB '%s' **", + ko.String("db.database")) + } else { + fmt.Println("** first time (idempotent) installation **") + } fmt.Println("") if prompt { @@ -38,10 +42,22 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) { } } + // If idempotence is on, check if the DB is already setup. + if idempotent { + if _, err := db.Exec("SELECT count(*) FROM settings"); err != nil { + // If "settings" doesn't exist, assume it's a fresh install. + if pqErr, ok := err.(*pq.Error); ok && pqErr.Code != "42P01" { + lo.Fatalf("error checking existing DB schema: %v", err) + } + } else { + lo.Println("skipping install as database appears to be already setup") + os.Exit(0) + } + } + // Migrate the tables. - err := installSchema(lastVer, db, fs) - if err != nil { - lo.Fatalf("Error migrating DB schema: %v", err) + if err := installSchema(lastVer, db, fs); err != nil { + lo.Fatalf("error migrating DB schema: %v", err) } // Load the queries. @@ -62,7 +78,7 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) { models.ListOptinSingle, pq.StringArray{"test"}, ); err != nil { - lo.Fatalf("Error creating list: %v", err) + lo.Fatalf("error creating list: %v", err) } if err := q.CreateList.Get(&optinList, uuid.Must(uuid.NewV4()), @@ -71,7 +87,7 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) { models.ListOptinDouble, pq.StringArray{"test"}, ); err != nil { - lo.Fatalf("Error creating list: %v", err) + lo.Fatalf("error creating list: %v", err) } // Sample subscriber. @@ -93,7 +109,7 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) { pq.Int64Array{int64(optinList)}, models.SubscriptionStatusUnconfirmed, true); err != nil { - lo.Fatalf("Error creating subscriber: %v", err) + lo.Fatalf("error creating subscriber: %v", err) } // Default template. @@ -132,8 +148,8 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) { lo.Fatalf("error creating sample campaign: %v", err) } - lo.Printf("Setup complete") - lo.Printf(`Run the program and access the dashboard at %s`, ko.MustString("app.address")) + lo.Printf("setup complete") + lo.Printf(`run the program and access the dashboard at %s`, ko.MustString("app.address")) } // installSchema executes the SQL schema and creates the necessary tables and types. diff --git a/cmd/main.go b/cmd/main.go index 37e5dd08..8c727405 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -122,7 +122,7 @@ func init() { // as the installer needs to work on an empty DB. if ko.Bool("install") { // Save the version of the last listed migration. - install(migList[len(migList)-1].version, db, fs, !ko.Bool("yes")) + install(migList[len(migList)-1].version, db, fs, !ko.Bool("yes"), ko.Bool("idempotent")) os.Exit(0) }