Skip to content

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:

  source scripts/push_checks.sh
  push_checks "$refs" origin git@github.com:owner/repo.git

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:

bash scripts/script-helpers/scripts/run_repo_tests.sh

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:

source scripts/security_hooks.sh && sec_pre_push "$refs_file" origin "$url" || exit 1

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:

pwsh -NoProfile -ExecutionPolicy Bypass -File scripts\setup-hooks.ps1 --doctor

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:

bash scripts/setup-hooks.sh --status

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:

ssh_keepalive_note "git@github.com:owner/repo.git" "[pre-push]"

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:

0  the call may proceed
2  the call is blocked; the reason on stderr goes back to the agent

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/bump_version.sh minor -f VERSION

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_changelog_section.sh --version 1.4.0

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_no_private_ips.sh --repo .

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_tag.sh --branch release/1.4.0 --fetch-tags --print-version

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_release_version.sh --branch release/1.4.0 --fetch-tags

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/check_sensitive.sh --range origin/main..HEAD

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_gitleaks_report.sh --output results.sarif --fail-on-findings true

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/ci_security.sh --fail-on-findings

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:

  bash scripts/script-helpers/scripts/prune_branches.sh --remote --apply

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/refresh_private_names.sh --owner my-org --check

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/release_notes.sh --version 1.4.0 --output release-body.md

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:

scripts/tag_release.sh

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:

0  Version updated.
1  Bad arguments or version update failed.

Example:

pwsh -File ps\scripts\bump_version.ps1 patch

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:

pwsh -File ps\scripts\preflight.ps1 -Quick

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:

0  Tag created and pushed, or reported with -DryRun.
1  Validation, tag, or push failed.

Example:

pwsh -File ps\scripts\tag_release.ps1 -DryRun

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_django.sh --db-image postgres:16

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_flutter.sh --workdir app

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_go.sh --skip-build

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_gradle.sh --workdir android

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_laravel.sh --db-image mysql:8.0

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_node.sh --workdir web

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_python.sh --requirements requirements-dev.txt

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_build.sh --workdir . --out-dir build

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_phpunit.sh --db-image mysql:8.0 --wp-version 6.5

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:

0  Requested Go checks passed.
1  A check failed or a required command is unavailable.

Example:

pwsh -File ps\scripts\ci_go.ps1 -Quick

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:

0  Requested Node.js checks passed.
1  A check failed or a required command is unavailable.

Example:

pwsh -File ps\scripts\ci_node.ps1 -TestCmd "npx","jest","--runInBand"

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:

0  Requested Python checks passed.
1  A check failed or a required command is unavailable.

Example:

pwsh -File ps\scripts\ci_python.ps1 -TestCmd "pytest","-k","my test"

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:

0  Requested Rust checks passed.
1  A check failed or a required command is unavailable.

Example:

pwsh -File ps\scripts\ci_rust.ps1 -Quick

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:

  bash scripts/local_test_bash32.sh --test tests/version_test.sh

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:

  bash scripts/local_test_flutter.sh --dir app

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:

  bash scripts/local_test_go.sh --quick

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:

  bash scripts/local_test_gradle.sh --dir android --android

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:

  bash scripts/local_test_node.sh --quick

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:

  SKIP_PHP_TESTS=1 bash scripts/local_test_php.sh --quick

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:

  bash scripts/local_test_python.sh --dir backend

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:

  bash scripts/local_test_rust.sh --quick

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:

pwsh -File ps\scripts\local_test_gradle.ps1 -Quick

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_arch_artifacts.sh --repo . --artifact-dir dist

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_brew_tarball.sh --name isoforge --repo . --exclude dist

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_deb_artifacts.sh --repo . --prebuild "make man"

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/build_rpm_artifacts.sh --repo . --artifact-dir dist

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:

  ./packaging_init.sh --repo .

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:

  ./ppa_upload.sh --ppa ppa:owner/name --key-id ABC123 --series jammy

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:

  ./publish_homebrew.sh --formula packaging/brew/myapp.rb --tap-repo owner/homebrew-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.

Usage: ./example_dialog_input.sh
No required parameters.
  -h, --help   Show this help and exit.

Exit codes:

0  The name was read and greeted.
1  dialog is missing, no terminal, or the prompt was cancelled.

Example:

./example_dialog_input.sh

scripts/example_docker_compose_cmd.sh#

Example script that detects and prints the available docker compose command.

Usage: ./example_docker_compose_cmd.sh
No required parameters.
  -h, --help   Show this help and exit.

Exit codes:

0  A compose command was found and printed.
1  Neither `docker compose` nor `docker-compose` is available.

Example:

./example_docker_compose_cmd.sh

scripts/example_docker_status.sh#

Example script that prints Docker runtime status using script-helpers.

Usage: ./example_docker_status.sh
No required parameters.
  -h, --help   Show this help and exit.

Exit codes:

0  Status printed.
1  docker_status failed (Docker missing or the daemon unreachable).

Example:

./example_docker_status.sh

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:

./example_download.sh https://example.com/file.bin /tmp/file.bin

scripts/example_env.sh#

Example script that demonstrates environment helpers and validation.

Usage: ./example_env.sh
No required parameters.
  -h, --help   Show this help and exit.

Exit codes:

0  The demo ran.
1  HOME or PATH is unset.

Example:

EXAMPLE_FOO=myvalue ./example_env.sh

scripts/example_json.sh#

Example script for JSON helpers including formatting and escaping.

Usage: ./example_json.sh
No required parameters.
  -h, --help   Show this help and exit.

Exit codes:

0  The demo ran.
1  A json helper failed.

Example:

./example_json.sh

scripts/example_logging.sh#

Example script that demonstrates logging and structured log helpers.

Usage: ./example_logging.sh
No required parameters.
  -h, --help   Show this help and exit.

Exit codes: 0 The demo ran (it prints error lines on purpose; they do not fail it).

Example:

./example_logging.sh

scripts/example_package_publish.sh#

Demonstrate package_publish helpers in a safe, non-network way.

Usage: ./example_package_publish.sh
No parameters.

Exit codes:

0  The demo ran (or help shown with -h/--help).
1  pkg_find_changes_file found no .changes file.

Example:

./example_package_publish.sh

ps/scripts/example_logging.ps1#

Demonstrates the logging module of the PowerShell script-helpers library.

Usage: pwsh -File ps\scripts\example_logging.ps1
No parameters.

Exit codes: 0 after printing the example output.

Example:

pwsh -File ps\scripts\example_logging.ps1

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 them with the 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/script-helpers/scripts/docs_site.sh preview --port 8080

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:

bash scripts/generate-android-keystore.sh ./release.jks

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:

  bash scripts/install_dev_cli.sh --repo ../my-project --shims build,test,update

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.sh --check
  scripts/install_ollama.sh --prefix "$HOME/.local"

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:

./install_ollama_model.sh -i -D -R -e .env

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.

Usage: ./lint_docs.sh
  -h, --help  Show this help and exit.

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:

  bash scripts/lint_docs.sh

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:

0  the file passes
1  one or more lines refused
2  bad arguments or no such file

Example:

  scripts/ollama_tiers_check.sh
  scripts/ollama_tiers_check.sh /tmp/new_tiers.txt

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/prepare_public.sh audit --source ../my-repo --work /tmp/pub-audit

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:

0  Keystore generated.
1  Generation failed or the output path is unsafe.

Example:

pwsh -File ps\scripts\generate-android-keystore.ps1 -OutputPath .\release.jks

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:

pwsh -File ps\scripts\install_docker.ps1 -Check

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:

pwsh -File ps\scripts\install_ollama.ps1 -Check