zero runtime · bash 3.2+ · curl or wget

Dependency management
for bash scripts.

Declare URLs in a .bashdep file. bashdep downloads them in parallel, verifies checksums, and keeps installs idempotent with a per-directory lockfile — no registry, no daemon, no runtime.

# why

Vendoring bash deps by hand doesn't scale

Shell projects pull helper scripts with copy-pasted curl lines — no pinning, no integrity, no idempotency, no record of what's installed.

hand-rolled

  • Re-downloads every run, or never updates
  • No record of what came from where
  • No checksum — trust whatever the URL served
  • Serial downloads; one slow host stalls them all
  • Dev-only tools mixed in with runtime deps

with bashdep

  • Idempotent per source URL via .bashdep.lock
  • The lockfile records every file's origin
  • Opt-in #sha256= integrity verification
  • Parallel downloads, tuned by BASHDEP_JOBS
  • @dev suffix routes tools to lib/dev/

# features

Small surface, real ergonomics

One ~800-line script. At runtime it calls only curl/wget, awk, and POSIX builtins.

01

idempotent lockfile

Per-directory .bashdep.lock records each URL; re-runs skip unchanged deps, a URL bump re-downloads only that entry.

02

parallel downloads

Fetch concurrently (default 4, set BASHDEP_JOBS). Lockfile writes batch into one atomic rewrite per dir.

03

checksum verify

Append #sha256=<hex> to any URL; a mismatch fails the install and leaves no file or lockfile entry.

04

dev / prod split

The @dev suffix routes a dependency to lib/dev/, keeping runtime and tooling apart.

05

curl or wget

Uses whichever is installed, preferring curl and falling back to wget automatically.

06

cli + completion

Run it as a command or source it as a library. Ships bash/zsh tab completion.

07

lifecycle tools

list, uninstall, clean, doctor — audit and repair lockfile / filesystem drift.

08

ci-friendly

Every command returns a meaningful, capped count — gate a pipeline on doctor or install directly.

09

bash 3.2+

Runs on macOS default bash and Linux bash 4+. No associative arrays, no external deps to install.

# how it works

declare → install → locked

A single install classifies each entry, downloads them in parallel, verifies any pins, then writes one lockfile per directory.

  .bashdep        classify        download ×N      verify         .bashdep.lock
 ┌──────────┐   ┌──────────┐   ┌──────────────┐  ┌──────────┐   ┌───────────────┐
 │  declare │──▶│  route   │──▶│ curl │ wget  │─▶│  sha256  │──▶│  atomic write │
 │  urls    │   │  dir     │   │ in parallel  │  │  reject≠ │   │  one per dir  │
 └──────────┘   └──────────┘   └──────────────┘  └──────────┘   └───────────────┘
   @dev            strip          BASHDEP_JOBS     #sha256=       commit .lock
01

vendor bashdep

$ mkdir -p lib
$ curl -fsSLo lib/bashdep …/releases/latest/download/bashdep
$ chmod +x lib/bashdep
02

declare your .bashdep

# one URL per line · # comments · @dev · #sha256=
https://github.com/TypedDevs/bashunit/releases/download/0.17.0/bashunit
https://example.com/dev-tool.sh@dev
https://example.com/pinned.sh#sha256=e3b0c44298fc1c149afbf…
03

install — and commit the lockfile

$ ./lib/bashdep install
> installed 3, skipped 0, failed 0
$ git add .bashdep.lock   # pin versions for collaborators

# quick start

two ways to drive it

as a cli

$ ./lib/bashdep install      # from ./.bashdep
$ ./lib/bashdep list         # what's installed
$ ./lib/bashdep doctor       # detect drift
$ ./lib/bashdep clean --dry-run
$ source <(./lib/bashdep completion bash)

as a sourced library

#!/bin/bash
set -euo pipefail

source lib/bashdep
bashdep::setup dir="vendor" dev-dir="local/dev"
bashdep::install_from   # reads ./.bashdep

# reference

cli at a glance

commandwhat it does
install [url…]Install given URLs, or read them from .bashdep
list [dir…]List installed dependencies (path + source URL)
uninstall <name…>Remove deps and their lockfile entries
cleanRemove files not recorded in the lockfile
doctorReport lockfile / filesystem inconsistencies
self-update [ref]Update bashdep itself from upstream
completion [bash|zsh]Print a shell completion script
versionPrint the bashdep version

flags: --dir --dev-dir --file --force --dry-run --silent --verbose --version