Personal Log »

Scala, Mill and GitLab CI

I use sbt to build Scala projects at work, so trying to learn new things, I decided to use Mill at home. And I’m very happy with it, although it is less mature than sbt, and the user community is smaller.

One of the consequences of the community being smaller is that GitLab provides a template with sbt, but nothing with Mill. Which is OK, because it is very simple; with the exception of setting up the cache –so our builds speed up and we don’t use resources upstream every time we re-download a dependency–.

In reality is not that hard, if you happen to know that Mill uses Coursier instead of Apache Ivy to deal with the dependencies.

I also wanted to generate an artifact (a .jar bundle) every time I make a release, that in my project is just tagging a commit. Then I’ll create the release itself in GitLab and link that artifact to that release –this can be automated as well using GitLab API–.

This is my .gitlab-ci.yml:

image: openjdk:8

variables:
  MILL_CLI: "-D coursier.cache=$CI_PROJECT_DIR/.cache -j 0"

cache:
  paths:
    - .cache/

test:
  script:
    - ./mill $MILL_CLI server.test

package:
  script:
    - ./mill $MILL_CLI server.assembly
  artifacts:
    paths:
      - out/server/assembly/dest/*.jar
    when: on_success
    expire_in: never
  rules:
    - if: $CI_COMMIT_TAG

I have defined two jobs:

  • test: that is run every time I push any commit, and it basically runs the tests.
  • package: that generates the .jar bundle, but only when there is a tag. Also I want to keep the keep the artifact “forever”, if the build is successful.

The cache is configured to use .cache/ directory –relative to the project directory–, so I set Coursier accordingly.

I use a variable to not repeat the flags passed to Mill, and that’s all!

The other bits I setup in my project are related to Mill itself. I use VCS Version to derive the project version from git, which is perfect given that I use git tags to control what is a release; and I changed the output filename used by Mill –defaults to out.jar– so it can be used directly in the releases.

A way of accomplishing that in Mill 0.9.8 is by adding this to you ScalaModule in build.sc:

  val name = "myproject"
  def assembly = T {
    val version = VcsVersion.vcsState().format()
    val newPath = T.ctx.dest / (name + "-" + version + ".jar")
    os.move(super.assembly().path, newPath)
    PathRef(newPath)
  }

It basically replaces assembly with a new command that renames that out.jar to something like myproject-VERSION.jar, integrating nicely with VCS Version.

Would you like to discuss the post? You can send me an email!