makefile-script-developer
Makefile Script Developer
Production-ready GNU Makefiles. Strict shell + validated env + logging + safety gates.
When to use
- The user asks to write, scaffold, or harden a
Makefile. - The user wants to add or refactor a
maketarget / workflow / build pipeline. - The user wants to automate Terraform / Helm / kubectl / Docker / build / release through
make. - The user wants a self-documenting
helptarget or wants to clean one up. - A task chain ends in "and put it in a Makefile so I can run
make deploy ...".
Required structure
Every Makefile you write starts from this skeleton. Do not omit SHELL := /bin/bash, the .SHELLFLAGS line, or .DEFAULT_GOAL := help. Recipes are TAB-indented, not spaces.
SHELL := /bin/bash
.SHELLFLAGS := -euo pipefail -c
.DEFAULT_GOAL := help
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables --no-print-directory
# ---- Configuration --------------------------------------------------
ALLOWED_ENVS := dev staging prod
ENV ?= dev
ifeq (,$(filter $(ENV),$(ALLOWED_ENVS)))
$(error ENV must be one of: $(ALLOWED_ENVS))
endif
TIMESTAMP := $(shell date +"%Y-%m-%d_%H-%M-%S")
LOG_DIR := logs/$(ENV)
# ---- Pre-flight -----------------------------------------------------
.PHONY: check-tools
check-tools:
@command -v terraform >/dev/null 2>&1 || { echo "terraform not installed"; exit 1; }
@echo "Tools verified"
# ---- Help -----------------------------------------------------------
.PHONY: help
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
Workflow
- Pick a starting template from
assets/templates/— copy and edit, do not rewrite from scratch:simple.mk.template— small build / lint / test Makefile.infrastructure.mk.template— Terraform / multi-env with logging, state backup, rollback.helm-deploy.mk.template— Helm install / upgrade / rollback / uninstall with timing.chart-package.mk.template— Helm chart lint / package / push to OCI registry.binary-build.mk.template— Cross-platform binary build with platform detection.
- Apply the patterns in
references/patterns.mdfor any non-obvious case (env validation, logging macros,foreacharg expansion,$(eval CMD=...)for per-recipe variables, layered.envincludes,definemacros for shared multi-step bodies). Read it on demand — don't preload it. - For multi-environment workflows (dev/staging/prod, multi-instance, multi-region, multi-layer), load
references/multi-env.md— it covers$(filter ...)validation, per-env plan files, layered-include .envpatterns, and instance auto-discovery fromenvs/*.env. - For Helm / kubectl / Terraform recipes, load
references/helm-kubectl-terraform.md— it covershelm upgrade --install --wait --timeout,helm-recover/helm-purgefor stuck releases,kubectl delete pod --force --grace-period=0, terraform plan/apply with state backup, and S3-compatible backend credential export. - For self-documenting help targets, load
references/help-target.md— covers grep-based auto-help (## comment), grouped sections, andmake helpdefaults. - For cross-platform Makefiles (Linux/macOS/Windows), load
references/cross-platform.md— coversunameplatform detection, separator differences (:vs;), exe suffix, BSD vs GNU sed. - Cross-check anti-patterns in
references/anti-patterns.mdbefore finishing — TAB vs spaces,$$vs$, missing.PHONY, recursive (=) vs simple (:=) assignment, unquoted$(VAR)in shell, missingset -o pipefail. - Validate the result. Run
bash scripts/validate-makefile.sh <your-Makefile>— it grep-scans the file for shell strict mode,.PHONYdeclarations, help target, env validation, TAB indentation,$$usage in recipes, and confirmation gates on destructive ops. Aim for ≥ 90%.
Available resources
assets/templates/simple.mk.template— minimal lint / build / test Makefile.assets/templates/infrastructure.mk.template— Terraform-style multi-env with logging, state backup, rollback, confirmation gates.assets/templates/helm-deploy.mk.template— Helm install/upgrade/rollback/uninstall with per-step timing and stuck-release recovery.assets/templates/chart-package.mk.template— Helm chart lint/package/push to OCI registry.assets/templates/binary-build.mk.template— Cross-platform binary build with platform detection.assets/examples/terraform-multi-env.mk— full reference implementation (3-env, logging, backup, rollback, helm-recover).scripts/validate-makefile.sh— score a Makefile against the checklist (run after writing).references/patterns.md— load when implementing logging macros, foreach var expansion,definebodies,$(eval)per-recipe vars, layered-include.references/anti-patterns.md— load when reviewing or rewriting an existing Makefile.references/multi-env.md— load when handling dev/staging/prod, multi-instance, multi-region, or multi-layer setups.references/helm-kubectl-terraform.md— load when wrapping Helm / kubectl / Terraform inmaketargets.references/help-target.md— load when writing a self-documentinghelptarget.references/cross-platform.md— load when targeting macOS or Windows alongside Linux.
Top gotchas (always inline — do not skip)
- Recipes are indented with a TAB, not spaces. GNU make rejects space-indented recipes with
*** missing separator. Stop.Configure your editor to keep tabs inMakefilefiles. - Use
$$to escape$in recipes.$VARin a recipe is interpreted by Make first; write$$VARto mean "shell variable" and$(VAR)for "Make variable". Single$followed by an unrecognized character silently expands to empty. SHELL := /bin/bash, never the default/bin/sh. Without it,pipefail,[[ ]], process substitution, and arrays are unavailable. macOS and Alpine/bin/share dash-like — recipes that work locally will fail on CI..SHELLFLAGS := -euo pipefail -cis the minimum.-eexits on error,-uon undefined vars,-o pipefailmakescmd1 | cmd2fail whencmd1fails. Without these, a failed step in a pipeline is silently swallowed.- Always declare non-file targets
.PHONY. Otherwise a stray local file nameddeployorcleanmakes Make skip the recipe with "target is up to date". :=(simple) vs=(recursive). Use:=for almost everything — it expands once at parse time.=re-evaluates on every reference, which is slow and surprising (e.g.TIMESTAMP = $(shell date)regenerates the timestamp on every reference). Use=deliberately when you need lazy evaluation (e.g.LOG_FILE = $(LOG_DIR)/$(CMD).logwhereCMDis set per-target via$(eval CMD=apply)).- Validate
ENVearly with$(filter ...).ifeq (,$(filter $(ENV),$(ALLOWED_ENVS)))+$(error ...)halts before any side effects. Don't rely on a runtime[ ... ] || exit 1inside a recipe — it runs after dependencies. - Confirmation gates use
read -p+ check.[ "$$confirm" = "destroy-$(ENV)" ] || (echo "Cancelled"; exit 1)— the$(ENV)ties the confirmation string to the environment so a copy-pasted prompt can't destroy prod by mistake. - Secrets in recipes: prefix lines with
@to suppress the echoed command, and neverecho $$SECRET. Pull credentials from env vars or ayq-readable secrets file rather than hardcoding them; use--password-stdininstead of-pflags. make -jis unsafe by default: targets sharingLOG_FILEor temp files will clobber each other. Use.NOTPARALLEL:for workflows that must serialize, or generate per-target temp paths.$(MAKE)notmakefor sub-invocations —$(MAKE)propagates-n,-j, andMAKEFLAGS. Plainmakestarts a fresh process and breaks dry-run / parallel mode.shellis evaluated at parse time.TIMESTAMP := $(shell date)runs once when Make reads the file — that's usually what you want, butfind ... -deleteinside$(shell ...)will run before any target executes.
What you DO
- Start every Makefile from
assets/templates/. - Set
SHELL := /bin/bash,.SHELLFLAGS := -euo pipefail -c,.DEFAULT_GOAL := help,.DELETE_ON_ERROR:, andMAKEFLAGS += --warn-undefined-variables --no-print-directory. - Validate
ENV(and any other enum input) up-front with$(filter ...)+$(error ...). - Declare every non-file target as
.PHONY. - Provide pre-flight
check-tools/check-env/check-statetargets and depend on them. - Use a
define run_with_log ... endefmacro to capture all output tologs/<scope>/<env>/<cmd>_<timestamp>.log. - Group related operations into composite workflow targets (
deploy: validate init plan apply). - Add confirmation gates (
read -p "Type 'destroy-$(ENV)' to confirm") on destructive ops; bypass withCI=truefor non-interactive runs. - Use
:=(simple expansion) by default; reserve=for genuinely lazy values. - Use
$$to refer to shell variables inside recipes; use$(VAR)for Make variables. - Write a
helptarget with grouped sections and concrete examples; make it the.DEFAULT_GOAL. - Use
$(MAKE)(notmake) for sub-invocations. - Run
scripts/validate-makefile.shon the result; iterate until ≥ 90%.
What you do NOT do
- Indent recipes with spaces, or rely on auto-detection.
- Forget
set -e/pipefailand let pipe failures slip past silently. - Skip
.PHONYdeclarations on non-file targets. - Use
=(recursive) where:=(simple) would work — it bites later. - Run destructive ops without a
read -pconfirmation, or withoutCI=trueto bypass for automation. - Leak secrets to logs (no
echo $$PASS, no missing@on credential-bearing lines). - Use
makeinstead of$(MAKE)in recursive invocations. - Hardcode
/home/<user>/...or/Users/<user>/...— use relative paths or$(CURDIR). - Write a Makefile without a
helptarget, or ahelpthat just lists targets without examples. - Mix multiple unrelated responsibilities in a single recipe (deploy + log-cleanup + notify) — split them and depend.
More from mkabumattar/skills
linux-script-developer
Write production-ready Bash scripts with strict error handling (`set -euo pipefail`), validated argument parsing, colored user feedback, and cross-platform compatibility (Linux, macOS, Windows via Git Bash/WSL). Use this skill whenever the user asks for a `.sh` script, a shell script, a Bash one-liner installer, a deployment script, an automation/CI script, a CLI wrapper, or a file-batch processor — including casual phrasings like "write a script to ...", "automate this in bash", or "make me a shell tool". Also use when reviewing or hardening an existing Bash script.
16skill-builder
Build a new Agent Skill that follows the agentskills.io specification and best practices — slim `SKILL.md` (≤ 200 lines / 5K tokens), valid kebab-case `name`, imperative `description` under 1024 chars, progressive disclosure via `references/`, bundled `assets/` and `scripts/`, and an MIT `LICENSE`. Use this skill whenever the user asks to create, scaffold, build, write, or author a new Agent Skill — including phrasings like "build a skill for X", "scaffold a new skill", "create an agent skill", "make me a skill that does X", "write a SKILL.md for ...", or "I want to publish a skill on agentskills.io". Also use when reviewing or refactoring an existing oversized `SKILL.md` (a sign that detail should be moved to `references/`).
15python-script-developer
Write production-ready Python CLI tools, automation scripts, and batch file processors with type hints, structured `logging` (never `print` for diagnostics), `argparse` interfaces, `pathlib` for filesystem work, specific exception handling, and cross-platform support (Linux, macOS, Windows). Use this skill whenever the user asks to create a Python script, `.py` utility, CLI tool, automation, batch processor, or data pipeline — including casual phrasings like "write a python script that ...", "automate this in python", "I need a small tool", or "give me a one-off processor". Also use when reviewing or hardening an existing Python script.
15information-architecture
Plan the structural and execution architecture of a feature, app, or site — produce both an `INFORMATION_ARCHITECTURE.md` (site map, navigation, content hierarchy, user flows, URL strategy, naming conventions, component reuse map) AND a phased `PLAN.md` (phases by impact/effort/risk, vertical-slice tasks with sub-tasks, dependencies, estimates, and a detailed task breakdown with Why/How/Impact/Effort). Use this skill whenever the user wants to plan a product or feature, design site structure, lay out information architecture, map user flows, organize content, break work into phases, build a roadmap, plan an implementation order, or hits you with phrases like "plan the IA", "map the structure", "break this into tasks", "give me a roadmap", "phase out the work", "create an enhancement plan", or "what should I build first". Also use when reviewing or refactoring an existing IA or project plan.
15qa
Run an interactive QA session — the user describes bugs and issues conversationally, you ask brief clarifying questions, explore the codebase for domain context, decide whether to file one issue or break it down, and create durable user-focused GitHub issues via `gh issue create` — without referencing internal file paths or line numbers. Use this skill whenever the user wants to do QA, report bugs, file issues, walk through a list of problems, or hits you with phrases like "let's do a QA session", "I found a bug", "this is broken", "file this as an issue", "I have a few things to report", or "let's go through these one by one". Also use when the user is reviewing a deployed feature and wants to track defects.
14gitops-pipeline-developer
Author production-ready GitOps release pipelines (Jenkins-style by default; portable patterns for GitHub Actions, GitLab CI, Drone) that combine Gitflow branching, Conventional Commits, automatic SemVer bumping, a SonarQube quality gate, Grype container image scanning, and an aggregated quality+security scorecard with policy alignment checks. Use this skill whenever the user wants to write or harden a CI/CD pipeline, set up a release flow, automate versioning, enforce conventional commits, gate merges on SonarQube, scan images with Grype, build a release scorecard, or hits you with phrases like "set up the CI", "write me a Jenkinsfile", "add SonarQube", "enforce conventional commits", "wire SemVer", "scan our images for CVEs", "add a quality gate", "CI for this repo", or "I need a release pipeline".
14