Skip to content

Repository files navigation

envprobe

CI

Check that a machine has the tools and services you expect — a laptop, a CI runner, or a test box.

envprobe reads a config file that names what should be there, then verifies it: executables on PATH (with their versions), TCP ports that answer, and the Docker daemon. There are no built-in defaults. What you write in the config is exactly what gets checked.

$ envprobe doctor
using /Users/you/envprobe.yaml
✓  git            2.50.1   9ms
✓  Java           21.0.11  93ms
✓  Go             1.26.5   6ms
✗  terraform               0s
✓  postgres                2ms
✗  redis                   1ms  (connection refused)
✓  docker-daemon           160ms
5 of 7 checks passed

Install

Download a binary from the latest release, or build from source:

go install github.com/nchatzak/envprobe@latest

Linux and macOS. Windows is out of scope.

Usage

Write a starter config, then run it:

envprobe config init     # writes ./envprobe.yaml
envprobe doctor          # run every check

doctor looks for envprobe.yaml in the current directory, then your home directory, then ~/.config/envprobe, and uses the first one it finds. The file it chose is printed to stderr, so you always know which config produced the output.

Checks run concurrently, each with a 5-second timeout, so one unreachable host does not hold up the rest. A check that times out is reported as a failure.

A row says why in parentheses when the alone would leave you guessing — redis above was refused rather than slow. terraform gets none because missing from PATH is the whole story.

Commands

Command What it does
envprobe doctor Run every configured check and print a table
envprobe doctor --json Same, as JSON on stdout
envprobe doctor --ci Exit non-zero when a check fails (see below)
envprobe config init Write an example config to ./envprobe.yaml (-o to change the path, --force to overwrite)
envprobe config validate [file] Parse a config without running anything
envprobe config example Print the annotated example config to stdout
envprobe completion <shell> Print a shell completion script (bash, zsh, fish)
envprobe --version Print the version (-v works too)

Shell completion

envprobe completion <shell> prints a script — it does not install one. Until that script is where your shell looks, TAB does nothing and nothing says why. On macOS with zsh:

envprobe completion zsh > $(brew --prefix)/share/zsh/site-functions/_envprobe

Then restart your shell; the session that wrote the file will not pick it up. envprobe completion <shell> --help has the paths and setup steps for each shell — zsh, bash and fish, on both macOS and Linux — including the bash-completion package that bash needs.

Two things that help does not mention:

  • zsh ignores the file unless its completion system is on, silently. Check with (( $+functions[compdef] )) && echo on || echo off. If that prints off, add autoload -Uz compinit && compinit to ~/.zshrc — but check first, as many tools run it for you when sourced. To confirm envprobe itself loaded: print -l ${(k)_comps} | grep envprobe.
  • The script contains no command or flag names — it asks the binary on your PATH as you type, so upgrades bring their own and it never needs regenerating.

JSON output

Results go to stdout, diagnostics to stderr, so --json stays pipeable:

$ envprobe doctor --json | jq '.[] | select(.found | not) | .name'
"terraform"
"redis"

Each result carries name, found and duration_ms. path and version are present only when the check found them, so a port check reports neither.

problem carries the same cause the table prints in parentheses, under the same rule. It is not limited to failures: wrong version_args give found: true with problem: version command failed.

The 5 of 7 checks passed line is a diagnostic, so it goes to stderr in both formats — stdout stays exactly the results array. It prints on every run that had checks to run, whether or not --ci is set, so one grep finds the tally in any log.

Configuration

Every check has the same three fields:

  • name — the label in the output and the key under --json. Must be unique.
  • typebinary, port, docker-daemon, or env.
  • with — the payload for that type. Keys are validated: an unknown key is an error that names the key, so a typo fails loudly instead of being ignored.

The four types:

Type Checks with:
binary The executable is on PATH, and its version if you ask for one target (defaults to name), version_args (optional)
port Something is listening, which proves a service is running rather than merely installed target, required, as "host:port"
docker-daemon docker info answers, which the binary check alone cannot tell you none
env An environment variable is set, and matches a pattern if you ask for one target (defaults to name), matches (optional regexp)

An env check with no matches asks only whether the variable is set, so an empty value passes. Write matches: "." to require a non-empty one.

For a worked config with every type and the pitfalls annotated — which tools print their version to stderr, which want no dashes at all:

envprobe config example    # print it
envprobe config init       # write it to ./envprobe.yaml

An empty checks: list runs nothing and exits 0, with a warning on stderr.

Exit codes

Code Meaning
0 Every check passed, or there was nothing to check
1 The checks ran and at least one failed (--ci only)
2 envprobe could not check: no config file, no checks under --ci, a config it could not build, or a bad flag

Without --ci, a failing check is information, not an error: doctor reports it and exits 0. --ci turns the same run into a gate. The 1/2 split matters there — 1 says the environment is wrong, 2 says envprobe never got far enough to form an opinion, and a pipeline should treat those differently.

- name: Verify toolchain
  run: envprobe doctor --ci

Development

./scripts/check.sh       # gofmt, build, vet, test -race, lint — what CI runs
./scripts/exit-codes.sh  # drives the built binary to verify the exit-code contract

Design rationale lives in docs/decisions.md.

License

MIT. See LICENSE.

About

Check that a machine has the tools and services you expect: binaries on PATH, TCP ports, and the Docker daemon, from one config file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages