0

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"?

Saurabh Nanda
  • 6,373
  • 5
  • 31
  • 60

1 Answers1

1

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:

withPostgres :: ConnectionString -> (PostgresHandle -> IO a) -> IO a
withPostgres = written without using Shake at all

Then define your main as:

main = withPostgres "" $ \pg ->
    shakeArgs ... using pg

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):

rules = do
    startPostgres <- newCache $ \() -> do
        ... start Postgres here ...
        runAfter $ ... shut down Postgres if you want ...

    "*.txt" %> \out -> do
        startPostgres ()
        ... now use Postgres ...

In both cases, I'd leverage the fact that starting Postgres is entirely in IO, and use normal catch/finally etc to handle exceptions. If you are in the Action monad, you can use liftIO to run whatever IO operation starts Postgres.

More information can be found at a GitHub issue which covered similar ground: https://github.com/ndmitchell/shake/issues/615

Neil Mitchell
  • 9,090
  • 1
  • 27
  • 85