Skip to main content

Release helper for Anki add-ons and shared decks.

Project description

anki-addon-release

Python 3.11+ Source on GitHub

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 .ankiaddon archive
  • 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 listing metadata, 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 calls 1Password, reads password-manager references, or stores AnkiWeb credentials in config. If you want automated login, resolve secrets outside the process and expose plain environment variables only for the command that needs them:

export ANKIWEB_EMAIL="$(op read op://Private/AnkiWeb/email)"
export ANKIWEB_PASSWORD="$(op read op://Private/AnkiWeb/password)"

Then reference the variable names in project config:

[tool.anki-addon-release.ankiweb]
login_email_env = "ANKIWEB_EMAIL"
login_password_env = "ANKIWEB_PASSWORD"

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"
title = "Study Triage"
support_url = "https://github.com/example/study-triage"
description_file = "README.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.

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 pyproject.toml, 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"
title = "Geography Deck"
tags = "geography maps"
support_url = "https://github.com/example/geography-deck"
description_file = "release/ankiweb-description.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_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.

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.

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 metadata
  • browser-checklist.md: human browser checklist
  • codex-browser-prompt.md: prompt for a Codex browser-operator session
  • description.txt and changelog.txt when 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.

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.0
git push origin v0.2.0

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

anki_addon_release-0.2.0.tar.gz (31.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anki_addon_release-0.2.0-py3-none-any.whl (24.7 kB view details)

Uploaded Python 3

File details

Details for the file anki_addon_release-0.2.0.tar.gz.

File metadata

  • Download URL: anki_addon_release-0.2.0.tar.gz
  • Upload date:
  • Size: 31.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for anki_addon_release-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c483a459733a34f6775785137d1b62d8d166b7876a785f78545ab234ef2ebf67
MD5 068cf634576addbeda4d2e96226a73fa
BLAKE2b-256 fa4fd7cecb676eec5a93b259d70b19d97e55f75e6aa9f1eee23bec3f65e140c4

See more details on using hashes here.

Provenance

The following attestation bundles were made for anki_addon_release-0.2.0.tar.gz:

Publisher: release.yml on elvis-sik/anki-addon-release

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file anki_addon_release-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for anki_addon_release-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 66a3221663f2dbe91ee6f2b8f785ac9ebf3b712df46894d73f145f25bc9e48b7
MD5 9dc77ed69f3ec78512563720745ea8b3
BLAKE2b-256 d5ba86053ae89b0248d7c392d18817912153078fb2783c3f1e5595db1a91fa58

See more details on using hashes here.

Provenance

The following attestation bundles were made for anki_addon_release-0.2.0-py3-none-any.whl:

Publisher: release.yml on elvis-sik/anki-addon-release

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page