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