Interfaces
Every command-line flag, Make target, and file contract this repository exposes. This is the
reference; usage.md is the narrative walkthrough.
make — the entry point
| Target | What it does |
|---|---|
make help |
List every target with its description. The default goal. |
make new TARGET=<dir> [PROFILE=<name>] |
Scaffold a repository. Never overwrites. |
make dry-run TARGET=<dir> [PROFILE=<name>] |
Print what new would write, change nothing. |
make audit [REPO=<dir>] [LEVEL=<level>] |
OpenSSF readiness report. Defaults to this repository. |
make test |
The end-to-end suite: scaffold every profile, audit the output. |
make check |
check-shell + check-profiles + check-placeholders. |
make check-shell |
ShellCheck every script, or bash -n when it is not installed. |
make check-profiles |
Assert every profile implements all nine lang-* targets. |
make check-placeholders |
List every {{VAR}} used and fail on one nothing binds. |
make check-pins |
Report action pins under template/ that have fallen behind. |
make verify-pins |
Assert every pinned SHA in every workflow names a real commit. |
make pin-actions |
Rewrite those pins to the newest release in the current major. |
make profiles |
List the available language profiles. |
make clean |
Remove .scratch/. |
BOOTSTRAP_ARGS passes extra flags through to the scaffolder:
scripts/bootstrap.sh
Renders template/common/ and template/<profile>/ into a target directory.
| Flag | Meaning |
|---|---|
--target <dir> |
Required. Destination directory, created if missing. |
--profile <name> |
python, node, rust, or generic. Prompted for if omitted. |
--answers <file> |
Load answers from a previously written answers.env. |
--set KEY=VALUE |
Override one variable. Repeatable. Wins over --answers. |
--merge |
Skip files that already exist. This is the default; the flag states it. |
--force |
Overwrite existing files. |
--dry-run |
Print what would be written; write nothing. |
--non-interactive |
Never prompt. Fails if a required value has no default. |
-h, --help |
Usage. |
Precedence for any variable, highest first: --set, --answers, an interactive answer, the
profile's profile.env, the built-in default.
Exit codes: 0 success, 1 a fatal error (unknown profile, missing required value in
non-interactive mode, unreadable answers file), 2 a usage error.
Side effects: writes into --target only. Also writes <target>/.template/answers.env, which
records every resolved variable so the same scaffold can be replayed. Appends the profile's
.gitignore.append fragment to <target>/.gitignore, guarded by a # Profile: <name> header so a
second run does not duplicate it. Never fetches anything over the network.
scripts/openssf-audit.sh
Reads a repository and reports how close it is to the OpenSSF Best Practices badge.
| Flag | Meaning |
|---|---|
[path] |
Repository to audit. Defaults to the current directory. |
--level <level> |
Report only one badge level. Default: all three. |
--quiet |
Print only failures. |
Result symbols: ✔ satisfied, ✘ missing (with the remediation), ? needs a human decision —
bus factor, coverage percentages, cryptographic choices. A criterion a script cannot honestly decide
is never silently passed.
Exit codes: 0 when nothing is missing, 1 otherwise. Suitable as a CI gate.
Workflow safety. Beyond the badge criteria, the audit reads every workflow it finds under any
.github/workflows/ in the tree — a generated repository's own, and each profile's copy when run
against this repository — and reports:
| Check | Fails when |
|---|---|
dangerous_workflow |
pull_request_target is combined with a checkout of the PR's head ref. Reported as needing a human decision when the trigger is used without such a checkout. |
script injection |
An untrusted context (github.event.pull_request.*, github.head_ref, comment and issue bodies) is interpolated directly inside a run: block. |
token_permissions |
A workflow declares no top-level permissions: and so inherits the default token. |
pinned_dependencies |
A uses: reference names a tag or branch instead of a 40-character commit SHA. |
scripts/pin-actions.sh
Refreshes the action SHA pins inside template/, which Dependabot does not traverse.
| Flag | Meaning |
|---|---|
--check |
Report stale pins and exit 1 if any. Writes nothing. |
--verify |
Assert every pinned SHA resolves to a real commit in that action's repository. Covers every workflow in the tree, this repository's own included. Exits 1 on any that does not. |
--allow-major |
Consider releases beyond the current major version. |
Requires an authenticated gh. It reads the trailing version comment on each uses: line to decide
what "newer" means, so that comment is part of the data, not documentation. A pin whose comment is a
branch name is re-resolved to that branch's current head.
The lang-* contract
A generated repository's Makefile knows nothing about its language. It calls these nine targets,
which the profile's make/<profile>.mk implements. profiles.md covers adding one.
| Target | Obligation |
|---|---|
lang-setup |
Bring a fresh checkout to a working development environment. |
lang-lint |
Lint, format-check, and type-check. Must fail on any finding. |
lang-format |
Auto-format and auto-fix in place. |
lang-test |
Fast unit tests. No network, no privileges. |
lang-test-integration |
Tests needing a real environment. |
lang-fuzz |
Property-based or fuzz tests. |
lang-audit |
Scan dependencies for known vulnerabilities. |
lang-build |
Produce release artifacts into $(DIST_DIR). |
lang-clean |
Remove language-specific caches and artifacts. |
Each profile also ships a profile.env assigning PRIMARY_LANGUAGE, PRIMARY_LANGUAGE_LOGO,
MIN_LANG_VERSION, CODEQL_LANGUAGE, DEPENDABOT_ECOSYSTEM, LINTER_NAME, and SKELETON_PATHS —
the source directories written all-or-nothing, so a repository that already has its own source never
receives a test importing a module the scaffolder skipped.
Placeholder substitution
scripts/lib/render.sh substitutes {{KEY}} using Bash parameter expansion over a fixed key list —
not sed, and not eval. Two consequences: a value containing slashes, ampersands, or quotes needs
no escaping, and a GitHub Actions expression such as ${{ github.ref }} passes through untouched
because github.ref is not a bound key. placeholders.md lists every key and
where its value comes from.