#!/usr/bin/env bash # bash -n every shell script and every workflow `run:` block. actionlint when present. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "${ROOT}" for script in scripts/*.sh; do bash -n "${script}" done python3 - "${ROOT}" << 'PY' import pathlib, re, subprocess, sys, tempfile root = pathlib.Path(sys.argv[1]) failures = 0 workflow_count = 0 block_count = 0 for workflow in sorted((root / ".github/workflows").glob("*.yml")) + sorted( (root / ".github/workflows").glob("*.yaml") ): workflow_count += 1 text = workflow.read_text(encoding="utf-8") blocks = [] for match in re.finditer(r"^(\s+)run:\s*\|[^\n]*\n((?:\1 .*\n)+)", text, re.M): indent = len(match.group(1)) + 2 body = [] for line in match.group(2).splitlines(): body.append(line[indent:] if len(line) >= indent else line.lstrip()) blocks.append("\n".join(body) + "\n") block_count += len(blocks) for index, block in enumerate(blocks, start=1): with tempfile.NamedTemporaryFile("w", suffix=".sh", delete=False) as handle: handle.write(block) name = handle.name result = subprocess.run(["bash", "-n", name], capture_output=True, text=True) pathlib.Path(name).unlink() if result.returncode != 0: failures += 1 sys.stderr.write(f"{workflow.relative_to(root)} run block {index}: {result.stderr}") if failures: raise SystemExit(1) print( f"bash -n passed for scripts and {block_count} run blocks in {workflow_count} workflows" ) PY if command -v actionlint >/dev/null 2>&1; then actionlint -color else echo "actionlint not installed; skipped (CI installs it)" fi