Contributing shell scripts and Makefiles
July 30, 2026 · View on GitHub
Purpose
This is the primary doc for writing and modifying the build automation in Radius — the Bash scripts that support the build and the GNU Make targets that drive it. It is reference material for anyone editing a .sh script, the Makefile, or a build/*.mk include. The detailed conventions live in two instruction files — shell and Make — which Copilot applies automatically to matching files; this doc gives the map of where the scripts and Make includes live.
Where these files live
- Make: the root
Makefileincludes topic-scoped files underbuild/(build.mk,test.mk,docker.mk,generate.mk, and others). Runmake helpto list every target. - Shell: helper scripts live under
build/,.github/, anddeploy/; many are invoked by Make targets or CI workflows.
Conventions
Follow the shell instruction file and the Make instruction file. Their emphasis for Radius:
- Safe Bash — start scripts with
set -euo pipefail, quote expansions, and validate arguments. - Well-structured Make — declare
.PHONYtargets, keep recipes small, and add a##-style help comment somake helpstays complete. - One home per target — add a new target to the
build/*.mkinclude that owns its topic rather than the rootMakefile.
Managing external CLI pins
The canonical versions, release sources, platform assets, and SHA-256 checksums for downloaded CLI tools live in build/tools.yaml. Run make update-tools to check every source and refresh available versions and checksums. The command regenerates the committed build/tools.generated.mk include that supplies metadata to the existing make install-<tool> targets. The updater is built into the ignored bin/ directory before it runs, which avoids Windows security software blocking Go's temporary go run executable. A newer release is adopted only once it is at least cooldownDays old, reducing the risk of pinning a release that is withdrawn shortly after publication. See the tool updater reference for the manifest schema and supported values. Bicep is checked but intentionally held at its compatibility-pinned version until newer releases support the local registries used by functional tests.
Linting shell scripts with ShellCheck
Every tracked .sh script is validated with ShellCheck as part of the pull request checks, so run it locally before you push. Install the pinned version (into a user-owned bin directory — no sudo) and lint every tracked script with:
make install-shellcheck
make lint-shell
make lint-shell applies the shared configuration in .shellcheckrc and skips the third-party Spec Kit tooling under .specify/. The version and per-platform checksums that make install-shellcheck pins live in build/tools.yaml (SHELLCHECK_VERSION).
The easy path is the dev container, which installs the ShellCheck CLI on the PATH and the ShellCheck VS Code extension for you, so you can run make lint-shell (and see findings inline as you edit) without installing anything.
Resolve every finding rather than leaving it. When a warning is a genuine false positive, suppress it narrowly with a # shellcheck disable=SCxxxx directive immediately above the affected command and add a short comment explaining why.
Verification
make helplists your new/changed target with its description.- The target runs to completion, and every script passes
make lint-shell(ShellCheck) and a quickbash -nsyntax check.
Related docs
- Building the repo — the primary
maketargets contributors run. - Documentation index — every contributing doc.