Open-source project
coursier/coursier avatar
coursier/coursier

coursier: three tools in one binary, built with Mill and a browserify frontend

Pure Scala Artifact Fetching

2,136 stars334 forksScalaApache-2.0

At a glance

What is it?
coursier is an Apache-2.0 Scala artifact and application manager published as io.get-coursier, built with Mill rather than sbt and bootstrapped through cs.sh. The README is four sentences long, so most of what an evaluator needs is in the repository itself: a three-way split between launcher, environment and cache, a nightly tag in the release list, and a dependency-graph viewer still on React 16 and Bootstrap 3.
Who is it for?
Adopt coursier if you are working in Scala and want an artifact cache that behaves predictably, or an application launcher that does not require a running sbt. The cache is the part with the strongest claim on your attention, because that is where coursier is used as a library by build tools rather than as a command you type.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Scala, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The build tool is Mill, and the README does not mention it

Coursier's README is four sentences of description and a link to a website. Everything an evaluator needs to know about building it is in the repository listing, and the first thing there is that the build tool is Mill.

The evidence is a cluster of five entries: build.mill, mill, mill-build/, mill.bat and a DEVELOP.md. Mill is a JVM build tool that takes its build definition as executable Scala code, and a mill wrapper script checked into the repository is the convention for making the build reproducible without asking contributors to install a specific Mill version first. mill.bat is the Windows equivalent, which is the detail that tells you the wrapper is being maintained for contributors rather than left to chance.

Set against that, there is a project/ directory, which is sbt's convention for build definition, and the repository topics include sbt-plugin. Both of those are real and neither contradicts the Mill finding. A project can publish an sbt plugin as one of its artifacts while using a different tool to build itself, and it can retain a project/ directory for a module or for historical reasons. What it means for an evaluator is that seeing sbt-plugin in the topic list does not tell you the project is built with sbt, and reaching for sbt commands in this repository is likely to be the wrong first move.

The build definition being .mill rather than .sbt also means the build logic is compiled Scala rather than a DSL. That has a practical consequence for anyone reading build.mill to understand what gets published: you are reading a program, and the module structure is expressed in the modules/ directory rather than in a declarative list.

The artefacts themselves are published under the io.get-coursier organisation, and the Maven Central badge names coursier_2.13, which is the Scala 2.13 cross-build of the core module. That is the coordinate a build tool will resolve, and it is also the answer to the question of which Scala version the published line targets.

Three products behind one binary, and one bootstrap script

The repository description is three words, Pure Scala Artifact Fetching, and the README then describes three distinct capabilities that share an implementation and a launcher.

The first is installing Scala applications. The second is setting up a Scala development environment. The third is downloading and caching artifacts from the web. Those are not three features of one tool, they are three tools with different operational profiles, and conflating them is the most common misreading of coursier.

The application launcher is a user-facing command. You ask it for a program and it puts a working copy on your machine, which means it needs a cache on disk, a platform-specific download, and somewhere to put the result. Its failure mode is a broken install on one machine.

The environment setup is a developer-workflow tool. It provisions a Scala installation, a build tool and the pieces around them into a prefix directory, so the failure mode is a toolchain that is subtly different from what you expected.

The artifact cache is server-shaped. This is the mode where coursier stops being a command and becomes a library that build tools call, and it is the mode with the strongest claim on production scrutiny, because a cache has to be correct about what it has already downloaded, about whether a checksum matches, and about what happens when the remote repository disagrees with the local copy. The io.get-coursier organisation naming and the coursier_2.13 Maven coordinate are aimed at that audience: consumers are other tools resolving a library, not a person typing at a prompt.

The entry point for all of it is cs.sh, checked into the repository root. That is the bootstrap script the native distribution is built around, and its presence at the root alongside the mill wrapper tells you the project is conscious about first-run experience. The README does not explain what cs.sh does, which is the clearest example in this repository of documentation deferring to a website.

A tag named nightly sits in the release list

The three most recent releases in this repository are v2.1.25 dated 2026-09-16, a tag named nightly dated 2026-08-03, and v2.1.25-M26 dated 2026-06-19.

Three things are mixed in that list. A stable semantic version, a milestone, and a rolling tag. The last of these is the one that matters for anyone automating a resolution.

A release feed that includes a mutable tag cannot be treated as a list of immutable versions. If a build system resolves the newest entry in the release list, it will sometimes get v2.1.25 and sometimes get whatever nightly currently points at, and the two produce different binaries from the same repository state on the same day. Nothing errors. The build succeeds and the artefact changes, which is the hardest class of build failure to diagnose because there is no failure.

The milestone tag v2.1.25-M26 has a related problem, milder. Milestones are published to make a pre-release testable, which is useful, but the M26 suffix means the same version string will be superseded by M27, and nothing in the tag name says whether the artefact behind it is meant to be consumed or only inspected.

The correct discipline follows from the naming. Resolve a semantic version, not a release-list position. Record the checksum of what you fetched. Treat the nightly tag as a thing to test against deliberately, on a schedule, rather than as a fallback when the stable version is inconvenient. Coursier is a tool other build tools depend on, so a moving tag in its own release feed is a hazard that propagates, and a consumer that copies the pattern propagates it further.

The last push was on 2026-09-28, two days after the v2.1.25 tag, so the default branch is moving at the same time as the releases. That is normal for an actively maintained project and it is another reason to pin rather than track.

React 16, Bootstrap 3, webpack 4 and browserify in one package.json

The most revealing file in this repository is package.json, because it belongs to a browser application inside a JVM project and its contents have not moved in years.

json
"dependencies": {
    "bootstrap": "3.4.1",
    "bootstrap-treeview": "1.2.0",
    "browserify": "^16.2.3",
    "graphdracula": "1.2.1",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "requirejs": "2.3.7",
    "sax": "1.2.4"
  },

This is a dependency graph visualiser. graphdracula does the graph drawing, react and react-dom provide the view, bootstrap and bootstrap-treeview provide chrome and a collapsible tree, and sax is an XML parser, which tells you the input is an XML document rather than a JSON one. In other words, there is a web front end here that renders a dependency graph, and it is built on React 16.13.1 and Bootstrap 3.4.1, both of which are several major versions behind their current lines.

The devDependencies make the picture sharper:

json
"devDependencies": {
    "cheerio": "0.22.0",
    "scalajs-friendly-source-map-loader": "0.1.5",
    "sha1": "1.1.1",
    "source-map-loader": "0.2.3",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.2",
    "xhr2": "0.1.4",
    "xmldom": "0.1.27"
  },

Webpack 4 is a devDependency, and there is a webpack.config.js in the repository root, but the only script in package.json is browserify, which runs the browserify binary. So three bundlers are present: webpack with a config file, browserify as a runtime dependency with a script, and requirejs as a third runtime dependency. Only one of them has a script. That is what a build system looks like after a migration that was started, partially completed and then left alone, which is a fair thing to find in a project whose focus is elsewhere.

The rest of the devDependencies are browser shims for a Node toolchain, xhr2 and xmldom providing XMLHttpRequest and a DOM parser, cheerio for server-side HTML, and sha1 providing a hash implementation. The presence of sha1 as a pinned dependency is a small but concrete detail about the project: its checksum story includes SHA-1, and the package carrying it is at version 1.1.1, which is old enough to have accumulated its own advisories. For a tool whose job includes verifying downloads, that is worth knowing about and is not addressed anywhere in the README.

The honest reading is that this front end is preserved rather than maintained, and an evaluator should treat the JavaScript toolchain as historical context, not as a signal about the current state of the Scala codebase.

Scala Steward, scalafmt, scalafix and a blame-ignore list

Four configuration files in the repository root describe a project that automates its own maintenance, and none of them appear in the README.

.scala-steward.conf is the configuration for Scala Steward, the service that opens pull requests to bump library versions in Scala builds. For a project that exists to fetch other people's libraries, automating its own dependency updates is close to mandatory, and having the config checked in means the policy is a reviewable artefact rather than a habit. It also means the repository's pull request list is likely to contain a stream of mechanical bumps alongside real changes, which is worth knowing before you read its history.

.scalafmt.conf and .scalafix.conf are two different tools doing two different jobs. scalafmt reformats code and nothing else, and its config pins the style so that formatting is not a review topic. scalafix applies semantic rules and can rewrite code, so its config is a statement about which patterns the maintainers want rewritten automatically. A repository with both is one where mechanical changes have been taken off the table and automated instead.

.git-blame-ignore-revs is the least obvious and the most considerate. It lists commits to be excluded from git blame, which is what you need after a project-wide reformat: without it, every line's blame points at the reformat commit and the history becomes useless. Adding that list is a small piece of maintenance that only matters once, and then forever, and its presence is a better signal of care than any badge.

.gitmodules tells you part of the tree is composed of submodules, which for a project of this size usually means documentation or a vendored component is tracked in its own repository. Given that doc/ and docs/ both exist at the root, one of them is a likely candidate, and the submodule arrangement is probably how the website at get-coursier.io relates to the source.

AGENTS.md, SECURITY.md and CODE_OF_CONDUCT-adjacent governance files complete the picture. The repository is one where the mechanics of maintenance are configured in version control rather than left to the maintainer's memory, which is the single most reliable signal available from a file listing.

One maintainer's employment has funded this since 2018

The Acknowledgments section is two sentences and one of them is the most consequential line in the README.

Large parts of the developments in coursier since Sep. 2018 have been funded by the Scala Center, through the employment or contracting of myself (Alexandre Archambault).

The phrasing is unusually direct. This is not a company employing a team to work on an open-source project, and it is not a foundation grant. It is one person's employment or contract, at a specific institution, funding the work, and the person writing it is that person. The Scala Center is an EPFL-linked body, referenced with its swirl logo, and EPFL is also the home of the Scala language itself, so the funding is coming from adjacent to the ecosystem coursier serves rather than from a user company.

What that means for an adopter is a continuity question rather than a quality question. The project has been able to do substantial work for roughly eight years because this arrangement held, and the artifacts of that funding are visible in the repository: a Mill build, Scala Steward, a blame-ignore list, an AGENTS.md, a nightlies pipeline and a release cadence that produced v2.1.25 in September 2026. A project with a bus factor of one can still be excellent, and this one has the receipts to say it was.

The risk is structural and it is worth naming without melodrama. A tool that other build tools call as a library, that resolves artefacts for CI systems and that has a rolling nightly channel, depends on somebody being able to do the work. If that employment ends, the maintenance burden transfers to whoever is left, and the most likely outcome is a slower cadence rather than an abandonment. Projects in this position usually mitigate by broadening governance, and this repository does have a GOVERNANCE-shaped commitment in its code of conduct and contribution terms, but the README's own account of its funding names one person and no successor.

The other half of the Code of Conduct section is a contribution licensing rule rather than a behavioural one: all code or documentation that is provided must be licensed with the same license that coursier is licensed with, Apache 2.0. That is a contribution licensing requirement stated in the README without a separate CLA file, and it means an inbound patch carries the same terms as the project by default.

What the Apache-2.0 grant covers, and what it does not

The licence is Apache 2.0, stated in the README, in the package.json licence field and in a LICENSE file at the repository root. All three agree, which is more than most repositories manage.

The grant covers the code and the documentation, and the contribution rule extends it to inbound contributions so there is no ambiguity about terms for a patch. The code of conduct expectation is separate and is specific: people are expected to follow the Scala Code of Conduct when discussing coursier on GitHub, on the Gitter channel, or on other venues. That is an unusual specificity, naming a parent community's code rather than running its own, and it tells you the project sees itself as part of the Scala community rather than adjacent to it.

What the Apache 2.0 grant does not do is settle the operational questions an adopter has. It does not commit anyone to a support response time, and a project funded by one person's employment is not going to have one. It does not create a compatibility promise for the io.get-coursier coordinates, so a consumer that resolves coursier_2.13 is entitled to expect the licence terms and nothing about what changes in the next version. And it does not tell you anything about the artifacts coursier downloads on your behalf, which are other projects' artefacts under their own terms.

The last point is the one that belongs in an adoption review rather than a licence review. Coursier is a fetcher. It resolves, downloads, caches and verifies third-party binaries on behalf of a build. Its own licence governs its own code; the things it hands you are governed by whatever their authors chose, and the mechanism by which you find out is the dependency metadata coursier reads, not the licence of the tool doing the reading. If your organisation has a policy about what may be fetched and executed during a build, coursier is the component that policy has to be written about, and the SHA-1 dependency in its own package.json is a reminder that the tool's verification story is worth reading rather than assuming.

Nothing here is a reason to avoid the project. It is a reason to pin a version, record checksums, and read the cache documentation on the website rather than assuming the README covers it, because on the evidence of this repository it does not.

Editorial conclusion

Adopt coursier if you are working in Scala and want an artifact cache that behaves predictably, or an application launcher that does not require a running sbt. The cache is the part with the strongest claim on your attention, because that is where coursier is used as a library by build tools rather than as a command you type. Do not pin to the newest release by date, because the release list contains a tag named nightly that moves, so resolve a semantic version and record the checksum. Do not expect the dependency-graph web interface to be maintained tooling; the package.json puts React at 16.13.1, Bootstrap at 3.4.1 and webpack at 4 alongside browserify, with a single npm script, which is a stack frozen in time. Verify four things. Read DEVELOP.md, since the README does not name the build tool and the repository says it is Mill. Check that your Scala version matches the published artifact, the Maven Central badge points at coursier_2.13. Decide whether the binary distribution or the library integration suits you, since they carry different operational obligations. And note the funding position: development since September 2018 has been funded by the Scala Center through the employment of one named maintainer, which is a continuity question rather than a quality one. The deciding fact is that coursier is infrastructure other tools depend on, so its release discipline matters more to you than its feature set.

Frequently asked questions

What is coursier used for?

The README describes three capabilities: installing Scala applications, setting up a Scala development environment, and downloading and caching artifacts from the web. It is also consumed as a library by other build tools, published to Maven Central under the io.get-coursier organisation with the core module cross-built for Scala 2.13.

What build tool does coursier use?

Mill. The repository contains build.mill, a mill wrapper script, mill-build/ and mill.bat, plus a DEVELOP.md. Note that the project topics include sbt-plugin, which refers to an artefact the project publishes rather than the tool it builds itself, and a project/ directory remains in the root.

How do I choose which coursier release to use?

Pin a semantic version rather than a position in the release list. The three most recent releases are v2.1.25 from 2026-09-16, a tag named nightly from 2026-08-03, and the milestone v2.1.25-M26 from 2026-06-19, so a mutable tag is present in the feed and resolving the newest entry can silently change your artefact.

What licence is coursier released under?

Apache 2.0, stated consistently in the README, the package.json licence field and the LICENSE file. The README also requires that any code or documentation contributed be licensed under the same terms, and expects contributors to follow the Scala Code of Conduct when discussing the project.

Who maintains coursier and how is it funded?

The README states that large parts of the developments since September 2018 have been funded by the Scala Center, through the employment or contracting of Alexandre Archambault, who writes the acknowledgment. The project also configures Scala Steward for automated dependency updates and uses scalafmt, scalafix and a git-blame-ignore-revs list.

Official sources

  1. coursier/coursier on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/coursier-coursier.svg)](https://hysenlabs.com/projects/coursier-coursier)