Cross-platform dependency manager with runtime auto-installation and embeddable Go library
A modern dependency manager that goes beyond simple binary installation. deps provides flexible package management, runtime auto-installation for Node.js/Python/Java/PowerShell scripts, and can be embedded directly into your Go applications.
- 7+ Package Sources: GitHub (Releases/Tags/Builds), GitLab, Apache, Maven, direct URLs with smart auto-detection
- Runtime Auto-Installation: Auto-install and run Node.js, Python, Java, and PowerShell scripts with dependency management
- Embeddable Go Library: Simple
deps.Install()anddeps.Run*()APIs for Go programs - GitHub Action: Native action with multi-platform support and intelligent caching
- Built-in Registry: Pre-configured defaults for 30+ popular tools (kubectl, helm, jq, yq, kind, postgres, maven, etc.)
- CEL Post-Processing: Complex transformations with
glob(),unarchive(),move(),chmod()expressions - Lock Files: Reproducible builds with
deps-lock.yamlcontaining resolved versions and checksums - Directory Mode: Install full applications with symlink management, not just single binaries
- Checksum Verification: Multiple strategies including inline, URL patterns, and CEL expressions
- Version Constraints: Semantic versioning, version pinning, or "latest" resolution with intelligent config merging
Binary Download (Recommended)
# Linux (amd64)
curl -L https://github.com/flanksource/deps/releases/latest/download/deps-linux-amd64.tar.gz -o deps.tar.gz
tar -xf deps.tar.gz
chmod +x deps
sudo mv deps /usr/local/bin/
# macOS (Apple Silicon)
curl -L https://github.com/flanksource/deps/releases/latest/download/deps-darwin-arm64.tar.gz -o deps.tar.gz
tar -xf deps.tar.gz
chmod +x deps
sudo mv deps /usr/local/bin/
# macOS (Intel)
curl -L https://github.com/flanksource/deps/releases/latest/download/deps-darwin-amd64.tar.gz -o deps.tar.gz
tar -xf deps.tar.gz
chmod +x deps
sudo mv deps /usr/local/bin/
# Windows (PowerShell)
Invoke-WebRequest -Uri https://github.com/flanksource/deps/releases/latest/download/deps-windows-amd64.exe -OutFile deps.exe
Move-Item deps.exe C:\Windows\System32\deps.exeUsing Go
go install github.com/flanksource/deps/cmd/deps@latestCLI
# Install multiple tools at once
deps install kubectl helm jq
# Install with specific version
deps install yq@v4.40.5
# Generate lock file for reproducible builds
deps lockGitHub Action
- uses: flanksource/deps@v1
with:
tools: |
yq
kubectl
helmGo Library
import "github.com/flanksource/deps"
// Install a tool
result, err := deps.Install("jq", "latest",
deps.WithBinDir("./bin"))
// Run Python script with auto-install
result, err := deps.RunPython("script.py", deps.RunOptions{
Version: ">=3.9",
})The deps GitHub Action automatically installs tools across Linux, macOS, and Windows runners with intelligent caching and parallel downloads.
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install tools
uses: flanksource/deps@v1
with:
tools: |
yq
kubectl
helm
- name: Use installed tools
run: |
yq --version
kubectl version --client
helm versionjobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: Install tools
uses: flanksource/deps@v1
with:
tools: yq,kubectl,helm
- name: Run tests
run: yq --version- uses: flanksource/deps@v1
with:
tools: |
yq@v4.40.5
kubectl@v1.28.0
helm@v3.13.0
version: v1.0.0 # Pin deps version| Input | Description | Required | Default |
|---|---|---|---|
tools |
List of tools to install (comma-separated or multiline) | Yes | - |
version |
Version of deps to use (e.g., v1.0.0 or latest) |
No | latest |
GITHUB_TOKEN |
GitHub token for accessing the API and avoiding rate limit | No | - |
| Output | Description |
|---|---|
tools-installed |
JSON array of installed tools with versions |
The action automatically caches:
- deps binary: Cached per OS/arch/version
- Installed tools: Cached per OS/arch/tool list
No manual cache configuration needed!
# Install from deps.yaml
deps install
# Install specific tools
deps install kubectl helm jq
# Install with version
deps install yq@v4.40.5
# Install with options
deps install kubectl --bin-dir=./tools --force
# Select a GitHub release asset by exact name, wildcard, or !exclusion.
# A bare name is retried as a prefix when no exact asset exists. The selected filename is shown in progress, and its version/platform suffix is removed when deriving the installed binary name.
deps install owner/repo --asset cli-linux --asset '!*-debug*'
# Select the newest GitHub release whose tag or title matches.
deps install owner/repo --release-filter 'v2*'Generate a lock file for reproducible builds:
# Lock all dependencies in deps.yaml
deps lock
# Lock specific packages
deps lock kubectl helm
# Lock for specific platforms
deps lock --platforms linux-amd64,darwin-amd64,darwin-arm64The lock file (deps-lock.yaml) contains resolved versions, URLs, and checksums:
dependencies:
- name: kubectl
version: v1.28.0
platforms:
linux-amd64:
url: https://dl.k8s.io/release/v1.28.0/bin/linux/amd64/kubectl
checksum: sha256:abc123...
darwin-arm64:
url: https://dl.k8s.io/release/v1.28.0/bin/darwin/arm64/kubectl
checksum: sha256:def456...# Check versions of installed tools
deps check
# Check specific tool
deps check kubectl
# Check for updates
deps update
# Update specific dependency
deps update yq# List all available tools from registry
deps list
# Show authentication status
deps whoamiExecute scripts in multiple languages with automatic runtime detection and installation:
# Run Python script
deps run script.py
# Run JavaScript/Node.js script
deps run server.js
# Run TypeScript script (requires tsx or ts-node)
deps run app.ts
# Run Java program (automatically compiles and executes)
deps run Main.java
# Run PowerShell script
deps run deploy.ps1
# With version constraint
deps run --version ">=3.9" script.py
deps run --version ">=18" server.js
# With timeout
deps run --timeout 30s script.py
# With environment variables
deps run --env "API_KEY=secret" --env "DEBUG=true" script.py
# With script arguments
deps run script.py arg1 arg2
# With custom working directory
deps run --working-dir /tmp script.js
# Install dependencies automatically
deps run --install script.pySupported Languages:
- Python (.py) - Auto-installs from requirements.txt
- JavaScript (.js, .mjs, .cjs) - Auto-installs from package.json
- TypeScript (.ts, .tsx) - Requires tsx or ts-node
- Java (.java, .jar, .class) - Auto-compiles .java files
- PowerShell (.ps1) - Uses pwsh or powershell
See examples/scripts/ for example scripts.
Embed deps functionality directly in your Go applications.
package main
import (
"fmt"
"log"
"github.com/flanksource/deps"
)
func main() {
// Install a tool
result, err := deps.Install("jq", "latest",
deps.WithBinDir("./bin"),
deps.WithForce(true),
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Installed %s %s to %s\n",
result.Package.Name,
result.Version,
result.Path,
)
}deps.Install("kubectl", "v1.28.0",
deps.WithBinDir("./bin"), // Binary installation directory
deps.WithAppDir("./apps"), // Application directory (for directory mode)
deps.WithTmpDir("./tmp"), // Temporary directory
deps.WithCacheDir("./cache"), // Enable caching
deps.WithForce(true), // Force reinstall
deps.WithSkipChecksum(false), // Skip checksum verification
deps.WithStrictChecksum(true), // Fail on checksum errors
deps.WithDebug(true), // Enable debug logging
deps.WithOS("linux", "amd64"), // Override platform detection
)Run Node.js, Python, Java, or PowerShell scripts with automatic runtime installation:
package main
import (
"log"
"time"
"github.com/flanksource/deps"
)
func main() {
// Run Python script
pyResult, err := deps.RunPython("analyze.py", deps.RunOptions{
Version: ">=3.9",
Timeout: 30 * time.Second,
Args: []string{"--input", "data.csv"},
})
if err != nil {
log.Fatal(err)
}
log.Println(pyResult.Stdout)
// Run Node.js script
nodeResult, err := deps.RunNode("server.js", deps.RunOptions{
Version: ">=18.0",
Env: map[string]string{
"PORT": "3000",
},
})
if err != nil {
log.Fatal(err)
}
// Run npx command
npxResult, err := deps.RunNode("npx:cowsay hello", deps.RunOptions{})
if err != nil {
log.Fatal(err)
}
log.Println(npxResult.Stdout)
// Run Java program
javaResult, err := deps.RunJava("Main.jar", deps.RunOptions{
Version: ">=17",
Env: map[string]string{
"CLASSPATH": "./lib/*",
},
})
if err != nil {
log.Fatal(err)
}
// Run PowerShell script
psResult, err := deps.RunPowershell("script.ps1", deps.RunOptions{
Version: ">=7.0",
})
if err != nil {
log.Fatal(err)
}
}package main
import (
"github.com/flanksource/deps"
"github.com/flanksource/deps/pkg/config"
"github.com/flanksource/deps/pkg/installer"
)
func main() {
// Load custom registry
cfg, err := config.LoadConfigFromFile("custom-deps.yaml")
if err != nil {
panic(err)
}
// Create installer with custom config
inst := installer.NewWithConfig(cfg,
installer.WithBinDir("./tools"),
installer.WithCacheDir("./.cache"),
)
// Install with custom config
result, err := inst.InstallWithResult("custom-tool", "latest", nil)
if err != nil {
panic(err)
}
// Check installation status
switch result.Status {
case deps.InstallStatusInstalled:
println("✓ Installed")
case deps.InstallStatusAlreadyInstalled:
println("✓ Already installed")
case deps.InstallStatusFailed:
println("✗ Failed")
}
// Check checksum verification
if result.VerifyStatus == deps.VerifyStatusChecksumMatch {
println("✓ Checksum verified")
}
}deps can automatically detect, install, and run scripts in various languages.
Node.js
# Auto-installs Node.js if needed, runs script
deps run server.js
# With package.json, automatically runs npm install
deps run index.js
# Run npx commands
deps run npx:create-react-app my-app
# Specify Node version
deps run --runtime-version=">=18.0" server.jsExample with automatic dependency installation:
// package.json
{
"dependencies": {
"express": "^4.18.0",
"lodash": "^4.17.21"
}
}deps automatically runs npm install before executing your script.
Python
# Auto-installs Python if needed
deps run analyze.py
# With requirements.txt, automatically runs pip install
deps run main.py
# Specify Python version
deps run --runtime-version=">=3.9" script.pyExample with automatic dependency installation:
# requirements.txt
requests>=2.28.0
pandas>=1.5.0
numpy>=1.23.0
deps automatically runs pip install -r requirements.txt before executing your script.
Java
# Auto-installs JDK, compiles and runs
deps run HelloWorld.java
# Run JAR file
deps run application.jar
# Run compiled class
deps run com.example.Main
# With CLASSPATH
deps run --env=CLASSPATH=./lib/* Main.jarPowerShell
# Auto-installs PowerShell Core (cross-platform)
deps run script.ps1
# Specify PowerShell version
deps run --runtime-version=">=7.0" advanced.ps1deps-start (a separate binary from the start/ submodule) launches services from
the registry — postgres, opensearch, valkey, mysql, mssql, elasticsearch, loki,
prometheus, grafana, jaeger, otel-collector, clickhouse, nats, rabbitmq, rclone,
ministack, k3s, kind, kube-apiserver, trivy, activemq, activemq-artemis — via four
runtimes, and prints a
commons-db connection:
- binary — installs the artifact with deps and supervises it (clicky
SupervisedProcess) - docker — named containers via the docker SDK (honors
DOCKER_HOSTand the docker CLI context) - helm —
helm upgrade --install --wait(helm auto-installed via deps); connections reference chart secrets viasecret://<name>/<key>and in-clustersvc://URLs - command — CLI-driven lifecycles (
kind create/delete cluster)
Services listen on 127.0.0.1 by default; --bind 0.0.0.0 exposes them on all
interfaces, and a specific address (e.g. --bind 192.168.1.10) both binds and
becomes the connection host.
The same commands are available from the main binary as deps svc <verb>, which
forwards to deps-start, installing it on demand into ~/.deps/bin:
deps svc start postgres@17
deps svc status
deps svc logs postgres -f
deps svc stop postgresStarting leaves a supervisor running in the background. The command streams the service's logs and what it is waiting for to stderr until the service is ready or the start timeout is reached, then returns:
| exit code | meaning |
|---|---|
0 |
ready |
| the supervisor's own code | it exited while starting (its log is quoted in the error) |
1 |
the start timeout was reached — the service is left running, follow it with deps-start logs <service> -f |
--foreground/-f runs the service in this terminal instead and stops it on exit.
Windows has no background mode, so services always run in the foreground there.
Starting is not an upgrade: the installed artifact is used as-is and no version is
resolved or downloaded, so a re-start is offline and near-instant. --update resolves the
version constraint and installs the newest match. A version given on the command line
(postgres@17) is always honoured; one replayed from a previous start is not a reason to
go back to the network.
A service is ready only when it passes both stages, in order:
- the health check from its registry spec —
stdout_match, dockerexec,httpor a TCP wait; - a TCP connect to its primary published port.
The second stage is what makes stdout_match services honest: postgres logs
"ready to accept connections" before its socket accepts, so the log line alone
is not readiness. Restart is gated the same way — a process that came back
or a container that is running again is not ready until it accepts connections,
and its state stays ready: false until it does.
While waiting, the service's own output is streamed to stderr prefixed with
the service name, alongside a line naming the unmet condition
(waiting for tcp 127.0.0.1:5432 (4s)). stdout carries only the structured
service envelope, so deps-start postgres > conn.json stays clean.
# starts postgres, prints the connection once ready, leaves it running
deps-start postgres --port 15432
# `start` is an equivalent spelling, symmetric with stop (flags follow the service)
deps-start start postgres --port 15432
# run it in this terminal instead; Ctrl-C stops the service
deps-start postgres --port 15432 -f
# pin versions with the same name@version syntax and constraint
# semantics as deps install (resolved through the package's registry)
deps-start postgres@17
deps-start start nats@2.11
# install the newest match for the constraint rather than reusing what is installed
deps-start postgres@17 --update
# lifecycle
deps-start valkey # docker runtime (valkey has no binary artifact)
deps-start list
deps-start info valkey
deps-start logs valkey -f
deps-start restart valkey # in place: SupervisedProcess restart / docker restart
deps-start stop --all
# helm: connection password resolves from the chart secret at hydration time
deps-start postgres --runtime helm -n dev --port 15432
# url: svc://deps-postgres.dev:15432 (the chart Service port is overridden too)
# password: secret://deps-postgres/POSTGRES_PASSWORD
# typed service-specific and Kubernetes resource flags are shown in service help
deps-start opensearch --jvm-memory 1g
deps-start opensearch --runtime helm --cpu-request 500m --memory-limit 2Gi
deps-start nats --jetstream=false
# repeating the same command reuses the running service and credentials;
# changing an option reconciles the runtime while keeping its password
deps-start nats --runtime docker --jetstream=false
deps-start nats --runtime docker --jetstream=true
# Docker uses a host bind by default. The source defaults to
# ~/.deps/services/<service>/data and the container target comes from the registry.
deps-start postgres --runtime docker --data-dir ./postgres-data
deps-start postgres --runtime docker --volume-mode persistent
deps-start postgres --runtime docker --volume-mode ephemeral
# Helm defaults to a chart-backed persistent volume. Available modes are
# chart-specific and shown in service metadata; unsupported modes fail before upgrade.
deps-start opensearch --runtime helm --volume-mode ephemeral
# JSON is the default structured service envelope on stdout. It includes the
# connection plus runtime, image/chart, parameters, ports, volume and state paths.
# Clicky lifecycle messages and configuration diffs are written to stderr.
deps-start postgres
# explicit output formats: json (default), yaml, env (connection fields only)
deps-start postgres -o yaml
deps-start postgres -o envAs a library:
import "github.com/flanksource/deps/start"
instance, err := start.Start(ctx, "opensearch",
start.WithPort(19200),
start.WithParameters(map[string]string{"jvm-memory": "1g"}),
start.WithLogWriter(os.Stderr), // tee the service's output while it starts
start.WithOnWaiting(func(r start.Readiness) { // what the readiness wait is blocked on
log.Printf("waiting for %s (%s)", r.Waiting, r.Elapsed)
}),
)
// instance.Connection is a commons-db models.Connection
defer instance.Stop(ctx)Service metadata (ports, typed parameters, credentials, health checks, images,
charts, and connection templates) lives in the embedded registry under
pkg/config/services*.yaml. Runtime support per service is implied by which of
the binary/docker/helm blocks its service: spec defines. Parameters can be
limited to specific runtimes; using a Helm-only resource flag with another
runtime fails before the service starts.
Create or edit deps.yaml:
dependencies:
- name: mytool
version: v1.2.3
registry:
mytool:
source: github.com/owner/repo
# Optional: specific asset pattern
asset_pattern: "mytool-{{.Version}}-{{.OS}}-{{.Arch}}.tar.gz"registry:
kubectl:
source: github.com/kubernetes/kubernetes
# Auto-detects releasesregistry:
tool:
manager: github-tags
source: github.com/owner/repo
# Uses tags instead of releases (no API rate limits)registry:
tool:
manager: gitlab
source: gitlab.com/group/projectregistry:
maven:
manager: apache
source: apache.org/maven
extra:
archive_path: "maven/maven-3"registry:
postgres:
name: postgres-embedded
manager: maven
extra:
group_id: io.zonky.test.postgres
artifact_id: embedded-postgres-binaries-{{.os}}-{{.arch}}
packaging: jar
repository: https://repo1.maven.org/maven2registry:
custom:
url: "https://example.com/tool-{{.Version}}-{{.Platform}}.tar.gz"File Mode (default): Extracts binary to bin directory
registry:
jq:
source: github.com/jqlang/jq
mode: file # Single binaryDirectory Mode: Extracts entire archive, creates symlinks
registry:
postgres:
mode: directory # Full application
symlinks:
- from: "pgsql/bin/*"
to: "{{.Name}}"Use Common Expression Language for complex transformations:
registry:
tool:
source: github.com/owner/repo
post_process:
# Unarchive a nested archive
- unarchive(glob("*.zip")[0])
# Move files
- move("bin/tool", ".")
# Set permissions
- chmod("tool", 0755)
# Delete unwanted files
- delete(glob("*.txt"))
# Change working directory
- chdir("subdir")Available CEL functions:
glob(pattern)- Find files matching patternunarchive(file)- Extract archivemove(from, to)- Move filesdelete(pattern)- Delete fileschmod(file, mode)- Change permissionschdir(dir)- Change directory
registry:
tool:
asset_pattern: "tool-{{.Version}}-{{.OS}}-{{.Arch}}.{{.Ext}}"
templates:
ext:
windows: "zip"
default: "tar.gz"
# Platform-specific post-processing
post_process:
- condition: "{{.OS}} == 'windows'"
steps:
- unarchive("tool.zip")
- condition: "{{.OS}} != 'windows'"
steps:
- unarchive("tool.tar.gz")registry:
tool:
checksum: "sha256:abc123..."registry:
tool:
checksum_url: "https://example.com/tool-{{.Version}}.sha256"For checksums in multi-file format:
registry:
tool:
checksum_url: "https://example.com/checksums.txt"
checksum_expr: |
string(body).split('\n')
.filter(line, line.contains('{{.Asset}}'))
.map(line, line.split(' ')[0])[0]Custom version resolution:
registry:
tool:
version_expr: |
releases.filter(r, !r.prerelease && !r.draft)
.map(r, r.tag_name)[0]# Global settings
target: ./bin # Binary installation directory (default: ./bin)
app_dir: ./apps # Application directory for directory mode
cache_dir: ./.deps-cache # Cache directory
mode: file # Default mode: file or directory
# Dependencies to install
dependencies:
- name: kubectl
version: v1.28.0
- name: helm
version: latest
- name: yq
source: github.com/mikefarah/yq # Override source
version: v4.40.5
# Custom package definitions
registry:
custom-tool:
source: github.com/owner/repo
asset_pattern: "tool-{{.Version}}-{{.OS}}-{{.Arch}}.tar.gz"
checksum_url: "https://example.com/checksums.txt"
post_process:
- unarchive(glob("*.tar.gz")[0])
- move("bin/tool", ".")User configurations intelligently merge with built-in defaults. You can:
- Override specific fields while inheriting others:
registry:
kubectl:
version: v1.28.0 # Override version, keep other kubectl defaults- Completely replace a package definition:
registry:
kubectl:
source: custom.example.com/kubectl # Replaces all defaults
url: "https://custom.example.com/kubectl-{{.Version}}"- Add new packages alongside built-in ones
Generate deps-lock.yaml for reproducible builds:
deps lockThe lock file contains:
- Resolved versions
- Platform-specific URLs
- SHA256 checksums
- Download metadata
Commit deps-lock.yaml to version control for reproducible builds across environments.
Set environment variables for private repositories:
# GitHub
export GITHUB_TOKEN=ghp_...
# GitLab
export GITLAB_TOKEN=glpat-...Check authentication status:
deps whoamiChoose deps when:
- Embeddable Go Library: You're building Go applications and want to embed tool management directly in your binary.
- Runtime Auto-Installation: You need to run Node.js, Python, Java, or PowerShell scripts with automatic runtime and dependency installation.
- Complex Transformations: You need CEL-based post-processing for advanced package manipulation (unarchive, move, chmod, etc.).
- Flexible Sources: You require packages from diverse sources like Maven, Apache, or GitLab, not just GitHub releases.
- Project-Local: You prefer explicit, project-local configuration (
deps.yaml) with reproducible lock files (deps-lock.yaml). - GitHub Action: You want a native GitHub Action with intelligent caching and multi-platform support.
vs. aqua (https://aquaproj.github.io/)
aqua strengths:
- Massive Registry: 20,000+ packages in the standard registry
- Security Verification: Built-in slsa-verifier and cosign support for supply chain security
- Lazy Installation: Tools installed on first use with aqua-proxy
- Policy as Code: Aqua Policy for governance and security controls
Choose aqua when:
- You need access to a vast registry of pre-configured packages
- Supply chain security verification (SLSA, Cosign) is critical
- You want policy-based governance and approval workflows
- You prefer lazy installation with proxy execution
vs. mise (https://mise.jdx.dev/)
mise strengths:
- System-Wide Management: Designed for system-level tool and runtime management
- Environment Variables: Built-in environment variable management per project
- asdf Compatibility: Drop-in replacement for asdf with backend plugin support
- Version Files: Supports .tool-versions, .mise.toml, and language-specific version files
- Dev Environment: Complete development environment management
- Tasks: Built-in task runner similar to Make
Choose mise when:
- You want system-wide tool version management
- You need environment variable management per directory
- You're migrating from asdf or need asdf plugin compatibility
- You want a task runner integrated with your tool manager
- You need support for .tool-versions files and per-directory environments
Contributions are welcome! Please see our contributing guidelines for details.
# Clone the repository
git clone https://github.com/flanksource/deps
cd deps
# Install dependencies
go mod download
# Build
make build
# Run tests
make test
# Run linter
make lint# Run all tests
make test
# Run with coverage report
make test:report
# Run only failed tests
make test:failed
# Run end-to-end tests
make test:e2e