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 Makefile includes topic-scoped files under build/ (build.mk, test.mk, docker.mk, generate.mk, and others). Run make help to list every target.
  • Shell: helper scripts live under build/, .github/, and deploy/; 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 .PHONY targets, keep recipes small, and add a ##-style help comment so make help stays complete.
  • One home per target — add a new target to the build/*.mk include that owns its topic rather than the root Makefile.

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 help lists your new/changed target with its description.
  • The target runs to completion, and every script passes make lint-shell (ShellCheck) and a quick bash -n syntax check.