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.