@taste.md
@commit-guidelines.md
Before running tests, always ask the user which site to use.
Use the following command format (note: --site goes before run-tests, not after):
bench --site <site> run-tests --app press --module press.press.doctype.site_update.test_site_updateTo run a single test:
bench --site <site> run-tests --app press --module press.press.doctype.site_update.test_site_update --test test_specific_thingIf the test site is missing doctypes, migrate it first (use --skip-failing to avoid getting blocked by unrelated errors):
bench --site <site> migrate --skip-failingSee guide-to-testing.md for how to write tests for this project, and the Frappe testing docs for framework-level testing reference.
See guide-to-ui-testing.md for setup and conventions.
Quick reference — run from dashboard/:
# Headed (opens browser)
yarn test:e2e:headed
# Single file
npx playwright test tests-e2e/tests/dashboard/site-update-banner.test.ts --headedRequires a running bench (bench start) and dashboard/tests-e2e/.env with credentials.
Python (via ruff — runs on pre-commit):
ruff check press/
ruff format press/JavaScript/Vue (via Biome):
npx biome check dashboard/
npx biome format dashboard/Set up pre-commit hooks once with:
bash setup-pre-commit.shcd dashboard && yarn dev # development server
cd dashboard && yarn build # production build
yarn build # builds dashboard + email/marketplace/saas CSSError messages and dialogs often link to Frappe Cloud docs. Don't guess slugs —
they 404 (the uninstall page is how-to-uninstall-an-app-from-the-site, not
sites/uninstall-an-app). Search the wiki API instead:
curl -s "https://docs.frappe.io/api/method/wiki.frappe_wiki.doctype.wiki_document.search.search?query=<TERM>&space=0uh9cfn2fk"Returns {message:{results:[{title, route, content, score}], total}}. Take the
top result's route and prepend https://docs.frappe.io/. space=0uh9cfn2fk
scopes results to the Cloud space (all routes start with cloud/); omit
space to search all of docs.frappe.io (framework, etc.).
Press is a Frappe app that powers Frappe Cloud — a self-serve cloud hosting platform for the Frappe stack.
- Frappe framework docs: https://docs.frappe.io/framework/
- Frappe Cloud user docs: https://docs.frappe.io/cloud
Backend (press/): A Frappe app. Business logic lives in doctypes at press/press/doctype/. The REST API consumed by the dashboard is in press/api/. Scheduled tasks and event hooks are wired up in press/hooks.py.
Frontend (dashboard/): A Vue 3 SPA using Frappe UI. Pages live in dashboard/src/pages/, reusable components in dashboard/src/components/. The frontend calls the whitelisted Python API methods in press/api/.
There are two parallel dependency chains that meet at Bench:
App/release chain (what runs on a bench):
App
└── AppSource (a branch of a GitHub repo)
└── AppRelease (a specific commit)
└── ReleaseGroup (a set of AppSources + their versions, user-facing "Bench group")
└── DeployCandidate (snapshot of a group ready to be built)
└── DeployCandidateBuild (the Docker build; separate arm64 / x86_64)
Infrastructure chain (where a bench runs):
Cluster (a cloud region)
└── VirtualMachine (a cloud VM)
├── Server (app server — runs gunicorn/redis)
├── DatabaseServer (runs MariaDB)
└── ProxyServer (runs nginx)
Bench is where the two chains converge — it references a ReleaseGroup, a DeployCandidate, a DeployCandidateBuild, and a Server (paired with a DatabaseServer). Site lives on a Bench and belongs to a Team.
The creation flow: group.create_deploy_candidate() → candidate.build() → Deploy (calls create_benches(), creating Bench records on target servers) → sites are created on those benches.
Each key doctype has a README.md in its folder with more detail:
agent_job/— how Press talks to serversserver/— Server, DatabaseServer, ProxyServerrelease_group/— user-facing bench groupdeploy_candidate/— build snapshotdeploy_candidate_build/— Docker image buildbench/— running bench on a serversite/— the Frappe sitesite_update/— moving a site to a newer bench
press/agent.py contains the Agent class — the sole interface for Press to talk to the Agent flask app running on each server. Every operation on a remote server (create site, install app, run backup, etc.) goes through Agent, which creates an AgentJob record and sends an HTTP request. Agent jobs are polled via poll_pending_jobs (runs every 5 seconds) and callbacks are dispatched on completion.
Ansible playbooks in press/playbooks/ handle server provisioning. Docker images for benches are built by DeployCandidateBuild. Virtual machines are managed via cloud provider APIs (AWS, Hetzner, OCI, Frappe Compute) through the VirtualMachine doctype.