Repository: InsightSoftwareConsortium/ITK-Wasm (language: python)

Issue:
Improve podman support
Add more support for podman. Some testing on Arch Linux -- it basically works.

Our scripts sometimes call docker directly -- these should switch to looking for `OCI_EXE`, behavior similar to dockcross:

https://github.com/dockcross/dockcross/blob/6eda62728abec6b200e94db9abee4555beb38a10/imagefiles/dockcross.sh#L25-L34

If podman is easier to install, recommend it in the docs.


Discussion / hints:

Fix patch:
diff --git a/docs/cxx/tutorial/debugging.md b/docs/cxx/tutorial/debugging.md
index 851f835d6..aac291172 100644
--- a/docs/cxx/tutorial/debugging.md
+++ b/docs/cxx/tutorial/debugging.md
@@ -1,174 +1,174 @@
-# Debugging WebAssembly
-
-Effective debugging results in effective programming. **itk-wasm** makes effective debugging of WebAssembly possible.
-
-This example walks through the different techniques that can be used with itk-wasm to debug WebAssembly during development.
-
-We will debug the following C++ code:
-
-```cpp
-#include <iostream>
-
-int main() {
-  std::cout << "Hello debugger world!" << std::endl;
-
-  const char * wasmDetails = "are no longer hidden";
-
-  const int a = 1;
-  const int b = 2;
-  const auto c = a + b;
-
-  // Simulate a crash.
-  abort();
-  return 0;
-}
-```
-
-[The example](https://github.com/InsightSoftwareConsortium/itk-wasm/tree/main/examples/debugging) provides npm scripts as a convenient way to execute debugging commands that you may also invoke directly in a command line shell.
-
-To run these examples, first [install and test](./hello_world) Docker and Node/NPM. Then, install the package dependencies and run the example commands.
-
-```
-cd itk-wasm/examples/Debugging/
-npm install
-npm run <name>
-```
-
-where `<name>` is the npm script. Available names can be found by calling `npm run` without any arguments.
-
-## Native
-
-The CMake-based itk-wasm build system tooling enables the same C++ build system configuration and code to be reused when building a native system binary or a WebAssembly binary. As a result, native binary debugging tools, such as [GDB](https://sourceware.org/gdb/), [LLDB](https://lldb.llvm.org/), or the [Visual Studio debugger](https://docs.microsoft.com/en-us/visualstudio/debugger/?view=vs-2022).
-
-We can build the project's standard CMake build configuration,
-
-```cmake
-cmake_minimum_required(VERSION 3.10)
-project(DebuggingWebAssemblyExample)
-
-add_executable(DebugMe DebugMe.cxx)
-```
-
-with standard CMake commands,
-
-![Native build](/_static/tutorial/debugging/native-build.png)
-
-The native binary can then be debugged in the standard way. For example, with `gdb` on Linux:
-
-![Native debug Linux](/_static/tutorial/debugging/native-debug-linux.png)
-
-## WASI
-
-The most direct way to debug WebAssembly is through the [WebAssembly System Interface (WASI)](https://wasi.dev/). In itk-wasm we can build to WASI with the [WASI SDK](https://github.com/WebAssembly/wasi-sdk) by specifying the `itkwasm/wasi` toolchain image. A backtrace can quickly be obtained with the `itk-wasm` CLI. Or, a fully fledged debugger session can be started with LLDB.
-
-First, build to WASI WebAssembly with debugging symbols available:
-
-![WASI debug build](/_static/tutorial/debugging/wasi-build-debug.png)
-
-Then, the `itk-wasm` CLI can conveniently run the Wasm binary with the included WASI runtime:
-
-![Run WASI debug](/_static/tutorial/debugging/run-wasi-debug.png)
-
-We can see that `abort` is called in the `main` function at line 13 in `DebugMe.cxx`.
-
-A full debugging session is also possible after [LLDB](https://lldb.llvm.org/) >= 13 and [Wasmtime](https://wasmtime.dev/) are installed.
-
-![LLDB WASI debug](/_static/tutorial/debugging/lldb-wasi-debug.png)
-
-**Note:** when calling `wasmtime` directly and passing local files into a pipeline, `--dir` arguments must be set. This gives `wasmtime` permission to access the directories containing the files. This is required due to WASI's [capability-based security](https://en.wikipedia.org/wiki/Capability-based_security) model. For example, if a file path starts with `./`, then add `--dir ./` arguments to the `wasmtime` invocation. `--dir` can be specified multiple times.
-
-## Node.js
-
-When debugging WebAssembly built with the itk-wasm Emscripten toolchain, set the `CMAKE_BUILD_TYPE` to `Debug` as is required to debug native builds.
-
-As with native builds, this builds debugging symbols, the human-readable names of functions, variables, etc., into the binary.  This also adds support for C++ exceptions and retrieving the string name associated with exceptions. Without this itk-wasm instrumentation, a C++ exception will through an error with an opaque integer value. And, Emscripten JavaScript WebAssembly bindings will not be minified, which facilitates debugging.
-
-When built with the default `Release` build type:
-
-![Emscripten build Release](/_static/tutorial/debugging/emscripten-build-release.png)
-
-the JavaScript support code is minified, and difficult to debug:
-
-![Run Node Release](/_static/tutorial/debugging/run-node-release.png)
-
-However, when built with the `Debug` build type:
-
-![Emscripten build Debug](/_static/tutorial/debugging/emscripten-build-debug.png)
-
-a useful backtrace can be obtained:
-
-![Run Node Debug](/_static/tutorial/debugging/run-node-debug.png)
-
-In order to run a debugger with Node, add the `--inspect-brk` flag when invoking `node`:
-
-![Node inspect](/_static/tutorial/debugging/node-inspect.png)
-
-This will pause execution on start a debugging remote interface. To connect to the remote interface with a Chromium browser, visit `chrome://inspect` and click the *inspect* link on the corresponding *Remote Target*:
-
-![Node inspect Remote Target](/_static/tutorial/debugging/node-inspect-remote-target.png)
-
-This will open the Chrome Dev Tools debugger:
-
-![Node inspect Chrome DevTools](/_static/tutorial/debugging/node-inspect-chrome-dev-tools.png)
-
-Other debugger interfaces [are also available](https://nodejs.org/en/docs/inspector), like a CLI debugger or the VSCode debugger.
-
-This is helpful for debugging issues that occur in Emscripten JavaScript interface. The next section describes how to debug issues inside the Emscripten-generated WebAssembly.
-
-## Chromium-based browsers
-
-Recent Chromium-based browsers have support for debugging C++-based WebAssembly in the browser. With a few extra steps described in this section, it is possible to interactively step through and inspect C++-compiled WebAssembly running in the browser.
-
-WebAssembly debugging in DevTools requires a few extra setup steps from a default browser installation.
-
-First, [install the Chrome WebAssembly Debugging extension](https://goo.gle/wasm-debugging-extension).
-
-Next, enable it in DevTools.
-
-  Open DevTools -> Click the *gear (⚙)* icon in the top right corner -> go to the *Experiments* panel -> and tick *WebAssembly Debugging: Enable DWARF support*.
-
-![Enable Wasm Debugging](/_static/tutorial/debugging/enable-chrome-wasm-debugging.png)
-
-After exitting Settings, you will be prompted to reload DevTools -- reload.
-
-Next, open the options for Chrome WebAssembly Debugging extension:
-
-![Wasm Debugging Options](/_static/tutorial/debugging/devtools-options.png)
-
-Since itk-wasm performs builds in a clean Docker environment, the debugging source paths in the Docker environment are different than the paths on the host system. The debugging extension has a path substitution system that can account for these differences. In the Docker image, the directory where `itk-wasm` is invoked is mounted as `/work`. Substitute `/work` with the directory where the `itk-wasm` CLI is invoked. For example, if `itk-wasm` was invoked at `/home/matt/src/itk-wasm/examples/Debugging`, then:
-
-![Path substitution](/_static/tutorial/debugging/path-substitution.png)
-
-Build the project with itk-wasm and the `Debug` `CMAKE_BUILD_TYPE` to include DWARF debugging information:
-
-![Emscripten build Debug](/_static/tutorial/debugging/emscripten-build-debug.png)
-
-Here we load and run the WebAssembly with a simple HTML file and server:
-
-```html
-<html>
-  <head>
-    <script src="https://cdn.jsdelivr.net/npm/itk-wasm@1.0.0-a.11/dist/umd/itk-wasm.js"></script>
-  </head>
-  <body>
-    <p>This is an example to demonstrate browser-based debugging of
-    C++-generated WebAssembly. For more information, please see the
-    <a target="_blank" href="https://wasm.itk.org/examples/debugging.html">associated
-      documentation</a>.</p>
-
-    <script>
-      window.addEventListener('load', (event) => {
-        const pipeline = new URL('emscripten-build-debug/DebugMe', document.location)
-        itk.runPipeline(null, pipeline)
-      });
-    </script>
-
-  </body>
-</html>
-```
-
-![HTTP Server](/_static/tutorial/debugging/http-server.png)
-
-And we can debug the C++ code in Chrome's DevTools debugger along side the executing JavaScript!
-
-![Debug C++ DevTools](/_static/tutorial/debugging/debug-cxx-devtools.png)
+# Debugging WebAssembly
+
+Effective debugging results in effective programming. **itk-wasm** makes effective debugging of WebAssembly possible.
+
+This example walks through the different techniques that can be used with itk-wasm to debug WebAssembly during development.
+
+We will debug the following C++ code:
+
+```cpp
+#include <iostream>
+
+int main() {
+  std::cout << "Hello debugger world!" << std::endl;
+
+  const char * wasmDetails = "are no longer hidden";
+
+  const int a = 1;
+  const int b = 2;
+  const auto c = a + b;
+
+  // Simulate a crash.
+  abort();
+  return 0;
+}
+```
+
+[The example](https://github.com/InsightSoftwareConsortium/itk-wasm/tree/main/examples/debugging) provides npm scripts as a convenient way to execute debugging commands that you may also invoke directly in a command line shell.
+
+To run these examples, first [install and test](./hello_world) Podman or Docker and Node/NPM. Then, install the package dependencies and run the example commands.
+
+```
+cd itk-wasm/examples/Debugging/
+npm install
+npm run <name>
+```
+
+where `<name>` is the npm script. Available names can be found by calling `npm run` without any arguments.
+
+## Native
+
+The CMake-based itk-wasm build system tooling enables the same C++ build system configuration and code to be reused when building a native system binary or a WebAssembly binary. As a result, native binary debugging tools, such as [GDB](https://sourceware.org/gdb/), [LLDB](https://lldb.llvm.org/), or the [Visual Studio debugger](https://docs.microsoft.com/en-us/visualstudio/debugger/?view=vs-2022).
+
+We can build the project's standard CMake build configuration,
+
+```cmake
+cmake_minimum_required(VERSION 3.10)
+project(DebuggingWebAssemblyExample)
+
+add_executable(DebugMe DebugMe.cxx)
+```
+
+with standard CMake commands,
+
+![Native build](/_static/tutorial/debugging/native-build.png)
+
+The native binary can then be debugged in the standard way. For example, with `gdb` on Linux:
+
+![Native debug Linux](/_static/tutorial/debugging/native-debug-linux.png)
+
+## WASI
+
+The most direct way to debug WebAssembly is through the [WebAssembly System Interface (WASI)](https://wasi.dev/). In itk-wasm we can build to WASI with the [WASI SDK](https://github.com/WebAssembly/wasi-sdk) by specifying the `itkwasm/wasi` toolchain image. A backtrace can quickly be obtained with the `itk-wasm` CLI. Or, a fully fledged debugger session can be started with LLDB.
+
+First, build to WASI WebAssembly with debugging symbols available:
+
+![WASI debug build](/_static/tutorial/debugging/wasi-build-debug.png)
+
+Then, the `itk-wasm` CLI can conveniently run the Wasm binary with the included WASI runtime:
+
+![Run WASI debug](/_static/tutorial/debugging/run-wasi-debug.png)
+
+We can see that `abort` is called in the `main` function at line 13 in `DebugMe.cxx`.
+
+A full debugging session is also possible after [LLDB](https://lldb.llvm.org/) >= 13 and [Wasmtime](https://wasmtime.dev/) are installed.
+
+![LLDB WASI debug](/_static/tutorial/debugging/lldb-wasi-debug.png)
+
+**Note:** when calling `wasmtime` directly and passing local files into a pipeline, `--dir` arguments must be set. This gives `wasmtime` permission to access the directories containing the files. This is required due to WASI's [capability-based security](https://en.wikipedia.org/wiki/Capability-based_security) model. For example, if a file path starts with `./`, then add `--dir ./` arguments to the `wasmtime` invocation. `--dir` can be specified multiple times.
+
+## Node.js
+
+When debugging WebAssembly built with the itk-wasm Emscripten toolchain, set the `CMAKE_BUILD_TYPE` to `Debug` as is required to debug native builds.
+
+As with native builds, this builds debugging symbols, the human-readable names of functions, variables, etc., into the binary.  This also adds support for C++ exceptions and retrieving the string name associated with exceptions. Without this itk-wasm instrumentation, a C++ exception will through an error with an opaque integer value. And, Emscripten JavaScript WebAssembly bindings will not be minified, which facilitates debugging.
+
+When built with the default `Release` build type:
+
+![Emscripten build Release](/_static/tutorial/debugging/emscripten-build-release.png)
+
+the JavaScript support code is minified, and difficult to debug:
+
+![Run Node Release](/_static/tutorial/debugging/run-node-release.png)
+
+However, when built with the `Debug` build type:
+
+![Emscripten build Debug](/_static/tutorial/debugging/emscripten-build-debug.png)
+
+a useful backtrace can be obtained:
+
+![Run Node Debug](/_static/tutorial/debugging/run-node-debug.png)
+
+In order to run a debugger with Node, add the `--inspect-brk` flag when invoking `node`:
+
+![Node inspect](/_static/tutorial/debugging/node-inspect.png)
+
+This will pause execution on start a debugging remote interface. To connect to the remote interface with a Chromium browser, visit `chrome://inspect` and click the *inspect* link on the corresponding *Remote Target*:
+
+![Node inspect Remote Target](/_static/tutorial/debugging/node-inspect-remote-target.png)
+
+This will open the Chrome Dev Tools debugger:
+
+![Node inspect Chrome DevTools](/_static/tutorial/debugging/node-inspect-chrome-dev-tools.png)
+
+Other debugger interfaces [are also available](https://nodejs.org/en/docs/inspector), like a CLI debugger or the VSCode debugger.
+
+This is helpful for debugging issues that occur in Emscripten JavaScript interface. The next section describes how to debug issues inside the Emscripten-generated WebAssembly.
+
+## Chromium-based browsers
+
+Recent Chromium-based browsers have support for debugging C++-based WebAssembly in the browser. With a few extra steps described in this section, it is possible to interactively step through and inspect C++-compiled WebAssembly running in the browser.
+
+WebAssembly debugging in DevTools requires a few extra setup steps from a default browser installation.
+
+First, [install the Chrome WebAssembly Debugging extension](https://goo.gle/wasm-debugging-extension).
+
+Next, enable it in DevTools.
+
+  Open DevTools -> Click the *gear (⚙)* icon in the top right corner -> go to the *Experiments* panel -> and tick *WebAssembly Debugging: Enable DWARF support*.
+
+![Enable Wasm Debugging](/_static/tutorial/debugging/enable-chrome-wasm-debugging.png)
+
+After exitting Settings, you will be prompted to reload DevTools -- reload.
+
+Next, open the options for Chrome WebAssembly Debugging extension:
+
+![Wasm Debugging Options](/_static/tutorial/debugging/devtools-options.png)
+
+Since itk-wasm performs builds in a clean Docker environment, the debugging source paths in the Docker environment are different than the paths on the host system. The debugging extension has a path substitution system that can account for these differences. In the Docker image, the directory where `itk-wasm` is invoked is mounted as `/work`. Substitute `/work` with the directory where the `itk-wasm` CLI is invoked. For example, if `itk-wasm` was invoked at `/home/matt/src/itk-wasm/examples/Debugging`, then:
+
+![Path substitution](/_static/tutorial/debugging/path-substitution.png)
+
+Build the project with itk-wasm and the `Debug` `CMAKE_BUILD_TYPE` to include DWARF debugging information:
+
+![Emscripten build Debug](/_static/tutorial/debugging/emscripten-build-debug.png)
+
+Here we load and run the WebAssembly with a simple HTML file and server:
+
+```html
+<html>
+  <head>
+    <script src="https://cdn.jsdelivr.net/npm/itk-wasm@1.0.0-a.11/dist/umd/itk-wasm.js"></script>
+  </head>
+  <body>
+    <p>This is an example to demonstrate browser-based debugging of
+    C++-generated WebAssembly. For more information, please see the
+    <a target="_blank" href="https://wasm.itk.org/examples/debugging.html">associated
+      documentation</a>.</p>
+
+    <script>
+      window.addEventListener('load', (event) => {
+        const pipeline = new URL('emscripten-build-debug/DebugMe', document.location)
+        itk.runPipeline(null, pipeline)
+      });
+    </script>
+
+  </body>
+</html>
+```
+
+![HTTP Server](/_static/tutorial/debugging/http-server.png)
+
+And we can debug the C++ code in Chrome's DevTools debugger along side the executing JavaScript!
+
+![Debug C++ DevTools](/_static/tutorial/debugging/debug-cxx-devtools.png)
diff --git a/docs/cxx/tutorial/hello_world.md b/docs/cxx/tutorial/hello_world.md
index a80f56fbb..bd584d8c4 100644
--- a/docs/cxx/tutorial/hello_world.md
+++ b/docs/cxx/tutorial/hello_world.md
@@ -1,158 +1,158 @@
-# Hello Wasm World!
-
-## Introduction
-
-This example walks through how to compile a *hello world* executable written in C++ to [WebAssembly](https://webassembly.org/) and how to execute it with standalone WebAssembly runtimes, the Node.js JavaScript runtime, and web browser runtimes!
-
-Before getting started, make sure [Node.js](https://nodejs.org/en/download/) and [Docker](https://docs.docker.com/install/) are installed. On Linux, make sure you can run [`docker` without `sudo`](https://askubuntu.com/questions/477551/how-can-i-use-docker-without-sudo). On Windows, we recommend [WSL 2 with Docker enabled](https://docs.docker.com/desktop/windows/wsl/).
-
-While we recommend following along step-by-step, the complete example can also be found in the [`examples/` directory of the project repository](https://github.com/InsightSoftwareConsortium/itk-wasm/tree/main/examples/hello-world).
-
-Let's get started! 🚀
-
-## Write the code
-
-First, let's create a new directory to house our project.
-
-```sh
-mkdir HelloWorld
-cd HelloWorld
-```
-
-Let's write some code! Populate *hello.cxx* with our Hello World program:
-
-```cpp
-#include <iostream>
-
-int main() {
-  std::cout << "Hello Wasm world!" << std::endl;
-  return 0;
-}
-```
-
-Next, provide a [CMake](https://cmake.org/) build configuration at *CMakeLists.txt*:
-
-```cmake
-cmake_minimum_required(VERSION 3.16)
-project(HelloWorld)
-
-add_executable(hello hello.cxx)
-```
-
-## Install itk-wasm
-
-We use the `add_executable` command to build executables with itk-wasm. The [Emscripten](https://kripken.github.io/emscripten-site/) and [WASI](https://github.com/WebAssembly/wasi-sdk) toolchains along with itk-wasm build and execution configurations are contained in itk-wasm [dockcross](https://github.com/dockcross/dockcross) Docker images invoked by the itk-wasm command line interface (CLI).  Note that the same code can also be built and tested with native operating system toolchains. This is useful for development and debugging.
-
-Build the program with the itk-wasm CLI, `itk-wasm`. This is shipped with the `itk-wasm` Node.js package. First install *itk-wasm* with the Node Package Manager, `npm`, the CLI that ships with Node.js.
-
-```sh
-# Initialize an empty project in the current directory
-npm init --yes
-npm install itk-wasm@1.0.0-b.106
-```
-
-## WASI
-
-Build the project with the WASI `itkwasm/wasi` toolchain in the `./wasi-build/` directory:
-
-```sh
-npx itk-wasm -i itkwasm/wasi build
-```
-
-A `hello.wasi.wasm` WebAssembly binary is built in the `./wasi-build/` directory.
-
-```sh
-❯ ls wasi-build
-build.ninja          CMakeFiles          libwasi-exception-shim.a
-cmake_install.cmake  hello.wasi.wasm
-```
-
-Execute the binary with the `run` `itk-wasm` subcommand.
-
-```sh
-❯ npx itk-wasm run wasi-build/hello.wasi.wasm
-Hello Wasm world!
-```
-
-Congratulations! You just executed a C++ program compiled to WebAssembly. 🎉
-
-The binary can also be executed with other [WASI runtimes](https://github.com/mbasso/awesome-wasm#non-web-embeddings).
-
-## Node.js
-
-For Node.js or the Browser, build the project with the default [Emscripten](https://emscripten.org/) toolchain. The project is built in the `./emscripten-build` directory by default.
-
-```sh
-npx itk-wasm build
-```
-
-To execute the project, create an `index.mjs` JavaScript file to [invoke the module](../api/node_pipelines.html):
-
-```js
-import path from 'path'
-import { runPipelineNode } from 'itk-wasm'
-
-const pipelinePath = path.resolve('emscripten-build', 'hello')
-const args = []
-await runPipelineNode(pipelinePath, args)
-```
-
-**Important**: Inside the *package.json* file, we must also add `"type": "module",` to tell Node.js that we have a modern JavaScript project that uses [ES modules](https://nodejs.org/api/esm.html#modules-ecmascript-modules).
-
-And run it!
-
-```sh
-❯ npx node ./index.mjs
-Hello Wasm world!
-```
-
-Congratulations! You just executed a C++ program in JavaScript. 🎉
-
-## Browser
-
-The same Emscripten Wasm module can be executed in a web browser.
-
-Create an HTML file named `index.html` that will call the Wasm module through JavaScript and display its output in the HTML DOM:
-
-```html
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>itk-wasm Browser Hello World!</title>
-    <meta charset="UTF-8" />
-    <script src="https://cdn.jsdelivr.net/npm/itk-wasm@1.0.0-b.106/dist/umd/itk-wasm.min.js"></script>
-  </head>
-
-  <body>
-    <textarea readonly>WebAssembly output...</textarea>
-
-    <script>
-      const outputTextArea = document.querySelector("textarea");
-      outputTextArea.textContent = "Loading...";
-
-      const wasmURL = new URL('emscripten-build/hello', document.location)
-      const args = []
-      const inputs = null
-      const outputs = null
-      itk.runPipeline(null, wasmURL, args, inputs, outputs).then(
-        ({ stdout, webWorker }) => {
-          webWorker.terminate()
-          outputTextArea.textContent = stdout
-          })
-    </script>
-  </body>
-</html>
-```
-
-Serve the web page and Wasm module with an http server:
-
-```sh
-npm install http-server
-http-server .
-```
-
-And point your browser to `http://127.0.0.1:8080/`.
-
-![Hello Wasm World!](/_static/tutorial/hello_wasm_world.png)
-
-Congratulations! You just executed a C++ program in your web browser. 🎉
+# Hello Wasm World!
+
+## Introduction
+
+This example walks through how to compile a *hello world* executable written in C++ to [WebAssembly](https://webassembly.org/) and how to execute it with standalone WebAssembly runtimes, the Node.js JavaScript runtime, and web browser runtimes!
+
+Before getting started, make sure [Node.js](https://nodejs.org/en/download/) and [Podman (recommended)](https://podman.io/docs/installation) or [Docker](https://docs.docker.com/install/) are installed. On Linux, make sure you can run [`podman` or `docker` without `sudo`](https://askubuntu.com/questions/477551/how-can-i-use-docker-without-sudo). On Windows, we highly recommend [Podman](https://podman.io/docs/installation).
+
+While we recommend following along step-by-step, the complete example can also be found in the [`examples/` directory of the project repository](https://github.com/InsightSoftwareConsortium/itk-wasm/tree/main/examples/hello-world).
+
+Let's get started! 🚀
+
+## Write the code
+
+First, let's create a new directory to house our project.
+
+```sh
+mkdir HelloWorld
+cd HelloWorld
+```
+
+Let's write some code! Populate *hello.cxx* with our Hello World program:
+
+```cpp
+#include <iostream>
+
+int main() {
+  std::cout << "Hello Wasm world!" << std::endl;
+  return 0;
+}
+```
+
+Next, provide a [CMake](https://cmake.org/) build configuration at *CMakeLists.txt*:
+
+```cmake
+cmake_minimum_required(VERSION 3.16)
+project(HelloWorld)
+
+add_executable(hello hello.cxx)
+```
+
+## Install itk-wasm
+
+We use the `add_executable` command to build executables with itk-wasm. The [Emscripten](https://kripken.github.io/emscripten-site/) and [WASI](https://github.com/WebAssembly/wasi-sdk) toolchains along with itk-wasm build and execution configurations are contained in itk-wasm [dockcross](https://github.com/dockcross/dockcross) Docker images invoked by the itk-wasm command line interface (CLI).  Note that the same code can also be built and tested with native operating system toolchains. This is useful for development and debugging.
+
+Build the program with the itk-wasm CLI, `itk-wasm`. This is shipped with the `itk-wasm` Node.js package. First install *itk-wasm* with the Node Package Manager, `npm`, the CLI that ships with Node.js.
+
+```sh
+# Initialize an empty project in the current directory
+npm init --yes
+npm install itk-wasm@1.0.0-b.106
+```
+
+## WASI
+
+Build the project with the WASI `itkwasm/wasi` toolchain in the `./wasi-build/` directory:
+
+```sh
+npx itk-wasm -i itkwasm/wasi build
+```
+
+A `hello.wasi.wasm` WebAssembly binary is built in the `./wasi-build/` directory.
+
+```sh
+❯ ls wasi-build
+build.ninja          CMakeFiles          libwasi-exception-shim.a
+cmake_install.cmake  hello.wasi.wasm
+```
+
+Execute the binary with the `run` `itk-wasm` subcommand.
+
+```sh
+❯ npx itk-wasm run wasi-build/hello.wasi.wasm
+Hello Wasm world!
+```
+
+Congratulations! You just executed a C++ program compiled to WebAssembly. 🎉
+
+The binary can also be executed with other [WASI runtimes](https://github.com/mbasso/awesome-wasm#non-web-embeddings).
+
+## Node.js
+
+For Node.js or the Browser, build the project with the default [Emscripten](https://emscripten.org/) toolchain. The project is built in the `./emscripten-build` directory by default.
+
+```sh
+npx itk-wasm build
+```
+
+To execute the project, create an `index.mjs` JavaScript file to [invoke the module](../api/node_pipelines.html):
+
+```js
+import path from 'path'
+import { runPipelineNode } from 'itk-wasm'
+
+const pipelinePath = path.resolve('emscripten-build', 'hello')
+const args = []
+await runPipelineNode(pipelinePath, args)
+```
+
+**Important**: Inside the *package.json* file, we must also add `"type": "module",` to tell Node.js that we have a modern JavaScript project that uses [ES modules](https://nodejs.org/api/esm.html#modules-ecmascript-modules).
+
+And run it!
+
+```sh
+❯ npx node ./index.mjs
+Hello Wasm world!
+```
+
+Congratulations! You just executed a C++ program in JavaScript. 🎉
+
+## Browser
+
+The same Emscripten Wasm module can be executed in a web browser.
+
+Create an HTML file named `index.html` that will call the Wasm module through JavaScript and display its output in the HTML DOM:
+
+```html
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>itk-wasm Browser Hello World!</title>
+    <meta charset="UTF-8" />
+    <script src="https://cdn.jsdelivr.net/npm/itk-wasm@1.0.0-b.106/dist/umd/itk-wasm.min.js"></script>
+  </head>
+
+  <body>
+    <textarea readonly>WebAssembly output...</textarea>
+
+    <script>
+      const outputTextArea = document.querySelector("textarea");
+      outputTextArea.textContent = "Loading...";
+
+      const wasmURL = new URL('emscripten-build/hello', document.location)
+      const args = []
+      const inputs = null
+      const outputs = null
+      itk.runPipeline(null, wasmURL, args, inputs, outputs).then(
+        ({ stdout, webWorker }) => {
+          webWorker.terminate()
+          outputTextArea.textContent = stdout
+          })
+    </script>
+  </body>
+</html>
+```
+
+Serve the web page and Wasm module with an http server:
+
+```sh
+npm install http-server
+http-server .
+```
+
+And point your browser to `http://127.0.0.1:8080/`.
+
+![Hello Wasm World!](/_static/tutorial/hello_wasm_world.png)
+
+Congratulations! You just executed a C++ program in your web browser. 🎉
diff --git a/docs/development/hacking_itk_wasm.md b/docs/development/hacking_itk_wasm.md
index 726efd183..39d35b1b2 100644
--- a/docs/development/hacking_itk_wasm.md
+++ b/docs/development/hacking_itk_wasm.md
@@ -1,257 +1,257 @@
-# Hacking ITK-Wasm
-
-## TLDR
-
-Install Docker, and
-
-```sh
-npm i -g pnpm
-pnpm install
-pnpm build
-pnpm test
-```
-
-and contribute the patch with standard GitHub best practices.
-
-## Introduction
-
-**Welcome to the ITK community! ☀️**
-
-We are glad you are here and appreciate your contribution. Please keep in mind our [community participation guidelines](https://github.com/InsightSoftwareConsortium/ITK/blob/master/CODE_OF_CONDUCT.md).
-
-We follow [standard GitHub contribution best practices]: pull requests made from forks are tested with continuous integration tests, and updates are made via code review before integration.
-
-We use the [conventional commit] standard for our commit message format in the *itk-wasm* repository.
-
-The C++ core and Docker build environment can be developed independently from the CLI, language-specific libraries, and example packages. The latter are developed as a [pnpm workspace]. Individual packages in the workspace can be developed independently after `pnpm install` is executed at the root.
-
-The following sections describe how to contribute to [**ITK-Wasm's constituent parts**](../introduction/parts.md).
-
-## C++ core
-
-ITK-Wasm's [C++ core](../introduction/parts.md#cxx-core) can be developed with native toolchains. The steps are:
-
-0. Install a [C++ compiler toolchain] and [CMake]
-1. Build [ITK]
-2. Build the `WebAssemblyInterface` module from this module against ITK
-3. Run the tests
-
-From a command line, these steps in brief are:
-
-```sh
-# Build ITK
-git clone https://github.com/InsightSoftwareConsortium/ITK
-cmake -BITK-build -SITK -DBUILD_TESTING=OFF -DModule_MeshToPolyData=ON
-cmake --build ITK-build
-
-# Build the WebAssemblyInterface module from this module against ITK
-git clone https://github.com/InsightSoftwareConsortium/itk-wasm
-cmake -Bitk-wasm-build -Sitk-wasm -DBUILD_TESTING=ON -DITK_DIR=$PWD/ITK-build
-cmake --build itk-wasm-build
-
-# Run the tests
-ctest --test-dir itk-wasm-build
-```
-
-For additional guidance on C++ development, see the [ITK Software Guide].
-
-If changes are needed in the ITK repository, start from the [current repository and branch] where we store changes that are being pushed upstream.
-
-### Testing data
-
-To prevent Git repository bloat, we add testing data through [CMake content links](https://docs.itk.org/en/latest/contributing/upload_binary_data.html) of [Content Identifiers (CIDs)](https://proto.school/anatomy-of-a-cid). To add new test data,
-
-1. Upload the data and download its content link *.cid* file with the [CMake w3 ExternalData Upload Tool](https://content-link-upload.itk.org/)
-2. Move the *.cid* file to *itk-wasm/test/Input/*
-3. Reference the content link with a `DATA{<path>}` call in *itk-wasm/test/CMakeLists.txt*.
-
-## Build environment Docker images
-
-Builds of the [wasm build environment Docker images](../introduction/parts.md#docker) or WebAssembly modules require [Docker](https://docs.docker.com/install/).
-
-* On Linux, make sure you can run [`docker` without `sudo`](https://askubuntu.com/questions/477551/how-can-i-use-docker-without-sudo).
-* On Windows, we recommend [WSL 2 with Docker enabled](https://docs.docker.com/desktop/windows/wsl/). Note that this also avoids licensing issues related to Docker Desktop.
-
-To pull the `latest` the build environment Docker images,
-
-```sh
-./src/docker/pull.sh
-```
-
-To build the `latest` build environment Docker images from the Docker configuration and local C++ core,
-
-```sh
-./src/docker/build.sh --with-debug
-```
-
-The `--with-debug` flag will also build the `latest-debug` tagged images.
-
-To use these locally built images in the pnpm build, remove (*clean*) the old build artifacts from the repository first.
-
-```sh
-# Remove old build artifacts
-pnpm clean
-
-pnpm install
-pnpm build
-```
-
-## Command line interface (CLI)
-
-The `itk-wasm` [command line interface (CLI)](../introduction/parts.md#cli) is a [Node.js / NPM] script developed with the [PNPM] package manager tool. This CLI is included with the [`itk-wasm` package].
-
-Development requires:
-
-1. [Node.js / NPM](https://nodejs.org/en/download/)
-2. Bash, e.g. macOS or Linux terminal, [Windows Git Bash] or [WSL] terminal
-3. [PNPM], which can be installed with `npm i -g pnpm`
-4. Chrome and Firefox installed
-
-To build and test the CLI,
-
-```sh
-git clone https://github.com/InsightSoftwareConsortium/itk-wasm
-cd itk-wasm/packages/core/typescript/itk-wasm
-pnpm install
-pnpm build
-pnpm test
-```
-
-## Language binding libraries
-
-The [language binding libaries](../introduction/parts.md#language-libraries):
-
-### JavaScript
-
-The development of `itk-wasm` is described above in the Command line interface section.
-
-### Python
-
-To develop the `itkwasm` Python package, which configured with [hatch](https://hatch.pypa.io/latest/):
-
-```sh
-git clone https://github.com/InsightSoftwareConsortium/itk-wasm
-cd itk-wasm/packages/core/python/itkwasm
-
-pip install hatch
-hatch run download-pyodide
-hatch run test
-```
-
-## Example packages
-
-[Example packages](../introduction/parts.md#example-packages) are maintained in the *itk-wasm/packages/* directory. These packages contain in the top level directory
-
-1. their C++ pipelines,
-2. CMake configuration code,
-3. a NPM *package.json* file to drive the build with pnpm.
-
-To develop these packages, at the top level, run:
-
-```sh
-npm i -g pnpm
-pnpm install
-pnpm build
-pnpm test
-```
-
-This will build and test the packages in order, according to their dependency topology, and the packages will use local workspace dependencies, including the `itk-wasm` CLI.
-
-The `build` and `test` targets are high-level targets that call other targets. They include targets for direct WebAssembly execution, TypeScript bindings, and Python bindings. These can be called individually either with [pnpm filters] or by changing into the package directory and calling the targets from there.
-
-### Debug builds
-
-In development, it often useful to [build the wasm in debug mode](../cxx/tutorial/debugging.md). To create a debug build, at the root level, clean the tree, call `build:emscripten:debug` and `build:wasi:debug`, then `build` and `test` as usual:
-
-```sh
-pnpm clean
-pnpm build:emscripten:debug
-pnpm build:wasi:debug
-pnpm build
-pnpm test
-```
-
-### Testing data
-
-Testing data is stored in a package's *test/data* directory and shared across the direct wasm and language bindings tests. It is downloaded via the `test:data:download` target, which is called by the `test` target.
-
-To add new or modify testing data, add new files into the *test/data/input/* or *test/data/baseline/* directory, then run:
-
-```sh
-pnpm test:data:pack
-```
-
-This will generate a new *test/data.tar.gz* tarball and output its CID.
-
-Upload the tarball to a publicly hosted HTTP location. A few example free services that can serve files include [FileBase], [web3.storage], and [data.kitware.com].
-
-Then, update the CID and HTTP URL in *package.json*.
-
-## Documentation
-
-The [wasm.itk.org](https://wasm.itk.org/) documentation sources are found at the *docs/* directory.
-
-Documentation for individual packages's documentation can be found in the *typescript/README.md*, or *python/\<dispatch-package-name\>/docs* directory.
-
-To preview wasm.itk.org documentation changes, three options are available.
-
-### Option 0: Edit on GitHub, use the pull request preview
-
-If files are edited with GitHub's web user interface, the pull request will
-build a preview of changes with a pull request check called `docs/readthedocs.org:itkwasm`.
-Click on the *Details* link to view the documentation build preview.
-
-### Option 1: Build and serve locally
-
-To compile the document locally create a python virtual environment and install the required packages.
-
-For example in Linux / macOS:
-
-```bash
-cd itk-wasm/docs
-python -m venv env
-source env/bin/activate
-pip install -r requirements.txt
-```
-
-Use `make html` in this directory to build the documentation.
-Open `_build/html/index.html` in your browser to inspect the result.
-
-### Option 2: Autobuild and serve locally
-
-To automatically rebuild the website with any input markdown changes and serve
-the result, use [sphinx-autobuild]
-
-```bash
-cd itk-wasm/docs
-pip install -r requirements.txt
-pip install sphinx-autobuild
-```
-
-```bash
-sphinx-autobuild -a . _build/html
-```
-
-This will start a server at [http://127.0.0.1:8000](http://127.0.0.1:8000)
-and rebuild whenever the documentation changes.
-
-[C++ compiler toolchain]: https://docs.itk.org/en/latest/supported_compilers.html
-[CMake]: https://cmake.org
-[conventional commit]: https://www.conventionalcommits.org/en/v1.0.0/#summary
-[current repository and branch]: https://github.com/InsightSoftwareConsortium/itk-wasm/blob/main/src/docker/itk-wasm-base/Dockerfile#L39-L41
-[data.kitware.com]: https://data.kitware.com/
-[FileBase]: https://filebase.com/
-[hatch]: https://hatch.pypa.io/latest/
-[`itk-wasm` package]: https://www.npmjs.com/package/itk-wasm
-[`itkwasm` Python package]: https://pypi.org/project/itkwasm/
-[ITK]: https://docs.itk.org
-[ITK Software Guide]: https://itk.org/ItkSoftwareGuide.pdf
-[Node.js / NPM]: https://nodejs.org/en/download/
-[pnpm workspace]: https://pnpm.io/workspaces
-[PNPM]: https://pnpm.io/
-[pnpm filters]: https://pnpm.io/filtering
-[sphinx-autobuild]: https://github.com/executablebooks/sphinx-autobuild
-[standard GitHub contribution best practices]: https://docs.itk.org/en/latest/contributing/index.html
-[web3.storage]: https://web3.storage/
-[Windows Git Bash]: https://gitforwindows.org/
+# Hacking ITK-Wasm
+
+## TLDR
+
+Install Podman, and
+
+```sh
+npm i -g pnpm
+pnpm install
+pnpm build
+pnpm test
+```
+
+and contribute the patch with standard GitHub best practices.
+
+## Introduction
+
+**Welcome to the ITK community! ☀️**
+
+We are glad you are here and appreciate your contribution. Please keep in mind our [community participation guidelines](https://github.com/InsightSoftwareConsortium/ITK/blob/master/CODE_OF_CONDUCT.md).
+
+We follow [standard GitHub contribution best practices]: pull requests made from forks are tested with continuous integration tests, and updates are made via code review before integration.
+
+We use the [conventional commit] standard for our commit message format in the *itk-wasm* repository.
+
+The C++ core and Docker build environment can be developed independently from the CLI, language-specific libraries, and example packages. The latter are developed as a [pnpm workspace]. Individual packages in the workspace can be developed independently after `pnpm install` is executed at the root.
+
+The following sections describe how to contribute to [**ITK-Wasm's constituent parts**](../introduction/parts.md).
+
+## C++ core
+
+ITK-Wasm's [C++ core](../introduction/parts.md#cxx-core) can be developed with native toolchains. The steps are:
+
+0. Install a [C++ compiler toolchain] and [CMake]
+1. Build [ITK]
+2. Build the `WebAssemblyInterface` module from this module against ITK
+3. Run the tests
+
+From a command line, these steps in brief are:
+
+```sh
+# Build ITK
+git clone https://github.com/InsightSoftwareConsortium/ITK
+cmake -BITK-build -SITK -DBUILD_TESTING=OFF -DModule_MeshToPolyData=ON
+cmake --build ITK-build
+
+# Build the WebAssemblyInterface module from this module against ITK
+git clone https://github.com/InsightSoftwareConsortium/itk-wasm
+cmake -Bitk-wasm-build -Sitk-wasm -DBUILD_TESTING=ON -DITK_DIR=$PWD/ITK-build
+cmake --build itk-wasm-build
+
+# Run the tests
+ctest --test-dir itk-wasm-build
+```
+
+For additional guidance on C++ development, see the [ITK Software Guide].
+
+If changes are needed in the ITK repository, start from the [current repository and branch] where we store changes that are being pushed upstream.
+
+### Testing data
+
+To prevent Git repository bloat, we add testing data through [CMake content links](https://docs.itk.org/en/latest/contributing/upload_binary_data.html) of [Content Identifiers (CIDs)](https://proto.school/anatomy-of-a-cid). To add new test data,
+
+1. Upload the data and download its content link *.cid* file with the [CMake w3 ExternalData Upload Tool](https://content-link-upload.itk.org/)
+2. Move the *.cid* file to *itk-wasm/test/Input/*
+3. Reference the content link with a `DATA{<path>}` call in *itk-wasm/test/CMakeLists.txt*.
+
+## Build environment Docker images
+
+Builds of the [wasm build environment Docker images](../introduction/parts.md#docker) or WebAssembly modules require [Podman (recommended)](https://podman.io/docs/installation) or [Docker](https://docs.docker.com/install/).
+
+* On Linux, make sure you can run [`podman` or `docker` without `sudo`](https://askubuntu.com/questions/477551/how-can-i-use-docker-without-sudo).
+* On Windows, we highly recommend [Podman](https://podman.io/docs/installation).
+
+To pull the `latest` the build environment Docker images,
+
+```sh
+./src/docker/pull.sh
+```
+
+To build the `latest` build environment Docker images from the Docker configuration and local C++ core,
+
+```sh
+./src/docker/build.sh --with-debug
+```
+
+The `--with-debug` flag will also build the `latest-debug` tagged images.
+
+To use these locally built images in the pnpm build, remove (*clean*) the old build artifacts from the repository first.
+
+```sh
+# Remove old build artifacts
+pnpm clean
+
+pnpm install
+pnpm build
+```
+
+## Command line interface (CLI)
+
+The `itk-wasm` [command line interface (CLI)](../introduction/parts.md#cli) is a [Node.js / NPM] script developed with the [PNPM] package manager tool. This CLI is included with the [`itk-wasm` package].
+
+Development requires:
+
+1. [Node.js / NPM](https://nodejs.org/en/download/)
+2. Bash, e.g. macOS or Linux terminal, [Windows Git Bash] or [WSL] terminal
+3. [PNPM], which can be installed with `npm i -g pnpm`
+4. Chrome and Firefox installed
+
+To build and test the CLI,
+
+```sh
+git clone https://github.com/InsightSoftwareConsortium/itk-wasm
+cd itk-wasm/packages/core/typescript/itk-wasm
+pnpm install
+pnpm build
+pnpm test
+```
+
+## Language binding libraries
+
+The [language binding libaries](../introduction/parts.md#language-libraries):
+
+### JavaScript
+
+The development of `itk-wasm` is described above in the Command line interface section.
+
+### Python
+
+To develop the `itkwasm` Python package, which configured with [hatch](https://hatch.pypa.io/latest/):
+
+```sh
+git clone https://github.com/InsightSoftwareConsortium/itk-wasm
+cd itk-wasm/packages/core/python/itkwasm
+
+pip install hatch
+hatch run download-pyodide
+hatch run test
+```
+
+## Example packages
+
+[Example packages](../introduction/parts.md#example-packages) are maintained in the *itk-wasm/packages/* directory. These packages contain in the top level directory
+
+1. their C++ pipelines,
+2. CMake configuration code,
+3. a NPM *package.json* file to drive the build with pnpm.
+
+To develop these packages, at the top level, run:
+
+```sh
+npm i -g pnpm
+pnpm install
+pnpm build
+pnpm test
+```
+
+This will build and test the packages in order, according to their dependency topology, and the packages will use local workspace dependencies, including the `itk-wasm` CLI.
+
+The `build` and `test` targets are high-level targets that call other targets. They include targets for direct WebAssembly execution, TypeScript bindings, and Python bindings. These can be called individually either with [pnpm filters] or by changing into the package directory and calling the targets from there.
+
+### Debug builds
+
+In development, it often useful to [build the wasm in debug mode](../cxx/tutorial/debugging.md). To create a debug build, at the root level, clean the tree, call `build:emscripten:debug` and `build:wasi:debug`, then `build` and `test` as usual:
+
+```sh
+pnpm clean
+pnpm build:emscripten:debug
+pnpm build:wasi:debug
+pnpm build
+pnpm test
+```
+
+### Testing data
+
+Testing data is stored in a package's *test/data* directory and shared across the direct wasm and language bindings tests. It is downloaded via the `test:data:download` target, which is called by the `test` target.
+
+To add new or modify testing data, add new files into the *test/data/input/* or *test/data/baseline/* directory, then run:
+
+```sh
+pnpm test:data:pack
+```
+
+This will generate a new *test/data.tar.gz* tarball and output its CID.
+
+Upload the tarball to a publicly hosted HTTP location. A few example free services that can serve files include [FileBase], [web3.storage], and [data.kitware.com].
+
+Then, update the CID and HTTP URL in *package.json*.
+
+## Documentation
+
+The [wasm.itk.org](https://wasm.itk.org/) documentation sources are found at the *docs/* directory.
+
+Documentation for individual packages's documentation can be found in the *typescript/README.md*, or *python/\<dispatch-package-name\>/docs* directory.
+
+To preview wasm.itk.org documentation changes, three options are available.
+
+### Option 0: Edit on GitHub, use the pull request preview
+
+If files are edited with GitHub's web user interface, the pull request will
+build a preview of changes with a pull request check called `docs/readthedocs.org:itkwasm`.
+Click on the *Details* link to view the documentation build preview.
+
+### Option 1: Build and serve locally
+
+To compile the document locally create a python virtual environment and install the required packages.
+
+For example in Linux / macOS:
+
+```bash
+cd itk-wasm/docs
+python -m venv env
+source env/bin/activate
+pip install -r requirements.txt
+```
+
+Use `make html` in this directory to build the documentation.
+Open `_build/html/index.html` in your browser to inspect the result.
+
+### Option 2: Autobuild and serve locally
+
+To automatically rebuild the website with any input markdown changes and serve
+the result, use [sphinx-autobuild]
+
+```bash
+cd itk-wasm/docs
+pip install -r requirements.txt
+pip install sphinx-autobuild
+```
+
+```bash
+sphinx-autobuild -a . _build/html
+```
+
+This will start a server at [http://127.0.0.1:8000](http://127.0.0.1:8000)
+and rebuild whenever the documentation changes.
+
+[C++ compiler toolchain]: https://docs.itk.org/en/latest/supported_compilers.html
+[CMake]: https://cmake.org
+[conventional commit]: https://www.conventionalcommits.org/en/v1.0.0/#summary
+[current repository and branch]: https://github.com/InsightSoftwareConsortium/itk-wasm/blob/main/src/docker/itk-wasm-base/Dockerfile#L39-L41
+[data.kitware.com]: https://data.kitware.com/
+[FileBase]: https://filebase.com/
+[hatch]: https://hatch.pypa.io/latest/
+[`itk-wasm` package]: https://www.npmjs.com/package/itk-wasm
+[`itkwasm` Python package]: https://pypi.org/project/itkwasm/
+[ITK]: https://docs.itk.org
+[ITK Software Guide]: https://itk.org/ItkSoftwareGuide.pdf
+[Node.js / NPM]: https://nodejs.org/en/download/
+[pnpm workspace]: https://pnpm.io/workspaces
+[PNPM]: https://pnpm.io/
+[pnpm filters]: https://pnpm.io/filtering
+[sphinx-autobuild]: https://github.com/executablebooks/sphinx-autobuild
+[standard GitHub contribution best practices]: https://docs.itk.org/en/latest/contributing/index.html
+[web3.storage]: https://web3.storage/
+[Windows Git Bash]: https://gitforwindows.org/
 [WSL]: https://learn.microsoft.com/en-us/windows/wsl/install
\ No newline at end of file
diff --git a/packages/core/typescript/itk-wasm/package.json b/packages/core/typescript/itk-wasm/package.json
index 8a6325c76..f1cd096b1 100644
--- a/packages/core/typescript/itk-wasm/package.json
+++ b/packages/core/typescript/itk-wasm/package.json
@@ -92,6 +92,7 @@
     "@thewtex/zstddec": "^0.2.0",
     "@types/emscripten": "^1.39.10",
     "axios": "^1.6.2",
+    "chalk": "^5.3.0",
     "comlink": "^4.4.1",
     "commander": "^11.1.0",
     "fs-extra": "^11.2.0",
diff --git a/packages/core/typescript/itk-wasm/src/cli/build.js b/packages/core/typescript/itk-wasm/src/cli/build.js
index 5e4d7f4bb..c7a200dba 100644
--- a/packages/core/typescript/itk-wasm/src/cli/build.js
+++ b/packages/core/typescript/itk-wasm/src/cli/build.js
@@ -1,8 +1,9 @@
 import { spawnSync } from 'child_process'
 
-import processCommonOptions from "./process-common-options.js"
+import processCommonOptions from './process-common-options.js'
 
 import program from './program.js'
+import die from './die.js'
 
 function build(options) {
   const { buildDir, dockcrossScript } = processCommonOptions(program)
@@ -12,28 +13,42 @@ function build(options) {
   if (hyphenIndex !== -1) {
     cmakeArgs = program.rawArgs.slice(hyphenIndex + 1)
   }
-  if(process.platform === "win32"){
-    var dockerBuild = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
-      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env web-build ${buildDir} ` + cmakeArgs.join(' ') + '"'], {
-      env: process.env,
-      stdio: 'inherit',
-      shell: true
-    });
+  if (process.platform === 'win32') {
+    var dockerBuild = spawnSync(
+      '"C:\\Program Files\\Git\\bin\\sh.exe"',
+      [
+        '--login',
+        '-i',
+        '-c',
+        `"${buildDir}/itk-wasm-build-env web-build ${buildDir} ` +
+          cmakeArgs.join(' ') +
+          '"'
+      ],
+      {
+        env: process.env,
+        stdio: 'inherit',
+        shell: true
+      }
+    )
 
     if (dockerBuild.status !== 0) {
-      console.error(dockerBuild.error);
+      die(dockerBuild.error)
     }
-    process.exit(dockerBuild.status);
+    process.exit(dockerBuild.status)
   } else {
-    const dockerBuild = spawnSync('bash', [dockcrossScript, 'web-build', buildDir].concat(cmakeArgs), {
-      env: process.env,
-      stdio: 'inherit'
-    })
+    const dockerBuild = spawnSync(
+      'bash',
+      [dockcrossScript, 'web-build', buildDir].concat(cmakeArgs),
+      {
+        env: process.env,
+        stdio: 'inherit'
+      }
+    )
     if (dockerBuild.status !== 0) {
-      console.error(dockerBuild.error);
+      die(dockerBuild.error)
     }
     process.exit(dockerBuild.status)
   }
 }
 
-export default build
\ No newline at end of file
+export default build
diff --git a/packages/core/typescript/itk-wasm/src/cli/die.js b/packages/core/typescript/itk-wasm/src/cli/die.js
new file mode 100644
index 000000000..321b4cd00
--- /dev/null
+++ b/packages/core/typescript/itk-wasm/src/cli/die.js
@@ -0,0 +1,8 @@
+import chalk from 'chalk'
+
+function die(message) {
+  console.error(`\n😵  ${chalk.red(message)}`)
+  process.exit(1)
+}
+
+export default die
diff --git a/packages/core/typescript/itk-wasm/src/cli/find-oci-exe.js b/packages/core/typescript/itk-wasm/src/cli/find-oci-exe.js
new file mode 100644
index 000000000..4895fc0bd
--- /dev/null
+++ b/packages/core/typescript/itk-wasm/src/cli/find-oci-exe.js
@@ -0,0 +1,46 @@
+import fs from 'fs'
+import path from 'path'
+import os from 'os'
+
+import chalk from 'chalk'
+
+import die from './die.js'
+
+function findOciExe() {
+  // Check for OCI_EXE environmental variable
+  const ociExe = process.env.OCI_EXE
+  if (ociExe && fs.existsSync(ociExe)) {
+    return ociExe
+  }
+
+  // Get the PATH environment variable
+  const PATH = process.env.PATH.split(path.delimiter)
+
+  // Check for podman executable
+  const podmanExe = os.platform() === 'win32' ? 'podman.exe' : 'podman'
+  for (let p of PATH) {
+    if (fs.existsSync(path.join(p, podmanExe))) {
+      return podmanExe
+    }
+  }
+
+  // Check for docker executable
+  const dockerExe = os.platform() === 'win32' ? 'docker.exe' : 'docker'
+  for (let p of PATH) {
+    if (fs.existsSync(path.join(p, dockerExe))) {
+      return dockerExe
+    }
+  }
+
+  // If none of the above exist, die
+  die(`${chalk.magenta(`Could not find podman or docker executable in the
+    PATH or OCI_EXE environmental variables.`)}
+
+${chalk.blue(`Please find installation instructions at:
+
+    https://podman.io/docs/installation`)}
+
+`)
+}
+
+export default findOciExe
diff --git a/packages/core/typescript/itk-wasm/src/cli/pnpm-script.js b/packages/core/typescript/itk-wasm/src/cli/pnpm-script.js
index d756d8577..09da0bfb7 100644
--- a/packages/core/typescript/itk-wasm/src/cli/pnpm-script.js
+++ b/packages/core/typescript/itk-wasm/src/cli/pnpm-script.js
@@ -3,9 +3,9 @@ import { spawnSync } from 'child_process'
 
 import path from 'path'
 
-import program from './program.js'
 import defaultImageTag from './default-image-tag.js'
 
+import die from './die.js'
 import { download as damDownload } from '@itk-wasm/dam'
 
 function configValue(
@@ -28,10 +28,9 @@ function configValue(
   }
 
   if (required) {
-    console.error(
+    die(
       `Required option\n\n  --${name}\n\n or package.json entry for\n\n  ["itk-wasm"]["${name}"]\n\n not provided`
     )
-    process.exit(1)
   }
 
   return defaultValue
@@ -74,8 +73,7 @@ async function pnpmScript(name, extraArgs, options) {
     shell: true
   })
   if (pnpmRootProcess.status !== 0) {
-    console.error(pnpmRootProcess.error)
-    process.exit(pnpmRootProcess.status)
+    die(pnpmRootProcess.error)
   }
   const nodeModulesDir = pnpmRootProcess.stdout.toString().trim()
   const packageJsonPath = path.join(nodeModulesDir, '..', 'package.json')
@@ -523,7 +521,7 @@ async function pnpmScript(name, extraArgs, options) {
     shell: true
   })
   if (pnpmProcess.status !== 0) {
-    console.error(pnpmProcess.error)
+    die(pnpmProcess.error)
   }
   process.exit(pnpmProcess.status)
 }
diff --git a/packages/core/typescript/itk-wasm/src/cli/process-common-options.js b/packages/core/typescript/itk-wasm/src/cli/process-common-options.js
index 721f87105..5a5e9dfc6 100644
--- a/packages/core/typescript/itk-wasm/src/cli/process-common-options.js
+++ b/packages/core/typescript/itk-wasm/src/cli/process-common-options.js
@@ -1,103 +1,94 @@
-import fs from 'fs-extra'
-import path from 'path'
-import { spawnSync } from 'child_process'
-
-import defaultImageTag from './default-image-tag.js'
-
-function processCommonOptions(program, wasiDefault=false) {
-  const options = program.opts()
-
-  // Check that we have docker and can run it.
-  const dockerVersion = spawnSync('docker', ['--version'], {
-    env: process.env,
-    stdio: [ 'ignore', 'ignore', 'ignore' ]
-  })
-  if (dockerVersion.status !== 0) {
-    console.error("Could not run the 'docker' command.")
-    console.error('This tool requires Docker.')
-    console.error('')
-    console.error('Please find installation instructions at:')
-    console.error('')
-    console.error('  https://docs.docker.com/install/')
-    console.error('')
-    process.exit(dockerVersion.status)
-  }
-
-  let dockerImage = `itkwasm/emscripten:${defaultImageTag}`
-  if (options.image) {
-    dockerImage = options.image
-    if (dockerImage === 'itkwasm/wasi') {
-      dockerImage = `itkwasm/wasi:${defaultImageTag}`
-    }
-  }
-
-  const dockerImageCheck = spawnSync('docker', ['images', '--quiet', dockerImage], {
-    env: process.env,
-    stdio: 'pipe',
-    encoding: 'utf-8',
-  })
-
-  if (dockerImageCheck.stdout === '') {
-    console.log(`Build environment image not found, pulling ${dockerImage}...`)
-    const dockerPull = spawnSync('docker', ['pull', dockerImage], {
-      env: process.env,
-      stdio: 'inherit',
-      encoding: 'utf-8',
-    })
-    if (dockerPull.status !== 0) {
-      console.error(`Could not pull docker image ${dockerImage}`)
-      process.exit(dockerPull.status)
-    }
-  }
-
-  let sourceDir = '.'
-  if (options.sourceDir) {
-    sourceDir = options.sourceDir
-  }
-
-  // Check that the source directory exists and chdir to it.
-  if (!fs.existsSync(sourceDir)) {
-    console.error('The source directory: ' + sourceDir + ' does not exist!')
-    process.exit(1)
-  }
-  process.chdir(sourceDir)
-
-  let buildDir = dockerImage.includes('wasi') || wasiDefault ? 'wasi-build' : 'emscripten-build'
-  if (options.buildDir) {
-    buildDir = options.buildDir
-  }
-
-  // Make the build directory to hold the dockcross script and the CMake
-  // build.
-  try {
-    fs.mkdirSync(buildDir)
-  } catch (err) {
-    if (err.code !== 'EEXIST') throw err
-  }
-
-  // Ensure we have the 'dockcross' Docker build environment driver script
-  const dockcrossScript = path.join(buildDir, 'itk-wasm-build-env')
-  try {
-    fs.statSync(dockcrossScript)
-  } catch (err) {
-    if (err.code === 'ENOENT') {
-      const output = fs.openSync(dockcrossScript, 'w')
-      const dockerCall = spawnSync('docker', ['run', '--rm', dockerImage], {
-        env: process.env,
-        stdio: [ 'ignore', output, null ]
-      })
-      if (dockerCall.status !== 0) {
-        console.error(dockerCall.stderr.toString())
-        process.exit(dockerCall.status)
-      }
-      fs.closeSync(output)
-      fs.chmodSync(dockcrossScript, '755')
-    } else {
-      throw err
-    }
-  }
-
-  return { dockerImage, dockcrossScript, buildDir }
-}
-
-export default processCommonOptions
\ No newline at end of file
+import fs from 'fs-extra'
+import path from 'path'
+import { spawnSync } from 'child_process'
+
+import defaultImageTag from './default-image-tag.js'
+import findOciExe from './find-oci-exe.js'
+
+function processCommonOptions(program, wasiDefault = false) {
+  const options = program.opts()
+
+  const ociExePath = findOciExe()
+
+  let dockerImage = `itkwasm/emscripten:${defaultImageTag}`
+  if (options.image) {
+    dockerImage = options.image
+    if (dockerImage === 'itkwasm/wasi') {
+      dockerImage = `itkwasm/wasi:${defaultImageTag}`
+    }
+  }
+
+  const dockerImageCheck = spawnSync(
+    ociExePath,
+    ['images', '--quiet', dockerImage],
+    {
+      env: process.env,
+      stdio: 'pipe',
+      encoding: 'utf-8'
+    }
+  )
+
+  if (dockerImageCheck.stdout === '') {
+    console.log(`Build environment image not found, pulling ${dockerImage}...`)
+    const dockerPull = spawnSync(ociExePath, ['pull', dockerImage], {
+      env: process.env,
+      stdio: 'inherit',
+      encoding: 'utf-8'
+    })
+    if (dockerPull.status !== 0) {
+      die(`Could not pull docker image ${dockerImage}`)
+    }
+  }
+
+  let sourceDir = '.'
+  if (options.sourceDir) {
+    sourceDir = options.sourceDir
+  }
+
+  // Check that the source directory exists and chdir to it.
+  if (!fs.existsSync(sourceDir)) {
+    die('The source directory: ' + sourceDir + ' does not exist!')
+  }
+  process.chdir(sourceDir)
+
+  let buildDir =
+    dockerImage.includes('wasi') || wasiDefault
+      ? 'wasi-build'
+      : 'emscripten-build'
+  if (options.buildDir) {
+    buildDir = options.buildDir
+  }
+
+  // Make the build directory to hold the dockcross script and the CMake
+  // build.
+  try {
+    fs.mkdirSync(buildDir)
+  } catch (err) {
+    if (err.code !== 'EEXIST') throw err
+  }
+
+  // Ensure we have the 'dockcross' Docker build environment driver script
+  const dockcrossScript = path.join(buildDir, 'itk-wasm-build-env')
+  try {
+    fs.statSync(dockcrossScript)
+  } catch (err) {
+    if (err.code === 'ENOENT') {
+      const output = fs.openSync(dockcrossScript, 'w')
+      const dockerCall = spawnSync(ociExePath, ['run', '--rm', dockerImage], {
+        env: process.env,
+        stdio: ['ignore', output, null]
+      })
+      if (dockerCall.status !== 0) {
+        die(dockerCall.stderr.toString())
+      }
+      fs.closeSync(output)
+      fs.chmodSync(dockcrossScript, '755')
+    } else {
+      throw err
+    }
+  }
+
+  return { dockerImage, dockcrossScript, buildDir }
+}
+
+export default processCommonOptions
diff --git a/packages/core/typescript/itk-wasm/src/cli/publish.js b/packages/core/typescript/itk-wasm/src/cli/publish.js
index 7f374c4d6..1886868c5 100644
--- a/packages/core/typescript/itk-wasm/src/cli/publish.js
+++ b/packages/core/typescript/itk-wasm/src/cli/publish.js
@@ -4,60 +4,124 @@ import path from 'path'
 import glob from 'glob'
 
 import runHatch from './run-hatch.js'
+import die from './die.js'
 
 function publish(options) {
   const iface = options.interface ?? 'python'
   const outputDir = options.outputDir ?? iface
   if (!fs.existsSync(outputDir)) {
-    console.error(`Could not find output bindings directory: ${outputDir}`)
-    process.exit(1)
+    die(`Could not find output bindings directory: ${outputDir}`)
   }
 
-  const micromambaBinaryPath = options.micromambaBinaryPath ?? path.join(outputDir, '..', 'micromamba', 'micromamba')
+  const micromambaBinaryPath =
+    options.micromambaBinaryPath ??
+    path.join(outputDir, '..', 'micromamba', 'micromamba')
   if (!fs.existsSync(micromambaBinaryPath)) {
-    console.error(`Could not find micromamba binary: ${micromambaBinaryPath}`)
-    process.exit(1)
+    die(`Could not find micromamba binary: ${micromambaBinaryPath}`)
   }
 
-  const micromambaRootPath = options.micromambaRootPath ?? path.join(outputDir, '..', 'micromamba')
+  const micromambaRootPath =
+    options.micromambaRootPath ?? path.join(outputDir, '..', 'micromamba')
   if (!fs.existsSync(micromambaRootPath)) {
-    console.error(`Could not find micromamba root: ${micromambaRootPath}`)
-    process.exit(1)
+    die(`Could not find micromamba root: ${micromambaRootPath}`)
   }
 
-  const micromambaName = options.micromambaName ?? path.basename(path.resolve(path.join(outputDir, '..')))
+  const micromambaName =
+    options.micromambaName ??
+    path.basename(path.resolve(path.join(outputDir, '..')))
 
   const packageName = options.packageName
 
   switch (iface) {
     case 'python':
       let packagePath = path.join(outputDir, `${packageName}-wasi`)
-      runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['build'])
-      let currentVersion = runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version']).trim()
+      runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['build']
+      )
+      let currentVersion = runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['version']
+      ).trim()
       console.log(`Publishing ${packageName}-wasi version ${currentVersion}`)
-      let artifacts = glob.sync(path.join(path.resolve(packagePath), 'dist', `*${currentVersion}*`))
-      runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['publish'].concat(artifacts))
+      let artifacts = glob.sync(
+        path.join(path.resolve(packagePath), 'dist', `*${currentVersion}*`)
+      )
+      runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['publish'].concat(artifacts)
+      )
 
       packagePath = path.join(outputDir, `${packageName}-emscripten`)
-      runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['build'])
-      currentVersion = runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version']).trim()
-      console.log(`Publishing ${packageName}-emscripten version ${currentVersion}`)
-      artifacts = glob.sync(path.join(path.resolve(packagePath), 'dist', `*${currentVersion}*`))
-      runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['publish'].concat(artifacts))
+      runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['build']
+      )
+      currentVersion = runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['version']
+      ).trim()
+      console.log(
+        `Publishing ${packageName}-emscripten version ${currentVersion}`
+      )
+      artifacts = glob.sync(
+        path.join(path.resolve(packagePath), 'dist', `*${currentVersion}*`)
+      )
+      runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['publish'].concat(artifacts)
+      )
 
       packagePath = path.join(outputDir, packageName)
-      runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['build'])
-      currentVersion = runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version']).trim()
+      runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['build']
+      )
+      currentVersion = runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['version']
+      ).trim()
       console.log(`Publishing ${packageName} version ${currentVersion}`)
-      artifacts = glob.sync(path.join(path.resolve(packagePath), 'dist', `*${currentVersion}*`))
-      runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['publish'].concat(artifacts))
+      artifacts = glob.sync(
+        path.join(path.resolve(packagePath), 'dist', `*${currentVersion}*`)
+      )
+      runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['publish'].concat(artifacts)
+      )
       break
     default:
-      console.error(`Unexpected interface: ${iface}`)
-      process.exit(1)
+      die(`Unexpected interface: ${iface}`)
   }
 
   process.exit(0)
 }
 
-export default publish
\ No newline at end of file
+export default publish
diff --git a/packages/core/typescript/itk-wasm/src/cli/run-hatch.js b/packages/core/typescript/itk-wasm/src/cli/run-hatch.js
index 97e684667..0880c9dbf 100644
--- a/packages/core/typescript/itk-wasm/src/cli/run-hatch.js
+++ b/packages/core/typescript/itk-wasm/src/cli/run-hatch.js
@@ -1,16 +1,36 @@
 import { spawnSync } from 'child_process'
 
-function runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, cwd, hatchArgs) {
-  let versionProcess = spawnSync(micromambaBinaryPath, ['-r', micromambaRootPath, 'run', '-n', micromambaName, '--cwd', cwd, 'hatch'].concat(hatchArgs), {
-    env: process.env,
-    stdio: 'pipe',
-    encoding: 'utf-8',
-  })
+import die from './die.js'
+
+function runHatch(
+  micromambaBinaryPath,
+  micromambaRootPath,
+  micromambaName,
+  cwd,
+  hatchArgs
+) {
+  let versionProcess = spawnSync(
+    micromambaBinaryPath,
+    [
+      '-r',
+      micromambaRootPath,
+      'run',
+      '-n',
+      micromambaName,
+      '--cwd',
+      cwd,
+      'hatch'
+    ].concat(hatchArgs),
+    {
+      env: process.env,
+      stdio: 'pipe',
+      encoding: 'utf-8'
+    }
+  )
   if (versionProcess.status !== 0) {
-    console.error(`Could not run hatch at ${cwd} version with args ${hatchArgs}`)
-    process.exit(versionProcess.status)
+    die(`Could not run hatch at ${cwd} version with args ${hatchArgs}`)
   }
   return versionProcess.stdout.toString()
 }
 
-export default runHatch
\ No newline at end of file
+export default runHatch
diff --git a/packages/core/typescript/itk-wasm/src/cli/run.js b/packages/core/typescript/itk-wasm/src/cli/run.js
index 976694656..20291271a 100644
--- a/packages/core/typescript/itk-wasm/src/cli/run.js
+++ b/packages/core/typescript/itk-wasm/src/cli/run.js
@@ -1,9 +1,10 @@
 import fs from 'fs-extra'
 import { spawnSync } from 'child_process'
 
-import processCommonOptions from "./process-common-options.js"
+import processCommonOptions from './process-common-options.js'
 
 import program from './program.js'
+import die from './die.js'
 
 function run(wasmBinary, options) {
   const { buildDir, dockcrossScript } = processCommonOptions(program, true)
@@ -23,46 +24,65 @@ function run(wasmBinary, options) {
     wasmRuntime = options.runtime
   }
   let wasmRuntimeArgs = []
-  const quotes = process.platform === "win32" ? '\'' : ''
+  const quotes = process.platform === 'win32' ? "'" : ''
   switch (wasmRuntime) {
-  case 'wasmtime':
-    wasmRuntimeArgs = ['--args', `${quotes}-e WASMTIME_BACKTRACE_DETAILS=1${quotes}`, 'wasmtime-pwd.sh',]
-    break
-  case 'wasmer':
-    wasmRuntimeArgs = ['sudo', 'wasmer-pwd.sh',]
-    break
-  case 'wasm3':
-    wasmRuntimeArgs = ['wasm3',]
-    break
-  case 'wavm':
-    wasmRuntimeArgs = ['wavm', 'run']
-    break
-  default:
-    throw Error('unexpected wasm runtime')
+    case 'wasmtime':
+      wasmRuntimeArgs = [
+        '--args',
+        `${quotes}-e WASMTIME_BACKTRACE_DETAILS=1${quotes}`,
+        'wasmtime-pwd.sh'
+      ]
+      break
+    case 'wasmer':
+      wasmRuntimeArgs = ['sudo', 'wasmer-pwd.sh']
+      break
+    case 'wasm3':
+      wasmRuntimeArgs = ['wasm3']
+      break
+    case 'wavm':
+      wasmRuntimeArgs = ['wavm', 'run']
+      break
+    default:
+      throw Error('unexpected wasm runtime')
   }
 
-  if(process.platform === "win32"){
-    var dockerRun = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
-      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env ${wasmRuntimeArgs.join(' ')} ${wasmBinaryRelativePath} ${wasmBinaryArgs.join(' ')}"`], {
-      env: process.env,
-      stdio: 'inherit',
-      shell: true
-    });
+  if (process.platform === 'win32') {
+    var dockerRun = spawnSync(
+      '"C:\\Program Files\\Git\\bin\\sh.exe"',
+      [
+        '--login',
+        '-i',
+        '-c',
+        `"${buildDir}/itk-wasm-build-env ${wasmRuntimeArgs.join(' ')} ${wasmBinaryRelativePath} ${wasmBinaryArgs.join(' ')}"`
+      ],
+      {
+        env: process.env,
+        stdio: 'inherit',
+        shell: true
+      }
+    )
 
     if (dockerRun.status !== 0) {
-      console.error(dockerRun.error);
+      die(dockerRun.error)
     }
-    process.exit(dockerRun.status);
+    process.exit(dockerRun.status)
   } else {
-    const dockerRun = spawnSync('bash', [dockcrossScript,].concat(wasmRuntimeArgs).concat(wasmBinaryRelativePath).concat(wasmBinaryArgs), {
-      env: process.env,
-      stdio: 'inherit'
-    })
+    const dockerRun = spawnSync(
+      'bash',
+      [dockcrossScript]
+        .concat(wasmRuntimeArgs)
+        .concat(wasmBinaryRelativePath)
+        .concat(wasmBinaryArgs),
+      {
+        env: process.env,
+        stdio: 'inherit'
+      }
+    )
     if (dockerRun.status !== 0) {
-      console.error(dockerRun.error);
+      die(dockerRun.error)
     }
     process.exit(dockerRun.status)
   }
 }
 
-export default run
\ No newline at end of file
+export default run
diff --git a/packages/core/typescript/itk-wasm/src/cli/version-sync.js b/packages/core/typescript/itk-wasm/src/cli/version-sync.js
index b42cca147..bd73cb460 100644
--- a/packages/core/typescript/itk-wasm/src/cli/version-sync.js
+++ b/packages/core/typescript/itk-wasm/src/cli/version-sync.js
@@ -2,67 +2,109 @@ import fs from 'fs-extra'
 import path from 'path'
 
 import runHatch from './run-hatch.js'
+import die from './die.js'
 
 function versionSync(options) {
   const iface = options.interface ?? 'python'
   const outputDir = options.outputDir ?? iface
   if (!fs.existsSync(outputDir)) {
-    console.error(`Could not find output bindings directory: ${outputDir}`)
-    process.exit(1)
+    die(`Could not find output bindings directory: ${outputDir}`)
   }
 
-  const typescriptDir = options.typescriptDir ?? path.join(outputDir, '..', 'typescript')
+  const typescriptDir =
+    options.typescriptDir ?? path.join(outputDir, '..', 'typescript')
   if (!fs.existsSync(typescriptDir)) {
-    console.error(`Could not find typescript bindings directory: ${typescriptDir}`)
-    process.exit(1)
+    die(`Could not find typescript bindings directory: ${typescriptDir}`)
   }
   const typescriptPackageJsonPath = path.join(typescriptDir, 'package.json')
-  const typescriptVersion = JSON.parse(fs.readFileSync(typescriptPackageJsonPath)).version
-  console.log(`Syncing ${iface} bindings version to typescript bindings version: ${typescriptVersion}`)
+  const typescriptVersion = JSON.parse(
+    fs.readFileSync(typescriptPackageJsonPath)
+  ).version
+  console.log(
+    `Syncing ${iface} bindings version to typescript bindings version: ${typescriptVersion}`
+  )
 
-  const micromambaBinaryPath = options.micromambaBinaryPath ?? path.join(outputDir, '..', 'micromamba', 'micromamba')
+  const micromambaBinaryPath =
+    options.micromambaBinaryPath ??
+    path.join(outputDir, '..', 'micromamba', 'micromamba')
   if (!fs.existsSync(micromambaBinaryPath)) {
-    console.error(`Could not find micromamba binary: ${micromambaBinaryPath}`)
-    process.exit(1)
+    die(`Could not find micromamba binary: ${micromambaBinaryPath}`)
   }
 
-  const micromambaRootPath = options.micromambaRootPath ?? path.join(outputDir, '..', 'micromamba')
+  const micromambaRootPath =
+    options.micromambaRootPath ?? path.join(outputDir, '..', 'micromamba')
   if (!fs.existsSync(micromambaRootPath)) {
-    console.error(`Could not find micromamba root: ${micromambaRootPath}`)
-    process.exit(1)
+    die(`Could not find micromamba root: ${micromambaRootPath}`)
   }
 
-  const micromambaName = options.micromambaName ?? path.basename(path.resolve(path.join(outputDir, '..')))
+  const micromambaName =
+    options.micromambaName ??
+    path.basename(path.resolve(path.join(outputDir, '..')))
 
   const packageName = options.packageName
 
   switch (iface) {
     case 'python':
       let packagePath = path.join(outputDir, packageName)
-      let currentVersion = runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version']).trim()
+      let currentVersion = runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['version']
+      ).trim()
       if (currentVersion !== typescriptVersion) {
         console.log(`Syncing ${packagePath} version`)
-        runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version', typescriptVersion])
+        runHatch(
+          micromambaBinaryPath,
+          micromambaRootPath,
+          micromambaName,
+          packagePath,
+          ['version', typescriptVersion]
+        )
       }
       packagePath = path.join(outputDir, `${packageName}-emscripten`)
-      currentVersion = runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version']).trim()
+      currentVersion = runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['version']
+      ).trim()
       if (currentVersion !== typescriptVersion) {
         console.log(`Syncing ${packagePath} version`)
-        runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version', typescriptVersion])
+        runHatch(
+          micromambaBinaryPath,
+          micromambaRootPath,
+          micromambaName,
+          packagePath,
+          ['version', typescriptVersion]
+        )
       }
       packagePath = path.join(outputDir, `${packageName}-wasi`)
-      currentVersion = runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version']).trim()
+      currentVersion = runHatch(
+        micromambaBinaryPath,
+        micromambaRootPath,
+        micromambaName,
+        packagePath,
+        ['version']
+      ).trim()
       if (currentVersion !== typescriptVersion) {
         console.log(`Syncing ${packagePath} version`)
-        runHatch(micromambaBinaryPath, micromambaRootPath, micromambaName, packagePath, ['version', typescriptVersion])
+        runHatch(
+          micromambaBinaryPath,
+          micromambaRootPath,
+          micromambaName,
+          packagePath,
+          ['version', typescriptVersion]
+        )
       }
       break
     default:
-      console.error(`Unexpected interface: ${iface}`)
-      process.exit(1)
+      die(`Unexpected interface: ${iface}`)
   }
 
   process.exit(0)
 }
 
-export default versionSync
\ No newline at end of file
+export default versionSync
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a958bd957..555fae655 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -310,6 +310,9 @@ importers:
       axios:
         specifier: ^1.6.2
         version: 1.6.2(debug@4.3.4)
+      chalk:
+        specifier: ^5.3.0
+        version: 5.3.0
       comlink:
         specifier: ^4.4.1
         version: 4.4.1
diff --git a/src/docker/itk-wasm-base/build.sh b/src/docker/itk-wasm-base/build.sh
index c06d3b539..d84f0ddf2 100755
--- a/src/docker/itk-wasm-base/build.sh
+++ b/src/docker/itk-wasm-base/build.sh
@@ -3,6 +3,8 @@
 set -exo pipefail
 
 script_dir="`cd $(dirname $0); pwd`"
+source "$script_dir/../oci_exe.sh"
+exe=$(ociExe)
 
 TAG=$(date '+%Y%m%d')-$(git rev-parse --short HEAD)
 VCS_REF=$(git rev-parse --short HEAD)
@@ -34,7 +36,7 @@ emscripten_debug_c_flags="-fno-lto -Wno-warn-absolute-paths"
 wasi_debug_ld_flags="-fno-lto -lwasi-emulated-process-clocks -lwasi-emulated-signal -lc-printscan-long-double"
 wasi_debug_c_flags="-fno-lto -D_WASI_EMULATED_PROCESS_CLOCKS -D_WASI_EMULATED_SIGNAL"
 
-docker build -t itkwasm/emscripten-base:latest \
+$exe build -t itkwasm/emscripten-base:latest \
         --build-arg IMAGE=itkwasm/emscripten-base \
         --build-arg CMAKE_BUILD_TYPE=Release \
         --build-arg VCS_REF=${VCS_REF} \
@@ -42,7 +44,7 @@ docker build -t itkwasm/emscripten-base:latest \
         --build-arg BUILD_DATE=${BUILD_DATE} \
         $script_dir $@
 if $version_tag; then
-        docker build -t itkwasm/emscripten-base:${TAG} \
+        $exe build -t itkwasm/emscripten-base:${TAG} \
                 --build-arg IMAGE=itkwasm/emscripten-base \
                 --build-arg CMAKE_BUILD_TYPE=Release \
                 --build-arg VERSION=${TAG} \
@@ -53,7 +55,7 @@ if $version_tag; then
 fi
 
 if $wasi; then
-  docker build -t itkwasm/wasi-base:latest \
+  $exe build -t itkwasm/wasi-base:latest \
           --build-arg IMAGE=itkwasm/wasi-base \
           --build-arg CMAKE_BUILD_TYPE=Release \
           --build-arg VCS_REF=${VCS_REF} \
@@ -64,7 +66,7 @@ if $wasi; then
           --build-arg CFLAGS="${wasi_c_flags}" \
           $script_dir $@
         if $version_tag; then
-                docker build -t itkwasm/wasi-base:${TAG} \
+                $exe build -t itkwasm/wasi-base:${TAG} \
                         --build-arg IMAGE=itkwasm/wasi-base \
                         --build-arg CMAKE_BUILD_TYPE=Release \
                         --build-arg VERSION=${TAG} \
@@ -80,7 +82,7 @@ fi
 
 
 if $debug; then
-  docker build -t itkwasm/emscripten-base:latest-debug \
+  $exe build -t itkwasm/emscripten-base:latest-debug \
           --build-arg IMAGE=itkwasm/emscripten-base \
           --build-arg CMAKE_BUILD_TYPE=Debug \
           --build-arg USE_DCMTK=OFF \
@@ -91,7 +93,7 @@ if $debug; then
           --build-arg CFLAGS="${emscripten_debug_c_flags}" \
           $script_dir $@
   if $version_tag; then
-        docker build -t itkwasm/emscripten-base:${TAG}-debug \
+        $exe build -t itkwasm/emscripten-base:${TAG}-debug \
                 --build-arg IMAGE=itkwasm/emscripten-base \
                 --build-arg CMAKE_BUILD_TYPE=Debug \
                 --build-arg USE_DCMTK=OFF \
@@ -104,7 +106,7 @@ if $debug; then
                 $script_dir $@
   fi
   if $wasi; then
-    docker build -t itkwasm/wasi-base:latest-debug \
+    $exe build -t itkwasm/wasi-base:latest-debug \
             --build-arg IMAGE=itkwasm/wasi-base \
             --build-arg CMAKE_BUILD_TYPE=Debug \
             --build-arg VCS_REF=${VCS_REF} \
@@ -115,7 +117,7 @@ if $debug; then
             --build-arg CFLAGS="${wasi_debug_c_flags}" \
             $script_dir $@
     if $version_tag; then
-        docker build -t itkwasm/wasi-base:${TAG}-debug \
+        $exe build -t itkwasm/wasi-base:${TAG}-debug \
                 --build-arg IMAGE=itkwasm/wasi-base \
                 --build-arg CMAKE_BUILD_TYPE=Debug \
                 --build-arg VERSION=${TAG} \
diff --git a/src/docker/itk-wasm/Dockerfile b/src/docker/itk-wasm/Dockerfile
index efaaa1b90..308d84cb7 100644
--- a/src/docker/itk-wasm/Dockerfile
+++ b/src/docker/itk-wasm/Dockerfile
@@ -40,8 +40,7 @@ COPY web-build /usr/local/bin/
 
 # Trigger Emscripten to cache builds of required system libraries
 ADD median-filter-pipelineCopy /median-filter-pipelineCopy
-ADD populate-cache.sh /usr/local/bin/populate-cache.sh
-RUN /usr/local/bin/populate-cache.sh && rm /usr/local/bin/populate-cache.sh
+RUN if [ $BASE_IMAGE = "itkwasm/emscripten-base" ]; then cd /median-filter-pipelineCopy; /usr/local/bin/web-build emscripten-build -DCMAKE_EXE_LINKER_FLAGS='-flto=thin -s DISABLE_EXCEPTION_CATCHING=0'; rm -rf ./web-build; /usr/local/bin/web-build emscripten-build -DCMAKE_EXE_LINKER_FLAGS='-flto -s DISABLE_EXCEPTION_CATCHING=0'; rm -rf ./web-build; /usr/local/bin/web-build emscripten-build -DCMAKE_EXE_LINKER_FLAGS='-fno-lto -s DISABLE_EXCEPTION_CATCHING=1'; rm -rf /median-filter-pipelineCopy; chmod -R 777 /emsdk/upstream/emscripten/cache; fi
 
 # Build-time metadata as defined at http://label-schema.org
 ARG BUILD_DATE
diff --git a/src/docker/itk-wasm/build.sh b/src/docker/itk-wasm/build.sh
index 33d9e0754..69b706602 100755
--- a/src/docker/itk-wasm/build.sh
+++ b/src/docker/itk-wasm/build.sh
@@ -3,15 +3,18 @@
 set -exo pipefail
 
 script_dir="`cd $(dirname $0); pwd`"
+source "$script_dir/../oci_exe.sh"
+exe=$(ociExe)
 
 cd $script_dir
 
 mkdir -p ITKWebAssemblyInterfaceModuleCopy
-rsync -a ../../../{include,CMakeLists.txt,itk-module.cmake} ./ITKWebAssemblyInterfaceModuleCopy/
-rsync -a ../../../src/{*.cxx,CMakeLists.txt} ./ITKWebAssemblyInterfaceModuleCopy/src/
-rsync -a ../../../src/emscripten-module ./ITKWebAssemblyInterfaceModuleCopy/src/
+cp_exe=$(which rsync 2>/dev/null || which cp)
+$cp_exe -a ../../../{include,CMakeLists.txt,itk-module.cmake} ./ITKWebAssemblyInterfaceModuleCopy/
+$cp_exe -a ../../../src/{*.cxx,CMakeLists.txt} ./ITKWebAssemblyInterfaceModuleCopy/src/
+$cp_exe -a ../../../src/emscripten-module ./ITKWebAssemblyInterfaceModuleCopy/src/
 mkdir -p median-filter-pipelineCopy
-rsync -a ../../../packages/core/typescript/itk-wasm/test/pipelines/median-filter-pipeline/{CMakeLists.txt,median-filter-test.cxx} ./median-filter-pipelineCopy
+$cp_exe -a ../../../packages/core/typescript/itk-wasm/test/pipelines/median-filter-pipeline/{CMakeLists.txt,median-filter-test.cxx} ./median-filter-pipelineCopy
 
 TAG=$(date '+%Y%m%d')-$(git rev-parse --short HEAD)
 VCS_REF=$(git rev-parse --short HEAD)
@@ -34,7 +37,7 @@ for param; do
 done
 set -- "${newparams[@]}"  # overwrites the original positional params
 
-docker build -t itkwasm/emscripten:latest \
+$exe build -t itkwasm/emscripten:latest \
         --build-arg IMAGE=itkwasm/emscripten \
         --build-arg CMAKE_BUILD_TYPE=Release \
         --build-arg VCS_REF=${VCS_REF} \
@@ -42,7 +45,7 @@ docker build -t itkwasm/emscripten:latest \
         --build-arg BUILD_DATE=${BUILD_DATE} \
         $script_dir $@
 if $version_tag; then
-        docker build -t itkwasm/emscripten:${TAG} \
+        $exe build -t itkwasm/emscripten:${TAG} \
                 --build-arg IMAGE=itkwasm/emscripten \
                 --build-arg CMAKE_BUILD_TYPE=Release \
                 --build-arg VERSION=${TAG} \
@@ -53,7 +56,7 @@ if $version_tag; then
 fi
 
 if $wasi; then
-  docker build -t itkwasm/wasi:latest  \
+  $exe build -t itkwasm/wasi:latest  \
           --build-arg IMAGE=itkwasm/wasi \
           --build-arg CMAKE_BUILD_TYPE=Release \
           --build-arg BASE_IMAGE=itkwasm/wasi-base \
@@ -62,7 +65,7 @@ if $wasi; then
           --build-arg BUILD_DATE=${BUILD_DATE} \
           $script_dir $@
   if $version_tag; then
-        docker build -t itkwasm/wasi:${TAG} \
+        $exe build -t itkwasm/wasi:${TAG} \
                 --build-arg IMAGE=itkwasm/wasi \
                 --build-arg CMAKE_BUILD_TYPE=Release \
                 --build-arg VERSION=${TAG} \
@@ -76,7 +79,7 @@ if $wasi; then
 fi
 
 if $debug; then
-  docker build -t itkwasm/emscripten:latest-debug \
+  $exe build -t itkwasm/emscripten:latest-debug \
           --build-arg IMAGE=itkwasm/emscripten \
           --build-arg CMAKE_BUILD_TYPE=Debug \
           --build-arg BASE_TAG=${TAG}-debug \
@@ -85,7 +88,7 @@ if $debug; then
           --build-arg BUILD_DATE=${BUILD_DATE} \
           $script_dir $@
   if $version_tag; then
-        docker build -t itkwasm/emscripten:${TAG}-debug \
+        $exe build -t itkwasm/emscripten:${TAG}-debug \
                 --build-arg IMAGE=itkwasm/emscripten \
                 --build-arg CMAKE_BUILD_TYPE=Debug \
                 --build-arg VERSION=${TAG}-debug \
@@ -96,7 +99,7 @@ if $debug; then
                 $script_dir $@
   fi
   if $wasi; then
-    docker build -t itkwasm/wasi:latest-debug  \
+    $exe build -t itkwasm/wasi:latest-debug  \
             --build-arg IMAGE=itkwasm/wasi \
             --build-arg CMAKE_BUILD_TYPE=Debug \
             --build-arg BASE_IMAGE=itkwasm/wasi-base \
@@ -106,7 +109,7 @@ if $debug; then
             --build-arg BUILD_DATE=${BUILD_DATE} \
             $script_dir $@
     if $version_tag; then
-        docker build -t itkwasm/wasi:${TAG}-debug \
+        $exe build -t itkwasm/wasi:${TAG}-debug \
                 --build-arg IMAGE=itkwasm/wasi \
                 --build-arg CMAKE_BUILD_TYPE=Debug \
                 --build-arg VERSION=${TAG} \
diff --git a/src/docker/itk-wasm/populate-cache.sh b/src/docker/itk-wasm/populate-cache.sh
deleted file mode 100755
index 270485a3a..000000000
--- a/src/docker/itk-wasm/populate-cache.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-
-if [ $BASE_IMAGE = "itkwasm/emscripten-base" ]; then
-  cd /median-filter-pipelineCopy
-
-  /usr/local/bin/web-build emscripten-build -DCMAKE_EXE_LINKER_FLAGS='-flto=thin -s DISABLE_EXCEPTION_CATCHING=0'
-  rm -rf ./web-build
-
-  /usr/local/bin/web-build emscripten-build -DCMAKE_EXE_LINKER_FLAGS='-flto -s DISABLE_EXCEPTION_CATCHING=0'
-  rm -rf ./web-build
-
-  /usr/local/bin/web-build emscripten-build -DCMAKE_EXE_LINKER_FLAGS='-fno-lto -s DISABLE_EXCEPTION_CATCHING=1'
-
-  rm -rf /median-filter-pipelineCopy
-  chmod -R 777 /emsdk/upstream/emscripten/cache
-fi
diff --git a/src/docker/oci_exe.sh b/src/docker/oci_exe.sh
new file mode 100644
index 000000000..f3d7cffb1
--- /dev/null
+++ b/src/docker/oci_exe.sh
@@ -0,0 +1,23 @@
+function ociExe() {
+    # Check for OCI_EXE environmental variable
+    if [[ -n "$OCI_EXE" && -x "$OCI_EXE" ]]; then
+        echo "$OCI_EXE"
+        return
+    fi
+
+    # Check for podman executable
+    if which podman > /dev/null 2>&1; then
+        echo "podman"
+        return
+    fi
+
+    # Check for docker executable
+    if which docker > /dev/null 2>&1; then
+        echo "docker"
+        return
+    fi
+
+    # If none of the above exist, return nothing
+    echo "Could not find podman or docker executable" >&2
+    exit 1
+}
\ No newline at end of file
diff --git a/src/docker/pull.sh b/src/docker/pull.sh
index 296a3cdcb..989b6aa18 100755
--- a/src/docker/pull.sh
+++ b/src/docker/pull.sh
@@ -1,9 +1,13 @@
 #!/usr/bin/env bash
 
+script_dir="`cd $(dirname $0); pwd`"
+source "$script_dir/oci_exe.sh"
+exe=$(ociExe)
+
 set -eo pipefail
 
-docker pull itkwasm/emscripten:latest
-docker pull itkwasm/emscripten:latest-debug
+$exe pull itkwasm/emscripten:latest
+$exe pull itkwasm/emscripten:latest-debug
 
-docker pull itkwasm/wasi:latest
-docker pull itkwasm/wasi:latest-debug
+$exe pull itkwasm/wasi:latest
+$exe pull itkwasm/wasi:latest-debug
diff --git a/src/docker/push.sh b/src/docker/push.sh
index 9ece3bae7..933a9f9eb 100755
--- a/src/docker/push.sh
+++ b/src/docker/push.sh
@@ -1,5 +1,9 @@
 #!/usr/bin/env bash
 
+script_dir="`cd $(dirname $0); pwd`"
+source "$script_dir/oci_exe.sh"
+exe=$(ociExe)
+
 set -eo pipefail
 
 debug=true
@@ -15,20 +19,20 @@ set -- "${newparams[@]}"  # overwrites the original positional params
 TAG=$(date '+%Y%m%d')-$(git rev-parse --short HEAD)
 
 if test ! -z ${DOCKERHUB_ITKWasm_PASSWORD+x}; then
-  echo $DOCKERHUB_ITKWasm_PASSWORD | docker login --username "$DOCKERHUB_ITKWasm_USERNAME" --password-stdin
+  echo $DOCKERHUB_ITKWasm_PASSWORD | $exe login --username "$DOCKERHUB_ITKWasm_USERNAME" --password-stdin
 fi
 
 function push_image() {
   local image=$1
   local tag=$2
   local debug=$3
-  docker push ${image}:${tag}
-  docker tag ${image}:${tag} quay.io/${image}:${tag}
-  docker push quay.io/${image}:${tag}
+  $exe push ${image}:${tag}
+  $exe tag ${image}:${tag} quay.io/${image}:${tag}
+  $exe push quay.io/${image}:${tag}
   if $debug; then
-    docker push ${image}:${tag}-debug
-    docker tag ${image}:${tag}-debug quay.io/${image}:${tag}-debug
-    docker push quay.io/${image}:${tag}-debug
+    $exe push ${image}:${tag}-debug
+    $exe tag ${image}:${tag}-debug quay.io/${image}:${tag}-debug
+    $exe push quay.io/${image}:${tag}-debug
   fi
 }
 


Test patch:
diff --git a/packages/core/typescript/itk-wasm/src/cli/test.js b/packages/core/typescript/itk-wasm/src/cli/test.js
index 06badd7b5..1640a276e 100644
--- a/packages/core/typescript/itk-wasm/src/cli/test.js
+++ b/packages/core/typescript/itk-wasm/src/cli/test.js
@@ -3,6 +3,7 @@ import { spawnSync } from 'child_process'
 import processCommonOptions from './process-common-options.js'
 
 import program from './program.js'
+import die from './die.js'
 
 function test(options) {
   const { buildDir, dockcrossScript } = processCommonOptions(program, true)
@@ -15,28 +16,42 @@ function test(options) {
   if (hyphenIndex !== -1) {
     ctestArgs = program.rawArgs.slice(hyphenIndex + 1)
   }
-  if(process.platform === "win32"){
-    var dockerBuild = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
-      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env ctest --test-dir ${ctestTestDir} ` + ctestArgs.join(' ') + '"'], {
-      env: process.env,
-      stdio: 'inherit',
-      shell: true
-    });
+  if (process.platform === 'win32') {
+    var dockerBuild = spawnSync(
+      '"C:\\Program Files\\Git\\bin\\sh.exe"',
+      [
+        '--login',
+        '-i',
+        '-c',
+        `"${buildDir}/itk-wasm-build-env ctest --test-dir ${ctestTestDir} ` +
+          ctestArgs.join(' ') +
+          '"'
+      ],
+      {
+        env: process.env,
+        stdio: 'inherit',
+        shell: true
+      }
+    )
 
     if (dockerBuild.status !== 0) {
-      console.error(dockerBuild.error);
+      die(dockerBuild.error)
     }
-    process.exit(dockerBuild.status);
+    process.exit(dockerBuild.status)
   } else {
-    const dockerBuild = spawnSync('bash', [dockcrossScript, 'ctest', '--test-dir', ctestTestDir].concat(ctestArgs), {
-      env: process.env,
-      stdio: 'inherit',
-    })
+    const dockerBuild = spawnSync(
+      'bash',
+      [dockcrossScript, 'ctest', '--test-dir', ctestTestDir].concat(ctestArgs),
+      {
+        env: process.env,
+        stdio: 'inherit'
+      }
+    )
     if (dockerBuild.status !== 0) {
-      console.error(dockerBuild.error);
+      die(dockerBuild.error)
     }
     process.exit(dockerBuild.status)
   }
 }
 
-export default test
\ No newline at end of file
+export default test