Platform setup: Linux, macOS, Windows
Once the prerequisites are in place the workflow is identical on all three platforms:
make doctor # is this machine wired up? (run this first)make run # build and serve natively — no container runtime neededmake status # is it actually serving?This emulator is a single static Go binary, so Docker is optional. make run compiles and serves at https://localhost:8443 with nothing else
installed; make up runs the published image instead if you would rather have
a container. That makes the Windows story unusually simple — you do not need a
container runtime at all to get a token.
| Need | Why | Linux | macOS | Windows |
|---|---|---|---|---|
| POSIX shell | the Makefile recipes and scripts/*.sh are /bin/sh | built in | built in | Git for Windows (sh.exe) |
| GNU Make | the target wrappers | make package | Xcode Command Line Tools | ezwinports.make |
| Go ≥ 1.25 | make build, run, test | package manager or go.dev | brew install go | winget install GoLang.Go |
| Docker (optional) | make up, make smoke | Docker Engine | Docker Desktop / OrbStack / Colima | Docker Desktop / Rancher Desktop |
| Python 3 (optional) | make e2e | usually present | Xcode CLT or Homebrew | winget |
sudo apt-get install -y make golang-go python3 # Debian/UbuntuIf your distro’s Go is older than 1.25, install from https://go.dev/dl/
instead. Only if you want make up / make smoke:
curl -fsSL https://get.docker.com | shsudo usermod -aG docker "$USER" && newgrp dockerSkipping that last step produces the most common Linux first-run failure, and
its message points at a socket rather than at group membership:
permission denied while trying to connect to the Docker daemon socket.
make and Python 3 come with the Xcode Command Line Tools:
xcode-select --installbrew install goThat installs GNU Make 3.81, which is ancient but sufficient — nothing in
this Makefile needs 4.x. brew install make provides a current one as
gmake if you prefer.
Everything here is arm64-native, so Apple silicon needs no special handling.
Windows
Section titled “Windows”The emulator runs natively from PowerShell — no WSL shell, no second checkout. Two winget packages, neither needing administrator rights:
winget install Git.Git # sh.exe + grep/awk/cut/curl — the POSIX userlandwinget install ezwinports.make # GNU Makewinget install GoLang.Go # for make build / run / testInstalling Git here is not about version control: it is how Windows gets the
POSIX shell that every recipe runs under, plus the grep, awk and curl the
scripts call.
Open a new terminal afterwards. winget adds make to the user PATH, and an
already-running shell will not see it — the single most common “I installed it
and it still says make is not recognized”.
PowerShell, cmd, and Git Bash all work, because make switches to sh.exe for
the recipe bodies regardless of which shell launched it. What does not work is
running the scripts through cmd or PowerShell directly
(.\scripts\status.sh) — go through make, or use sh scripts/status.sh.
If you use Docker on Windows
Section titled “If you use Docker on Windows”Docker Desktop and Rancher Desktop both work, but they share the docker context list, so a stale active context produces an error naming only a pipe:
error during connect: … open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.It means the docker CLI being invoked and the daemon actually serving belong
to different vendors. Rancher Desktop serves the default context:
docker context ls # the one marked * is active; find the reachable onedocker context use defaultmake doctor reports the active context by name and lists the alternatives when
it is unreachable — and treats a missing daemon as a warning, since make run
does not need one.
Three Windows traps
Section titled “Three Windows traps”Each fails somewhere other than where it originates, which is what makes them expensive. All three are handled; this records what they were.
python3 is a fake. Windows ships a Microsoft Store alias stub named
python3. It sits on PATH, so command -v python3 succeeds and any “is Python
installed?” check passes — then running it exits 49, while a real Python at
python right beside it is never consulted. The Makefile and scripts therefore
detect an interpreter by executing each candidate (python3, python,
py) and taking the first that runs. Override with PY=.
/dev/null is not a path curl understands. Git Bash’s shell understands
/dev/null, but curl.exe is a native Windows binary that does not — it fails
to open its output file and exits 23 after already printing the status
code. Under set -e that aborted scripts/docker-smoke.sh on its first
check. The scripts now use NUL on Windows.
GNU Make falls back to cmd.exe. When Make cannot find a shell on PATH it
uses cmd.exe, which cannot run a single line of these recipes — so the failure
looks like a broken Makefile rather than a missing dependency. The Makefile pins
SHELL := sh.exe on Windows, so a missing Git for Windows fails by naming the
shell.
Troubleshooting by symptom
Section titled “Troubleshooting by symptom”| Symptom | Platform | Cause |
|---|---|---|
make is not recognized | Windows | PATH not refreshed — open a new terminal |
| recipes fail with cmd.exe syntax errors | Windows | Git for Windows not installed, so no sh.exe |
permission denied … docker daemon socket | Linux | not in the docker group; newgrp docker |
open //./pipe/dockerDesktopLinuxEngine | Windows | wrong docker context — docker context use default |
Python was not found | Windows | the Store alias stub; install a real Python |
| port 8443 already answering | any | another family member’s compose stack already publishes entra there |
set: Illegal option - running a script | Linux, macOS | the script was checked out with CRLF; see below |
A note on line endings
Section titled “A note on line endings”scripts/*.sh must be LF. A shell script checked out with CRLF fails at the
shebang — sh reads the trailing \r as part of the interpreter path, and the
error names a file that plainly exists. Git for Windows sets
core.autocrlf=true in its system config, so this is the Windows default
rather than a misconfiguration. .gitattributes pins
*.sh, *.py and Makefile to eol=lf so the checkout is byte-identical
everywhere.
The rest of the family
Section titled “The rest of the family”azure-keyvault-emulator
and fabric-emulator use the
same make doctor / make up / make status verbs, and both ship a
docker-compose.yml that publishes this emulator on :8443 alongside them.