Runny

Makefiles are for boomers. The future is Runny.

$ brew install simonwhitaker/tap/runny

Your project commands, finally organized

Every project accumulates a pile of shell commands. Build scripts, test runners, deploy steps, dependency wrangling. Runny gives them a home in a clean .runny.yaml file and gets out of your way.

YAML you already know

Syntax inspired by GitHub Actions. If you've written a workflow file, you already know how Runny works.

IDE autocomplete

Full JSON schema means your editor can autocomplete keys, validate structure, and catch typos as you type.

Compose workflows

Chain commands with needs and then. Run steps conditionally with if. No more copy-pasting sequences.

Arguments built in

Pass arguments to commands with argnames. No quoting gymnastics or positional parameter headaches.

Clean command lists

Mark helper commands as internal to keep your command list focused. Add description for quick reference.

Tiny and fast

A single Go binary. No runtime, no dependencies. Installs in seconds, runs instantly.

How it works

1

Install

One Homebrew command. Also available via go install or GitHub releases.

2

Define

Create a .runny.yaml in your project root. Or run runny --init to generate one.

3

Run

Type runny <command>. Tab completion works out of the box for bash, zsh, and fish.

$ runny
build go build -o dist/myapp ./...
deploy Deploy to production
lint golangci-lint run ./...
test go test ./...
test-coverage go test -coverprofile=c.out ./...

$ runny test
ok   myapp/api     0.42s
ok   myapp/core    0.31s
ok   myapp/web     0.18s

See it in action

Real configs for real workflows. Drop one of these into your project and go.

Python project

.runny.yaml
shell: /bin/bash
commands:
  install-uv:
    internal: true    # hidden from command list
    if: "! command -v uv"
    run: pip install uv
  pip-sync:
    description: Sync dependencies from requirements.txt
    needs:
      - install-uv
    run: uv pip sync requirements.txt
  pip-install:
    argnames:
      - packagespec
    run: echo $packagespec >> requirements.in
    then:
      - pip-compile-and-sync

Go project

.runny.yaml
commands:
  clean:
    run: |
      go clean ./...
      rm -rf dist
  test:
    run: go test ./...
  release:
    needs:
      - clean
      - install-goreleaser
    run: |
      export GITHUB_TOKEN=$(gh auth token)
      goreleaser

Docker Compose

.runny.yaml
commands:
  up:
    run: docker compose up -d
  down:
    run: docker compose down
  logs:
    argnames:
      - service
    run: docker compose logs $service
  shell:
    argnames:
      - service
    run: docker compose exec $service sh