Packages
Secure BEAM sandbox runtime for LLM code mode and MCP aggregation. Run concurrent LLM/tool clients safely while agents orchestrate approved tools, call upstream MCP/OpenAPI servers, and transform data.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/ptc.install_babashka.ex
defmodule Mix.Tasks.Ptc.InstallBabashka do
@moduledoc """
Installs Babashka for Clojure validation.
Downloads the appropriate binary for your OS/architecture
and places it in `_build/tools/bb`.
## Usage
mix ptc.install_babashka
mix ptc.install_babashka --force # Reinstall even if present
mix ptc.install_babashka --version 1.4.192 # Specific version
## Supported Platforms
- macOS (Apple Silicon and Intel)
- Linux (x86_64)
## What This Does
1. Detects your OS and architecture
2. Downloads the appropriate Babashka binary from GitHub releases
3. Extracts and places it at `_build/tools/bb`
4. Makes the binary executable
5. Verifies the installation
"""
use Mix.Task
@shortdoc "Install Babashka for Clojure validation"
@default_version "1.4.192"
@download_base "https://github.com/babashka/babashka/releases/download"
@install_dir "_build/tools"
@bb_path "_build/tools/bb"
@impl Mix.Task
def run(args) do
{opts, _, _} =
OptionParser.parse(args,
strict: [force: :boolean, version: :string],
aliases: [f: :force, v: :version]
)
force = Keyword.get(opts, :force, false)
version = Keyword.get(opts, :version, @default_version)
# Check if already installed
if File.exists?(@bb_path) and not force do
Mix.shell().info("Babashka already installed at #{@bb_path}")
Mix.shell().info("Use --force to reinstall")
verify_installation()
:ok
else
install_babashka(version)
end
end
defp install_babashka(version) do
Mix.shell().info("Installing Babashka v#{version}...")
# Detect OS and architecture
{os, arch} = detect_platform()
Mix.shell().info("Detected platform: #{os}/#{arch}")
# Build download URL
filename = build_filename(os, arch, version)
url = "#{@download_base}/v#{version}/#{filename}"
Mix.shell().info("Downloading from: #{url}")
# Create install directory
File.mkdir_p!(@install_dir)
# Download
archive_path = Path.join(@install_dir, filename)
case download_file(url, archive_path) do
:ok ->
Mix.shell().info("Downloaded successfully")
# Extract
extract_archive(archive_path, @install_dir)
File.rm(archive_path)
# Make executable
File.chmod!(@bb_path, 0o755)
Mix.shell().info("Installed Babashka to #{@bb_path}")
verify_installation()
{:error, reason} ->
Mix.raise("Failed to download Babashka: #{reason}")
end
end
defp detect_platform do
os =
case :os.type() do
{:unix, :darwin} -> :macos
{:unix, :linux} -> :linux
{:win32, _} -> :windows
other -> Mix.raise("Unsupported OS: #{inspect(other)}")
end
arch =
case :erlang.system_info(:system_architecture) |> to_string() do
"aarch64" <> _ -> :aarch64
"arm64" <> _ -> :aarch64
"x86_64" <> _ -> :amd64
"amd64" <> _ -> :amd64
other -> Mix.raise("Unsupported architecture: #{other}")
end
{os, arch}
end
defp build_filename(os, arch, version) do
case {os, arch} do
{:macos, :aarch64} ->
"babashka-#{version}-macos-aarch64.tar.gz"
{:macos, :amd64} ->
"babashka-#{version}-macos-amd64.tar.gz"
{:linux, :amd64} ->
"babashka-#{version}-linux-amd64-static.tar.gz"
{:linux, :aarch64} ->
"babashka-#{version}-linux-aarch64-static.tar.gz"
{os, arch} ->
Mix.raise("Unsupported platform: #{os}/#{arch}")
end
end
defp download_file(url, dest_path) do
# Use curl for reliable downloading with redirect support
case System.cmd("curl", ["-L", "-o", dest_path, "-f", "--silent", "--show-error", url],
stderr_to_stdout: true
) do
{_, 0} ->
:ok
{output, code} ->
{:error, "curl failed (exit #{code}): #{output}"}
end
end
defp extract_archive(archive_path, dest_dir) do
Mix.shell().info("Extracting archive...")
case System.cmd("tar", ["-xzf", archive_path, "-C", dest_dir]) do
{_, 0} ->
:ok
{output, code} ->
Mix.raise("Failed to extract archive (exit code #{code}): #{output}")
end
end
defp verify_installation do
# Use absolute path for verification
bb_abs_path = Path.join(File.cwd!(), @bb_path)
case System.cmd(bb_abs_path, ["--version"]) do
{version, 0} ->
Mix.shell().info("Verified: #{String.trim(version)}")
:ok
{output, code} ->
Mix.shell().error("Verification failed (exit code #{code}): #{output}")
:error
end
end
end