Disposable Anki profile, add-on install, Docker/Xvfb, and GUI workbench tooling for add-on and deck development.
Project description
๐ฌ anki-addon-workbench
Headless, disposable-profile testing & cross-platform GUI tooling for Anki add-on and deck development.
Disposable Anki profile, add-on install, Docker/Xvfb, smoke-test, and GUI workbench tooling for Anki add-on and deck development.
It helps an agent or developer launch Anki away from a real profile, install the add-on under test, run a probe add-on, capture marked screenshots, and send mouse or keyboard input โ locally on macOS/Linux or inside a virtual display.
The first target users are add-on authors who want to make GUI work less manual without risking their daily Anki profile, and deck authors who need to see how cards actually render inside Anki (where JavaScript and styling can behave differently than in a browser) without manually closing and reopening Anki โ which triggers a sync round-trip each time.
How it works
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ live Anki โ move ยท click ยท type โ screenshot โ
โ disposable โ โโโโโโโโโโโโโโโโโโโโโโถ โ cursor-marked โ
โ profile โ (pyautogui) โ .png + .json โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโฌโโโโโโโโโ
โฒ โ
โ agent reads & decides โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
A disposable Anki the agent drives, screenshots, and adjusts โ no profile risk, no sync churn.
Install
From PyPI:
# core smoke/profile/Docker tooling only (dependency-light)
uv add anki-addon-workbench
# include the GUI primitives (pyautogui + Pillow) for screenshot/move/click/type
uv add 'anki-addon-workbench[gui]'
(pip install 'anki-addon-workbench[gui]' works too.)
The GUI extra is optional: the core smoke, launch (without screenshots),
profile, and dockerfile tooling has no heavy dependencies. The
screenshot/pointer/keyboard commands lazy-load pyautogui + Pillow and fail with
a clear install hint if the [gui] extra is missing.
Platform notes. GUI primitives are cross-platform via pyautogui:
- macOS โ works against your local
/Applications/Anki.app; grant Terminal (or your runner) Screen Recording and Accessibility permission once. - Linux/Xvfb โ works headless; pyautogui uses
scrot+python-xlib(installed by the bundled Docker image).
Configure An Add-On
Add this to the add-on project's pyproject.toml:
[tool.anki-addon-workbench]
project_name = "My Add-on"
addon_package = "my_addon"
source_root = "."
include = ["__init__.py", "manifest.json", "README.md"]
exclude = ["tests", "user_files", ".git", "__pycache__"]
seed_apkgs = []
probe_addon = "tests/gui_smoke/probe_addon"
anki_version = "25.09"
profile = "User 1"
docker_image = "my-addon-anki-gui"
# Optional. Override when dogfooding an unreleased workbench build.
docker_workbench_spec = "anki-addon-workbench[gui]"
If the project does not have a pyproject.toml, place the same keys in
anki-workbench.toml.
For deck-only projects, omit addon_package and point seed_apkgs at one or
more generated .apkg files:
[tool.anki-addon-workbench]
project_name = "My Deck"
seed_apkgs = ["out/my-deck.apkg"]
anki_version = "25.09"
docker_image = "my-deck-anki-gui"
The workbench imports those packages into the disposable profile before Anki's
GUI starts. If seed_apkgs is configured and no custom probe_addon is set,
smoke automatically installs a built-in deck probe that verifies the
collection has cards, lists the imported decks and note types, and renders a
small card sample. Use a custom probe only when the deck needs project-specific
assertions.
CLI
anki-workbench doctor
anki-workbench smoke
anki-workbench launch --xvfb --pointer 500,180 --keep
anki-workbench screenshot --out .tmp/shot.png --meta .tmp/shot.json
anki-workbench move 500 180
anki-workbench click
anki-workbench key Escape
anki-workbench type "search text"
anki-workbench dockerfile --out tests/gui_smoke/Dockerfile
anki-workbench docker-smoke-local --uv-command "sfw uv"
anki-workbench init-probe --out tests/gui_smoke/probe_addon
smoke installs the configured add-on and optional probe add-on into a
disposable base folder, seeds the profile so the first-run language dialog does
not appear, imports any configured APKGs, launches Anki, waits for the probe to
write JSON, prints the JSON, and removes the base folder unless --keep is
passed. Deck-only projects with seed_apkgs get the built-in deck smoke probe
automatically when no custom probe is configured.
launch starts disposable Anki without the probe add-on so an agent can inspect
and interact with the live GUI. It can start Xvfb, wait for the Anki window,
move the pointer, and capture a cursor-marked screenshot.
All commands print JSON. The screenshot command draws a high-contrast synthetic marker at the pointer (headless screenshots do not include the real cursor), which is far more reliable than depending on the display server to render it.
typeis ASCII-oriented. It uses pyautogui's keystroke synthesis, which reliably types ASCII and common keys but cannot reliably type non-ASCII text (e.g. CJK, accented characters) โ those need an input method the synthetic keystrokes bypass. For non-Latin search/input, set the field value through Anki/AnkiConnect rather thantype. (This is a regression from the oldxdotool typebackend, traded for cross-platform support.)
The Probe Contract
smoke works by installing a small probe add-on alongside the add-on under
test. When Anki finishes starting, the probe writes a JSON result to the path in
the ANKI_ADDON_WORKBENCH_RESULT environment variable and then exits Anki. The
runner reads that file, prints it, and uses ok for its exit status.
For deck-only projects, the built-in APKG probe is usually enough. It is enabled
automatically when seed_apkgs is non-empty and probe_addon is omitted. Set
deck_smoke_render_limit = 10 if you want it to render more than the default
five sample cards.
Scaffold one in one command โ you rarely need to write a probe by hand:
anki-workbench init-probe --out tests/gui_smoke/probe_addon
This writes a documented, self-contained __init__.py with a run_checks()
function to edit. Then point probe_addon at that directory in your config.
The result contract:
| Field | Type | Meaning |
|---|---|---|
ok |
bool | Required. Whether the probe's checks passed. |
checks |
list | Optional. [{"name": str, "ok": bool, ...}] per-check detail. |
| anything else | json | Optional. Echoed back verbatim in the smoke output. |
The runner validates that ok is present and boolean; if a probe forgets it (or
never writes / never exits, which times out), the smoke output says exactly what
went wrong and points you back to init-probe. A probe may also read
ANKI_ADDON_WORKBENCH_SCREENSHOT for a path to capture a screenshot to.
Docker/Xvfb
Render the reusable Anki launcher image template in the add-on project:
anki-workbench dockerfile --out tests/gui_smoke/Dockerfile
Then build it and run it with the add-on project mounted:
docker build -f my-addon/tests/gui_smoke/Dockerfile -t my-addon-anki-gui my-addon
docker run --rm -v "$PWD":/workspace -w /workspace/my-addon my-addon-anki-gui
The image installs the Anki Linux launcher, Xvfb, xdotool, scrot,
QtWebEngine runtime libraries, Python, and the configured workbench package spec
(anki-addon-workbench[gui] by default). The Anki binary path is provided via
the ANKI_BIN environment variable (set in the image), not hardcoded in the
library. The workbench itself is installed into the image, so generated
Dockerfiles are standalone and do not require a sibling source checkout.
When dogfooding an unreleased workbench build, either set
docker_workbench_spec in [tool.anki-addon-workbench] or pass an override:
anki-workbench dockerfile \
--out tests/gui_smoke/Dockerfile \
--workbench-spec "anki-addon-workbench[gui] @ https://github.com/elvis-sik/anki-addon-workbench/archive/<commit>.zip"
For workbench maintainers, the higher-level local-wheel path avoids publishing or pushing before testing:
anki-workbench docker-smoke-local --uv-command "sfw uv"
It builds a wheel from --workbench-source (default: .), creates a small
Docker build context containing that wheel, renders a Dockerfile that installs
the wheel with [gui], builds the configured image with a -local suffix, and
runs the configured smoke test with the project mounted at /workspace.
Artifacts and command logs are written under .tmp/anki-workbench-local.
Development
make check
make docker-smoke-local
Tests run on pytest (make test) and cover config loading, add-on copy
filtering, profile seeding, command construction, Dockerfile rendering, probe
scaffolding, and the GUI marker rendering. Docker GUI smoke tests are kept behind
explicit CI commands because Anki launcher downloads and QtWebEngine startup are
comparatively slow. make docker-smoke-local is the maintainer confidence check
for unreleased workbench changes: it installs the local wheel into the generated
Docker image instead of pulling the published PyPI package.
Releasing
Releases publish to PyPI via GitHub Actions using Trusted Publishing (OIDC) โ no API token is stored. To cut a release:
# 1. bump `version` in pyproject.toml, commit
# 2. tag and push โ the tag must match the version
git tag v0.3.1
git push origin v0.3.1
The release.yml workflow checks that the tag
matches pyproject.toml, builds the sdist + wheel with uv build, and publishes.
One-time setup on PyPI (Account โ Publishing โ Add a pending publisher):
- PyPI Project Name:
anki-addon-workbench - Owner / Repository: your GitHub org/repo
- Workflow name:
release.yml - Environment name:
pypi
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file anki_addon_workbench-0.4.0.tar.gz.
File metadata
- Download URL: anki_addon_workbench-0.4.0.tar.gz
- Upload date:
- Size: 72.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1e2b7f2beda0fe880eb3575be89cd75f9ccd12c929117d8d5180ff1bd2637bb
|
|
| MD5 |
b246548bd1c09241d0571ddd920b1f79
|
|
| BLAKE2b-256 |
5f376e2a2beedcde0b25961763be3211d7efa342476fc1368e0c7ba7e48af6ba
|
Provenance
The following attestation bundles were made for anki_addon_workbench-0.4.0.tar.gz:
Publisher:
release.yml on elvis-sik/anki-addon-workbench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anki_addon_workbench-0.4.0.tar.gz -
Subject digest:
f1e2b7f2beda0fe880eb3575be89cd75f9ccd12c929117d8d5180ff1bd2637bb - Sigstore transparency entry: 1955334023
- Sigstore integration time:
-
Permalink:
elvis-sik/anki-addon-workbench@4ae27299900507062f6388a9d4c7183d36993e33 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/elvis-sik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4ae27299900507062f6388a9d4c7183d36993e33 -
Trigger Event:
push
-
Statement type:
File details
Details for the file anki_addon_workbench-0.4.0-py3-none-any.whl.
File metadata
- Download URL: anki_addon_workbench-0.4.0-py3-none-any.whl
- Upload date:
- Size: 35.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d86971d2c3181b33ffdd4c7005165d05913a93c6de7681538fad5dff8c6fbb2
|
|
| MD5 |
721135f312c96ba851291a0fccf31d41
|
|
| BLAKE2b-256 |
15f41fe0f0ffcd243e3265e6070b5d9c72b3f7261861caea22646979c6f0e5bf
|
Provenance
The following attestation bundles were made for anki_addon_workbench-0.4.0-py3-none-any.whl:
Publisher:
release.yml on elvis-sik/anki-addon-workbench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anki_addon_workbench-0.4.0-py3-none-any.whl -
Subject digest:
8d86971d2c3181b33ffdd4c7005165d05913a93c6de7681538fad5dff8c6fbb2 - Sigstore transparency entry: 1955334139
- Sigstore integration time:
-
Permalink:
elvis-sik/anki-addon-workbench@4ae27299900507062f6388a9d4c7183d36993e33 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/elvis-sik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4ae27299900507062f6388a9d4c7183d36993e33 -
Trigger Event:
push
-
Statement type: