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
- Local reproduction:
make ciruns the exact same steps locally - CI system portability: Switch from GitHub Actions to GitLab CI by changing YAML, not build logic
- Caching awareness: Make's dependency tracking avoids rebuilding unchanged artifacts
- Parallelism:
make -j4runs 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.