BenchPress Documentation

BenchPress Documentation

Open in ChatGPT
Ask ChatGPT about this page
Open in Claude
Ask Claude about this page

CLI and scripts

CLI and scripts

What you can run from a terminal, and what each one changes.

Who this is for. Somebody working on the host, or on a checkout.

Before you start. BenchPress has no bench subcommands of its own. Nothing
registers a command in hooks.py. Everything below is either the parent
repository's entry.py, a plain Frappe command, or a script in this repository.

entry.py, in the parent repository

entry.py orchestrates the whole control plane stack in Python standard
library only. It has no dependencies to install.

Run it from benchpress_devops, not from this app.

Command Does
./entry.py --preset benchpress --frappe-version version-16 first-time setup: clones apps, builds the image, brings everything up
./entry.py --restart restarts the services
./entry.py --stop stops the services
./entry.py --destroy removes containers and volumes. This is data loss
./entry.py --build --preset benchpress rebuilds the image after a preset change
./entry.py --pull fetches the image and recreates containers, with no local build
./entry.py --push publishes the tag in IMAGE_NAME
./entry.py --check-host reports each kernel ceiling and exits non-zero if one is too low

Public deployment is one flag. --domain layers the production overlay,
moves nginx to loopback, puts Traefik on 80 and 443, and issues a wildcard
certificate.

Flag Does
--domain <fqdn> persists PUBLIC_HOSTNAME and covers *.<fqdn>
--acme-email the Let's Encrypt contact address
--cf-token the Cloudflare token for the DNS-01 challenge
--acme-staging issues untrusted certificates with no rate limit to burn
--acme-production switches back after --acme-staging
--reload-proxy re-renders the Traefik configuration and recreates only Traefik
--project <name> a second Compose project, to run staging beside production

Use --acme-staging while proving out a domain or a new token. Let's Encrypt
allows five certificates for each identifier set every seven days, and a
failed attempt still spends one.

Bench commands

Ordinary Frappe commands, run inside the backend container.

docker compose exec backend bench --site frontend migrate
docker compose exec backend bench --site frontend clear-cache
docker compose exec backend bench --site frontend console
docker compose exec backend bench --site frontend run-tests --app benchpress
docker compose exec backend bench build --app benchpress

After a Python change, restart backend and any worker that imports the
changed module. After a frontend change, run bench build --app benchpress,
then restart backend and frontend.

A multi-line block piped to bench console fails silently. The console is
IPython. A block arriving on a pipe is auto-indented until it no longer parses,
and it produces no output and no error, which reads exactly like a query that
found nothing.

Wrap it in a single exec() call, or use bench execute instead:

bench --site frontend execute frappe.client.get_list \
  --kwargs "{'doctype':'Lab','fields':['name','status'],'limit_page_length':0}"

Use --kwargs, never --args. There is no set-value command. Write a value
with frappe.client.set_value through the same execute call.

Scripts in this repository

Two shell scripts run against an existing bench.

Script Usage Does
setup.sh bash apps/benchpress/setup.sh <site> [--strict] the one-time post-install setup, after bench install-app benchpress
upgrade.sh bash apps/benchpress/upgrade.sh <site> [ref] [--dry-run] upgrades an install end to end, behind a backup gate

setup.sh --strict exits non-zero when Docker userns-remap is absent or cannot
be verified. Use it on a production host. The default warns and continues, which
suits a development box.

upgrade.sh aborts on any failure and takes a backup first. --dry-run reports
what it would do. It follows Upgrading step for step.

Neither script owns the VPN. Tunnels, peers and address allocation belong to the
vpn_management app.

The four Python scripts

Script Answers
scripts/seed_docs_demo.py creates or removes the demo labs and users the documentation screenshots come from
scripts/admission_drill.py how many of N parallel deploys the admission gate really admits
scripts/density_drill.py how many endpoints a bridge of a given prefix length really takes
scripts/golden_drill.py what a golden restore costs against a cold deploy

The three drills are measurement harnesses. None of them reimplements the thing
it measures. Every request goes over HTTP to the shipped endpoint, and every
duration is read back out of the run's own Deploy Log.

All three share three safety properties, and they are worth knowing before you
run one on a host serving real users.

Property Means
They call the host's own nginx the public name is behind Cloudflare, which answers a dozen machine clients with 1010 rather than with the endpoint. BENCHPRESS_URL overrides the target
They refuse the wrong site --i-know-this-is must match the site's own base_domain, read back from the control plane
They clean up in a finally and only ever touch what they created, never filtering on status

density_drill.py fills a network it made, always named bpdrill-<n>, and
asserts the name before the first create. A drill that filled benchpress-0
would take every bench on that bridge offline at the moment it believed it was
measuring capacity.

admission_drill.py stops queue-long for the run unless --allow-deploys.
Every deploy is enqueued after commit, so nothing reaches Redis and the whole
admission decision stays measurable with no worker running.

golden_drill.py --cold is the control arm. It turns restore_from_golden off
for the run and puts it back afterwards, so both arms deploy the same lab.

seed_docs_demo.py --destroy removes only what the script creates and refuses
every other name.

The frontend

cd frontend && yarn dev        # hot reload
cd frontend && yarn test:run   # unit tests
cd e2e && npx playwright test  # end to end

Playwright has its own configuration in e2e/. Running it from the app root
resolves nothing.

Never run yarn lint. That is biome, a different style than this repository
has ever used, and it rewrites every frontend file. Lint with pre-commit:

git add -A && uvx [email protected] run --all-files

git add first. pre-commit skips untracked files, so a new file lints green
while it is invisible and then fails in CI.

The documentation pipeline

Four commands build and gate these pages. Read the exit code, never the last
line of output.

npm run docs:build && npm run docs:lint && npm run docs:score; echo "exit=$?"
Command Does
npm run docs:site generates docs-site/: llms.txt, llms-full.txt, search and sitemap
npm run docs:bundle generates docs-bundle/: AGENTS.md, SKILL.md and a markdown mirror
npm run docs:build both of the above
npm run docs:lint leadtype lint docs --max-warnings 0
npm run docs:score leadtype score --src docs --out docs-site --min 100

Two traps are already handled by the scripts, and both cost a debugging session
to find.

  1. Lint the source folder positionally. leadtype lint --src . walks the
    whole project root, finds the generated AGENTS.md, SKILL.md and
    sitemap.md, and reports errors because none of them carries a title.
    Generated files are not pages.
  2. The config is docs.config.mjs, not .ts. A TypeScript config needs the
    optional peer dependency jiti. Do not convert it.

Never hand-edit anything under docs-site/ or docs-bundle/. They are
generated, and a gate regenerates and fails on any difference.

Verify

Confirm the whole pipeline is clean:

npm run docs:build && npm run docs:lint && npm run docs:score; echo "exit=$?"

exit=0 is the only passing result.

Troubleshooting

Symptom Cause Fix
bench console printed nothing A piped multi-line block was truncated Wrap it in one exec(), or use bench execute
bench execute rejects its arguments --args was used Use --kwargs with a dict
Lint fails on files you did not write lint --src . scanned generated output Run npm run docs:lint, which lints docs positionally
Lint passes locally and fails in CI The new file is untracked git add -A first
The docs config is not loaded It was renamed to .ts Rename it back to .mjs
Playwright finds no tests It was run from the app root Run it from e2e/
A frontend change is not visible Assets are built, not served from source bench build --app benchpress, then restart
--destroy lost the database It removes volumes as well as containers It says so. Use --stop

Reference

Fact Value
Bench subcommands this app adds 0
Shell scripts setup.sh, upgrade.sh
Python scripts 4
Documentation commands 5
Passing pipeline result exit=0
Lint uvx [email protected] run --all-files
Last updated 4 hours ago
Was this helpful?
Thanks!