Quick start¶
For developers. One command in the POS core repository creates a working extension: a handler that honours the store's switch, a gated endpoint, a button on the sale screen, a release note, its tests and its API reference. Check it with a second command, make it yours, and package it for the Extensions page.
Before you start¶
- A checkout of the POS core repository (
opengunstore) with its development stack running:make dev-upthere. The first time, create the development site as the core's README describes. - Docker and Python 3.9 or newer on your machine.
- Run every command below from the root of the core checkout unless a step says otherwise.
1. Create the extension¶
make ext-new ID=acme_hello TITLE="Acme Hello" PUBLISHER="Acme Software LLC"
IDis<vendor>_<name>: lowercase letters, digits and underscores, with a vendor of 2 to 12 characters. It becomes the folder, the Python package and the extension id.TITLEis what staff read, at most 30 characters.- The extension is created next to the core checkout, in
../acme_hello. AddOUT=<folder>to put it somewhere else. An existing, non-empty folder is never overwritten.
The command prints created acme_hello in ../acme_hello (21 files) and writes:
acme_hello/
├── acme_hello/
│ ├── __init__.py __version__ = "0.1.0"
│ ├── hooks.py the manifest and the hooks
│ ├── modules.txt one line: Acme Hello
│ ├── patches.txt
│ ├── acme_hello/__init__.py the module package
│ ├── api/greet.py two gated, documented endpoints: greet (POST) and ping (GET)
│ ├── hooks_impl/customer.py a Customer after_insert handler, wrapped in @extension_hook
│ ├── public/js/acme_hello.bundle.js the Say hi button, registered with the POS
│ ├── change_log/v0/v0_1_0.md the release note staff read
│ └── tests/test_greet.py switched off, no role, repeat call
├── docs/api/ the API reference, generated from the code
├── README.md For the counter / For the manager / For developers
├── pyproject.toml
├── LICENSE
└── .gitignore
It follows the developer specification as generated: the lint finds nothing and the tests pass.
2. Check it¶
make ext-test ID=acme_hello DIR=../acme_hello
This copies the extension into the development stack, installs it on the development site, and runs three checks — the same three the core's CI runs on Old Steel Arsenal's own extensions:
- Tests:
bench run-tests --app acme_hello. - Lint: the checks the Extensions page runs on every upload.
- API docs:
docs/api/must match the code.
It ends with one line, and exits non-zero if any check failed:
ext-test acme_hello: tests OK (5 ran) · lint OK · docs OK
The extension stays installed on the development site. Running the command again after a change copies the new code, migrates the development site — so new or changed DocTypes, fields, fixtures and patches are in place — and repeats the three checks.
make ext-test does not install bundled wheels yet: an extension with a wheels/ folder (see step 4) is refused with a message saying so.
3. Make it yours¶
- In
acme_hello/hooks.py, fill inhomepageandsupportin the manifest. The manager sees both on the Extensions page. Declare a permission for everything your code does (specification §5.3); the lint names what is missing. - Replace the greeting with your feature. Every endpoint opens with
require_enabled(APP, roles=…)and carries a docstring withArgs:andReturns:(specification §9). Everydoc_eventshandler is wrapped in@extension_hook(APP). - Keep the three kinds of tests for every endpoint and hook you add: switched off, no role, and a repeat call that does not write twice.
-
After changing an endpoint or a DocType, regenerate the API reference and commit it:
python3 scripts/generate_api_docs.py --app ../acme_hello -
Run
make ext-test ID=acme_hello DIR=../acme_helloagain.
4. Package it¶
In the extension's folder:
cd ../acme_hello
git init
git add -A
git commit -m "Acme Hello 0.1.0"
git archive --format=zip -o acme_hello-0.1.0.zip HEAD
The zip root is the app root (pyproject.toml next to the acme_hello/ package). Keep it under 50 MB and free of .git, .env, keys and build caches — the lint refuses them, and git archive leaves out everything .gitignore ignores.
If the repository is public, a tag is enough: the manager can fetch https://github.com/<you>/acme_hello at tag v0.1.0 from the same tab instead of uploading a file. A library your extension needs goes into wheels/ as a pure-Python wheel and into dependencies in pyproject.toml (specification §12).
5. Install it¶
Hand the zip to the store manager. On Settings → Extensions → Upload they choose the file, read the review card and the permission chips, type acme_hello to confirm, and the store does the rest: the code is staged, the POS restarts once, and the extension is installed and switched on. What the manager sees at each step is on the Extensions page.
To ship a new version, raise the version in acme_hello/__init__.py and in the manifest in hooks.py, add change_log/v0/v0_2_0.md, and hand over the new zip or tag. The manager uploads it the same way; the review card shows it as an upgrade, and the store backs up the database, swaps the code and runs your patches. The POS is unavailable for about 1–3 minutes.
What to read next¶
- Developer specification — every rule the lint enforces and why.
- Core hooks and the registry — what the POS offers to plug into.