A zero-config task runner for monorepos that discovers modules via .devops/commands.yaml files and runs commands across them — sequentially or in a side-by-side parallel TUI.
Drop a .devops/commands.yaml file in any subdirectory (up to 3 levels deep). Each file defines commands and the Docker Compose containers (or host) they run in. When you invoke devops <command>, it finds every module that defines that command and runs them, respecting priority ordering and parallelising same-priority groups in a split-screen terminal UI.
Grab the latest binary for your platform from the releases page and place it somewhere on your $PATH:
# macOS (Apple Silicon)
curl -L https://github.com/convidera/devops-cli/releases/latest/download/devops-darwin-arm64 -o /usr/local/bin/devops
chmod +x /usr/local/bin/devops
# macOS (Intel)
curl -L https://github.com/convidera/devops-cli/releases/latest/download/devops-darwin-amd64 -o /usr/local/bin/devops
chmod +x /usr/local/bin/devops
# Linux (amd64)
curl -L https://github.com/convidera/devops-cli/releases/latest/download/devops-linux-amd64 -o /usr/local/bin/devops
chmod +x /usr/local/bin/devopsRequires Go 1.22+.
go install github.com/convidera/devops-cli@latestOr clone and build:
git clone https://github.com/convidera/devops-cli
cd devops-cli
go build -o devops .Each module needs a .devops/commands.yaml file. The structure is:
<command>:
<container>:
- <script>
- <script><container> is either a Docker Compose service name or host (runs directly on the machine without Docker).
All scripts listed under the same <container> run in a single shell process, in order, so cd, export, and other shell state carry over from one line to the next (a failing line aborts the rest, like set -e). Different containers — and different modules — still run as separate processes.
my-monorepo/
├── backend/
│ └── .devops/
│ └── commands.yaml
└── frontend/
└── .devops/
└── commands.yaml
backend/.devops/commands.yaml
migrate:
app:
- php artisan migrate
seed:
app:
- php artisan db:seed
test:
app:
- php artisan testfrontend/.devops/commands.yaml
test:
node:
- npm testRun all tests across all modules:
devops testBy default every entry has priority 100. Lower numbers run first. Modules with the same effective priority for a command run in parallel. Use the map form to set a custom priority:
migrate:
app:
- script: php artisan migrate
priority: 10 # runs before priority-100 modulesdevops <command> Run command across all modules
devops <module> <command> Run command for a specific module
devops all <command> Run command across all modules (explicit)
devops <module> exec [cmd...] Open interactive shell in module's container
devops <module> shell Alias for exec
devops help Show this help
devops reinstall Download and install the latest release
# Run migrations across all modules (respects priority order)
devops migrate
# Run tests only for the backend module
devops backend test
# Open a shell in the backend module's container
devops backend exec
# Run an arbitrary command in a container
devops backend exec php artisan tinker
# Fall through to docker compose if no module defines the command
devops psWhen multiple modules share the same priority for a command they run in parallel inside a split-screen terminal UI:
- Tab / ← → or h / l — switch focus between panels
- ↑ ↓ or j / k — scroll one line
- PgUp / PgDn or Ctrl+U / Ctrl+D — scroll by page
- g / G — jump to top / bottom (G re-enables auto-scroll)
- q / Q / Ctrl+C — quit
After all panels finish, a summary shows which modules succeeded or failed.
# Simple form — uses default priority (100)
build:
app:
- npm run build
# Map form — explicit priority
setup:
db:
- script: php artisan migrate
priority: 10
app:
- script: php artisan db:seed
priority: 20
host:
- script: echo "done"
priority: 30A command can target multiple containers; they run in the order they appear in the file.