2023-08-30

Suppressing bloop for scala-cli managed services


I am a huge fan of scala-cli.

Among its many virtues, it reproduces the ergonomics of an interpreted language for compiled, super typesafe, Scala. (Java too!)

Recently, I've written custom services that I execute using scala-cli run, and then deploy directly as systemd units.

Here's a snippet of how I used to do this:

...

[Service]
Type=simple
User=unify-rss
Group=unify-rss
WorkingDirectory=/home/unify-rss/development/gitproj/unify-rss
ExecStart=scala-cli run main
...

I noticed, however, where I expected one long-running java process for a service's dedicated user, I ended up with two! In addition to my own application, a service called bloop.Bloop was running as well.

JVMs are not exactly lightweight, and I don't want double the fun when running a service.

bloop is a Scala build service that many different editors, build systems, and other tools use to efficiently, incrementally, compile and run scala code. It runs as a persistent background process, which external tools tools can trigger with commands to build or execute the codebase, recompiling only what is necessary given what has changed.

This is great for fast iteration during development, but unwieldy (and potentially increases a threat surface area) during deployment of persistent services.

Fortunately, scala-cli, you can turn bloop off. scala-cli then falls back to the plain old Scala compiler to rebuild your application. It retains its core ergonomics: You can edit, then run, without any sort of compile / assemble / publish ceremony. The first run after a change might take a bit longer than it would have with bloop.

Here is a snippet (slightly simplified) of my current systemd unit.

...

[Service]
Type=simple
User=unify-rss
Group=unify-rss
WorkingDirectory=/home/unify-rss/development/gitproj/unify-rss
ExecStart=scala-cli run --server=false main
...

Note the --server=false argument to scala-cli run.

Executing the service works just the same as before, rebuilding if necessary. But now no bloop service squanders precious server-side memory.

My application is small enough that the additional build time is not an issue. The only visible difference when running scala-cli is that, when bloop is enabled and something has changed, I would see a message like

Compiling project (Scala 3.3.0, JVM)
Compiled project (Scala 3.3.0, JVM)

With the --server=false flag gone (it defaults to true), that message disappears.

But changes still recompile, and everything works great.


Note: the --server=false argument has to come after the run subcommand. Otherwise...

$ scala-cli --server=false run main
[error]  run is not a scala-cli sub-command and it is not a valid path to an input file or directory.
Try viewing the relevant help to see the list of available sub-commands and options.
  scala-cli --help

The error message is unhelpfully mistaken. run in fact is a valid scala-cli subcommand. But --server is not a valid command-line option to the base scala-cli command, it is a valid command-line option to scala-cli run.

$ scala-cli --version
Scala CLI version: 1.0.4
Scala version (default): 3.3.0