just icon indicating copy to clipboard operation
just copied to clipboard

Cross-platform `sh` via `deno` or `busybox`

Open cspotcode opened this issue 11 months ago • 3 comments

I wanted to quickly share an option for writing cross-platform justfiles using (limited) sh-style syntax: leverage deno's task subcommand.

Context

I'm an OSS maintainer, projects support Windows, I want to use justfiles, I must make it extremely easy for potential contributors to install all necessary dependencies that are outside of the language ecosystem's core tools. For example, Golang devs obviously have golang installed, but they shouldn't be expected to jump through hoops to install shells and command runners.

Setup

Tell just to use deno's sh interpreter subcommand, deno task.

set shell := ["deno", "task", "--quiet", "--eval"]

# Sample recipe
prune-docs:
    cat *.md | markdown-formatter-tool dist/docs.md

PowerShell can't do this, because PowerShell's cat is subtly different -- breaks newlines, breaks binary files -- and PowerShell doesn't do * globbing.

Deno handles it correctly.

Installation

For contributors on Windows:

winget install -e --id Casey.Just
winget install -e --id DenoLand.Deno

Why deno?

"Isn't deno a JavaScript/TypeScript thing? I hate JavaScript/TypeScript! My project is C#/golang/rust/etc!"

In this case, we are using deno's built-in ability to run one-line commands written in sh-style syntax, exactly what just needs for vanilla recipes! We are not running any JavaScript.

https://docs.deno.com/runtime/reference/cli/task/#syntax

Deno installs as a single, ~100MB binary file.

cspotcode avatar Mar 20 '25 15:03 cspotcode

Linking to tickets related to the challenges of configuring a justfile shell correctly on Windows:

  • #1907
  • https://github.com/casey/just/issues/2532#issuecomment-2740633128
  • #2604
  • #2610
  • #2641
  • #2656
  • https://github.com/casey/just/issues/1592#issuecomment-1533842558
  • #534
  • #1549

cspotcode avatar Mar 20 '25 15:03 cspotcode

Immediately after posting this, I remembered to search winget for busybox. I feel a bit silly because, sure enough, there it is:

winget install -e --id frippery.busybox-w32

The great thing about busybox is that it bakes-in the standard suite of unix tools which are often shipped as external binaries -- cat, echo, rm, awk, etc. And it's a single, 1/2MB binary.

Setup like so:

set windows-shell := ["busybox", "sh", "-eu", "-c"]
set script-interpreter := if os() == "windows" { ["busybox", "sh", "-eu"] } else { ["sh", "-eu"] } # This conditional syntax is not supported; how to do it??

cspotcode avatar Mar 20 '25 15:03 cspotcode

Good find! I think that's something that could probably be added to the readme, assuming that there are no gotchas.

casey avatar Mar 20 '25 19:03 casey

There is bash bundled with git which is (IMO) more self-contained than installing an external binary as busybox, future readers can consider this as alternative approach.

It's living inside C:\Program Files\Git\bin\bash.exe / $env:ProgramFiles\Git\bin\bash.exe and we can make some special attributes like this.

set unstable # this is pretty much stable though.

[script("C://Program Files//Git//bin//bash.exe","-eu")]
show-big-object:
    git ls-tree  -rl HEAD | sort -k4 -n | tail | awk '{print $4, $5}' | numfmt --to=iec-i

Random thoughts

Another alternative that is coreutils from uutils team, it's integrated within nushell and nu is pretty usable at this moment so I think it's feasible to integrate uu crate for basic usage of a primitive utils set so just can work without putting any [script] or shell interpreter.

groutoutlook avatar Aug 31 '25 02:08 groutoutlook