Architecture specification

Scripts

Narrow, deterministic command helpers for make modules, CI, and local tooling — glue that takes its inputs from the caller and leaves orchestration to it.

Applies at
scripts/
Status
Stable
Source · managed
scripts/README.md

01 Defines

The home for repository scripts: behaviour too large or awkward for a make recipe, given a clear entry point while the caller decides when and with what inputs it runs.

02 Applies when

  • A make recipe has grown past a line or two of shell and needs a readable home.
  • A make module, a CI workflow, and a developer all need the same command.
  • Glue around existing tools — linters, Terraform, the AWS CLI — needs one documented entry point.

03 Boundaries

  • Scripts are glue; behaviour with its own structure and contract belongs in tools/.
  • Scripts do not decide orchestration — which components, which mode, which environment — on their own.
  • Dependency installation and long-running setup stay out of ad hoc scripts.

Expected behaviour

  1. 01
    Inputs arrive as arguments, standard input, or environment variables; results leave as output and exit codes.
  2. 02
    Component lists come from the caller rather than hidden discovery.
  3. 03
    A required check that fails exits non-zero.
  4. 04
    An intentionally unavailable capability prints a clear skip: message.
  5. 05
    Generated output paths are explicit, and networked setup stays separate from offline validation.

Behaviour#

A script exists because something does not fit comfortably in a make recipe. It gives that behaviour a name and an entry point, but it does not take over decisions that belong to its caller. The make module decides that Terraform roots are $(TERRAFORM_DIRS); the script works on whatever it is handed.

That narrowness keeps scripts predictable. Run twice with the same arguments and environment, a script does the same thing, because it does not reach for ambient repository state or set up its own prerequisites along the way.

FlowWho decides what
  1. 01 Caller A make target, CI step, or developer chooses components, mode, and environment Make Module Registries
  2. 02 Script Does one narrow job with those inputs
  3. 03 Exit code Zero for success or an explicit skip, non-zero for a failed check Explicit Skips

Layout#

  • scripts/
  • build/ Numbered make modules and repository.mk Make Module Registries
  • ci/
  • setup-ci-toolchain.sh Installs pinned Hugo and Terraform for CI jobs
  • docs/
  • prepare-site-content.mjs Stages docs/ into .cache/docs-site Staged Content Mounts
  • check-links.mjs Link checker used by check.links
  • deploy.sh Build, provision, upload, invalidate Static Site Delivery
  • lint.sh The aggregate lint gate Lint Gate
  • security-scan.sh Secret, vulnerability, and IaC scanners

Scripts and tools#

Scripts

  • Glue around existing commands
  • Invoked by make modules, CI, or a developer
  • Small enough to read before running

Tools

  • Own a defined workflow with its own contract
  • May be application code with richer structure
  • Shaped to move into a shared toolkit

A script may call a tool. The reverse is discouraged: a tool’s contract should not depend on undocumented behaviour of the script that happens to call it. See Tools .

Examples#

scripts/deploy.sh takes everything from its arguments and the environment and rejects anything it does not recognise:

scripts/deploy.sh
# usage: deploy.sh [apply|plan|destroy] [workspace|staging|prod]
command="${1:-apply}"
environment="${2:-${ENV:-workspace}}"

The make module that calls it is a one-line delegation, so the orchestration lives in make and the behaviour in the script:

scripts/build/81-deploy.mk
deploy: ## Build, provision, upload, and invalidate (ENV=workspace|staging|prod)
	@bash scripts/deploy.sh apply $(ENV)