Development - worxbend/binstaller GitHub Wiki

๐Ÿ› ๏ธ Development

Scala 3 ยท Mill ยท GraalVM. No sbt, Maven, Gradle, npm or Make โ€” use the checked-in ./mill launcher for everything.


๐Ÿ“‹ Requirements

Need Version
JDK 21+
GraalVM 25.4; 25.0.1 on Intel macOS, only for building native images locally
Mill Pinned by .mill-version; the ./mill launcher fetches it

๐Ÿ—๏ธ Module graph

app โ”€โ”€โ–ถ cli โ”€โ”€โ–ถ core โ”€โ”€โ–ถ config

The graph is acyclic and core never imports CLI code.

Module Owns
config YAML reading (SnakeYAML Engine), typed decoding, enum validation, rejecting installer scripts, duplicate names, unknown versionRefs, SHA-256 shape, sudo gating.
core Variable and version resolution, HTTPS validation, --only/--skip selection, bounded downloads, checksum verification, archive extraction, staging and replacement, symlinks, apply state, typed installer events.
cli Picocli parsing, exit codes, script-friendly output, coloured apply progress, command routing.
app Process entry and exit-code propagation. Nothing else.

โœ… Checks

Focused:

./mill config.test
./mill core.test
./mill cli.test

Broad:

./mill __.compile
./mill __.test
./mill mill.scalalib.scalafmt/checkFormatAll
git diff --check

Repair formatting:

./mill mill.scalalib.scalafmt/reformatAll

โš ๏ธ Don't pipe Mill output through grep, head, tail or /dev/null โ€” it hides diagnostics you'll want for the follow-up fix. Stream it to a file instead:

LOGFILE="/tmp/binstaller-check-$(date +%s).log"
./mill __.test 2>&1 | tee "$LOGFILE"

โ–ถ๏ธ Running from source

./mill app.run --help
./mill app.run plan     --config config.example.yaml
./mill app.run apply    --config config.example.yaml
./mill app.run versions --config config.example.yaml
./mill app.run lock     --config config.example.yaml --lock-file /tmp/binstaller.lock.json

๐Ÿ“ฆ Native image

GRAALVM_HOME=/path/to/graalvm ./mill app.nativeImage

Native options live in build.mill: --no-fallback, -O2, --initialize-at-build-time=scala. Picocli reflection configuration is generated as part of the build.


๐Ÿงช Test patterns

Tests use utest and never hit the network. Downloads and text fetches are injected:

  • A fake HttpTextClient returns pinned resolver values for http-text versions, and routes GitHub latest-release metadata used by versions.
  • A fake BinaryDownloadClient returns bytes or a typed BinaryDownloadError.
  • Progress tests override download(url, observer) and emit started / advanced / finished events before returning bytes.

Tests receive the repository root through -Dbinstaller.repoRoot, set by the build, so fixtures can locate checked-in files such as config.example.yaml.

When adding behaviour, prefer a test at the lowest module that owns the rule โ€” decoding rules in config, resolution and safety rules in core, output and exit codes in cli.


๐Ÿ“ก Event contract

core emits renderer-agnostic events; the CLI is one consumer among possible others.

ResolvingStarted ยท PlanReady ยท ToolStarted ยท ToolPhaseChanged ยท DownloadProgress
LogLine ยท ToolResult ยท ToolSkipped ยท Summary

Phases: Resolving, Planning, LoadingState, Downloading, VerifyingChecksum, Staging, ApplyingModes, ReplacingInstall, VerifyingExecutables, CreatingSymlinks, SavingState.

If you add a phase or event, update docs/architecture.md in the same change.


๐Ÿšข Releasing

Releases are GraalVM native binaries built by .github/workflows/release.yml, triggered by a v* tag or a manual workflow_dispatch with a tag input.

Per target (linux-amd64, linux-arm64, macos-amd64, macos-arm64) the workflow:

  1. Checks out and sets up GraalVM 25.4 with native-image (25.0.1 for Intel macOS)
  2. Runs ./mill __.test
  3. Builds ./mill app.nativeImage
  4. Packages binstaller-<version>-<target>.tar.gz
  5. Smokes native --help, plan and versions
  6. Writes a per-file .sha256

The publish job then re-verifies checksums, signs every artifact with keyless Sigstore (cosign sign-blob using the workflow's GitHub OIDC identity), and publishes the release assets including config.example.<version>.yaml and install.sh.

All GitHub Actions are pinned to commit SHAs; Mill is pinned via .mill-version.

Full detail: docs/release.md.


๐ŸŒ Website and wiki

Surface Source Published by
๐ŸŒ Website site/ + assets/ .github/workflows/pages.yml
๐Ÿ“š This wiki wiki/ .github/workflows/wiki.yml

Terminal screenshots in assets/ are SVG renderings of real command output โ€” regenerate them from a real run rather than hand-editing, so the docs can't drift from the tool.

Edit wiki pages in wiki/ and open a pull request. Direct edits in the GitHub wiki UI are overwritten on the next sync.


๐Ÿค Contributing

  1. Open an issue first for anything that changes the manifest contract or the CLI surface โ€” the narrow scope is a feature, and scope changes deserve a discussion.
  2. Keep core free of CLI concerns.
  3. Add tests at the module that owns the rule.
  4. Run ./mill __.test and ./mill mill.scalalib.scalafmt/checkFormatAll before pushing.
  5. Update the relevant docs/ page in the same change.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ