When CI/CD is down, the roadie ships it.
Local-build + SSH-deploy orchestrator. One JSON config per repo, multiple projects, scriptable steps. Cross-platform, zero npm dependencies.
CI/CD exists but the test environment is flaky and you keep deploying by hand: build locally, copy the artifact, restart the service, check it. This script turns those manual steps into a reproducible per-project pipeline you trigger from a TUI menu (or non-interactively from another script).
# 1. Generate a config skeleton in the current directory.
node roadie.js --init # → config.json
# 2. Edit config.json — fill in projects/steps for your apps.
# See config.example.json for a fully populated reference.
# 3. Run the interactive menu (↑↓ to pick, enter to deploy).
node roadie.js- Node.js 18+ (stdlib only — no
npm install) - OpenSSH client (
ssh,scp) on PATH- macOS / Linux: built-in
- Windows: built-in on Windows 10+ (Optional Feature: "OpenSSH Client")
- Key-based SSH to your target servers (ssh-agent or
identityFile). Password auth is intentionally not supported (BatchMode=yes).
node roadie.js # interactive menu
node roadie.js --project=<name> # run a specific project, non-interactive
node roadie.js --message="..." # success-only message (overrides project.notify)
node roadie.js --list # print every project + its steps
node roadie.js --validate # check the config schema (no run); summary on success
node roadie.js --init # write config.json (template)
node roadie.js --config=<path> # use a non-default config (default: config.json)
node roadie.js --sandbox-up # build + start Docker sshd sandbox (idempotent)
node roadie.js --sandbox-down # stop the sandbox container
node roadie.js --helpExit codes:
| Code | Meaning |
|---|---|
| 0 | Success (deploy finished or --validate passed) |
| 1 | Step failure, config error, or validation failure |
| 2 | Unknown CLI argument (help is printed to stderr) |
The first failing step stops the run.
{
"projects": [
{
"name": "my-app",
"ssh": { "host": "test.internal", "user": "deploy" },
"steps": [
{ "name": "Build", "type": "local", "run": "npm run build" },
{ "name": "Upload", "type": "transfer", "from": "dist/.", "to": "/var/www/app/" },
{ "name": "Reload", "type": "remote", "run": "sudo systemctl reload nginx" }
]
}
]
}Four step types: local (shell command here), transfer (scp -r),
remote (shell command on the SSH target), confirm (pause for user
Enter — manual gate before destructive ops). cwd and run accept per-OS
objects ({ win, mac, linux, default }) for cross-platform configs. Full
schema, validation rules, and a real-world example are documented separately.
- Configuration — full schema, step types, per-OS values, ssh block, notify,
--validate - Recipes — full real-world example + smaller patterns (health check, atomic swap)
- Testing — three-layer test strategy + Docker sshd sandbox setup
roadie/
├── LICENSE # MIT
├── README.md
├── roadie.js # thin CLI entry — calls lib/cli.run()
├── config.example.json # populated reference (full schema example)
├── config.sandbox.example.json # sandbox config — used by --sandbox-up + integration tests
├── docs/
│ ├── configuration.md
│ ├── recipes.md
│ ├── testing.md
│ └── assets/logo.svg
├── lib/
│ ├── cli.js # arg parsing + --help/--init/--list/--validate + sandbox flags + main flow
│ ├── colors.js # ANSI palette + bell
│ ├── config.js # JSON load + schema validation
│ ├── display.js # progress UI: header, per-step status, banner, notice
│ ├── menu.js # raw-TTY single-select project picker
│ ├── paths.js # ~ expansion + per-OS PathLike resolution
│ ├── runner.js # sequential step executor (local/transfer/remote/confirm)
│ └── ssh.js # spawn wrappers around system ssh / scp
└── test/
├── cli.test.js # spawn-based CLI smoke tests
├── config.test.js # unit tests for loadConfig + every validation rule
├── integration.test.js # end-to-end against the Docker sandbox
├── setup.sh # bash shim → test/sandbox/setup.js
├── teardown.sh # bash shim → test/sandbox/teardown.js
└── sandbox/
├── Dockerfile # alpine + openssh + sudo, key-only deploy user
├── docker-compose.yml # 127.0.0.1:2222 → 22
├── setup.js # cross-platform bootstrap (Docker + keys + probe)
└── teardown.js # docker compose down + known_hosts cleanup
- No password auth. SSH runs with
BatchMode=yes, so the deploy never hangs on a password prompt. Use ssh-agent,identityFile, or~/.ssh/config. - First-connection host keys are accepted (
StrictHostKeyChecking=accept-new) and stored in your known_hosts. Subsequent host-key changes will fail loudly. - Steps run sequentially, in order. The first failing step aborts the run and emits a red banner with the failing command's exit code.
FORCE_COLOR=0is set for local steps so build tools don't smear ANSI codes through the indented output.- No remote artifact verification beyond what your own
remotesteps check. For post-deploy sha256 validation, see the recipes doc.