My build process requires access to various services, such as, Postgres, Redis, and ElasticSearch. Taking Postgres as an example, I wrote the following "oracle":
data PostgresVersion = PostgresVersion deriving (Show, Typeable, Eq, Hashable, Binary, NFData, Generic)
type instance RuleResult PostgresVersion = String
shakeArgs shakeOptions{..} $ do
want [..]
addOracle $ \PostgresVersion -> do
fromStdout <$> cmd (AddEnv "PGPASSWORD" "REDACTED") ["psql", "-qAt", "-U", "pguser", "-h", "localhost", "dbname", "-c", "show server_version"]
Apart from ensuring that Postgres is running it also captures the version number which can be an extra assertion in the build process.
However, how do I handle the case where Postgres is NOT running? Should I use the standard Haskell try/catch to deal with it and start Postgres? Or should starting/stopping services be out-of-scope for a "Shakefile"?
The answer depends precisely on what result you want, but it sounds like you want to always make sure Postgres is available and have access to it during the build. In which case, I'd leverage the fact that a Shake script is just Haskell and write:
Then define your
mainas:The only downside is this will always start Postgres even if it isn't required. To only start it when necessary, you can turn the starting of Postgres into a cached action (will run at most once):
In both cases, I'd leverage the fact that starting Postgres is entirely in IO, and use normal
catch/finallyetc to handle exceptions. If you are in theActionmonad, you can useliftIOto run whateverIOoperation starts Postgres.More information can be found at a GitHub issue which covered similar ground: https://github.com/ndmitchell/shake/issues/615