Skip to content

curl-script

Downloads and executes shell installation scripts.

import { defineTool } from "@alexgorbatchev/dotfiles";
export default defineTool((install, ctx) =>
install("curl-script", {
url: "https://bun.sh/install",
shell: "bash",
}).bin("bun"),
);
ParameterTypeRequiredDescription
urlstringYesURL of the installation script
shell'bash' | 'sh'YesShell interpreter to use
argsstring[] | (ctx) => string[]NoArguments to pass to the script
envRecord<string, string> | (ctx) => Record<...>NoEnvironment variables (static or dynamic)
versionArgsstring[]NoArgs to pass to binary for version check
versionRegexstring | RegExpNoRegex to extract version from output

Note: The env and args parameters support both static values and dynamic functions. Dynamic functions receive a context with projectConfig, scriptPath, and stagingDir.

When the curl-script installer runs, it creates a temporary staging directory where the installation takes place. This is critical to understand because:

  1. The system expects binaries in stagingDir - After your installation script completes, the tool installer looks for the declared binaries (from .bin()) inside stagingDir. If they are not there, installation fails.

  2. stagingDir becomes the versioned directory - After successful installation, the entire staging directory is renamed to the final versioned path (e.g., ~/.dotfiles/tools/fnm/1.2.3). All files in stagingDir are preserved.

  3. Most scripts need to be redirected - By default, installation scripts install to their own preferred locations (like ~/.local/bin or ~/.<tool>). You must redirect them to stagingDir using the script’s configuration options.

Check the installation script’s source to find the right argument or environment variable:

Terminal window
# Download and inspect the script
curl -fsSL https://fly.io/install.sh | less
# Look for variables like:
# INSTALL_DIR, PREFIX, BIN_DIR, FLYCTL_INSTALL, etc.

Then use args or env with the dynamic context to redirect:

// Using args (if script accepts command-line arguments)
args: (ctx) => ["--install-dir", ctx.stagingDir];
// Using env (if script reads environment variables)
env: (ctx) => ({ FLYCTL_INSTALL: ctx.stagingDir });
export default defineTool((install, ctx) =>
install("curl-script", {
url: "https://fnm.vercel.app/install",
shell: "bash",
args: ["--skip-shell", "--install-dir", "$LOCAL_BIN"],
}).bin("fnm"),
);
export default defineTool((install, ctx) =>
install("curl-script", {
url: "https://fnm.vercel.app/install",
shell: "bash",
args: (argsCtx) => ["--install-dir", argsCtx.stagingDir],
}).bin("fnm"),
);

The args function receives a context with:

  • projectConfig - Project configuration with paths and settings
  • scriptPath - Absolute path to the downloaded script (in stagingDir, already chmod +x)
  • stagingDir - Temporary directory for this installation attempt. The script is downloaded here, along with any files your code creates. After successful installation, the entire directory is renamed to the versioned path (e.g., <tool-name>/1.2.3), preserving all contents.

Use dynamic env to redirect installation to stagingDir:

export default defineTool((install, ctx) =>
install("curl-script", {
url: "https://fly.io/install.sh",
shell: "sh",
env: (ctx) => ({ FLYCTL_INSTALL: ctx.stagingDir }),
}).bin("flyctl", "fly"),
);

Note: The fly.io script installs flyctl as the main binary. The second argument to .bin() creates fly as a symlink alias.

The env context provides:

  • projectConfig - Project configuration with paths and settings
  • stagingDir - Temporary directory for installation (becomes versioned path after success)
  • scriptPath - Absolute path to the downloaded script (curl-script specific)
export default defineTool((install, ctx) =>
install("curl-script", {
url: "https://example.com/install.sh",
shell: "bash",
})
.bin("tool")
.hook("after-download", async (ctx) => {
// Verify script before execution
}),
);

Security Note: Curl scripts execute arbitrary code. Only use trusted sources with HTTPS URLs.