mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-23 19:29:17 +03:00
[CLI] Prisma migration warning improvements (#682)
This commit is contained in:
parent
1a7a04b87c
commit
40041ff42b
@ -6,7 +6,7 @@ waspMigrate/.wasp/out/db/migrations/no-date-foo/migration.sql
|
||||
waspMigrate/.wasp/out/db/package.json
|
||||
waspMigrate/.wasp/out/db/schema.prisma
|
||||
waspMigrate/.wasp/out/db/schema.prisma.wasp-generate-checksum
|
||||
waspMigrate/.wasp/out/db/schema.prisma.wasp-migrate-checksum
|
||||
waspMigrate/.wasp/out/db/schema.prisma.wasp-last-db-concurrence-checksum
|
||||
waspMigrate/.wasp/out/installedFullStackNpmDependencies.json
|
||||
waspMigrate/.wasp/out/server/.npmrc
|
||||
waspMigrate/.wasp/out/server/README.md
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
module Wasp.Generator.DbGenerator
|
||||
( genDb,
|
||||
warnIfDbSchemaChangedSinceLastMigration,
|
||||
warnIfDbNeedsMigration,
|
||||
genPrismaClient,
|
||||
postWriteDbGeneratorActions,
|
||||
)
|
||||
@ -23,8 +23,8 @@ import Wasp.Generator.Common (ProjectRootDir)
|
||||
import Wasp.Generator.DbGenerator.Common
|
||||
( dbMigrationsDirInDbRootDir,
|
||||
dbRootDirInProjectRootDir,
|
||||
dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir,
|
||||
dbSchemaChecksumOnLastGenerateFileProjectRootDir,
|
||||
dbSchemaChecksumOnLastMigrateFileProjectRootDir,
|
||||
dbSchemaFileInDbTemplatesDir,
|
||||
dbSchemaFileInProjectRootDir,
|
||||
dbTemplatesDirInTemplatesDir,
|
||||
@ -83,42 +83,70 @@ genMigrationsDir spec =
|
||||
-- | This function operates on generated code, and thus assumes the file drafts were written to disk
|
||||
postWriteDbGeneratorActions :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO ([GeneratorWarning], [GeneratorError])
|
||||
postWriteDbGeneratorActions spec dstDir = do
|
||||
dbGeneratorWarnings <- maybeToList <$> warnIfDbSchemaChangedSinceLastMigration spec dstDir
|
||||
dbGeneratorWarnings <- maybeToList <$> warnIfDbNeedsMigration spec dstDir
|
||||
dbGeneratorErrors <- maybeToList <$> genPrismaClient spec dstDir
|
||||
return (dbGeneratorWarnings, dbGeneratorErrors)
|
||||
|
||||
-- | Checks if user needs to run `wasp db migrate-dev` due to changes they did in schema.prisma, and if so, returns a warning.
|
||||
-- | Checks if user needs to run `wasp db migrate-dev` due to changes in schema.prisma, and if so, returns a warning.
|
||||
-- When doing this, it looks at schema.prisma in the generated project.
|
||||
--
|
||||
-- This function makes following assumptions:
|
||||
-- - schema.prisma will exist in the generated project even if no Entities were defined.
|
||||
-- Due to how Prisma itself works, this assumption is currently fulfilled.
|
||||
-- - schema.prisma.wasp-checksum contains the checksum of the schema.prisma as it was during the last `wasp db migrate-dev`.
|
||||
-- - schema.prisma.wasp-last-db-concurrence-checksum contains the checksum of the schema.prisma as it was when we last ensured it matched the DB.
|
||||
--
|
||||
-- Given that, there are two cases in which we wish to warn the user to run `wasp db migrate-dev`:
|
||||
-- (1) If schema.prisma.wasp-checksum exists, but is not equal to checksum(schema.prisma), we know they made changes to schema.prisma and should migrate.
|
||||
-- (2) If schema.prisma.wasp-checksum does not exist, but the user has entities defined in schema.prisma (and thus, AppSpec).
|
||||
-- (1) If schema.prisma.wasp-last-db-concurrence-checksum exists, but is not equal to checksum(schema.prisma), we know there were changes to schema.prisma and they should migrate.
|
||||
-- (2) If schema.prisma.wasp-last-db-concurrence-checksum does not exist, but the user has entities defined in schema.prisma (and thus, AppSpec).
|
||||
-- This could imply they have never migrated locally, or that they have but are simply missing their generated project dir.
|
||||
-- Common scenarios for the second warning include:
|
||||
-- - After a fresh checkout, or after `wasp clean`; possible false positives in these cases, but for safety, it's still preferable to warn.
|
||||
-- - After a fresh checkout, or after `wasp clean`.
|
||||
-- - When they previously had no entities and just added their first.
|
||||
warnIfDbSchemaChangedSinceLastMigration :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO (Maybe GeneratorWarning)
|
||||
warnIfDbSchemaChangedSinceLastMigration spec projectRootDir = do
|
||||
-- In either of those scenarios, validate against DB itself to avoid redundant warnings.
|
||||
--
|
||||
-- NOTE: As one final optimization, if they do not have a schema.prisma.wasp-last-db-concurrence-checksum but the schema is
|
||||
-- in sync with the databse, we generate that file to avoid future checks.
|
||||
--
|
||||
-- NOTE: Because we currently only allow devs to migrate-dev, we only compare the schema to the DB since
|
||||
-- there are no likely scenarios where schema == db but schema != migrations dir. In the future, as we add more DB commands,
|
||||
-- we may wish to also compare the migrations dir to the DB as well.
|
||||
warnIfDbNeedsMigration :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO (Maybe GeneratorWarning)
|
||||
warnIfDbNeedsMigration spec projectRootDir = do
|
||||
dbSchemaChecksumFileExists <- doesFileExist dbSchemaChecksumFp
|
||||
|
||||
if dbSchemaChecksumFileExists
|
||||
then do
|
||||
dbSchemaFileChecksum <- hexToString <$> checksumFromFilePath dbSchemaFp
|
||||
dbChecksumFileContents <- readFile dbSchemaChecksumFp
|
||||
return $ warnIf (dbSchemaFileChecksum /= dbChecksumFileContents) "Your Prisma schema has changed, you should run `wasp db migrate-dev`."
|
||||
else return $ warnIf entitiesExist "Please run `wasp db migrate-dev` to ensure the local project is fully initialized."
|
||||
then warnIfSchemaDiffersFromChecksum dbSchemaFp dbSchemaChecksumFp
|
||||
else
|
||||
if entitiesExist
|
||||
then warnIfSchemaDiffersFromDb projectRootDir
|
||||
else return Nothing
|
||||
where
|
||||
dbSchemaFp = SP.fromAbsFile $ projectRootDir </> dbSchemaFileInProjectRootDir
|
||||
dbSchemaChecksumFp = SP.fromAbsFile $ projectRootDir </> dbSchemaChecksumOnLastMigrateFileProjectRootDir
|
||||
dbSchemaChecksumFp = SP.fromAbsFile $ projectRootDir </> dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir
|
||||
entitiesExist = not . null $ getEntities spec
|
||||
|
||||
warnIf :: Bool -> String -> Maybe GeneratorWarning
|
||||
warnIf b msg = if b then Just $ GeneratorNeedsMigrationWarning msg else Nothing
|
||||
warnIfSchemaDiffersFromChecksum :: FilePath -> FilePath -> IO (Maybe GeneratorWarning)
|
||||
warnIfSchemaDiffersFromChecksum dbSchemaFp dbSchemaChecksumFp = do
|
||||
dbSchemaFileChecksum <- hexToString <$> checksumFromFilePath dbSchemaFp
|
||||
dbChecksumFileContents <- readFile dbSchemaChecksumFp
|
||||
if dbSchemaFileChecksum /= dbChecksumFileContents
|
||||
then return . Just $ GeneratorNeedsMigrationWarning "Your Prisma schema has changed, please run `wasp db migrate-dev` when ready."
|
||||
else return Nothing
|
||||
|
||||
warnIfSchemaDiffersFromDb :: Path' Abs (Dir ProjectRootDir) -> IO (Maybe GeneratorWarning)
|
||||
warnIfSchemaDiffersFromDb projectRootDir = do
|
||||
-- NOTE: If we wanted to, we could also check that the migrations dir == db,
|
||||
-- but a schema check should handle all most likely cases.
|
||||
schemaMatchesDb <- DbOps.doesSchemaMatchDb projectRootDir
|
||||
case schemaMatchesDb of
|
||||
Just True -> do
|
||||
-- NOTE: Since we know schema == db, writing this file prevents future redundant Prisma checks.
|
||||
DbOps.writeDbSchemaChecksumToFile projectRootDir (SP.castFile dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir)
|
||||
return Nothing
|
||||
Just False -> return . Just $ GeneratorNeedsMigrationWarning "Your Prisma schema does not match your database, please run `wasp db migrate-dev`."
|
||||
-- NOTE: If there was an error, it could mean we could not connect to the SQLite db, since it does not exist.
|
||||
-- Or it could mean their DATABASE_URL is wrong, or database is down, or any other number of causes.
|
||||
-- In any case, migrating will either solve it (in the SQLite case), or allow Prisma to give them enough info to troubleshoot.
|
||||
Nothing -> return . Just $ GeneratorNeedsMigrationWarning "Wasp was unable to verify your database is up to date. Running `wasp db migrate-dev` may fix this and will provide more info."
|
||||
|
||||
genPrismaClient :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO (Maybe GeneratorError)
|
||||
genPrismaClient spec projectRootDir = do
|
||||
|
@ -1,7 +1,7 @@
|
||||
module Wasp.Generator.DbGenerator.Common
|
||||
( dbMigrationsDirInDbRootDir,
|
||||
dbRootDirInProjectRootDir,
|
||||
dbSchemaChecksumOnLastMigrateFileProjectRootDir,
|
||||
dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir,
|
||||
dbSchemaChecksumOnLastGenerateFileProjectRootDir,
|
||||
dbSchemaFileInDbTemplatesDir,
|
||||
dbSchemaFileInProjectRootDir,
|
||||
@ -19,10 +19,10 @@ data DbRootDir
|
||||
|
||||
data DbTemplatesDir
|
||||
|
||||
-- | This file represents the checksum of schema.prisma
|
||||
-- at the point at which `prisma db migrate-dev` was last run. It is used
|
||||
-- to help warn the user of instances when they may need to migrate.
|
||||
data DbSchemaChecksumOnLastMigrateFile
|
||||
-- | This file represents the checksum of schema.prisma at the point
|
||||
-- at which we last interacted with the DB to ensure they matched.
|
||||
-- It is used to help warn the user of instances when they may need to migrate.
|
||||
data DbSchemaChecksumOnLastDbConcurrenceFile
|
||||
|
||||
-- | This file represents the checksum of schema.prisma
|
||||
-- at the point at which `prisma generate` was last run. It is used
|
||||
@ -49,11 +49,11 @@ dbSchemaFileInProjectRootDir = dbRootDirInProjectRootDir </> dbSchemaFileInDbRoo
|
||||
dbMigrationsDirInDbRootDir :: Path' (Rel DbRootDir) (Dir DbMigrationsDir)
|
||||
dbMigrationsDirInDbRootDir = [reldir|migrations|]
|
||||
|
||||
dbSchemaChecksumOnLastMigrateFileInDbRootDir :: Path' (Rel DbRootDir) (File DbSchemaChecksumOnLastMigrateFile)
|
||||
dbSchemaChecksumOnLastMigrateFileInDbRootDir = [relfile|schema.prisma.wasp-migrate-checksum|]
|
||||
dbSchemaChecksumOnLastDbConcurrenceFileInDbRootDir :: Path' (Rel DbRootDir) (File DbSchemaChecksumOnLastDbConcurrenceFile)
|
||||
dbSchemaChecksumOnLastDbConcurrenceFileInDbRootDir = [relfile|schema.prisma.wasp-last-db-concurrence-checksum|]
|
||||
|
||||
dbSchemaChecksumOnLastMigrateFileProjectRootDir :: Path' (Rel ProjectRootDir) (File DbSchemaChecksumOnLastMigrateFile)
|
||||
dbSchemaChecksumOnLastMigrateFileProjectRootDir = dbRootDirInProjectRootDir </> dbSchemaChecksumOnLastMigrateFileInDbRootDir
|
||||
dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir :: Path' (Rel ProjectRootDir) (File DbSchemaChecksumOnLastDbConcurrenceFile)
|
||||
dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir = dbRootDirInProjectRootDir </> dbSchemaChecksumOnLastDbConcurrenceFileInDbRootDir
|
||||
|
||||
dbSchemaChecksumOnLastGenerateFileInDbRootDir :: Path' (Rel DbRootDir) (File DbSchemaChecksumOnLastGenerateFile)
|
||||
dbSchemaChecksumOnLastGenerateFileInDbRootDir = [relfile|schema.prisma.wasp-generate-checksum|]
|
||||
|
@ -1,5 +1,6 @@
|
||||
module Wasp.Generator.DbGenerator.Jobs
|
||||
( migrateDev,
|
||||
migrateDiff,
|
||||
generatePrismaClient,
|
||||
runStudio,
|
||||
)
|
||||
@ -49,6 +50,25 @@ migrateDev projectDir maybeMigrationName = do
|
||||
|
||||
runNodeCommandAsJob serverDir "script" scriptArgs J.Db
|
||||
|
||||
-- | Diffs the Prisma schema file against the db.
|
||||
-- Because of the --exit-code flag, it changes the exit code behavior
|
||||
-- to signal if the diff is empty or not (Empty: 0, Error: 1, Not empty: 2)
|
||||
migrateDiff :: Path' Abs (Dir ProjectRootDir) -> J.Job
|
||||
migrateDiff projectDir = do
|
||||
let serverDir = projectDir </> serverRootDirInProjectRootDir
|
||||
let schemaFileFp = SP.toFilePath $ projectDir </> dbSchemaFileInProjectRootDir
|
||||
let prismaMigrateDiffCmdArgs =
|
||||
[ "migrate",
|
||||
"diff",
|
||||
"--from-schema-datamodel",
|
||||
schemaFileFp,
|
||||
"--to-schema-datasource",
|
||||
schemaFileFp,
|
||||
"--exit-code"
|
||||
]
|
||||
|
||||
runNodeCommandAsJob serverDir (absPrismaExecutableFp projectDir) prismaMigrateDiffCmdArgs J.Db
|
||||
|
||||
-- | Runs `prisma studio` - Prisma's db inspector.
|
||||
runStudio :: Path' Abs (Dir ProjectRootDir) -> J.Job
|
||||
runStudio projectDir = do
|
||||
|
@ -1,6 +1,8 @@
|
||||
module Wasp.Generator.DbGenerator.Operations
|
||||
( migrateDevAndCopyToSource,
|
||||
generatePrismaClient,
|
||||
doesSchemaMatchDb,
|
||||
writeDbSchemaChecksumToFile,
|
||||
)
|
||||
where
|
||||
|
||||
@ -18,8 +20,8 @@ import Wasp.Generator.Common (ProjectRootDir)
|
||||
import Wasp.Generator.DbGenerator.Common
|
||||
( dbMigrationsDirInDbRootDir,
|
||||
dbRootDirInProjectRootDir,
|
||||
dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir,
|
||||
dbSchemaChecksumOnLastGenerateFileProjectRootDir,
|
||||
dbSchemaChecksumOnLastMigrateFileProjectRootDir,
|
||||
dbSchemaFileInProjectRootDir,
|
||||
)
|
||||
import qualified Wasp.Generator.DbGenerator.Jobs as DbJobs
|
||||
@ -57,7 +59,7 @@ finalizeMigration genProjectRootDirAbs dbMigrationsDirInWaspProjectDirAbs = do
|
||||
-- NOTE: We are updating a managed CopyDirFileDraft outside the normal generation process, so we must invalidate the checksum entry for it.
|
||||
Generator.WriteFileDrafts.removeFromChecksumFile genProjectRootDirAbs [Right $ SP.castDir dbMigrationsDirInProjectRootDir]
|
||||
res <- copyMigrationsBackToSource genProjectRootDirAbs dbMigrationsDirInWaspProjectDirAbs
|
||||
writeDbSchemaChecksumToFile genProjectRootDirAbs (SP.castFile dbSchemaChecksumOnLastMigrateFileProjectRootDir)
|
||||
writeDbSchemaChecksumToFile genProjectRootDirAbs (SP.castFile dbSchemaChecksumOnLastDbConcurrenceFileProjectRootDir)
|
||||
return res
|
||||
where
|
||||
dbMigrationsDirInProjectRootDir = dbRootDirInProjectRootDir SP.</> dbMigrationsDirInDbRootDir
|
||||
@ -96,3 +98,21 @@ generatePrismaClient genProjectRootDirAbs = do
|
||||
writeDbSchemaChecksumToFile genProjectRootDirAbs (SP.castFile dbSchemaChecksumOnLastGenerateFileProjectRootDir)
|
||||
return $ Right ()
|
||||
ExitFailure code -> return $ Left $ "Prisma client generation failed with exit code: " ++ show code
|
||||
|
||||
-- | Checks `prisma migrate diff` exit code to determine if schema.prisma is
|
||||
-- different than the DB. Returns Nothing on error as we do not know the current state.
|
||||
-- Returns Just True if schema.prisma is the same as DB, Just False if it is different, and
|
||||
-- Nothing if the check itself failed (exe: if a connection to the DB could not be established).
|
||||
-- NOTE: Here we only compare the schema to the DB, and not the migrations dir.
|
||||
doesSchemaMatchDb :: Path' Abs (Dir ProjectRootDir) -> IO (Maybe Bool)
|
||||
doesSchemaMatchDb genProjectRootDirAbs = do
|
||||
chan <- newChan
|
||||
(_, dbExitCode) <-
|
||||
concurrently
|
||||
(readJobMessagesAndPrintThemPrefixed chan)
|
||||
(DbJobs.migrateDiff genProjectRootDirAbs chan)
|
||||
-- Schema in sync: 0, Error: 1, Schema differs: 2
|
||||
case dbExitCode of
|
||||
ExitSuccess -> return $ Just True
|
||||
ExitFailure 2 -> return $ Just False
|
||||
ExitFailure _ -> return Nothing
|
||||
|
Loading…
Reference in New Issue
Block a user