diff --git a/Guide/database-migrations.markdown b/Guide/database-migrations.markdown index fa65e2b8a..692ef2f80 100644 --- a/Guide/database-migrations.markdown +++ b/Guide/database-migrations.markdown @@ -73,6 +73,20 @@ migrate A good value for `MINIMUM_REVISION` is typically the unix timestamp of the time when the database was initially created. + +### IHP MIGRATIONS DIR + +In production when running the migrations binary it is sometimes convenient to have all your Migrations in a non-standard place: +e.g. if you need to push migrations onto production server without rebuilding the application. There is an Environment variable +`IHP_MIGRATION_DIR` to accomplish this. + +``` +IHP_MIGRATION_DIR=path/to/my/migration/dir +``` + +This can be set in the environment attribute set of your IHP app flake. + + ## Common Issues ### ALTER TYPE ... ADD cannot run inside a transaction block diff --git a/IHP/SchemaMigration.hs b/IHP/SchemaMigration.hs index 942ed67ae..48f5ffab3 100644 --- a/IHP/SchemaMigration.hs +++ b/IHP/SchemaMigration.hs @@ -12,6 +12,7 @@ import qualified Data.Text.IO as Text import IHP.ModelSupport hiding (withTransaction) import qualified Data.Char as Char import IHP.Log.Types +import IHP.EnvVar data Migration = Migration { revision :: Int @@ -37,7 +38,9 @@ migrate options = do -- All queries are executed inside a database transaction to make sure that it can be restored when something goes wrong. runMigration :: (?modelContext :: ModelContext) => Migration -> IO () runMigration migration@Migration { revision, migrationFile } = do - migrationSql <- Text.readFile (cs $ migrationPath migration) + -- | User can specify migrations directory as environment variable (defaults to /Application/Migrations/...) + migrationFilePath <- migrationPath migration + migrationSql <- Text.readFile (cs migrationFilePath) let fullSql = [trimming| BEGIN; @@ -123,5 +126,7 @@ pathToMigration fileName = case revision of |> fmap textToInt |> join -migrationPath :: Migration -> Text -migrationPath Migration { migrationFile } = "Application/Migration/" <> migrationFile +migrationPath :: Migration -> IO Text +migrationPath Migration { migrationFile } = do + migrationDir <- envOrDefault "IHP_MIGRATION_DIR" "Application/Migration/" + pure (migrationDir <> migrationFile) diff --git a/NixSupport/nixosModules/options.nix b/NixSupport/nixosModules/options.nix index 952d571f6..310bc1d8e 100644 --- a/NixSupport/nixosModules/options.nix +++ b/NixSupport/nixosModules/options.nix @@ -13,63 +13,70 @@ with lib; type = types.str; default = "https://${config.services.ihp.domain}"; }; - + migrations = mkOption { type = types.path; }; - + schema = mkOption { type = types.path; }; - + fixtures = mkOption { type = types.path; }; - + httpsEnabled = mkOption { type = types.bool; default = true; }; - + databaseName = mkOption { type = types.str; default = "app"; }; - + databaseUser = mkOption { type = types.str; default = "ihp"; }; - + databaseUrl = mkOption { type = types.str; }; - + # https://ihp.digitallyinduced.com/Guide/database-migrations.html#skipping-old-migrations minimumRevision = mkOption { type = types.int; default = 0; }; - + + # https://ihp.digitallyinduced.com/Guide/database-migrations.html#ihp-migrations-dir + ihpMigrationDir = mkOption { + type = types.str; + default = "Application/Migration/"; + }; + + ihpEnv = mkOption { type = types.str; default = "Production"; }; - + appPort = mkOption { type = types.int; default = 8000; }; - + requestLoggerIPAddrSource = mkOption { type = types.str; default = "FromHeader"; }; - + sessionSecret = mkOption { type = types.str; }; - + additionalEnvVars = mkOption { type = types.attrs; default = {}; @@ -79,7 +86,7 @@ with lib; type = types.package; default = if config.services.ihp.optimized then self.packages."${pkgs.system}".optimized-prod-server else self.packages."${pkgs.system}".default; }; - + optimized = mkOption { type = types.bool; default = false; @@ -91,4 +98,3 @@ with lib; }; }; } - diff --git a/NixSupport/nixosModules/services/migrate.nix b/NixSupport/nixosModules/services/migrate.nix index a5f6d9ad5..ecd5d7cbe 100644 --- a/NixSupport/nixosModules/services/migrate.nix +++ b/NixSupport/nixosModules/services/migrate.nix @@ -22,6 +22,7 @@ in environment = { DATABASE_URL = cfg.databaseUrl; MINIMUM_REVISION = "${toString cfg.minimumRevision}"; + IHP_MIGRATION_DIR = cfg.ihpMigrationDir; }; }; -} \ No newline at end of file +} diff --git a/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs b/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs index 41f1d0d81..940c903ed 100644 --- a/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs +++ b/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs @@ -61,7 +61,7 @@ instance Controller MigrationsController where let errorMessage = case fromException exception of Just (exception :: EnhancedSqlError) -> cs exception.sqlError.sqlErrorMsg Nothing -> tshow exception - + setErrorMessage errorMessage redirectTo MigrationsAction Right _ -> do @@ -79,14 +79,14 @@ instance Controller MigrationsController where action UpdateMigrationAction { migrationId } = do migration <- findMigrationByRevision migrationId let sqlStatements = param "sqlStatements" - - Text.writeFile (cs $ SchemaMigration.migrationPath migration) sqlStatements + migrationFilePath <- SchemaMigration.migrationPath migration + Text.writeFile (cs migrationFilePath) sqlStatements redirectTo MigrationsAction action DeleteMigrationAction { migrationId } = do migration <- findMigrationByRevision migrationId - let path = cs $ SchemaMigration.migrationPath migration + path <- cs <$> SchemaMigration.migrationPath migration Directory.removeFile path @@ -101,7 +101,7 @@ instance Controller MigrationsController where let errorMessage = case fromException exception of Just (exception :: EnhancedSqlError) -> cs exception.sqlError.sqlErrorMsg Nothing -> tshow exception - + setErrorMessage errorMessage redirectTo MigrationsAction Right _ -> do @@ -109,7 +109,9 @@ instance Controller MigrationsController where redirectTo MigrationsAction readSqlStatements :: SchemaMigration.Migration -> IO Text -readSqlStatements migration = Text.readFile (cs $ SchemaMigration.migrationPath migration) +readSqlStatements migration = do + migrationFilePath <- (SchemaMigration.migrationPath migration) + pure Text.readFile (migrationFilePath) findRecentMigrations :: IO [SchemaMigration.Migration] findRecentMigrations = take 20 . reverse <$> SchemaMigration.findAllMigrations