DOC_INDEX
THEME
DOCS/Migrating to OrchStep/From Make

From Make

Translate a Makefile of phony targets into an OrchStep workflow - targets, prerequisites, variables, .PHONY, and make -j.

If you use make as a command runner - a Makefile full of .PHONY targets like build, test, deploy - OrchStep is a direct, friendlier replacement. You keep the same mental model (named commands that run shell) and lose the parts everyone trips over: tab-sensitive recipes, .PHONY bookkeeping, and Bash-only portability.

The 30-second version

make                       ->   orchstep run            # default task
make build                 ->   orchstep run build
make ci                    ->   orchstep run ci
make deploy ENV=prod V=2.1 ->   orchstep run deploy --var environment=prod --var version=2.1

How the concepts map

MakeOrchStep
A target (build:)A task (build: under tasks:)
A recipe line (tab + shell)A step: func: shell with do:
Prerequisites (ci: test build)Steps that call task: test / task: build
VAR ?= x and $(VAR)defaults: plus {{ vars.VAR }}
make build VAR=xorchstep run build --var var=x
.PHONY: buildNothing - tasks are never file targets
make -j2 a bA parallel: block
@echo (silent)--log-level / --output

Side by side

MAKEFILE
VERSION ?= 1.0.0
ENV     ?= staging

.PHONY: build test deploy ci

build:
	echo "building v$(VERSION)"

test:
	echo "running tests"

deploy:
	echo "deploying v$(VERSION) to $(ENV)"

ci: test build
ORCHSTEP.YML
name: app
desc: Build, test, and deploy the app

defaults:
  version: "1.0.0"
  environment: "staging"

tasks:
  build:
    steps:
      - name: compile
        func: shell
        do: echo "building v{{ vars.version }}"
  test:
    steps:
      - name: unit
        func: shell
        do: echo "running tests"
  deploy:
    steps:
      - name: ship
        func: shell
        do: echo "deploying v{{ vars.version }} to {{ vars.environment }}"
  ci:                         # "prerequisites" become task calls
    steps:
      - name: run_test
        task: test
      - name: run_build
        task: build

Run it

orchstep run ci
orchstep run deploy --var environment=production --var version=2.1.0
$ orchstep run deploy --var environment=production --var version=2.1.0
  $ echo "deploying v2.1.0 to production"
deploying v2.1.0 to production
Result: success

Note that make deploy ENV=prod (positional-ish VAR=value) becomes the explicit, named --var environment=prod. List everything with orchstep list-tasks.

Running targets in parallel

make -j2 lint test becomes a parallel: block - clearer, and it works the same on every OS:

tasks:
  check:
    steps:
      - name: fan_out
        parallel:
          - name: lint
            func: shell
            do: echo "linting"
          - name: test
            func: shell
            do: echo "testing"

Gotchas worth knowing

  • No tabs. OrchStep is YAML - the single most common Makefile error (spaces where a tab belongs) simply cannot happen.
  • No .PHONY. Tasks are never confused with files on disk, so there is nothing to declare.
  • env is reserved. Don't name a variable env - that namespace is for environment variables ({{ env.PATH }}). Use environment (the loader will stop you with a clear error otherwise).
  • Make as a build system is different. If you actually rely on file-target timestamps and automatic variables ($@, $<) for incremental builds, that is a build graph, not a command runner - keep Make for it and call it from a step (do: make dist/app). For the .PHONY command-runner case (the common one), OrchStep is the better home.

Migrate one target at a time

A step is just shell, so you can wrap an old target while you convert the rest:

tasks:
  build:
    steps:
      - name: legacy
        func: shell
        do: make -f Makefile.old build

What you gain

  • orchstep run --dry-run previews the whole chain (including ci -> test/build).
  • The same file runs on Windows without make installed.
  • Steps pass typed outputs instead of echoing strings.
  • Shared targets can become versioned modules.

Where to go next