Command reference#
Every script under scripts/ and ps/scripts/, from its own header. In a repository that
vendors script-helpers, the paths start with scripts/script-helpers/.
Most scripts print the same text with -h or --help.
This page is generated by scripts/gen_command_reference.py; edit the
script header, then run it. The library's functions are in the
API index.
Git hooks and setup#
scripts/foxguard_report.sh#
Scan files with foxguard and write the findings as formatted JSON, to keep as evidence or compare later.
Usage: bash scripts/foxguard_report.sh [--base <ref>] [--output <file>] [--] [<path>...]
--base <ref> Scan the files changed between <ref> and HEAD (the pre-push
hook prints this with the remote's commit). Default when no
paths are given: the upstream branch, else origin/HEAD.
--output <file> Where to write the JSON (default: foxguard-report.json).
<path>... Scan these files instead of a range.
Exit codes:
0 No findings.
1 Findings (written to the file).
2 Usage or git error, the report could not be written, or a file could not
be scanned (listed under "errors").
3 foxguard not installed and not installable here.
Example:
bash scripts/script-helpers/scripts/foxguard_report.sh --base origin/main --output foxguard-report.json
scripts/owner_scope.sh#
Decide whether a repository belongs to a configured owner, failing closed. Sourced by the global hooks and the Claude Code hook.
Usage: source scripts/owner_scope.sh; scope_owners; scope_owner_of <url>; scope_in_scope <owners> [url...]
SCRIPT_HELPERS_OWNERS Comma-separated GitHub users/orgs. Default:
git config --global script-helpers.owners.
-h, --help Executed directly: show this help.
Exit codes:
scope_owners returns 0 (owners printed, or none set) or 2 (git config unreadable).
scope_owner_of returns 0 with the owner printed, 1 for a non-github URL.
scope_in_scope returns 0 in scope, 1 out of scope.
scope_repo_urls returns 0, or 2 when git cannot list the remotes.
Executed directly: 0 for -h/--help, 2 otherwise (the file must be sourced).
Example:
source scripts/owner_scope.sh
scope_in_scope "$(scope_owners)" "$(git remote get-url origin)" && echo checked
scripts/push_checks.sh#
Sourced by the pre-push hook and the hook dispatcher: hygiene and lint of what a push changes; blocks on any finding.
Usage: source scripts/push_checks.sh; push_checks <refs-file> <remote-name> [<url>]
<refs-file> The ref lines git gave pre-push, saved to a file.
<remote-name> git's first pre-push argument.
<url> git's second pre-push argument.
-h, --help Executed directly: show this help.
Exit codes:
push_checks returns 0 when the push is clean and 1 on any finding (each
finding on stderr); push_tests returns 1 when tests fail or none are found.
Executed directly: 0 for -h/--help, 2 otherwise (the file must be sourced).
Example:
scripts/run_repo_tests.sh#
Run a repository's test suite, detecting the stack (preflight --quick first, else PHP, Node, Python, Go, Gradle, Rust, Flutter, make test, bats, tests/*_test.sh).
Usage: bash scripts/run_repo_tests.sh [--require]
--require Exit 4 when no test runner is found, instead of 0. The push
uses it, so an opted-in repository with no tests is not "passed".
-h, --help Show this help message.
Runs in the repository containing the current directory.
Exit codes:
0 tests passed, or none found without --require
1 tests failed (the runner's own exit code is in the message)
2 usage error
4 no tests found, with --require
Example:
scripts/security_hooks.sh#
The security steps every git hook runs, sourced by scripts/git-hooks/* and the global dispatcher.
Usage: source scripts/security_hooks.sh; sec_pre_push <refs-file> <remote-name> <url> | sec_pre_commit | sec_commit_msg <msg-file>
SCRIPT_HELPERS_SECURITY_DIR Directory holding check_private_names.sh and
check_sensitive.sh. Set by the global hooks;
otherwise they are looked up in the repository.
SECURITY_GATE_ALLOW_UNCHECKED=1 Let a push through when a check could not
run (exit 2, script missing, scope unknown), with
a warning. Narrow on purpose: --no-verify skips
every check, this skips none that can run.
SENSITIVE_ALLOW_UNCHECKED=1 is the same.
FOXGUARD_CMD foxguard command to run (default: the pinned
binary in the cache, else foxguard on PATH).
Exit codes:
Sourced: no output, no exit. Each sec_* function returns 0 to let the
commit or push continue and 1 to refuse; sec_find returns 127 when a
check script is missing.
Executed: 0 for -h/--help; 2 for anything else (it must be sourced).
Example:
scripts/setup-hooks.ps1#
Run the Bash hook setup command through Git Bash from PowerShell.
Usage: pwsh -NoProfile -ExecutionPolicy Bypass -File scripts\setup-hooks.ps1 [setup-hooks options]
Arguments are passed unchanged to scripts/setup-hooks.sh.
Exit codes:
0 The Bash setup command completed successfully.
* Any other code is scripts/setup-hooks.sh's own, passed through unchanged.
2 Also returned when Git Bash was not found on Windows.
Example:
scripts/setup-hooks.sh#
Configure git to use shared or repository-local hook scripts, and install the Claude Code security hook.
Usage: bash scripts/setup-hooks.sh [--claude-repo|--agent-repo <list>|--global|--claude|--agent <list>|--all|--managed] [--uninstall] | --repair | --status [--porcelain] | --doctor | --install-gitleaks | --install-foxguard
(none) Set up this repository. With no hooks running here yet, point its
core.hooksPath at the shared hooks. If hooks already run (a local or
global core.hooksPath set by another tool, or .git/hooks), keep them:
save the old value in .git/config and chain, in order, the security
checks (owners' repositories only), this repository's committed
hooks, then the hooks that ran before. Only untracked files change.
--uninstall alone undoes it and restores the previous value.
--global Install the git hooks machine-wide: copy them, with their own copy of
the check scripts, to <data>/script-helpers/git-hooks and set the
GLOBAL core.hooksPath there, after printing the old value and saving it.
--claude Install the Claude Code PreToolUse hook for this user: copy it to
<data>/script-helpers/claude-hooks and add one entry to
${CLAUDE_CONFIG_DIR:-~/.claude}/settings.json (matcher Bash, timeout
60 s, shell bash). Every other key and hook in the file is kept; an
older entry for the same hook is replaced; a backup is written first.
--claude-repo Install the Claude Code hook for this repository only: one entry in
.claude/settings.local.json, listed in .git/info/exclude so it is never
committed; backups go under .git/script-helpers/. With --uninstall:
remove it, and the file too when this script created it.
--agent-repo <list> The same check for other AI agents, this repository only.
<list>: codex, gemini, copilot, cursor, comma-separated, or all.
Writes our entry into the agent's project hook file, listed in
.git/info/exclude so it is never committed; other entries stay:
codex .codex/hooks.json (PreToolUse, matcher Bash)
gemini .gemini/settings.json (BeforeTool, run_shell_command)
copilot .github/hooks/script-helpers.json (preToolUse, bash|powershell)
cursor .cursor/hooks.json (beforeShellExecution, failClosed)
Each runs scripts/agent-hooks/agent_hook.sh <agent> from a copy in
<data>/script-helpers/agent-repo-hooks. Codex and Gemini ask you to
trust a project hook before it runs (Codex: /hooks). With
--uninstall: remove our entry, and the file when this script made it.
--agent <list> The same for this user: ~/.codex/hooks.json, ~/.gemini/settings.json,
${COPILOT_HOME:-~/.copilot}/hooks/script-helpers.json, ~/.cursor/hooks.json;
copy in <data>/script-helpers/agent-hooks.
--all --global and --claude.
--managed Install the Claude hook into the OS managed settings file instead,
which no user or project setting can turn off. Needs admin rights;
refuses (exit 2) without write access, and never runs sudo itself.
Linux /etc/claude-code/managed-settings.json
macOS /Library/Application Support/ClaudeCode/managed-settings.json
Windows C:\Program Files\ClaudeCode\managed-settings.json
The hook copy goes to script-helpers/ next to that file.
--uninstall With --global, --claude, --all or --managed: undo that install.
Other settings and hooks are left as they are.
--repair In this repository: if another tool replaced core.hooksPath after
the setup here (husky does on npm install), chain again: the new
value becomes the previous one, so its hooks and ours both run. The
value it replaced is kept as script-helpers.previous-hooksPath-history.
Also chains a direct setup that runs the repository's own hooks
without the current security checks (unscanned=yes below).
Also sets up again a setup whose hooks directory is gone (broken=yes).
Nothing to repair: exit 0. Not set up here: exit 1.
--status Show what is installed, and the prerequisites.
--porcelain With --status, in a repository: one line of tab-separated key=value
fields for scripts: mode (chained, direct, none), taken_over (yes,
no), core_hooksPath, expected, previous, target, claude_repo (yes,
no), global (yes when the --global hooks run here), agents (the
other agents with our hook here, comma-separated), unscanned (yes when
a direct setup points at hooks other than this copy's own, such as
an older vendored script-helpers or .githooks, so the current
security checks do not run; --repair chains them), broken (yes when
core.hooksPath names a missing directory, or our setup's has no
pre-push, so no push checks run; --repair sets it up again).
With --claude, anywhere: claude_user (ok; absent; unknown: no
Python to read the settings; broken: the script it runs is
missing; stale: it runs a file outside the installed copy, such
as a checkout), hook, installed (version).
--doctor The prerequisites, then four probes through the installed Claude
hook, from a throwaway repository under the first owner: a private
name from your list, an SSN-shaped number, and git push --no-verify
(each must be refused for that reason, with the deny decision on
stdout), and ls (must pass), with the time each took. A refusal for
another reason (no name list, no gitleaks) is a failure. Exit 1 on
any failure.
--install-gitleaks Download the pinned gitleaks (SHA-256 checked) into the cache.
--install-foxguard Download the pinned foxguard (SHA-256 checked) into the cache.
Every install offers both (asked on a terminal, printed otherwise); a push
with one missing installs it (not in CI, nor with
SCRIPT_HELPERS_NO_AUTO_INSTALL=1), and says what did not run when it cannot.
<data> is %LOCALAPPDATA% on Windows (Git Bash), else ${XDG_DATA_HOME:-~/.local/share}.
Installing refuses (exit 2) until owners are configured:
git config --global script-helpers.owners <user-or-org>[,<another>]
Only those owners' github.com repositories are checked; every other repository
runs only its own hooks. See docs/security-gates.md.
Hook directory priority (no option):
1. .githooks/ (repo-local overrides with both pre-commit and pre-push)
2. git-hooks/ next to this script: the hooks bundled with this copy of
script-helpers, wherever it is vendored (scripts/script-helpers,
vendor/script-helpers, ...) or script-helpers' own scripts/git-hooks
3. scripts/script-helpers/scripts/git-hooks/, then scripts/git-hooks/
Only this copy's own bundled hooks are used directly. Any other hooks
directory (.githooks, an older vendored script-helpers) runs chained after
the current security checks, so a clone is never only as safe as its copy.
The shared pre-push hands over to .githooks/pre-push when a repository has
one (for example the protected-refs guard), so a repository with only
.githooks/pre-push gets the shared hooks and still runs its own.
The pre-push hook blocks on security, hygiene and lint of the changed files
(scripts/push_checks.sh); it runs no tests (make test or ./dev test)
unless the repository opts in: git config script-helpers.push-tests quick. git
holds the push's connection open meanwhile. Over SSH a long run (a first or
very large push) is cut off (exit 141) unless a
keepalive is set: ~/.ssh/config "Host github.com" + "ServerAliveInterval 20"
(every repository), git config core.sshCommand "ssh -o ServerAliveInterval=20"
(one repository), or GIT_SSH_COMMAND="ssh -o ServerAliveInterval=20" git push
(one push). HTTPS remotes are not affected. The hook and --status say so.
Exit codes:
0 done
1 failed (or a --doctor probe failed)
2 refused: no owners, no Python 3, no write access, bad arguments
3 a settings/hooks JSON file is not valid JSON; with --install-gitleaks or
--install-foxguard: no release binary for this platform, or curl/wget or
a SHA-256 tool is missing
Example:
scripts/ssh_keepalive.sh#
Sourced by the pre-push hook and the hook dispatcher: warn, before the checks, when an SSH push can be cut off while they run.
Usage: source scripts/ssh_keepalive.sh; ssh_keepalive_note <remote-url> <prefix>
<remote-url> The URL git passes to pre-push as its second argument.
<prefix> The tag for the lines, e.g. "[pre-push]".
Exit codes:
Sourced: no output, no exit. ssh_keepalive_note always returns 0 and
prints its note on stderr; ssh_host_of prints the host or nothing, return 0.
Executed: 0 for -h/--help; 2 for anything else (it must be sourced).
Example:
scripts/git-hooks/commit-msg#
commit-msg hook: refuse a message that names a private repository or carries sensitive data.
Exit codes: not in the header; the script exits with 0, 1.
scripts/git-hooks/pre-commit#
pre-commit hook: refuse staged credentials, personal identifiers, PII and .env
Exit codes: not in the header; the script exits with 1.
scripts/git-hooks/pre-push#
pre-push hook: block a push on what must not leave the machine, fast.
Exit codes: not in the header; the script exits with 0, 1.
scripts/global-hooks/dispatch.sh#
Git hook dispatcher: security checks for the configured owners' repositories, then the hooks that ran before.
Usage: dispatch.sh [--local] <hook-name> [git's hook arguments...] (called by the wrappers setup-hooks.sh writes)
<hook-name> The git hook being run: pre-push, pre-commit, commit-msg, ...
SCRIPT_HELPERS_OWNERS Comma-separated GitHub users/orgs whose repositories are
checked. Default: git config --global script-helpers.owners.
Exit codes: not in the header; the script exits with 0, 1.
scripts/agent-hooks/agent_hook.sh#
Run the script-helpers security check for an AI coding agent other than Claude Code: translate its pre-command hook payload to the Claude PreToolUse shape, run pretooluse_private_names.sh, and answer in that agent's terms.
Usage: agent_hook.sh <codex|gemini|copilot|cursor> (the agent writes its JSON payload on stdin)
codex PreToolUse; payload as Claude's (tool_name, tool_input.command, cwd).
gemini BeforeTool; tool run_shell_command, tool_input.command, cwd.
copilot preToolUse; toolName bash/powershell, toolArgs (object or JSON text), cwd.
cursor beforeShellExecution; command and cwd at the top level.
Exit codes:
0 allowed
2 refused: the reason is on stderr, and on stdout in the agent's JSON form.
Every other outcome of the check (a crash, a missing script) is also 2,
because Cursor lets a command run on any other non-zero exit.
scripts/agent-hooks/merge_agent_hooks.py#
Add, remove or report the script-helpers pre-command hook in another AI agent's hook file.
Usage:
merge_agent_hooks.py install <agent> <file> <command>
merge_agent_hooks.py uninstall <agent> <file>
merge_agent_hooks.py status <agent> <file>
Exit codes:
0 done (install/uninstall), or the entry is present (status)
1 status: no entry
3 the file is not valid JSON, or a hooks value has the wrong type; nothing written
4 usage error, or the file could not be written
scripts/claude-hooks/merge_settings.py#
Add, remove or report the script-helpers PreToolUse hook in a Claude Code settings.json.
Usage:
merge_settings.py install <settings.json> <command> [timeout] [block]
merge_settings.py uninstall <settings.json>
merge_settings.py status <settings.json>
Exit codes:
0 done (install/uninstall), or the entry is present (status)
1 status: no entry
3 the file is not valid JSON or not an object; nothing was written
4 usage error, or the file could not be written
scripts/claude-hooks/pretooluse_private_names.sh#
Claude Code PreToolUse hook - stop an agent publishing text that names a private repository.
Usage: wired into a Claude Code settings.json PreToolUse hook for the Bash tool; reads the hook payload on stdin.
Exit codes:
Security and release checks#
scripts/bump_version.sh#
Bump the semantic version in a VERSION file using script-helpers.
Usage: ./bump_version.sh [major|minor|patch] [-f VERSION_FILE]
major|minor|patch Which part of the version to increment.
-f, --file PATH Version file path; a relative path is taken from the
current directory. Default: VERSION at the project root,
the top of the git work tree holding the cwd (the
superproject when run inside a vendored script-helpers
submodule), or the cwd outside git. Never the vendored
copy's own VERSION. A missing file is created,
starting from 0.1.0.
-h, --help Show this help.
Exit codes:
0 Version bumped and written (also -h/--help).
1 No arguments, unknown or repeated argument, a version that is not X.Y.Z,
or the version file or its directory could not be written.
2 -f/--file without its value.
Example:
scripts/check_changelog_section.sh#
On a release/X.Y.Z branch, require that CHANGELOG.md has a section with entries for that version.
Usage: scripts/check_changelog_section.sh [--branch <name>] [--version <X.Y.Z>] [--changelog <path>] [--repo <dir>]
--branch <name> Branch to read the version from (default: current branch).
--version <X.Y.Z> Check this version instead of deriving one from a branch.
--changelog <path> CHANGELOG to read, relative to --repo (default: CHANGELOG.md).
--repo <dir> Repository to read (default: current directory).
-h, --help Show this help message.
Exit codes:
0 Not a release branch (nothing to check), or the section exists and has
entries (also -h/--help).
1 CHANGELOG missing, no section for the version, or the section is empty.
2 Unknown argument, an option without a value, a malformed --version, or
--repo not a directory.
Example:
scripts/check_no_private_ips.sh#
Fail when an RFC 1918 private-range IP literal appears in a tracked file.
Usage: scripts/check_no_private_ips.sh [--repo <path>] [--path <dir>] [-h]
--repo <path> Repository to scan (default: cwd). Scans tracked files.
--path <dir> Scan a directory instead of tracked files. For exercising
the check against a known-bad fixture.
-h, --help Show this help message.
Exit codes:
0 No private-range address found (also -h/--help).
1 A private-range address was found in a tracked file.
2 Could not check: unknown argument, an option without a value, no such
directory, not a git repository, or grep failed.
Example:
scripts/check_private_names.sh#
Fail when the name of a private repository appears in text that is about to become public.
Usage: scripts/check_private_names.sh [--tree|--commits <range>|--file <path>|--stdin] [--list <path>] [--names <a,b>] [--repo <path>] [--only-public] [--for-repo <name>] [--strict-ambiguous] [-h]
--tree Scan tracked files (the default when nothing else is given).
--commits <range> Scan commit messages in a range, e.g. origin/main..HEAD.
--file <path> Scan a file: a pull request or issue body written out before posting.
--stdin Scan standard input.
--list <path> Name list to use. Overrides PRIVATE_NAMES_FILE and the cache.
--names <a,b> Comma-separated names, instead of a list file. For tests.
--repo <path> Repository to scan (default: cwd).
--only-public Do nothing unless this repository is public, per the list.
--for-repo <name> Judge visibility by this repository name, not the git remote.
--strict-ambiguous Fail on a bare everyday-word name, or an allowed word written in
the shape of a repository reference, rather than warning, and
check the tree for a bare one as well. For text about to be
posted publicly, where nobody will re-read it afterwards.
-h, --help Show this help message.
Exit codes:
0 No private name found (also -h/--help, and --only-public in a repository
that is not public).
1 A private name was found.
2 Could not check: unknown argument, an option without a value, no list or an
empty list, not a repository, unreadable file or commit range, or a failed
search.
Example:
scripts/check_private_names.sh --commits origin/main..HEAD
git log -1 --format=%B | scripts/check_private_names.sh --stdin
scripts/check_release_tag.sh#
Guard against tagging an existing release from a release/[v]X.Y.Z[-rcN] or release/[v]X.Y.Z[-rc.N] branch.
Usage: scripts/check_release_tag.sh --branch <branch> [--repo <path>] [--remote <name>] [--fetch-tags] [--print-version]
--branch <branch> Release branch name (defaults to GITHUB_REF_NAME/GITHUB_HEAD_REF).
--repo <path> Repository path (default: GITHUB_WORKSPACE or cwd).
--remote <name> Git remote used with --fetch-tags (default: origin).
--fetch-tags Fetch tags from --remote before checking.
--print-version Print the parsed version if eligible.
-h, --help Show this help message.
Exit codes:
0 Tag is free, or the branch is not a release branch (also -h/--help).
1 Tag already exists, or fetching tags failed.
2 Unknown argument, an option without its value, no branch given or in the environment, or --repo is not
a git work tree.
Example:
scripts/check_release_version.sh#
Validate VERSION against release/* branch and tag state.
Usage: scripts/check_release_version.sh [--branch <name>] [--version-file <path>] [--repo <dir>] [--fetch-tags]
--branch <name> Override branch name (default: current git branch).
--version-file <path> Path to VERSION file, relative to --repo (default: VERSION).
--repo <dir> Repository to check (default: current directory).
--fetch-tags Fetch tags from origin before checks.
-h, --help Show this help message.
Exit codes:
0 Not on a release branch, or VERSION matches the branch and the tag is free
(also -h/--help).
1 Release branch name invalid, VERSION missing, empty or mismatched, or the
tag already exists.
2 Unknown argument, an option without a value, or --repo not a directory.
Example:
scripts/check_sensitive.sh#
Fail when text about to be published carries a credential, a personal identifier or PII.
Usage: scripts/check_sensitive.sh [--push-refs <file>|--range <revs>|--staged|--file <path>|--stdin] [--repo <path>] [--no-gitleaks] [--no-machine] [--no-inline-allow] [--terms <path>] [--refresh-public-ip] [--list-identifiers] [-h]
--push-refs <file> The ref lines git gives a pre-push hook on stdin, saved to a file.
Checks every commit in the push that the target lacks (--remote).
--range <revs> Commits to check, as git log arguments, e.g. origin/main..HEAD.
--staged The staged changes (pre-commit).
--file <path> A file, all of it: a commit message, a PR or issue body. Repeatable.
--stdin Standard input, all of it.
--remote <name> With --push-refs: the remote pushed to (git's first pre-push
argument). A new branch is checked against that remote's refs
only; without it, or for a bare URL, its whole history.
--repo <path> Repository (default: cwd).
--no-gitleaks Skip the credential scan. It then reports nothing about credentials.
--no-machine Skip this machine's live identifiers (addresses, hostname, home path).
--terms <path> Personal terms file. Default: SENSITIVE_TERMS_FILE, else
${XDG_CONFIG_HOME:-~/.config}/script-helpers/sensitive-terms.txt
--refresh-public-ip Fetch this machine's public IPv4/IPv6 once and cache it, then exit.
The only network call this script makes; never run implicitly.
--list-identifiers Print the kinds of identifier checked and how many, never the values.
--no-inline-allow Ignore `sensitive:allow` in the text. For text whose author is
the one being checked (the agent hook): it could add the marker.
-h, --help Show this help message.
Exit codes:
0 Nothing found (also -h/--help, --refresh-public-ip, --list-identifiers).
1 Something found (block).
2 Could not check: bad or missing arguments, not a git repository, git could
not read the commits or index, gitleaks missing or failed, or the public IP
could not be fetched.
Example:
scripts/ci_gitleaks_report.sh#
Normalize and evaluate a Gitleaks SARIF report for reusable workflows and similar CI runners.
Usage: Invoke this helper with [--output <path>] [--requested-output <path>] [--report-format sarif] [--scan-path <path>] [--config-path <path>] [--fail-on-findings true|false]
--output <path> Report path produced by Gitleaks. Optional; defaults to results.sarif.
--requested-output <path> Target report path to copy/normalize to. Optional; defaults to results.sarif.
--report-format <fmt> Report format. Optional; defaults to sarif (only sarif is supported).
--scan-path <path> Requested scan path; logged when unsupported. Optional; defaults to ".".
--config-path <path> Requested config path; logged when unsupported.
--fail-on-findings <bool> Exit non-zero when findings exist. Optional; defaults to false.
-h, --help Show this help message.
Exit codes:
0 Report read; no findings, or findings with --fail-on-findings false (also
-h/--help).
1 Report missing, empty, not JSON, not the SARIF shape expected, an
unsupported --report-format.
2 Unknown argument, an option without its value, a bad --fail-on-findings, an empty --output or
--requested-output, or python3 not found.
3 Findings present and --fail-on-findings true.
Example:
scripts/ci_security.sh#
Run basic security checks (pip-audit/safety/bandit, npm audit, gitleaks, foxguard).
Usage: scripts/ci_security.sh [--workdir <path>] [--install] [--skip-python] [--skip-node] [--skip-gitleaks] [--skip-foxguard] [--check-foxguard] [--install-foxguard] [--fail-on-findings]
--workdir <path> Working directory (default: current dir).
--install Install required tools into current environment.
--skip-python Skip Python dependency checks.
--skip-node Skip Node.js audit.
--skip-gitleaks Skip gitleaks scan.
--skip-foxguard Skip the foxguard static-analysis scan.
--check-foxguard Exit 0 if foxguard is available, 3 if not (for preflight).
--install-foxguard Download the pinned foxguard release binary, check its
SHA-256 against lib/ci_defaults.sh, cache it, and exit.
--install-gitleaks Download the pinned gitleaks release, check its SHA-256
against lib/ci_defaults.sh, cache it, and exit.
--python-req <f> Python requirements file (default: requirements.txt if present).
--node-cmd <c> Override node audit command (default: npm audit --audit-level=high).
--python-version <v> Python Docker image tag (default: from ci_defaults module).
--node-version <v> Node Docker image tag (default: from ci_defaults module).
--gitleaks-version <v> Gitleaks Docker image tag (default: from ci_defaults module).
--gitleaks-digest <d> Pin gitleaks image to a specific digest for supply-chain security.
--python-image <i> Docker image override for python checks.
--node-image <i> Docker image override for node checks.
--gitleaks-image <i> Docker image override for gitleaks.
--no-docker Run on the host instead of Docker.
--gitleaks-log-opts <o> In git mode, scan only the commits these git log
options select, e.g. "HEAD --branches --tags --not --remotes"
(what a push can send). Default: the whole history.
--fail-on-findings Exit 1 when any check reports a finding (default: report
only). gitleaks then scans what git tracks, history
included, instead of every file on disk: ignored files
(.env, .venv, node_modules) are not the repository's
leaks, and failing on them fails every developer machine.
--workdir still limits the audits; git mode reads the
whole repository's history, from any subdirectory.
foxguard findings count only where the repository has
a foxguard config (.foxguard.yml, .foxguard.yaml,
foxguard.yml, foxguard.yaml); elsewhere they are
reported, not counted.
-h, --help Show this help message.
Exit codes:
0 Clean, or findings reported only (also -h/--help, --check-foxguard when
foxguard is available, and a successful --install-*).
1 Findings with --fail-on-findings; CI=true (local use only); unknown
argument; bad --gitleaks-digest; docker
missing in Docker mode; a temp directory could not be made; or an
--install-* download or checksum failed.
2 An option without its value.
3 --check-foxguard: foxguard not available. --install-*: no release binary
for this platform, or no curl/wget/sha256 tool. Under preflight
(PREFLIGHT_SKIP_FILE set): nothing it was asked for could run, reason in
that file.
Example:
scripts/pin_production.sh#
Point the production branch at a specific tag, forward or (explicitly) backward.
Usage: scripts/pin_production.sh <tag> [--allow-rewind] [--remote <name>] [--branch <name>] [--repo <path>] [--dry-run]
<tag> Release tag to point production at (e.g. 0.10.0).
--allow-rewind Permit moving production to a tag that is NOT ahead of it -
i.e. a rollback, or a move onto a diverged history. Without
this flag such a move is refused rather than attempted.
--remote <name> Remote to read and push (default: origin).
--branch <name> Branch to move (default: production). main, master and HEAD are refused.
--repo <path> Repository to act on (default: the git repository containing the
current directory, NOT the one this script lives in).
--dry-run Report what would change and push nothing.
-h, --help Show this help message.
Exit codes:
0 Moved, already there, or a dry run.
1 An error: no <tag> given, a refused branch name (main, master, HEAD),
git missing, not a repository, fetch/push failed, tag not found, or the
branch did not read back at the tag after the push.
2 An unknown option, an option without its value, or a second <tag>.
3 Refused: the tag is not ahead of the branch and --allow-rewind was not given.
Example:
scripts/pin_production.sh 0.10.0 # roll forward
scripts/pin_production.sh 0.9.0 --allow-rewind # roll back, deliberately
scripts/preflight.sh#
Run every check CI would have run, locally, before pushing.
Usage: bash scripts/preflight.sh [--quick] [--stack <name>] [--docker] [--skip-security|--security-only] [--list]
--quick Skip build/assemble steps. Tests and lint still run. The
iOS stack is skipped entirely: its analyze and test belong
to the flutter stack for the same directory, so the build
is all it does.
This is what the pre-push hook uses.
--stack <name> Check one stack only, instead of every stack detected.
Repeatable. One of: flutter gradle node python go rust php.
--docker Reproduce CI in containers via the ci_*.sh runners, using
the images pinned in lib/ci_defaults.sh. Slower and closer
to CI. The default runs on the host, which is what makes
this fast enough to sit in a git hook.
--skip-security Skip the secret / dependency scan.
--security-only Run only the secret / dependency scan (`./dev scan`). No
stack is needed.
--list Print the stacks detected and exit without running them.
--dir <path> Project directory (default: the repository root).
-h, --help Show this help message.
A repo may pin exactly what runs with a `.preflight` file at its root:
one "<stack> <dir>" per line, # comments allowed. When present it replaces
autodetection, which is how a repo excludes a helper directory that merely
happens to contain a package.json.
Exit codes:
0 Every check that ran passed.
1 At least one check failed, or run with CI=true (preflight is local only).
2 Bad arguments, or an unknown --stack.
3 No stack could be detected in this directory.
ENVIRONMENT:
PREFLIGHT_NO_INSTALL=1 Do not install a missing gitleaks or foxguard (the
pinned releases, into ~/.cache/script-helpers).
Example:
bash scripts/preflight.sh # everything, on the host
bash scripts/preflight.sh --quick # what pre-push runs
bash scripts/preflight.sh --stack flutter --docker
scripts/prune_branches.sh#
Remove branches whose work has already landed, and only those.
Usage: scripts/prune_branches.sh [--apply] [--remote] [OPTIONS]
--apply Actually delete. Without it nothing is removed: the
default is a report, because the failure mode of this
script is losing work.
--remote Also consider branches on the remote. Local only by
default -- a local branch is recoverable from the reflog
for weeks; a deleted remote branch is not.
--repo <path> Repository to operate on (default: current directory).
--base <branch> Branch that work merges into (default: the remote's HEAD,
then main/master/trunk/develop).
--remote-name <n> Remote to use (default: origin).
--keep <pattern> Never touch branches matching this glob. Repeatable.
--no-fetch Skip the fetch. Faster, and wrong if the remote moved:
a stale base makes a landed branch look unmerged.
Refused with --remote --apply, as is a failed fetch.
-h, --help Show this help message.
Exit codes:
0 Report printed, or every deletable branch was deleted.
1 Setup failed (not a repository, no base, failed fetch), a refused
--remote --apply --no-fetch, or at least one deletion failed.
2 Unknown argument, or an option without its value.
Example:
scripts/refresh_private_names.sh#
Build the private-repository name list that check_private_names.sh reads, from your own GitHub account or organization.
Usage: scripts/refresh_private_names.sh [--owner <user-or-org>] [--out <path>] [--stdout] [--check] [--force] [--codes <path>] [--limit <n>] [--ttl <days>] [-h]
--owner <name> Limit to this user or organization; repeatable. Default: every
account this token can see (you, plus your organizations).
--out <path> Where to write. Default: ${XDG_CONFIG_HOME:-~/.config}/script-helpers/private-names.tsv
--stdout Print the list instead of writing it.
--check Compare the written list with GitHub; the list is not written.
Always refetches, so it is the slow path and it does
refresh the per-owner caches.
--force Write even if it would drop names or codes.
--limit <n> Repositories to ask gh for per owner (default 8000).
--ttl <days> Refetch an owner whose cache is older than this. Default: 1 day
for your own account, 14 for an organization.
--cache-dir <d> Where the per-owner caches live.
--no-graphql Use `gh repo list` instead of the GraphQL query.
--codes <path> Two columns, `name<TAB>code`, used to fill the code column that
`gh repo list` cannot supply. Default:
~/.config/script-helpers/private-names-codes.tsv
-h, --help Show this help message.
ENVIRONMENT:
PRIVATE_NAMES_NEVER_AMBIGUOUS Names to match bare even though they are dictionary
words. Comma or newline separated.
PRIVATE_NAMES_NEVER_AMBIGUOUS_FILE A file of the same, one per line, comments allowed.
Default: ~/.config/script-helpers/private-names-unambiguous
Exit codes:
0 written (or, with --check, unchanged; or printed with --stdout)
1 --check found the list out of date or missing, or the write would drop
names or codes (rerun with --force)
2 bad arguments, the account has no private repositories, or no temporary
file could be created beside --out
3 gh is missing, not authenticated, or a listing failed part-way
Example:
scripts/release_notes.sh#
Produce the body of a GitHub Release, from CHANGELOG.md if it has a section for the version, otherwise from the commit range.
Usage: scripts/release_notes.sh --version <X.Y.Z> [--tag <tag>] [--changelog <path>] [--repo <dir>] [--output <file>]
--version <X.Y.Z> Version being released. Required.
--tag <tag> Tag for that version (default: X.Y.Z or vX.Y.Z, whichever
exists; X.Y.Z when neither does yet).
--changelog <path> CHANGELOG to read, relative to --repo (default: CHANGELOG.md).
--repo <dir> Repository to read (default: current directory).
--output <file> Write here instead of stdout, relative to the current
directory. Prefer this: notes reach actions/gh through
body_path, never through an interpolated string.
-h, --help Show this help message.
Exit codes:
0 notes written (stdout or --output)
1 no CHANGELOG section and the commits cannot be listed: not a git
repository, a shallow clone, or git log failed
2 bad arguments: missing or malformed --version, unknown option, or
--repo is not a directory
Example:
scripts/tag_release.sh#
Tag the current commit with the version from the VERSION file and push the tag to origin.
Usage: ./tag_release.sh
No parameters.
-h, --help prints this header and exits without tagging. Runs in the
script-helpers checkout that holds this script, from any directory.
Exit codes:
0 tag created and pushed (or help shown)
1 VERSION is missing or empty, the tag already exists, or git tag or
git push failed
2 bad arguments
Example:
ps/scripts/bump_version.ps1#
Bump the semantic version in the VERSION file.
Usage: pwsh -File ps\scripts\bump_version.ps1 <major|minor|patch> [-File <path>]
BumpType Required. One of: major, minor, patch.
-File <path> Path to the VERSION file; relative to the current directory.
Default: VERSION at the project root (top of the git work
tree holding the cwd, the superproject when run inside a
vendored script-helpers submodule, else the cwd).
-Help Show this help message.
Exit codes:
Example:
ps/scripts/preflight.ps1#
Run locally every project check CI would run before pushing.
Usage: pwsh -File ps\scripts\preflight.ps1 [-Quick] [-Stack <name>] [-Docker] [-SkipSecurity | -SecurityOnly] [-List] [-Dir <path>]
-Quick Skip dependency audits.
-Stack <name> Check one or more declared stacks.
-Docker Run supported checks in Docker.
-SkipSecurity Skip security checks.
-SecurityOnly Run only security checks.
-List Print detected stacks without running them.
-Dir <path> Project directory (default: repository root or current directory).
-Help Show this help message.
Exit codes:
0 Every check that ran passed.
1 At least one check failed.
2 Invalid arguments or unknown stack.
3 No stack was detected.
Example:
ps/scripts/tag_release.ps1#
Create an annotated git tag from the current VERSION file and push it.
Usage: pwsh -File ps\scripts\tag_release.ps1 [-File <path>] [-Remote <remote>] [-DryRun]
-File <path> Path to VERSION file (default: VERSION in project root).
-Remote <name> Git remote to push to (default: origin).
-DryRun Print the tag that would be created without actually tagging.
-Help Show this help message.
Exit codes:
Example:
CI runners#
scripts/ci_django.sh#
Run a Django project's tests, in CI or on a laptop, with the database in Docker.
Usage: scripts/ci_django.sh [options]
--workdir <path> Directory holding manage.py (default: .).
--db-engine <name> sqlite | mysql | pgsql (default: sqlite).
--db-image <image> Start this database in Docker and use it. The image
name decides the engine unless --db-engine says otherwise.
--db-host <host> Host for a database this script did not start (default: 127.0.0.1).
--db-port <port> Host port for a started database (default: 3306 / 5432).
--db-name <name> Database name (default: django).
--db-user <user> Database user (default: django).
--db-password <password> Database password (default: django).
--db-root-password <password> Root password for a started MySQL image (default: root).
Empty starts it with MYSQL_ALLOW_EMPTY_PASSWORD. Ignored
by postgres, which has no separate root account.
--db-wait-seconds <n> How long to wait for it to answer (default: 60).
--settings <module> DJANGO_SETTINGS_MODULE for every step.
--install-command <command> Dependency install. Empty skips it
(default: pip install -r requirements.txt, then
requirements-dev.txt, each when it exists).
--migrate-command <command> Schema. Empty skips it (default: python manage.py migrate --noinput).
--check-command <command> Schema drift, as its own step between the schema and the
tests. Empty skips it (default:
python manage.py makemigrations --check --dry-run --noinput).
--test-command <command> The tests (default: python manage.py test --noinput).
--python-image <image> Run every step in this image instead of on the host.
--docker-user <user> User for that image, as uid:gid (default: the invoking user).
-h, --help Show this help message.
Exit codes:
0 The tests passed (also -h/--help).
1 A step failed, or the database did not start.
2 Bad arguments, or a combination that cannot work.
N A failing step exits with its own code (e.g. the test command's).
Example:
scripts/ci_flutter.sh#
Run Flutter CI steps (analyze, test, build) with configurable commands.
Usage: scripts/ci_flutter.sh [--workdir <path>] [--skip-analyze] [--skip-test] [--skip-build]
--workdir <path> Working directory for flutter commands (default: current dir).
--skip-analyze Skip flutter analyze.
--skip-test Skip flutter test.
--skip-build Skip flutter build step.
--build-cmd <c> Override build command (default: flutter build appbundle --release).
--version <tag> Docker image tag (default: from ci_defaults module).
--image <img> Docker image override (default: ghcr.io/cirruslabs/flutter:<version>).
--digest <sha256> Pin image to specific digest for supply-chain security (e.g., sha256:d18e04...).
--no-docker Run on the host instead of Docker.
-h, --help Show this help message.
Exit codes:
0 All requested steps passed (also -h/--help).
1 CI=true (local use only), unknown argument, bad --digest, or docker
(Docker mode) or flutter (--no-docker) not found.
2 An option without its value.
N The first failing step exits with its own code.
Example:
scripts/ci_go.sh#
Run Go CI steps (lint, test, build) with configurable commands.
Usage: scripts/ci_go.sh [--workdir <path>] [--skip-lint] [--skip-test] [--skip-build]
--workdir <path> Working directory for go commands (default: current dir).
--skip-lint Skip lint step (default runs gofmt + go vet).
--skip-test Skip test step.
--skip-build Skip build step.
--lint-cmd <c> Override lint command (default: go mod tidy && test -z "$(gofmt -l .)" && go vet ./...).
--test-cmd <c> Override test command (default: go test -v ./...).
--build-cmd <c> Override build command (default: go build -v ./...).
--version <tag> Docker image tag (default: from ci_defaults module).
--image <img> Docker image override (default: golang:<version>).
--no-docker Run on the host instead of Docker.
-h, --help Show this help message.
Exit codes:
0 All requested steps passed (also -h/--help).
1 CI=true (local use only), unknown argument, or docker (Docker mode)
or go (--no-docker) not found.
2 An option without its value.
N The first failing step (lint, test, build) exits with its own code.
Example:
scripts/ci_gradle.sh#
Run Gradle CI steps (build, test, lint, detekt) with configurable tasks.
Usage: scripts/ci_gradle.sh [--workdir <path>] [--skip-build] [--skip-test] [--skip-lint] [--skip-detekt]
--workdir <path> Working directory (default: current dir).
--skip-build Skip assembleDebug task.
--skip-test Skip test task.
--skip-lint Skip lint task.
--skip-detekt Skip detekt task.
--build-task <t> Override build task (default: assembleDebug).
--test-task <t> Override test task (default: test).
--lint-task <t> Override lint task (default: lint).
--detekt-task <t> Override detekt task (default: detekt).
--no-daemon Run Gradle with --no-daemon (default: true).
--version <tag> Docker image tag (default: from ci_defaults module).
--image <img> Docker image override (default: gradle:<version>).
--no-docker Run on the host instead of Docker.
-h, --help Show this help message.
Exit codes:
0 All requested steps passed (also -h/--help).
1 CI=true (local use only), unknown argument, docker not found (Docker
mode), or gradlew not found (--no-docker).
2 An option without its value.
N The first failing step exits with its own code.
Example:
scripts/ci_ios.sh#
Run iOS Flutter CI steps (analyze, test, release build) on a macOS host.
Usage: scripts/ci_ios.sh [--workdir <path>] [--skip-analyze] [--skip-test] [--skip-build] [--export-plist <path>]
--workdir <path> Working directory for flutter commands (default: current dir).
--skip-analyze Skip flutter analyze.
--skip-test Skip flutter test.
--skip-build Skip the iOS build step.
--export-plist <path> ExportOptions.plist, relative to --workdir, for a signed IPA
(default: build an unsigned iOS app).
-h, --help Show this help message.
Exit codes:
0 All requested steps passed (also -h/--help).
1 Unknown argument, flutter not on PATH, or --workdir not found.
2 An option without its value.
3 Not macOS with Xcode: the Apple toolchain cannot run here.
N A failing flutter or xcodebuild step exits with its own code.
Example:
scripts/ci_ios.sh --skip-build
NOTE: Apple's toolchain runs only on macOS, so unlike the Docker-based ci_*.sh
helpers this runs on the host with no image. It exits early elsewhere.
scripts/ci_laravel.sh#
Run a Laravel application's tests, in CI or on a laptop, with the database in Docker.
Usage: scripts/ci_laravel.sh [options]
--workdir <path> Application directory (default: .).
--db-connection <name> sqlite | mysql | pgsql (default: sqlite).
--db-image <image> Start this database in Docker and use it. Implies a
server connection; the image name decides mysql or pgsql
unless --db-connection says otherwise.
--db-host <host> Database host when one is not started (default: 127.0.0.1).
--db-port <port> Host port for a started database (default: 3306 / 5432).
--db-name <name> Database name (default: laravel).
--db-user <user> Database user (default: laravel).
--db-password <password> Database password (default: laravel).
--db-root-password <password> Root password for a started MySQL image (default: root).
Empty starts it with MYSQL_ALLOW_EMPTY_PASSWORD. Ignored
by postgres, which has no separate root account.
--db-wait-seconds <n> How long to wait for it to answer (default: 60).
--install-command <command> Dependency install. Empty skips it
(default: composer install --no-interaction --prefer-dist).
--migrate-command <command> Schema. Empty skips it (default: php artisan migrate --force).
--test-command <command> The tests (default: php artisan test).
--env-file <path> Environment file to copy to .env. Empty picks
.env.testing, then .env.example, when .env is absent.
--php-image <image> Run everything in this image instead of on the host.
The image must have bash.
--docker-user <user> User for that image, as uid:gid (default: the invoking user).
-h, --help Show this help message.
Exit codes:
0 The tests passed (also -h/--help).
1 A step failed, or the database did not start.
2 Bad arguments, or a combination that cannot work.
N A failing step exits with its own code (e.g. the test command's).
Example:
scripts/ci_node.sh#
Run Node.js CI steps (install, lint, test, build) with configurable commands.
Usage: scripts/ci_node.sh [--workdir <path>] [--no-install] [--skip-lint] [--skip-test] [--skip-build]
--workdir <path> Working directory for npm commands (default: current dir).
--no-install Skip npm install step.
--skip-lint Skip lint command.
--skip-test Skip test command.
--skip-build Skip build command.
--install-cmd <c> Override install command (default: npm ci).
--lint-cmd <c> Override lint command (default: npm run lint).
--test-cmd <c> Override test command (default: npm run test).
--build-cmd <c> Override build command (default: npm run build).
--version <tag> Docker image tag (default: from ci_defaults module).
--image <img> Docker image override (default: node:<version>).
--no-docker Run on the host instead of Docker.
-h, --help Show this help message.
Exit codes:
0 All requested steps passed (also -h/--help).
1 CI=true (local use only), unknown argument, or docker (Docker mode)
or npm (--no-docker) not found.
2 An option without its value.
N The first failing step exits with its own code.
Example:
scripts/ci_pimcore_bundle_check.sh#
Run Pimcore bundle standalone and Docker-based checks from GitHub Actions or similar CI workflows.
Usage: scripts/ci_pimcore_bundle_check.sh [options]
--compose-file <path> Path to the Docker Compose file to use (default: test/docker-compose.yml).
--bundle-src <path> Path to the Pimcore bundle source directory (default: .).
--bundle-src-env <name> Environment variable name used for the bundle source path (default: BUNDLE_SRC).
--php-service <name> Docker Compose service name for the PHP container (default: php).
--db-service <name> Docker Compose service name for the database container (default: db).
--db-user <user> Database user for the readiness check (default: empty; mysqladmin uses its default user).
--db-password <password> Database password for the readiness check (default: empty).
--db-wait-seconds <seconds> Seconds to wait for the database service to become ready (default: 20).
--out-dir <path> Directory for temporary or output artifacts (default: test/tmp).
--composer-command <command> Composer command to run for dependency installation (default: composer install --no-interaction --prefer-dist).
--php-lint-command <command> Standalone PHP lint command to run before Docker-based checks.
--phpcs-standalone-command <command> Standalone PHPCS command to run before Docker-based checks.
--phpunit-standalone-command <command> Standalone PHPUnit command to run before Docker-based checks.
--phpunit-working-directory <path> Working directory for standalone PHPUnit execution (default: .).
--phpcs-command <command> PHPCS command to run inside the PHP container (default: vendor/bin/phpcs --standard=PSR12 --extensions=php src/).
--phpstan-command <command> PHPStan command to run inside the PHP container.
--phpunit-command <command> PHPUnit command to run inside the PHP container (default: vendor/bin/phpunit --testdox).
--phpunit-coverage-command <command> PHPUnit coverage command to run inside the PHP container.
--fail-on-findings <true|false> Whether findings should fail the script (default: true).
--cleanup <true|false> Whether Docker resources should be cleaned up after execution (default: true).
-h, --help Show this help message.
Exit codes:
0 All checks passed, or findings reported only with --fail-on-findings false
(also -h/--help).
1 PHPCS, PHPStan or PHPUnit findings (with --fail-on-findings true), the
database did not become ready, or docker compose not found.
2 Unknown argument, an option without its value, a bad true/false or --db-wait-seconds value, compose
file or bundle source not found, or a bad --bundle-src-env name.
N A failing composer, lint or standalone command exits with its own code.
Example:
scripts/ci_pimcore_bundle_check.sh --compose-file test/docker-compose.yml --phpstan-command "vendor/bin/phpstan analyse"
scripts/ci_python.sh#
Run Python CI steps (install + pytest) with configurable commands.
Usage: scripts/ci_python.sh [--workdir <path>] [--no-install] [--requirements <file>] [--constraints <file>]
--workdir <path> Working directory for Python commands (default: current dir).
--no-install Skip dependency install step.
--requirements <f> Requirements file to install (default: requirements.txt, then
requirements-dev.txt, each if present).
--constraints <f> Constraints file to use (optional).
--extra-install <c> Extra packages to install (e.g. "pytest tldextract").
--test-cmd <c> Test command (default: python -m pytest -q).
--pip-cmd <c> Override pip install command (replaces default install logic).
--version <tag> Docker image tag (default: from ci_defaults module).
--image <img> Docker image override (default: python:<version>).
--no-docker Run on the host instead of Docker.
--db-image <image> Start this database in Docker and export a connection to it.
The image name decides mysql or pgsql unless --db-engine says.
--db-engine <name> mysql | pgsql. Only needed for an image the name cannot place.
--db-name <name> Database name (default: app).
--db-user <user> Database user (default: app).
--db-password <p> Database password (default: app).
--db-root-password <p> Root password for a started MySQL image (default: root).
Empty starts it with MYSQL_ALLOW_EMPTY_PASSWORD; postgres ignores it.
--db-port <port> Host port, when the steps run with --no-docker (default: 3306 / 5432).
--db-wait-seconds <n> How long to wait for it to answer (default: 60).
--env NAME=VALUE Extra environment for every step. Repeatable.
-h, --help Show this help message.
Exit codes:
0 Install and tests passed (also -h/--help).
1 CI=true (local use only), unknown argument, a bad --env, an unknown
database engine, the database did not start, or docker (Docker mode) or
python (--no-docker) not found.
2 An option without its value.
N The failing install or test command exits with its own code.
Example:
scripts/ci_wp_build.sh#
Build a WordPress plugin into a deployable tree and zip, in CI or locally.
Usage: scripts/ci_wp_build.sh [options]
--workdir <path> Plugin source directory (default: .).
--slug <name> Plugin directory name in the package (default: the workdir's name).
--version <version> Version for the zip name (default: read from the plugin header).
--out-dir <path> Where the tree and zip are written (default: build).
--composer-command <command> Production dependency install. Empty skips it
(default: composer install --no-dev --optimize-autoloader --prefer-dist).
--asset-command <command> Front-end build, run when package.json exists. Empty skips it
(default: npm ci && npm run build).
--exclude-from <path> File of rsync-style excludes (default: .distignore when present).
--php-image <image> Run the composer step in this image instead of on the host.
The image must have bash: an alpine variant fails with
exit 127 and "bash: executable file not found".
--node-image <image> Run the asset step in this image instead of on the host.
Same bash requirement.
--docker-user <user> User for those images, as uid:gid (default: the invoking user).
--zip <true|false> Also produce <out-dir>/<slug>-<version>.zip (default: true).
-h, --help Show this help message.
Exit codes:
0 Tree (and zip) built (also -h/--help).
1 Symlinks that cannot be packaged, no PHP file at the staged root, or a
packaging step failed.
2 Bad arguments: unknown option, an option without its value, missing
--workdir, a bad --slug, --version or --zip, a tool missing on PATH, an
--out-dir that is the plugin directory or contains it, no usable
Version: header, or --exclude-from not found. An --out-dir inside the
plugin is allowed and left out of the package.
N A failing composer or asset command exits with its own code.
Example:
scripts/ci_wp_phpunit.sh#
Run a WordPress plugin's PHPUnit tests against the WordPress test suite, in CI or locally.
Usage: scripts/ci_wp_phpunit.sh [options]
--wp-version <version> Full X.Y.Z, a minor like 7.1, or "latest" (default: latest).
--wp-tests-dir <path> Absolute path for the test library (default: /tmp/wordpress-tests-lib).
--wp-core-dir <path> Absolute path for WordPress core (default: /tmp/wordpress).
--multisite <0|1> Run the suite as multisite (default: 0).
--db-host <host> Database host (default: 127.0.0.1).
--db-port <port> Database port (default: 3306).
--db-name <name> Database name (default: wordpress_test).
--db-user <user> Database user (default: wordpress).
--db-password <password> Database password (default: wordpress).
--db-image <image> Start this database in Docker and stop it afterwards. Empty means
the database already exists (default: empty).
--db-wait-seconds <seconds> Max seconds to wait for a started database (default: 60).
--workdir <path> Plugin directory to run the tests from (default: .).
--php-image <image> Run the tests in this PHP image instead of on the host. Empty
uses the host's PHP (default: empty).
--docker-user <user> User for --php-image, as uid:gid or a name. Empty runs as the
image's default, usually root (default: the invoking user).
--test-command <command> Command that runs the tests (default: ./vendor/bin/phpunit).
--skip-provision <true|false> Reuse an existing test library rather than downloading (default: false).
-h, --help Show this help message.
Exit codes:
0 The tests passed (also -h/--help).
1 The database did not start, the WordPress test library could not be
provisioned, or a step failed.
2 Bad arguments: unknown option, an option without its value, a relative
or unsafe --wp-tests-dir or --wp-core-dir, missing --workdir, or a
database host a container cannot reach.
N The PHPUnit command exits with its own code.
Example:
scripts/ci_wp_plugin_check.sh#
Run WordPress plugin-check and optional standalone PHP checks from GitHub Actions or similar CI workflows.
Usage: scripts/ci_wp_plugin_check.sh [options]
--compose-file <path> Path to the caller-repo Docker Compose file (default: test/docker-compose.yml).
--plugin-slug <slug> WordPress plugin slug (required).
--plugin-src <path> Path to the plugin source directory (default: .).
--plugin-src-env <name> Env var name for the plugin source path (default: PLUGIN_SRC).
--wpcli-service <name> Compose service for WP-CLI (default: wpcli).
--db-service <name> Compose service for the database (default: db).
--wordpress-service <name> Compose service for WordPress (default: wordpress).
--db-name <name> Database name (default: wordpress).
--db-user <user> Database user (default: wordpress).
--db-password <password> Database password (default: wordpress).
--host-port <port> WordPress host port (default: 8080).
--out-dir <path> Directory for output artifacts (default: test/tmp).
--exclude-directories <list> Comma-separated directories `wp plugin check` skips.
--exclude-files <list> Comma-separated files `wp plugin check` skips.
--db-wait-seconds <seconds> Max seconds to wait for DB readiness (default: 30).
--multisite <true|false> Enable multisite install (default: true).
--activate-network <true|false> Activate plugin network-wide (default: true).
--admin-user <user> WordPress admin username (default: admin).
--admin-password <password> WordPress admin password (default: admin).
--admin-email <email> WordPress admin email (default: admin@example.com).
--site-title <title> WordPress site title (default: WP Test Site).
--meta-check-script <path> Optional container-visible WP eval-file script for meta checks.
--php-lint-command <command> Standalone PHP lint command.
--phpcs-warning-command <command> Standalone PHPCS warning command.
--phpunit-command <command> Standalone PHPUnit command.
--phpunit-working-directory <path> Working directory for standalone PHPUnit (default: .).
--fail-on-findings <true|false> Fail on plugin-check errors (default: false).
--cleanup <true|false> Clean up Docker resources on exit (default: true).
-h, --help Show this help message.
Exit codes:
0 No plugin-check errors, or findings reported only with --fail-on-findings
false (also -h/--help).
1 The database did not become ready within --db-wait-seconds.
2 Bad arguments: unknown option, an option without its value, a bad
true/false, port or wait value, missing --plugin-slug, compose file or
plugin source not found, a bad --plugin-src-env name, or python3 missing
with --fail-on-findings true.
4 Plugin-check reported errors, or wp plugin check is unavailable, with
--fail-on-findings true.
5 The plugin-check or meta-check JSON could not be parsed, or wp plugin
check exited non-zero and reported no errors, with --fail-on-findings true.
N A failing --php-lint-command or --phpunit-command, or a failing
docker compose or wp setup step, exits with its own code.
Example:
scripts/ci_wp_plugin_check.sh --plugin-slug my-plugin --compose-file test/docker-compose.yml
ENVIRONMENT:
PLUGIN_CHECK_INSTALL_ATTEMPTS Tries to install plugin-check, 1-99 (default: 3).
PLUGIN_CHECK_RETRY_DELAY Seconds before retry n is n x this, 0-999 (default: 5).
ps/scripts/ci_go.ps1#
Run Go CI steps (vet, test) on Windows.
Usage: pwsh -File ps\scripts\ci_go.ps1 [-Workdir <path>] [-Quick] [-Module <path>]
-Workdir <path> Working directory (default: current dir).
-Quick Run go vet only, skip tests.
-Module <path> Specific module path to test.
-UseDocker Run inside Docker Desktop instead of natively.
-Image <img> Docker image override (requires -UseDocker).
-Help Show this help message.
Exit codes:
Example:
ps/scripts/ci_node.ps1#
Run Node.js CI steps (install, lint, test, build) on Windows.
Usage: pwsh -File ps\scripts\ci_node.ps1 [-Workdir <path>] [-NoInstall] [-SkipLint] [-SkipTest] [-SkipBuild]
-Workdir <path> Working directory for npm commands (default: current dir).
-NoInstall Skip npm install step.
-SkipLint Skip lint command.
-SkipTest Skip test command.
-SkipBuild Skip build command.
-InstallCmd <tok...> Override install command tokens (default: npm ci).
-LintCmd <tok...> Override lint command tokens (default: npm run lint).
-TestCmd <tok...> Override test command tokens (default: npm run test).
-BuildCmd <tok...> Override build command tokens (default: npm run build).
-UseDocker Run inside Docker Desktop instead of natively.
-Image <img> Docker image override (requires -UseDocker).
-Help Show this help message.
Exit codes:
Example:
ps/scripts/ci_python.ps1#
Run Python CI steps (venv, install, test) on Windows.
Usage: pwsh -File ps\scripts\ci_python.ps1 [-Workdir <path>] [-SkipTest] [-Quick]
-Workdir <path> Working directory (default: current dir).
-SkipTest Skip pytest step.
-Quick Skip install, only run tests.
-PythonBin <bin> Python binary to use (auto-detected by default).
-TestCmd <tok...> Override test command tokens (default: pytest).
-UseDocker Run inside Docker Desktop instead of natively.
-Image <img> Docker image override (requires -UseDocker).
-Help Show this help message.
Exit codes:
Example:
ps/scripts/ci_rust.ps1#
Run Rust CI steps (check, clippy, test) on Windows.
Usage: pwsh -File ps\scripts\ci_rust.ps1 [-Workdir <path>] [-Quick] [-Manifest <path>]
-Workdir <path> Working directory (default: current dir).
-Quick Run cargo check only, skip clippy and tests.
-Manifest <path> Path to Cargo.toml (default: Cargo.toml in workdir).
-UseDocker Run inside Docker Desktop instead of natively.
-Image <img> Docker image override (requires -UseDocker).
-Help Show this help message.
Exit codes:
Example:
Local test runners#
scripts/local_test_bash32.sh#
Run the test suite under bash 3.2, the shell macOS ships.
Usage: bash scripts/local_test_bash32.sh [--test <file>] [--shell]
--test Run a single test file instead of the whole suite.
--shell Drop into an interactive bash 3.2 shell in the container.
-h, --help Show this help and exit.
Exit codes:
0 every test passed under bash 3.2
1 a test failed, or run with CI=true (this is a local gate)
2 bad arguments
3 docker is unavailable
Example:
scripts/local_test_flutter.sh#
Fetch dependencies, analyze, and test a Flutter project.
Usage: bash scripts/local_test_flutter.sh [--quick] [--dir <path>]
--quick Skip dependency install and flutter analyze; run tests only.
--dir Project directory containing pubspec.yaml (default: .).
-h, --help Show this help and exit.
Exit codes:
0 Every step passed.
1 Bad arguments, directory or pubspec.yaml missing, or flutter not on PATH.
N The failing tool's own exit code, passed through (set -e).
Example:
scripts/local_test_go.sh#
Vet and test all Go modules in the repository.
Usage: bash scripts/local_test_go.sh [--quick] [--module <path>]
--dir Project directory, relative to the repository root (default: .).
--quick Skip vet; run tests only.
--module Path to a specific module directory (default: all go.mod roots).
-h, --help Show this help and exit.
Exit codes:
0 Every check that ran passed.
1 A check failed, or bad arguments.
3 Nothing could be checked: no module has Go packages. preflight reports it as SKIP.
N The failing tool's own exit code, passed through (set -e).
Example:
scripts/local_test_gradle.sh#
Lint, unit-test and assemble a Gradle project on the host.
Usage: bash scripts/local_test_gradle.sh [--quick] [--dir <path>] [--android]
--quick Run unit tests only; skip lint and the assemble step.
--dir Project directory containing gradlew (default: .).
--android Use the Android task names (lintDebug, testDebugUnitTest,
assembleDebug) instead of the plain JVM ones (lint, test, build).
Autodetected from the presence of an Android plugin when omitted.
-h, --help Show this help and exit.
Exit codes:
0 All requested checks passed.
1 A check failed, or no Gradle wrapper was found.
N The failing tool's own exit code, passed through (set -e).
Example:
scripts/local_test_node.sh#
Install dependencies and run tests for Node/npm projects. npm test runs with CI=true unless CI is set, so react-scripts test and vitest run once instead of watching.
Usage: bash scripts/local_test_node.sh [--quick] [--workspace <name>]
--dir Project directory, relative to the repository root (default: .).
--quick Skip install; run tests against existing node_modules.
--workspace Run tests only for a specific npm workspace.
-h, --help Show this help and exit.
Exit codes:
0 Every check that ran passed.
1 A check failed, or bad arguments.
3 Nothing could be checked: package.json declares no test script. preflight reports it as SKIP.
N The failing tool's own exit code, passed through (set -e).
Example:
scripts/local_test_php.sh#
Install dependencies, check style (Pint), and run tests for PHP/Laravel projects.
Usage: bash scripts/local_test_php.sh [--quick] [--dir <path>]
--quick Skip composer install; lint and test against existing vendor/.
--dir Subdirectory containing composer.json (default: .).
-h, --help Show this help and exit.
ENVIRONMENT:
SKIP_PHP_TESTS=1 Run style checks only; skip the (DB-backed) test suite.
Useful for a pre-push without a local database.
Exit codes:
0 Every check that ran passed (style only with SKIP_PHP_TESTS=1).
1 Bad arguments, a missing directory, composer.json, php, composer or test runner.
N The failing tool's own exit code, passed through (set -e).
Example:
scripts/local_test_python.sh#
Use a local virtualenv when available, run ruff (when the project configures it) and then pytest.
Usage: bash scripts/local_test_python.sh [--quick] [--dir <path>]
--quick Skip install; run lint and tests against the current environment.
--dir Subdirectory containing pyproject.toml/requirements.txt (default: .).
requirements-dev.txt, when present, installs after requirements.txt.
-h, --help Show this help and exit.
Exit codes:
0 Every check that ran passed.
1 A check failed, or bad arguments.
3 Nothing could be checked: pytest, or a configured ruff, is not installed, or pytest
collected no tests. preflight reports it as SKIP.
N The failing tool's own exit code, passed through (set -e).
Example:
scripts/local_test_rust.sh#
Check, lint, and test a Rust project.
Usage: bash scripts/local_test_rust.sh [--dir <path>] [--quick] [--manifest <path>] [--any-cargo]
--dir Project directory, relative to the repository root (default: .).
--quick Skip cargo check/clippy; run tests only.
--manifest Path to Cargo.toml (default: ./Cargo.toml).
--any-cargo Use whatever cargo PATH offers, instead of rustup's.
-h, --help Show this help and exit.
Exit codes:
0 Every step passed.
1 Bad arguments, a missing directory, no rustup cargo (without --any-cargo), or no cargo.
N The failing tool's own exit code, passed through (set -e).
Example:
ps/scripts/local_test_gradle.ps1#
Lint, unit-test, and assemble a Gradle project on the host.
Usage: pwsh -File ps\scripts\local_test_gradle.ps1 [-Quick] [-Dir <path>] [-Android]
-Quick Run unit tests only; skip lint and assemble.
-Dir <path> Project directory with the Gradle wrapper (default: .).
-Android Use Android task names. Autodetected when omitted.
-Help Show this help message.
Exit codes:
0 Requested Gradle tasks passed.
1 A task failed, the directory is missing, or no Gradle is available.
Example:
Packaging#
scripts/build_arch_artifacts.sh#
Build Arch package via PKGBUILD and emit artifacts in the parent directory.
Usage: ./build_arch_artifacts.sh [--repo PATH] [--pkg-dir PATH] [--prebuild COMMAND] [--artifact-dir PATH]
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--pkg-dir <path> PKGBUILD directory (default: packaging/arch).
--prebuild <command> Command to run before build.
--artifact-dir <path> Output directory for packages (default: ..).
-h, --help Show help.
Exit codes:
0 Packages built and copied to --artifact-dir (also -h/--help).
1 PKGBUILD not found, or a step failed.
2 Unknown argument, or an option without its value.
N A failing --prebuild command or makepkg exits with its own code.
Example:
scripts/build_brew_tarball.sh#
Build a Homebrew tarball for a repo.
Usage: ./build_brew_tarball.sh --name <name> [--repo PATH] [--version-file FILE] [--dist-dir DIR] [--exclude PATTERN]
--name <name> Package name (required).
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--version-file <file> Version file (default: VERSION in repo).
--dist-dir <dir> Output directory (default: repo/dist).
--exclude <pattern> Exclude path (repeatable).
-h, --help Show help.
Exit codes:
0 Tarball written to <dist-dir>/<name>-<version>.tar.gz (also -h/--help).
1 rsync or tar failed.
2 Unknown argument, an option without its value, --name missing, or the version file not found.
Example:
scripts/build_deb_artifacts.sh#
Build Debian packages and emit artifacts in the parent directory.
Usage: ./build_deb_artifacts.sh [--repo PATH] [--prebuild COMMAND] [--build COMMAND]
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--prebuild <command> Command to run before build (default: "make man").
--build <command> Build command override (default: dpkg-buildpackage -us -uc).
-h, --help Show help.
Exit codes:
0 Packages built (also -h/--help).
1 --repo is not a directory.
2 Unknown argument, an option without its value, or dpkg-buildpackage not on PATH.
N A failing --prebuild, --build or dpkg-buildpackage exits with its own code.
Example:
scripts/build_rpm_artifacts.sh#
Build RPM packages and emit artifacts in the parent directory.
Usage: ./build_rpm_artifacts.sh [--repo PATH] [--spec PATH] [--prebuild COMMAND] [--artifact-dir PATH]
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--spec <path> Spec file path (default: packaging/rpm/<app>.spec).
--prebuild <command> Command to run before build. The source tarball is then the
working tree (prebuild output included), without .git,
packaging/rpm/build or an --artifact-dir inside the tree;
without it, git archive HEAD.
--artifact-dir <path> Output directory for built RPMs (default: ..).
-h, --help Show help.
Exit codes:
0 RPMs built and copied to --artifact-dir (also -h/--help).
1 Spec file not found, or a step failed.
2 Unknown argument, or an option without its value.
N A failing --prebuild command or rpmbuild exits with its own code.
Example:
scripts/gen_brew_formula.sh#
Generate a Homebrew formula for a tarball release.
Usage: ./gen_brew_formula.sh --name <name> --desc <desc> --homepage <url> --tarball <path> [options]
--name <name> Formula/package name (required).
--desc <desc> Short description (required).
--homepage <url> Project homepage (required).
--tarball <path> Tarball path (required).
--url <url> Tarball URL (default: GitHub releases URL).
--repo-slug <slug> GitHub repo slug (owner/repo) for default URL (optional).
--version <version> Version override (default: from tarball name).
--license <license> License (default: MIT).
--dep <name> Dependency (repeatable).
--entrypoint <path> Entrypoint script (default: name).
--man-path <path> Manpage path (optional).
--formula-path <path> Output formula path (default: packaging/brew/<name>.rb).
--use-libexec Install into libexec and generate wrapper (default: false).
--env-var <name> Env var for wrapper (used with --use-libexec).
-h, --help Show help.
Exit codes:
0 Formula written (or help shown).
1 The formula could not be written (mkdir or write failed).
2 Unknown argument, an option without its value, a required option
missing, tarball not found, or --use-libexec without --env-var.
Example:
./gen_brew_formula.sh --name isoforge --desc "ISO tool" --homepage https://example.com --tarball dist/isoforge-1.0.0.tar.gz --url https://example.com/isoforge-1.0.0.tar.gz
scripts/packaging_init.sh#
Scaffold packaging files (debian/, rpm spec, PKGBUILD, Homebrew formula) from templates.
Usage: ./packaging_init.sh [--repo PATH] [--config PATH] [--template-dir PATH] [--force] [--init-only]
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--config <path> Packaging env file (default: packaging/packaging.env).
--template-dir <path> Template root (default: script-helpers/templates/packaging).
--force Overwrite existing files.
--init-only Only create packaging/packaging.env if missing.
-h, --help Show help.
Exit codes:
0 Files rendered, or packaging.env created (with --init-only, or on a first run).
1 Template metadata missing, or APP_NAME/MAINTAINER_* unset in the config.
2 Unknown argument, or an option without its value.
Example:
scripts/ppa_upload.sh#
Build a Debian source package and upload to Launchpad PPA.
Usage: ./ppa_upload.sh --ppa <ppa:owner/name> --key-id <gpg_key_id> [--series SERIES] [--repo PATH] [--prebuild COMMAND] [--build COMMAND] [--dry-run]
--ppa <ppa:owner/name> Launchpad PPA target (required).
--key-id <gpg_key_id> GPG key ID or fingerprint (required).
--series <codename> Optional distro codename to set with dch.
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--prebuild <command> Command to run before build (default: "make man").
--build <command> Command to build source package (default: debuild -S -sa).
--dry-run Do not upload, just print the .changes file.
-h, --help Show help.
Exit codes:
0 Uploaded, or the dry run printed the .changes file.
1 The build failed, or no .changes file was found.
2 --ppa or --key-id missing, an unknown argument, an option without its
value, or debuild, gpg or dput missing.
N The prebuild, build or dput command's own exit code, passed through.
Example:
scripts/publish_homebrew.sh#
Publish a Homebrew formula to a tap repository.
Usage: ./publish_homebrew.sh [--repo PATH] [--formula PATH] [--name NAME] [--tap-repo OWNER/REPO] [--tap-token TOKEN] [--tap-branch BRANCH] [--tap-dir DIR] [--commit-message MESSAGE]
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--formula <path> Formula path (default: packaging/brew/<name>.rb).
--name <name> Formula name (default: inferred from formula path).
--tap-repo <owner/repo> GitHub tap repository (default: HOMEBREW_TAP_REPO).
--tap-token <token> GitHub token (default: HOMEBREW_TAP_TOKEN). Prefer
the HOMEBREW_TAP_TOKEN env var: a command-line
token is visible to other users in `ps`.
--tap-branch <branch> Tap branch (default: HOMEBREW_TAP_BRANCH or main).
--tap-dir <dir> Destination directory in tap (default: Formula).
--commit-message <msg> Commit message (default: "Update <name> formula").
-h, --help Show help.
Exit codes:
0 Published, already up to date, or skipped because no tap repo/token is set.
1 Copying the formula into the tap checkout failed.
2 Unknown argument, an option without its value, or the formula file was
not found.
N A failing git command's own exit code (clone, commit, push), passed through.
Example:
scripts/render_brew_formula.sh#
Render a Homebrew formula from packaging metadata.
Usage: ./render_brew_formula.sh [--repo PATH] [--config PATH] [--output PATH] [--url URL] [--sha256 SHA] [--version VERSION]
--repo <path> Repo path (default: GITHUB_WORKSPACE or cwd).
--config <path> Packaging env file (default: packaging/packaging.env).
--output <path> Output formula path (default: packaging/brew/<formula>.rb).
--url <url> Override Brew URL.
--sha256 <sha> Override Brew SHA256.
--version <ver> Override version.
-h, --help Show help.
Exit codes:
0 formula written
1 packaging metadata missing, APP_NAME or BREW_SHA256 not set, or the
formula could not be written
2 bad arguments (unknown option, or an option without its value)
Example:
./render_brew_formula.sh --repo . --output /tmp/myapp.rb --url "https://example.com/myapp-1.0.0.tar.gz" --sha256 "$(sha256sum myapp-1.0.0.tar.gz | cut -d' ' -f1)"
Examples#
scripts/example_dialog_input.sh#
Example script showing dialog input prompts via script-helpers.
Exit codes:
Example:
scripts/example_docker_compose_cmd.sh#
Example script that detects and prints the available docker compose command.
Exit codes:
0 A compose command was found and printed.
1 Neither `docker compose` nor `docker-compose` is available.
Example:
scripts/example_docker_status.sh#
Example script that prints Docker runtime status using script-helpers.
Exit codes:
Example:
scripts/example_download.sh#
Example script that downloads a file with script-helpers progress support.
Usage: ./example_download.sh [URL] [OUTPUT_PATH]
URL Optional download URL (default: https://speed.hetzner.de/10MB.bin).
OUTPUT_PATH Optional output file path (default: /tmp/example-download.bin).
-h, --help Show this help and exit.
Exit codes:
0 The file was downloaded.
1 The download failed or left no file at OUTPUT_PATH.
N curl's own exit code, passed through (e.g. 2 for an argument curl rejects).
Example:
scripts/example_env.sh#
Example script that demonstrates environment helpers and validation.
Exit codes:
Example:
scripts/example_json.sh#
Example script for JSON helpers including formatting and escaping.
Exit codes:
Example:
scripts/example_logging.sh#
Example script that demonstrates logging and structured log helpers.
Exit codes: 0 The demo ran (it prints error lines on purpose; they do not fail it).
Example:
scripts/example_package_publish.sh#
Demonstrate package_publish helpers in a safe, non-network way.
Exit codes:
Example:
ps/scripts/example_logging.ps1#
Demonstrates the logging module of the PowerShell script-helpers library.
Exit codes: 0 after printing the example output.
Example:
Other tools#
scripts/adb_tool.sh#
Reusable CLI over the adb module: inspect, install to, copy files to/from, and debug Android devices. Multi-device safe (each command takes a list command). Thin wrapper so the same helpers are usable from the shell, not just sourced into other scripts.
Usage: scripts/adb_tool.sh <command> [args...]
list [iface] List ready devices: serial, model, OS, API, IP
status <serial> Status block (model, OS/API, battery, screen, IP)
ip <serial> [iface] Device IP on an interface (default wlan0)
install <serial> <apk> Install (replace) an APK on one device
install-all <apk> Install an APK on every ready device
push <serial> <local> <remote> Copy a local file/dir TO the device
pull <serial> <remote> [local] Copy a file/dir FROM the device
shell <serial> <cmd...> Run a shell command on the device
logcat <serial> [regex] Dump logcat (optionally grep a regex)
clear-logcat <serial> Clear the device logcat buffer
uninstall <serial> <package> Uninstall an app package
-h, --help Show this help
Exit codes:
0 Success (also -h/--help, and install-all with no ready devices).
1 adb is not on PATH, a device query failed, or a required <serial> is missing.
2 Unknown command, or missing/invalid arguments (e.g. APK not found).
N Any other code is adb's own exit status, passed through.
Example:
scripts/adb_tool.sh list
scripts/adb_tool.sh install R58M12ABCDE app-release.apk
CREATOR: Nik Reljin
scripts/docs_site.sh#
Build, serve, preview and verify a repository's documentation site (lib/docs_site.sh).
Usage: docs_site.sh [check|build|serve|preview|verify|deps|clean] [--dir REPO] [--port N] [--venv DIR] [-h]
check Build into a temporary directory and prove it: no unclosed code fence,
an entry page and search index, nothing published by accident, and every
page, link and asset answering over HTTP. For CI and hooks.
build Build into the site directory (MkDocs: --strict).
serve Live reload while writing (MkDocs); preview for a command generator. The default.
preview Build, then serve the built site over HTTP: what a visitor gets.
verify Crawl an already built site directory over HTTP (--dir is that directory).
deps Create or refresh the documentation virtualenv and exit.
clean Remove the built site and the virtualenv.
--dir R The repository (default: the git repository of the current directory).
--port N Port for serve and preview (default DOCS_SITE_PORT or 8000). A taken port is
asked about on a terminal; without one it is an error naming the owner.
--venv D Virtualenv (default ~/.cache/nr-docs-venv/<repo>).
-h Show this help message.
Exit codes:
0 Done.
1 Build or check failed, a clean target refused, or a taken port with nobody to ask.
2 Bad arguments, no site here, or no built site to verify.
3 No usable python3 for the virtualenv.
Example:
scripts/gen_command_reference.py#
Generate docs/commands.md from the headers of the scripts in scripts/.
Usage: python3 scripts/gen_command_reference.py [--check]
--check Exit 1 when docs/commands.md differs from what this would write.
Exit codes: 0 written or up to date; 1 out of date (--check); 2 bad arguments.
scripts/generate-android-keystore.sh#
Generate an Android signing keystore without placing passwords on a command line.
Usage: bash scripts/generate-android-keystore.sh <output.jks>
<output.jks> Required new keystore path. Existing files are refused.
Exit codes:
0 Keystore generated.
1 The output directory could not be created; a keytool failure exits with keytool's code.
2 Invalid arguments, or the output path already exists.
3 keytool was not found; install a JDK.
Example:
scripts/install_dev_cli.sh#
Install the shared ./dev entry point into a consumer repository.
Usage: bash scripts/install_dev_cli.sh [--repo <path>] [--shims <list>] [--force] [--dry-run]
--repo <path> Target repository (default: current directory).
--shims <list> Comma-separated root scripts to replace with thin shims,
e.g. "build,test,update,start"; "service" stands for
start,stop,restart,status,logs. Each becomes
`exec bash scripts/cli.sh <verb> "$@"`. Existing files are
backed up to <name>.pre-dev-cli unless --force is given.
--no-hooks Do not wire the pre-push hook.
--force Overwrite scripts/cli.sh and _bootstrap.sh if they differ.
--dry-run Print what would change and exit.
-h, --help Show this help message.
Exit codes:
0 Installed, or already up to date.
1 The target is not a git repository, or a file could not be written.
2 Bad arguments.
Example:
scripts/install_ollama.sh#
Install or upgrade Ollama from a checked source (ollama_install): the pinned release checked against its SHA-256, Homebrew on macOS, winget on Windows.
Usage: scripts/install_ollama.sh [--prefix <dir>] [--force] [--check] [-h]
--prefix <dir> Install the release archive into this directory (bin/ollama, lib/ollama).
Without it: /usr/local on Linux (sudo when needed), Homebrew on macOS,
winget on Windows.
--force Install even when the pinned version or a newer one is there.
--check Say what is installed and what is pinned, and whether this would
install, upgrade or do nothing. Changes nothing.
-h, --help Show this help.
Exit codes:
0 installed, upgraded, or nothing to do (with --check: nothing to do)
1 the download failed or does not match the pinned SHA-256 (with --check: it would install or upgrade)
2 bad arguments
3 a required tool is missing, or this platform has no checkable source
Example:
scripts/install_ollama_model.sh#
Wrapper to install Ollama CLI, prepare the model index from webfarmer/ollama-get-models, select a model/size, pull it, and optionally run it.
Usage: ./install_ollama_model.sh [-h] [-i] [-D] [-r <repo_dir>] [-e <env_file>] [-R] [-n]
-i : install Ollama CLI
-D : install common dependencies (dialog, curl, jq, python3, pip3, nodejs, git, xclip)
-r <repo_dir> : models repo directory (default: ./ollama-get-models in current directory)
-e <env_file> : env file to persist model and size (default: ./.env)
-R : run the selected model in background after pulling
-n : non-interactive; use model/size from env file and only pull (no dialog)
-h : show help
-h, --help : show help
Exit codes:
0 The model was pulled (and started with -R).
1 Unknown option, -n without 'model' in the env file, or a helper failed.
2 -r or -e without its value.
N The exit code of a cancelled dialog or a failed ollama step, passed through.
Example:
scripts/lint_docs.sh#
Validate docs coverage: modules, functions, the API index, the module lists in README.md and docs/README.md, every script, bin/ command and config/ file, and the module counts.
Exit codes:
0 Every module, function, script, command and config file is documented,
and the module counts are current.
1 At least one documentation gap (each printed on stderr).
Example:
scripts/ollama_models.sh#
The models this machine gets from a models file (ai-models.env) by its class (memory, largest GPU), or one model by type of work and host tier (select), for callers that cannot source the library (an Ansible task, a Makefile).
Usage: scripts/ollama_models.sh class [models_file]
scripts/ollama_models.sh select <type> <ollama_url> [env_file] [NAME] [default_model]
scripts/ollama_models.sh pick <models_file> [NAME...]
scripts/ollama_models.sh ensure <models_file> <ollama_url> [NAME...]
class Print this machine's memory and largest GPU in GiB, its tier (small,
standard, large, xlarge; unknown with no tiers file) and, given a
models file, the model each NAME gets.
select Print the one model to use for a type of work (general, reasoning,
classify, extract, vision, embed, code, ...): the approved model for this
host's tier (config/ollama_model_tiers.txt; a lower tier's when this host
is under the model's size-line floor), or the project's own model
(NAME from the environment, else from env_file, else default_model). When
they differ, a terminal is asked once and the answer is stored in env_file
(NAME_CHOICE, NAME_CHOICE_FOR, NAME_CHOICE_PROJECT) when git ignores
it; asked again when either model changes. A NAME_CHOICE
written by hand with no NAME_CHOICE_FOR is always used. With no
terminal or in CI: the tier's model if the Ollama has it, else the
project's, else exit 12. Pulls the chosen model if missing, after the
disk and memory check (OLLAMA_PULL_MISSING: 1 pull, 0 never, ask the
default). Never removes a model. Only the model is printed on stdout.
pick Print the models this machine gets, one per line: every NAME in the file,
or only the NAMEs given. A NAME set in the environment wins.
ensure Pull the picked models into the Ollama at <ollama_url> when missing, after
the disk and memory check (OLLAMA_PULL_MISSING decides: 1 pull, 0 never,
ask on a terminal). A model above the default column that does not fit
falls back one column.
AI_MODEL_TIER=small|standard|medium|large|xlarge names the class instead of measuring.
-h, --help Show this help.
Exit codes:
0 done; with ensure, every model is there; with class, also with no
tiers file (tier=unknown and a warning on stderr)
2 bad arguments (also select with no type or no address), a NAME that
is not a variable name, a bad AI_MODEL_TIER (select exits 11 for it),
a tiers file threshold that is not a whole number (select: 10), or a
models file reserve that is not valid (AI_TIER_GPU_RESERVE_GB not a
whole number, AI_TIER_RAM_RESERVE_PERCENT not below 100)
1-9 with ensure: as ollama_project_ensure_models (docs/local-models.md)
with select: 0 chosen and present; 1-8 as ollama_endpoint_ensure_models
(4 no Ollama, 5 missing and not pulled); 10 more than five arguments,
a bad type, NAME, model, data file or env_file, or a malformed size
line for the model it reaches; 11 bad AI_MODEL_TIER; 12 no terminal
and the Ollama has neither model
Example:
scripts/ollama_models.sh class ai-models.env
model="$(scripts/ollama_models.sh select general http://127.0.0.1:11434 .env OLLAMA_MODEL qwen2.5:7b)"
OLLAMA_PULL_MISSING=1 scripts/ollama_models.sh ensure ai-models.env http://127.0.0.1:11434 OLLAMA_CODE_MODEL
scripts/ollama_tiers_check.sh#
Check a model tiers file (config/ollama_model_tiers.txt): every line well formed, no model bigger than the tier cell it sits in, and floors that agree with the thresholds.
Usage: scripts/ollama_tiers_check.sh [tiers_file]
tiers_file The file to check (default: OLLAMA_MODEL_TIERS_FILE, else the
shipped config/ollama_model_tiers.txt).
Refused, one line on stderr each:
- a line that is not a threshold, type or size line, a threshold that is
not a whole number, a type row without four model references, a
malformed size line, a second line for one threshold, type or model,
a size line for a model no type row names, a file with no type rows,
a type name that is not lower-case letters, digits, - and _;
- a cell whose model has a size class above the cell's tier (an xlarge
model in a large cell);
- a size line whose GPU floor is outside its class: large needs at least
large_vram_gb and less than xlarge_vram_gb, xlarge at least
xlarge_vram_gb, standard less than large_vram_gb;
- in one type row, a higher tier's model that needs less GPU than a lower
tier's model of the same size class. Models of different classes are
never compared.
-h, --help Show this help.
Exit codes:
Example:
scripts/prepare_public.sh#
Audit a repository's whole history and build a cleaned copy of it.
Usage: scripts/prepare_public.sh <audit|clean|verify|plan-example> [options]
audit --source <path|url> --work <dir> Mirror-clone the source into a new work
directory, archive it, and audit every ref, commit, file, message,
tag and identity. Writes <work>/report.json and <work>/report.txt.
clean --work <dir> --plan <file> Build <work>/public.git from the mirror
with git filter-repo, as the plan says. Writes <work>/changes.txt.
verify --work <dir> Re-run the full audit on public.git.
Writes <work>/report-public.json and .txt. Exit 0 only with zero hits.
plan-example Print an example plan file.
--list <path> | --names <a,b> Name list for the name check
(default: the check's own list).
--self <name> This repository's own name, which is
not a hit (default: the source's name).
-h, --help Show this help message.
Exit codes:
0 nothing found (audit, verify); done (clean)
1 something found
2 usage error, or a check could not run: the result is NOT clean
130, 143 interrupted (INT, TERM); scratch directories are removed
Example:
scripts/signing_secrets.sh#
Set the package-signing secrets and variables (the names in ci-helpers docs/packaging.md) on a list of GitHub repositories, or check which repositories miss which name. Values go to gh on stdin, never on a command line, and are never printed. The windows group sets the names of ci-helpers docs/windows-signing.md.
Usage: ./signing_secrets.sh --repos <owner/a,owner/b> --group <gpg|apple|windows> [--group ...] [options] [--check|--dry-run]
--repos <list> Comma-separated owner/name list (required).
--group <gpg|apple|windows>
Names to set or check; repeat for more (required).
--gpg-key <fingerprint> gpg: key to export (40 hex characters).
Passphrase: GPG_PASSPHRASE env, else prompted.
--ppa <owner/name=ppa:id/name>
gpg: set PPA_TARGET on that repository; repeat.
--launchpad-ssh-key <file>
gpg: set LAUNCHPAD_SSH_PRIVATE_KEY (sftp only).
--apple-p12 <file> apple: Developer ID Application .p12.
--apple-identity <text> apple: "Developer ID Application: Name (TEAMID)".
--apple-team <id> apple: 10-character team ID.
--apple-id <email> apple: Apple ID for notarization.
Passwords: APPLE_CERTIFICATE_PASSWORD and
APPLE_APP_PASSWORD env, else prompted.
The .p12 must open with its password and hold
the --apple-identity certificate, in date, and
its private key (openssl). Warns when the
certificate expires within 30 days.
--skip-p12-check apple: store the .p12 without opening it
(where openssl is not installed).
--windows-pfx <file> windows: Authenticode code-signing .pfx.
Password: --windows-password-file, else
WINDOWS_CERTIFICATE_PASSWORD env, else prompted.
The .pfx must open with its password and hold
a private key and its certificate with the
code-signing EKU (1.3.6.1.5.5.7.3.3), in date
(openssl). Warns within 30 days of expiry.
--windows-password-file <file>
windows: read the .pfx password from this file;
a trailing LF or CRLF is dropped.
--skip-pfx-check windows: store the .pfx without opening it.
--check Report missing names per repository; exit 1 if any.
--dry-run Print the names that would be set; set nothing.
-h, --help Show help.
Exit codes:
0 Every name was set, or --check found every name on every repository.
1 --check found a missing name or could not list a repository, or gh
could not set a name.
2 Bad arguments, a missing tool (gh, gpg, openssl), a key not in the
keyring, a passphrase that does not unlock it, an --apple-identity
that does not end in the --apple-team, a .p12 that does not open with
APPLE_CERTIFICATE_PASSWORD, lacks the --apple-identity certificate or
its private key, or holds it expired, a .pfx that does not open
with its password, has no private key with an in-date code-signing
certificate, or an empty value; nothing was set.
130, 143 Interrupted (INT, TERM); the scratch directory is removed.
Example:
./signing_secrets.sh --repos me/tool --group gpg --gpg-key <FPR> --ppa me/tool=ppa:me/tool
./signing_secrets.sh --repos me/a,me/b --group gpg --group apple --check
./signing_secrets.sh --repos me/app --group windows --windows-pfx sign.pfx --windows-password-file pw.txt
scripts/site_verify.py#
Verify a built static site the way a visitor reaches it: over HTTP.
Usage:
python3 scripts/site_verify.py <site dir> # crawl from /
python3 scripts/site_verify.py --fences <docs dir> # fence balance in Markdown
Exit codes: 0 clean, 1 problems found (listed), 2 usage.
ps/scripts/generate-android-keystore.ps1#
Generate an Android signing keystore at a new output path.
Usage: pwsh -File ps\scripts\generate-android-keystore.ps1 -OutputPath <path>
-OutputPath <path> Required new keystore path. Existing files are refused.
-Help Show this help message.
Exit codes:
Example:
ps/scripts/install_docker.ps1#
Install or report Docker Desktop through the native PowerShell wrapper.
Usage: pwsh -File ps\scripts\install_docker.ps1 [-Check] [-Yes] [-DryRun] [-NoStart] [-TimeoutSec <seconds>]
-Check Report Docker state without installing.
-Yes Skip confirmation prompts.
-DryRun Print the planned actions without changing the machine.
-NoStart Install Docker Desktop without launching it.
-TimeoutSec <seconds> Daemon wait time (default: 120).
-Help Show this help message.
Exit codes:
0 Docker is usable or installation completed.
1 Docker is unavailable, installation failed, the user declined, or a
parameter is unknown.
2 Unsupported platform.
3 Installation completed but the daemon did not become ready.
Example:
ps/scripts/install_ollama.ps1#
Install or upgrade Ollama from a checked native PowerShell source.
Usage: pwsh -File ps\scripts\install_ollama.ps1 [-Prefix <path>] [-Force] [-Check]
-Prefix <path> Install the checked archive into this directory.
-Force Reinstall even when the pinned version is present.
-Check Report installed and pinned versions without changing the machine.
-Help Show this help message.
Exit codes:
0 Installed, upgraded, or already current.
1 Download, checksum, or install failure; -Check says installation is
needed; or a parameter is unknown.
3 No checkable archive is available for the platform.
Example: