Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions waspc/cli/src/Wasp/Cli/Command/BuildStart.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Wasp.Cli.Command (Command, CommandError (CommandError), require)
import Wasp.Cli.Command.BuildStart.ArgumentsParser (buildStartArgsParser)
import Wasp.Cli.Command.BuildStart.Client (buildClient, startClient)
import Wasp.Cli.Command.BuildStart.Config (BuildStartConfig, makeBuildStartConfig)
import qualified Wasp.Cli.Command.BuildStart.Job as BuildStartJob
import Wasp.Cli.Command.BuildStart.Server (buildServer, startServer)
import Wasp.Cli.Command.Call (Arguments)
import Wasp.Cli.Command.Compile (analyze)
Expand All @@ -20,9 +21,7 @@ import Wasp.Cli.Command.Require.InWaspProject (InWaspProject (InWaspProject))
import Wasp.Cli.Command.Require.ValidNodeAndNpm (ValidNodeAndNpm (ValidNodeAndNpm))
import Wasp.Cli.Command.Require.WaspSpecAvailable (WaspSpecAvailable (WaspSpecAvailable))
import Wasp.Cli.Util.Parser (withArguments)
import Wasp.Job.Except (ExceptJob)
import qualified Wasp.Job.Except as ExceptJob
import Wasp.Job.IO (readJobMessagesAndPrintThemPrefixed)
import qualified Wasp.Job.Output as Output
import qualified Wasp.Message as Msg

buildStart :: Arguments -> Command ()
Expand Down Expand Up @@ -59,20 +58,20 @@ buildAndStartServerAndClient config = do

cliSendMessageC $ Msg.Start "Starting client and server..."
runAndPrintJob "Starting Wasp app failed." $
ExceptJob.race_
BuildStartJob.race
(startClient config)
(startServer config)
where
runAndPrintJob :: String -> ExceptJob -> Command ()
runAndPrintJob errorMessage job = do
liftIO (runAndPrintJobIO job)
runAndPrintJob :: String -> BuildStartJob.JobExecution -> Command ()
runAndPrintJob errorMessage executeJob = do
liftIO (runAndPrintJobIO executeJob)
>>= either (throwError . CommandError errorMessage) return

runAndPrintJobIO :: ExceptJob -> IO (Either String ())
runAndPrintJobIO job = do
runAndPrintJobIO :: BuildStartJob.JobExecution -> IO (Either String ())
runAndPrintJobIO executeJob = do
chan <- newChan
(result, _) <-
concurrently
(runExceptT $ job chan)
(readJobMessagesAndPrintThemPrefixed chan)
(runExceptT $ executeJob chan)
(Output.printEventsPrefixedUntilExit chan)
return result
47 changes: 23 additions & 24 deletions waspc/cli/src/Wasp/Cli/Command/BuildStart/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,38 @@ module Wasp.Cli.Command.BuildStart.Client
)
where

import Data.Function ((&))
import Wasp.Cli.Command.BuildStart.Config (BuildStartConfig)
import qualified Wasp.Cli.Command.BuildStart.Config as Config
import qualified Wasp.Job as J
import Wasp.Job.Except (ExceptJob, toExceptJob)
import Wasp.Job.Process (runNodeCommandAsJob, runNodeCommandAsJobWithExtraEnv)
import qualified Wasp.Cli.Command.BuildStart.Job as BuildStartJob
import qualified Wasp.Job as Job
import qualified Wasp.Job.Node as Node

buildClient :: BuildStartConfig -> ExceptJob
buildClient :: BuildStartConfig -> BuildStartJob.JobExecution
buildClient config =
runNodeCommandAsJobWithExtraEnv
envVars
projectDir
"npx"
["vite", "build"]
J.WebApp
& toExceptJob (("Building the client failed with exit code: " <>) . show)
BuildStartJob.run (("Building the client failed with exit code: " <>) . show) $
Node.makeJobWithExtraEnv
envVars
projectDir
"npx"
["vite", "build"]
Job.WebApp
where
envVars = Config.clientEnvVars config
projectDir = Config.projectDir config

startClient :: BuildStartConfig -> ExceptJob
startClient :: BuildStartConfig -> BuildStartJob.JobExecution
startClient config =
runNodeCommandAsJob
projectDir
"npx"
[ "vite",
"preview", -- `preview` launches a static file server for the built client.
"--port",
port,
"--strictPort" -- This will make it fail if the port is already in use.
]
J.WebApp
& toExceptJob (("Serving the client failed with exit code: " <>) . show)
BuildStartJob.run (("Serving the client failed with exit code: " <>) . show) $
Node.makeJob
projectDir
"npx"
[ "vite",
"preview", -- `preview` launches a static file server for the built client.
"--port",
port,
"--strictPort" -- This will make it fail if the port is already in use.
]
Job.WebApp
where
port = show $ Config.clientPort config

Expand Down
32 changes: 32 additions & 0 deletions waspc/cli/src/Wasp/Cli/Command/BuildStart/Job.hs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is a bit meh, feels like there's a better API from the Job side hiding in here. But you know best, RAW.

@infomiho infomiho Jul 29, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You were right that there was an unnecessary layer here. ExceptT was being added and immediately removed, so I changed JobExecution to return IO (Either String ()) directly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see if I can also get rid of this module.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module Wasp.Cli.Command.BuildStart.Job
( JobExecution,
run,
race,
)
where

import Control.Concurrent (Chan)
import qualified Control.Concurrent.Async as Async
import Control.Monad.Except (ExceptT (ExceptT), runExceptT)
import Data.Functor ((<&>))
import System.Exit (ExitCode (..))
import qualified Wasp.Job as Job

type JobExecution = Chan Job.JobEvent -> ExceptT String IO ()

run :: (Int -> String) -> Job.Job -> JobExecution
run exitCodeToErrorMessage job events =
ExceptT $
Job.runJob job events
<&> fromExitCode exitCodeToErrorMessage
where
fromExitCode _ ExitSuccess = Right ()
fromExitCode toErrorMessage (ExitFailure code) = Left $ toErrorMessage code

race :: JobExecution -> JobExecution -> JobExecution
race first second events =
ExceptT $
either id id
<$> Async.race
(runExceptT $ first events)
(runExceptT $ second events)
37 changes: 17 additions & 20 deletions waspc/cli/src/Wasp/Cli/Command/BuildStart/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,35 @@ module Wasp.Cli.Command.BuildStart.Server
)
where

import Data.Function ((&))
import qualified StrongPath as SP
import System.Process (proc)
import Wasp.Cli.Command.BuildStart.Config (BuildStartConfig)
import qualified Wasp.Cli.Command.BuildStart.Config as Config
import qualified Wasp.Job as J
import Wasp.Job.Except (ExceptJob, toExceptJob)
import Wasp.Job.Process (runProcessAsJob)
import qualified Wasp.Cli.Command.BuildStart.Job as BuildStartJob
import qualified Wasp.Job as Job
import qualified Wasp.Job.Subprocess as Subprocess

buildServer :: BuildStartConfig -> ExceptJob
buildServer :: BuildStartConfig -> BuildStartJob.JobExecution
buildServer config =
runProcessAsJob
(proc "docker" ["build", "--tag", dockerImageName, dockerContextDir])
J.Server
& toExceptJob (("Building the server failed with exit code: " <>) . show)
BuildStartJob.run (("Building the server failed with exit code: " <>) . show) $
Job.makeJob Job.Server $
Subprocess.run (proc "docker" ["build", "--tag", dockerImageName, dockerContextDir])
where
dockerContextDir = SP.fromAbsDir buildDir
buildDir = Config.buildDir config
dockerImageName = Config.dockerImageName config

startServer :: BuildStartConfig -> ExceptJob
startServer :: BuildStartConfig -> BuildStartJob.JobExecution
startServer config =
runProcessAsJob
( proc
"docker"
( ["run", "--name", dockerContainerName, "--rm", "--network", "host"]
<> envVarParams
<> [dockerImageName]
)
)
J.Server
& toExceptJob (("Running the server failed with exit code: " <>) . show)
BuildStartJob.run (("Running the server failed with exit code: " <>) . show) $
Job.makeJob Job.Server $
Subprocess.run $
proc
"docker"
( ["run", "--name", dockerContainerName, "--rm", "--network", "host"]
<> envVarParams
<> [dockerImageName]
)
where
envVarParams = toEnvVarParams $ Config.serverEnvVars config
dockerContainerName = Config.dockerContainerName config
Expand Down
5 changes: 3 additions & 2 deletions waspc/cli/src/Wasp/Cli/Command/Db/Studio.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import Wasp.Cli.Command (Command, require)
import Wasp.Cli.Command.Message (cliSendMessageC)
import Wasp.Cli.Command.Require.InWaspProject (InWaspProject (InWaspProject))
import Wasp.Generator.DbGenerator.Jobs (runStudio)
import Wasp.Job.IO (readJobMessagesAndPrintThemPrefixed)
import qualified Wasp.Job as Job
import qualified Wasp.Job.Output as Output
import qualified Wasp.Message as Msg
import Wasp.Project.Common (generatedAppDirInWaspProjectDir)

Expand All @@ -23,6 +24,6 @@ studio = do
cliSendMessageC $ Msg.Start "Running studio..."

chan <- liftIO newChan
_ <- liftIO $ readJobMessagesAndPrintThemPrefixed chan `concurrently` runStudio genProjectDir chan
_ <- liftIO $ Output.printEventsPrefixedUntilExit chan `concurrently` Job.runJob (runStudio genProjectDir) chan

error "This should never happen, studio should never stop."
4 changes: 2 additions & 2 deletions waspc/src/Wasp/Generator/DbGenerator/Jobs.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Wasp.Generator.DbGenerator.Common (MigrateArgs (..), ResetArgs (..), dbSc
import Wasp.Generator.ServerGenerator.Common (serverRootDirInGeneratedAppDir)
import Wasp.Generator.ServerGenerator.Db.Seed (dbSeedNameEnvVarName)
import qualified Wasp.Job as J
import Wasp.Job.Process (runNodeCommandAsJobWithExtraEnv)
import qualified Wasp.Job.Node as Node
import Wasp.Project.Common (WaspProjectDir, waspProjectDirFromGeneratedAppDir)

migrateDev :: Path' Abs (Dir GeneratedAppDir) -> MigrateArgs -> J.Job
Expand Down Expand Up @@ -175,7 +175,7 @@ runPrismaCommandAsJobWithExtraEnv ::
[String] ->
J.Job
runPrismaCommandAsJobWithExtraEnv fromDir envVars generatedAppDir cmdArgs =
runNodeCommandAsJobWithExtraEnv envVars fromDir (absPrismaExecutableFp waspProjectDir) cmdArgs J.Db
Node.makeJobWithExtraEnv envVars fromDir (absPrismaExecutableFp waspProjectDir) cmdArgs J.Db
where
waspProjectDir = generatedAppDir </> waspProjectDirFromGeneratedAppDir

Expand Down
31 changes: 14 additions & 17 deletions waspc/src/Wasp/Generator/DbGenerator/Operations.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,8 @@ import Wasp.Generator.DbGenerator.Common
import qualified Wasp.Generator.DbGenerator.Jobs as DbJobs
import Wasp.Generator.FileDraft.WriteableMonad (WriteableMonad (copyDirectoryRecursive, doesDirectoryExist))
import qualified Wasp.Generator.WriteFileDrafts as Generator.WriteFileDrafts
import Wasp.Job.IO
( collectJobTextOutputUntilExitReceived,
printJobMsgsUntilExitReceived,
readJobMessagesAndPrintThemPrefixed,
)
import qualified Wasp.Job as Job
import qualified Wasp.Job.Output as Output
import Wasp.Project.Db.Migrations (DbMigrationsDir)
import Wasp.Util (checksumFromFilePath, hexToString)
import Wasp.Util.IO (deleteFileIfExists, doesFileExist)
Expand All @@ -61,8 +58,8 @@ migrateDevAndCopyToSource dbMigrationsDirInWaspProjectDirAbs generatedAppDirAbs
chan <- newChan
(_, dbExitCode) <-
concurrently
(printJobMsgsUntilExitReceived chan)
(DbJobs.migrateDev generatedAppDirAbs migrateArgs chan)
(Output.printEventsUntilExit chan)
(Job.runJob (DbJobs.migrateDev generatedAppDirAbs migrateArgs) chan)
case dbExitCode of
ExitSuccess -> finalizeMigration generatedAppDirAbs dbMigrationsDirInWaspProjectDirAbs (getOnLastDbConcurrenceChecksumFileRefreshAction migrateArgs)
ExitFailure code -> return $ Left $ "Migrate (dev) failed with exit code: " ++ show code
Expand Down Expand Up @@ -137,7 +134,7 @@ dbReset generatedAppDir resetArgs = do
removeDbSchemaChecksumFile generatedAppDir dbSchemaChecksumOnLastDbConcurrenceFileInGeneratedAppDir
chan <- newChan
((), exitCode) <-
readJobMessagesAndPrintThemPrefixed chan `concurrently` DbJobs.reset generatedAppDir resetArgs chan
Output.printEventsPrefixedUntilExit chan `concurrently` Job.runJob (DbJobs.reset generatedAppDir resetArgs) chan
return $ case exitCode of
ExitSuccess -> Right ()
ExitFailure c -> Left $ "Failed with exit code " <> show c
Expand All @@ -149,7 +146,7 @@ dbSeed ::
dbSeed generatedAppDir seedName = do
chan <- newChan
((), exitCode) <-
readJobMessagesAndPrintThemPrefixed chan `concurrently` DbJobs.seed generatedAppDir seedName chan
Output.printEventsPrefixedUntilExit chan `concurrently` Job.runJob (DbJobs.seed generatedAppDir seedName) chan
return $ case exitCode of
ExitSuccess -> Right ()
ExitFailure c -> Left $ "Failed with exit code " <> show c
Expand All @@ -159,12 +156,12 @@ testDbConnection ::
IO DbConnectionTestResult
testDbConnection generatedAppDir = do
chan <- newChan
exitCode <- DbJobs.dbExecuteTest generatedAppDir chan
exitCode <- Job.runJob (DbJobs.dbExecuteTest generatedAppDir) chan

case exitCode of
ExitSuccess -> return DbConnectionSuccess
ExitFailure _ -> do
outputLines <- collectJobTextOutputUntilExitReceived chan
outputLines <- Output.collectTextUntilExit chan
let databaseNotCreated = any prismaErrorContainsDbNotCreatedError outputLines

return $
Expand All @@ -186,8 +183,8 @@ generatePrismaClient generatedAppDir = do
chan <- newChan
(_, exitCode) <-
concurrently
(readJobMessagesAndPrintThemPrefixed chan)
(DbJobs.generatePrismaClient generatedAppDir chan)
(Output.printEventsPrefixedUntilExit chan)
(Job.runJob (DbJobs.generatePrismaClient generatedAppDir) chan)
case exitCode of
ExitFailure code -> return $ Left $ "Prisma client generation failed with exit code: " ++ show code
ExitSuccess -> do
Expand All @@ -207,8 +204,8 @@ doesSchemaMatchDb generatedAppDirAbs = do
chan <- newChan
(_, dbExitCode) <-
concurrently
(readJobMessagesAndPrintThemPrefixed chan)
(DbJobs.migrateDiff generatedAppDirAbs chan)
(Output.printEventsPrefixedUntilExit chan)
(Job.runJob (DbJobs.migrateDiff generatedAppDirAbs) chan)
-- Schema in sync: 0, Error: 1, Schema differs: 2
case dbExitCode of
ExitSuccess -> return $ Just True
Expand All @@ -225,8 +222,8 @@ areAllMigrationsAppliedToDb generatedAppDirAbs = do
chan <- newChan
(_, dbExitCode) <-
concurrently
(readJobMessagesAndPrintThemPrefixed chan)
(DbJobs.migrateStatus generatedAppDirAbs chan)
(Output.printEventsPrefixedUntilExit chan)
(Job.runJob (DbJobs.migrateStatus generatedAppDirAbs) chan)
case dbExitCode of
ExitSuccess -> return $ Just True
ExitFailure _ -> return Nothing
Loading
Loading