Release helper for Anki add-ons and shared decks.
Project description
anki-addon-release
anki-addon-release is a small release helper for Anki add-ons and shared decks.
For add-ons, it provides deterministic local release prep:
- read release config from
pyproject.toml - validate the add-on source tree and
manifest.json - build a clean
.ankiaddonarchive - inspect archive contents before upload
For shared decks, it can drive AnkiWeb's deck sharing form without storing the private source deck name or deck id in the public repository.
For AnkiWeb, it can either write a regular-browser handoff bundle for add-ons or
drive the relevant form with Playwright. Browser publishing is review-first by
default: the final AnkiWeb save/share button is clicked only when --submit is
passed.
Status
Early public release. It has been dogfooded against the
Study Triage add-on release flow,
including AnkiWeb login, create/update form filling, support URL filling,
branch compatibility fields, and local browser-flow regression tests.
Deck publishing supports public/private config splitting: public repos can hold
the listing file, while .anki-addon-release.local.toml or .env holds the
private Anki collection deck reference.
It is published on PyPI via Trusted Publishing.
Install
uvx anki-addon-release --help
For browser publishing support, include the browser extra and install the Playwright browser runtime:
uvx --from "anki-addon-release[browser]" playwright install chromium
uvx --from "anki-addon-release[browser]" anki-addon-release --help
For unreleased changes on main, run directly from GitHub:
uvx --from "anki-addon-release @ git+https://github.com/elvis-sik/anki-addon-release.git" \
anki-addon-release --help
Install For Local Development
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e .
No runtime dependencies are required for the initial local package/check workflow.
For browser publishing:
python -m pip install -e ".[browser]"
python -m playwright install chromium
Use sfw when installing from public registries in this workspace.
Credentials
The framework never stores plain AnkiWeb credentials in config. Point project
config at environment variable names, and put either plain values or 1Password
secret references under those same names in a private .env file:
ANKIWEB_EMAIL=op://Personal/AnkiWeb/username
ANKIWEB_PASSWORD=op://Personal/AnkiWeb/password
For local development, prefer resolving those references at the process boundary with 1Password CLI:
op run --env-file=.env -- anki-addon-release publish
Ordinary environment values still work, so CI can inject credentials without
1Password. Direct op:// values are also accepted for compatibility when the
CLI is run without op run.
Reference the variable names in project config:
[tool.anki-addon-release.ankiweb]
login_email_env = "ANKIWEB_EMAIL"
login_password_env = "ANKIWEB_PASSWORD"
publish uses these values automatically when they are present, logging into
the persistent browser profile before opening the add-on upload or deck share
form. If neither variable is present, publish keeps using the existing browser
session in the profile.
or pass them on the command line:
anki-addon-release login \
--email-env ANKIWEB_EMAIL \
--password-env ANKIWEB_PASSWORD \
--submit-login
Without --submit-login, the login command fills the fields and leaves the form
for review. In --headless mode, --submit-login is required.
Separate Publishing Account
For real publishing, prefer a dedicated AnkiWeb account used only for add-ons and decks. This is an isolation pattern, not a framework requirement: it keeps release automation separate from your personal synced collection and makes it easier to reason about which account a browser profile is logged into.
Two AnkiWeb account lifecycle rules matter for that pattern:
- New-account publishing guard: AnkiWeb may send very new accounts to its Account Too New page when they try to share add-ons. That public page says new accounts must meet certain criteria and can continue once the account is older, but it does not publish an exact wait period. Create the publishing account before you need it, or use an older dedicated account.
- Inactivity expiry: AnkiWeb's terms and account-removal article say account data may be deleted if the account is not accessed or synced in the last 6 months. Log in to the dedicated account at least every few months to keep it active. The account-removal article notes that shared add-ons are not subject to the usual data expiry, but keeping the publishing account active avoids losing account access or release ownership context.
Configure An Add-on
Add a section like this to the add-on repository's pyproject.toml:
[tool.anki-addon-release]
source_dir = "."
manifest = "manifest.json"
artifact_dir = "dist"
artifact_name = "study-triage.ankiaddon"
include = ["__init__.py", "manifest.json", "README.md"]
exclude = [
".git",
"__pycache__",
".mypy_cache",
".pytest_cache",
".ruff_cache",
".uv-cache",
"dist",
"tests",
"user_files",
]
[tool.anki-addon-release.ankiweb]
# Omit addon_id for a first publish; set it for updates.
addon_id = "1234567890"
listing_file = "release/ankiweb.md"
changelog_file = "CHANGELOG.md"
login_email_env = "ANKIWEB_EMAIL"
login_password_env = "ANKIWEB_PASSWORD"
include is optional. When omitted, the whole source_dir is considered and exclude filters out development files.
AnkiWeb Listing File
Put public-facing listing copy in a Markdown file so agents and humans can edit
it without turning TOML into a prose document. By default, release/ankiweb.md
is loaded when it exists:
---
title: Geography Deck
tags: geography maps
support_url: https://github.com/example/geography-deck
---
GitHub: [https://github.com/example/geography-deck](https://github.com/example/geography-deck)
Markdown description for AnkiWeb.
You can point at another path with listing_file. Inline title, tags,
support_url, and description values still work for small projects; explicit
title_file, tags_file, support_url_file, and description_file overrides
are available for unusual cases.
When support_url is a GitHub repository, check, publish --dry-run, and
browser publishing warn unless that exact GitHub URL is present as a clickable
Markdown link with the URL as its visible label. Use visible link text such as
GitHub: [https://github.com/example/geography-deck](https://github.com/example/geography-deck);
do not rely only on the support URL field, a plain URL, or a hidden Markdown
link target.
Configure A Deck
AnkiWeb shares decks from the logged-in user's synced collection. To avoid leaking private collection structure, keep the public listing metadata in the listing file, and keep the collection deck id/name in local config or env.
Public pyproject.toml:
[tool.anki-addon-release]
target = "deck"
[tool.anki-addon-release.ankiweb]
# Optional. Record the public shared item id once known.
shared_id = "1234567890"
listing_file = "release/ankiweb.md"
login_email_env = "ANKIWEB_EMAIL"
login_password_env = "ANKIWEB_PASSWORD"
[tool.anki-addon-release.deck]
# Safe to commit: this names the env var, not the private deck.
source_deck_id_env = "ANKIWEB_SOURCE_DECK_ID"
Private .anki-addon-release.local.toml:
[deck]
source_deck_id = "1650000000000"
copyright_confirmed = true
or private .env:
ANKIWEB_EMAIL=op://Personal/AnkiWeb/username
ANKIWEB_PASSWORD=op://Personal/AnkiWeb/password
ANKIWEB_SOURCE_DECK_ID=1650000000000
Both files are loaded automatically when present. Add them to every deck repo's
.gitignore. Existing environment variables win over .env values, which
lets op run --env-file=.env -- ... resolve secrets before the framework reads
project-local configuration.
If you prefer not to store the numeric deck id, use a private deck name instead:
[deck]
source_deck_name = "Private::Geography"
copyright_confirmed = true
Deck-name resolution uses AnkiConnect's deckNamesAndIds, so Anki must be open
with AnkiConnect running. Numeric source_deck_id is the most deterministic
release input.
Commands
From an add-on repository:
anki-addon-release check
anki-addon-release package
anki-addon-release inspect dist/study-triage.ankiaddon
From a deck repository:
anki-addon-release check
anki-addon-release publish --dry-run
Prepare an AnkiWeb publish plan without opening a browser:
anki-addon-release publish --dry-run
For deck targets, dry-run output redacts the private source deck id.
Preview the exact AnkiWeb Markdown description without opening a browser:
anki-addon-release publish --preview-description
Build a regular-browser handoff bundle for Codex or a human:
anki-addon-release handoff
The handoff bundle is written to .anki-addon-release/handoff/ by default and
contains:
release-handoff.json: machine-readable release metadatabrowser-checklist.md: human browser checklistcodex-browser-prompt.md: prompt for a Codex browser-operator sessiondescription.txtandchangelog.txtwhen configured
This path does not require Playwright. Use it when you want Codex to operate your regular logged-in Chrome session.
Open an AnkiWeb login page using a persistent project-local browser profile:
anki-addon-release login
Prepare an upload with Playwright without clicking the final submit button:
anki-addon-release publish --diagnostics-dir out/release-diagnostics
Click the final add-on submit button only when the flow is trusted:
anki-addon-release publish --submit --diagnostics-dir out/release-diagnostics
For deck targets, the final share button also requires an explicit copyright confirmation, either in private local config or as a command flag:
anki-addon-release publish --submit --confirm-copyright --diagnostics-dir out/release-diagnostics
Use --mode create for first-publish testing and --mode update for updating a configured addon_id. --mode auto uses update when ankiweb.addon_id is present and create otherwise.
The final AnkiWeb save/submit button is not clicked unless --submit is passed.
That review-first behavior is the default. In headed browser mode, the prepared
form stays open until you press Enter in the terminal. If the command is not
attached to an interactive terminal, it fails loudly instead of briefly opening
and closing the prepared form.
Without installation, from this repository:
PYTHONPATH=src python -m anki_addon_release --project /path/to/addon check
Development
make check
Browser-flow stress tests are opt-in because they need Playwright and a local fake AnkiWeb server:
ANKI_ADDON_RELEASE_BROWSER_TESTS=1 python -m unittest tests.test_browser_flows -v
or:
make test-browser
Those tests exercise separate create and update forms against a local HTTP server and verify that Playwright uploads the .ankiaddon artifact plus the expected metadata.
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.2.3
git push origin v0.2.3
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-release - Owner / Repository:
elvis-sik/anki-addon-release - Workflow name:
release.yml - Environment name:
pypi
Roadmap
- Add saved HTML/screenshot diagnostics on every browser failure.
- Add public artifact verification by downloading/installing the published add-on/deck into a disposable Anki profile, likely composed with
anki-addon-workbench. - Expand real-world compatibility coverage across more AnkiWeb form variants.
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_release-0.2.3.tar.gz.
File metadata
- Download URL: anki_addon_release-0.2.3.tar.gz
- Upload date:
- Size: 36.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
294d13f6a170fc612c79dad63e0f9a31173a16a01bf72175a070123713a62419
|
|
| MD5 |
71b495b7fef8fe51a374b979c1cc2b65
|
|
| BLAKE2b-256 |
b1744a5bcfae4666ef0f905cd994477ffc57c7a7378cf16e219991cdb0169b13
|
Provenance
The following attestation bundles were made for anki_addon_release-0.2.3.tar.gz:
Publisher:
release.yml on elvis-sik/anki-addon-release
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anki_addon_release-0.2.3.tar.gz -
Subject digest:
294d13f6a170fc612c79dad63e0f9a31173a16a01bf72175a070123713a62419 - Sigstore transparency entry: 2059971170
- Sigstore integration time:
-
Permalink:
elvis-sik/anki-addon-release@37a849b0f1837293adc182d4828103cf322503ca -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/elvis-sik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@37a849b0f1837293adc182d4828103cf322503ca -
Trigger Event:
push
-
Statement type:
File details
Details for the file anki_addon_release-0.2.3-py3-none-any.whl.
File metadata
- Download URL: anki_addon_release-0.2.3-py3-none-any.whl
- Upload date:
- Size: 27.8 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 |
ce4f1cb52fbb19ac2e4b038b7687695c961328fa262c1f62c4af7bdc10c87b06
|
|
| MD5 |
2295ca6d4eb6e5aa0b3d00b8a2395fad
|
|
| BLAKE2b-256 |
f45f22cca48ed37d5c3783c035e2430b5053779266ae3d79a31d6f6de34dfc46
|
Provenance
The following attestation bundles were made for anki_addon_release-0.2.3-py3-none-any.whl:
Publisher:
release.yml on elvis-sik/anki-addon-release
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anki_addon_release-0.2.3-py3-none-any.whl -
Subject digest:
ce4f1cb52fbb19ac2e4b038b7687695c961328fa262c1f62c4af7bdc10c87b06 - Sigstore transparency entry: 2059971354
- Sigstore integration time:
-
Permalink:
elvis-sik/anki-addon-release@37a849b0f1837293adc182d4828103cf322503ca -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/elvis-sik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@37a849b0f1837293adc182d4828103cf322503ca -
Trigger Event:
push
-
Statement type: