Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ on:
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
# One wrapper invocation everywhere: git-bash on the Windows runner runs ./mill too.
shell: bash
steps:
- uses: actions/checkout@v4

Expand All @@ -18,29 +23,44 @@ jobs:
distribution: temurin
java-version: '21'

- uses: actions/cache@v4
with:
path: ~/.cache/mill-bun
key: mill-bun-${{ runner.os }}-${{ hashFiles('millbun/src/mill/bun/BunToolchainModule.scala') }}

- name: Select Mill launcher
# The sh wrapper fetches Mill's native launcher, which has no Windows build
# ("This native mill launcher supports only Linux and macOS") — Windows goes
# through the committed mill.bat instead.
run: echo "MILL=./mill$([ "$RUNNER_OS" = "Windows" ] && echo .bat)" >> "$GITHUB_ENV"

- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.0

- name: Compile
run: ./mill --no-server millbun.compile
run: $MILL --no-server millbun.compile

- name: Unit Tests
run: ./mill --no-server millbun.test
run: $MILL --no-server millbun.test

# The suite defaults to the managed toolchain; this job pins system Bun to the same
# version so `findOnPath` resolution and version verification keep CI coverage, while
# the managed-bun job covers the download/cache path.
- name: Integration Tests
env:
MILL_BUN_USE_SYSTEM: 'true'
run: ./mill --no-server millbun.integration
run: $MILL --no-server millbun.integration

managed-bun:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4

Expand All @@ -49,7 +69,18 @@ jobs:
distribution: temurin
java-version: '21'

- uses: actions/cache@v4
with:
path: ~/.cache/mill-bun
key: mill-bun-${{ runner.os }}-${{ hashFiles('millbun/src/mill/bun/BunToolchainModule.scala') }}

- name: Select Mill launcher
# The sh wrapper fetches Mill's native launcher, which has no Windows build
# ("This native mill launcher supports only Linux and macOS") — Windows goes
# through the committed mill.bat instead.
run: echo "MILL=./mill$([ "$RUNNER_OS" = "Windows" ] && echo .bat)" >> "$GITHUB_ENV"

- name: Managed Bun smoke test
env:
MILL_BUN_USE_SYSTEM: 'false'
run: ./mill --no-server millbun.integration.testOnly mill.bun.BunManagedToolchainIntegrationTests
run: $MILL --no-server millbun.integration.testOnly mill.bun.BunManagedToolchainIntegrationTests
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `BunWorkspaceModule` for one install and lockfile across mixed Scala.js/TypeScript packages.
- Published dependency manifest schema v2 with runtime, optional, and peer requirements.
- `bunDoctor` diagnostics and managed-toolchain CI smoke coverage.
- A committed lockfile written by a newer Bun fails frozen installs with regeneration guidance,
instead of surfacing bun's raw `UnknownLockfileVersion`.

### Changed

- Scala.js linking delegates to Mill's standard linker hooks; applications now choose `scalaJSVersion` explicitly.
- Development dependencies are local tooling inputs and are no longer published transitively.
- `bunPackageJsonExtras` rejects dependency fields now represented by typed settings.
- Missing dependency versions are represented as `latest` instead of an empty package.json value.
- TypeScript `bunBundleFormat` is `Option[String]`, matching Scala.js; `None` lets `bun build` infer.
- `unmanagedDeps` entries are staged into `vendor/` and declared as `file:./vendor/<name>`
dependencies, so local packages install under frozen lockfiles and locks stay portable.
- The TypeScript install task is canonically `bunInstall`; Mill's inherited `npmInstall` delegates to it.
- `bunToolEnv` is defined once on `BunToolchainModule` for all toolchain subprocesses, and the
TypeScript `bunRuntimeEnv` is public.

### Deprecated

- Scala.js `bunBundle*` and `bunCompile*` task names in favor of their canonical aliases.
- `bunOptionalDeps` in favor of `npmOptionalDeps`.
- `managedBunExecutable` in favor of `bunExecutableOverride`.
- The TypeScript `bunCompileExecutable: Boolean` switch in favor of the `compileExecutable` task.
- The TypeScript test command `test` and the Scala.js test command `bunTest`, both in favor of `testForked`.

## [0.2.1] - Overridable test-time JS env (2026-04-17)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Scala.js versions are explicit: choose the version your application tests agains
./mill app.run
./mill app.bundle
./mill app.compileExecutable
./mill app.test.test
./mill app.test.testForked
```

`BunScalaJSModule` delegates `fastLinkJS`, `fullLinkJS`, and test linking to Mill's standard `ScalaJSModule` hooks. That keeps the plugin compatible with Mill's linker lifecycle and removes its former private linker-worker coupling.
Expand Down Expand Up @@ -76,7 +76,7 @@ object app extends BunTypeScriptModule {
./mill app.run
./mill app.bundle
./mill app.compileExecutable
./mill app.test.test
./mill app.test.testForked
```

## Web applications
Expand Down
6 changes: 4 additions & 2 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ object millbun extends ScalaModule, PublishModule {
// pins rather than whatever the developer happens to have on PATH. The shared
// download cache means all fixtures reuse one archive. Export MILL_BUN_USE_SYSTEM=true
// to run against a local Bun instead.
"MILL_BUN_USE_SYSTEM" -> Task.env.getOrElse("MILL_BUN_USE_SYSTEM", "false"),
"MILL_BUN_REQUIRE_LOCKFILE" -> Task.env.getOrElse("MILL_BUN_REQUIRE_LOCKFILE", "false")
"MILL_BUN_USE_SYSTEM" -> Task.env.getOrElse("MILL_BUN_USE_SYSTEM", "false")
// The lockfile requirement runs the SHIPPED default (strict): dependency-bearing
// fixtures commit a bun.lock, regenerated via mill.bun.RegenerateFixtureLocks, and
// fixtures that deliberately test the relaxed path opt out in their own build.mill.
)
}

Expand Down
27 changes: 27 additions & 0 deletions docs/MIGRATING-0.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ Subsequent installs are frozen. During a staged migration only, set `MILL_BUN_RE

Use `npmDeps`, `npmDevDeps`, `npmOptionalDeps`, `npmPeerDeps`, and `npmOverrides`. `bunPackageJsonExtras` remains available for unmodeled fields such as `scripts`, but now rejects dependency sections so task invalidation and published metadata remain correct.

### `bunBundleFormat` is `Option[String]` on TypeScript modules

Scala.js and TypeScript previously disagreed on this member's type — `Option[String]` versus
`String` — which no deprecation alias can bridge, so 0.3 takes the one-time break. Both are now
`T[Option[String]]`; `None` lets `bun build` infer the format.

```scala
// 0.2
override def bunBundleFormat = Task { "esm" }
// 0.3
override def bunBundleFormat = Task { Some("esm") }
```

### Local packages via `unmanagedDeps` must be directories with a `package.json`

Entries are staged into `vendor/` beside the generated package.json and declared as
`file:./vendor/<name>` dependencies, so they now work under frozen lockfile installs and the
lockfile stays independent of the checkout path. Tarballs are no longer accepted — unpack them.

## Renamed APIs

| 0.2 name | 0.3 name | Status |
Expand All @@ -39,9 +58,17 @@ Use `npmDeps`, `npmDevDeps`, `npmOptionalDeps`, `npmPeerDeps`, and `npmOverrides
| `bunCompileExecutables` | `compileExecutables` | Compatibility alias retained |
| `bunOptionalDeps` | `npmOptionalDeps` | Deprecated compatibility setting |
| `managedBunExecutable` | `bunExecutableOverride` | Deprecated compatibility setting |
| `npmInstall` (TypeScript) | `bunInstall` | Mill's inherited name delegates to `bunInstall` and stays usable |
| `test` (TypeScript test modules) | `testForked` | Deprecated compatibility command |
| `bunTest` (Scala.js test modules) | `testForked` (inherited from Mill) | Deprecated compatibility command |

The old names are planned for removal at 1.0.

Environment hooks share one vocabulary: `bunToolEnv` (toolchain subprocesses — install, lock,
build) is defined on `BunToolchainModule` for every module kind, and the TypeScript
`bunRuntimeEnv` (program and test processes) is now public. Scala.js keeps `bunJsEnv` /
`bunJsEnvArgs` / `bunTestJsEnv` for its Scala.js-test JS environment, unchanged.

## Toolchain behavior

The default is now checksum-verified managed Bun 1.4.0. To preserve the old PATH behavior:
Expand Down
2 changes: 1 addition & 1 deletion examples/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ object fullstack extends Module {
// No moduleDir or sources overrides needed.
//
// ./mill tsLib.compile # TypeScript type-check
// ./mill tsLib.test.test # Bun-native tests
// ./mill tsLib.test.testForked # Bun-native tests
// ./mill client.bundle # TS browser bundle (imports tsLib)
// ./mill scalaClient.bundle # Scala.js browser bundle
// ./mill backend.run # JVM Cask server on :8080
Expand Down
Loading
Loading