Makefile for CI/CD Pipelines

Design Makefiles that integrate with CI/CD systems like GitHub Actions, GitLab CI, and Jenkins. Standardize build, test, and deploy commands across environments.

Advanced Patterns

Detailed Explanation

Make as a CI/CD Interface

Using Makefiles as the interface between CI/CD systems and your build process creates a portable, locally-testable automation layer.

The Principle

CI/CD configuration files (.github/workflows/*.yml, .gitlab-ci.yml, Jenkinsfile) should call Make targets, not raw commands:

# .github/workflows/ci.yml
steps:
  - run: make install
  - run: make lint
  - run: make test
  - run: make build

CI-Friendly Makefile

.PHONY: install lint test build deploy ci

# Use CI environment variable to detect CI context
CI ?=

ifdef CI
  NPM_FLAGS = --prefer-offline --no-audit
  DOCKER_FLAGS = --quiet
else
  NPM_FLAGS =
  DOCKER_FLAGS =
endif

install:
	npm ci $(NPM_FLAGS)

lint:
	npm run lint
ifdef CI
	npm run lint -- --format=json --output-file=lint-report.json
endif

test:
	npm test -- --coverage
ifdef CI
	mv coverage/lcov.info coverage/lcov-$(shell date +%s).info
endif

build:
	npm run build

# CI meta-target: run all CI steps in order
ci: install lint test build
	@echo "CI pipeline complete"

Docker CI Targets

REGISTRY   ?= ghcr.io/myorg
IMAGE_NAME ?= myapp
GIT_SHA    := $(shell git rev-parse --short HEAD)
BRANCH     := $(shell git rev-parse --abbrev-ref HEAD)

docker-ci: docker-build docker-test docker-push

docker-build:
	docker build $(DOCKER_FLAGS) \
	  --label "org.opencontainers.image.revision=$(GIT_SHA)" \
	  --label "org.opencontainers.image.source=$(shell git remote get-url origin)" \
	  -t $(REGISTRY)/$(IMAGE_NAME):$(GIT_SHA) .

docker-test:
	docker run --rm $(REGISTRY)/$(IMAGE_NAME):$(GIT_SHA) npm test

docker-push:
	docker push $(REGISTRY)/$(IMAGE_NAME):$(GIT_SHA)
ifeq ($(BRANCH),main)
	docker tag $(REGISTRY)/$(IMAGE_NAME):$(GIT_SHA) $(REGISTRY)/$(IMAGE_NAME):latest
	docker push $(REGISTRY)/$(IMAGE_NAME):latest
endif

Benefits of Make in CI/CD

  1. Local reproduction: make ci runs the exact same steps locally
  2. CI system portability: Switch from GitHub Actions to GitLab CI by changing YAML, not build logic
  3. Caching awareness: Make's dependency tracking avoids rebuilding unchanged artifacts
  4. Parallelism: make -j4 runs independent targets in parallel

Parallel Execution

.PHONY: lint-all
lint-all: lint-js lint-css lint-markdown

lint-js:
	npx eslint src/
lint-css:
	npx stylelint 'src/**/*.css'
lint-markdown:
	npx markdownlint docs/

make -j3 lint-all runs all three linters simultaneously, reducing CI time.

Use Case

Creating a consistent build interface that works identically in local development and CI/CD pipelines, allowing developers to run `make ci` locally to reproduce CI failures.

Try It — Makefile Generator

Open full tool