Deployment: PyPI and uvx¶
Once workflow logic is stable, publishing it as a PyPI package makes
distribution trivial. Alfred users install the workflow once and upgrade by
changing a single version string — no virtualenv management, no pip install,
no per-machine setup.
Why uvx¶
uvx (part of uv) downloads a package from
PyPI, caches it in ~/.cache/uv/, and runs the requested entry point — all
in one command. From the workflow’s perspective it behaves like a normal
binary:
~/.local/bin/uvx --from afwf==1.0.1 \
afwf-examples search-bookmarks --query '{query}'
On the first call uvx downloads afwf==1.0.1 and its dependencies.
Subsequent calls reuse the cache — the latency added to Alfred invocations is
negligible after the first run.
The version pin (afwf==1.0.1) guarantees that every machine runs the same
code. Rolling back to a previous version is a one-line change in Alfred’s
Script field.
Package Entry Point¶
The entry point is declared in pyproject.toml:
[project.scripts]
afwf-examples = "afwf.examples.cli:main"
uvx --from afwf makes afwf-examples available as a command for the
duration of that invocation. No installation into /usr/local/bin or
~/.local/bin occurs.
If your workflow uses optional extras (fuzzy matching, disk cache), declare
them in the uvx call:
~/.local/bin/uvx --from "afwf[fuzzy,cache]==1.0.1" \
afwf-examples search-bookmarks --query '{query}'
Release Checklist¶
Bump the version in
afwf/_version.pyand confirmpyproject.tomlreads from it (or update both if they are separate).Update ``release-history.rst`` with the change summary for the new version.
Run the full test suite to confirm nothing is broken:
mise run cov
Build and publish to PyPI. The project uses the standard
uvworkflow:uv build uv publish
CI (
mise run publishor the GitHub Actions workflow) automates this on tag push.Update the Script field in Alfred’s workflow for each Script Filter that needs the new version:
# Before ~/.local/bin/uvx --from afwf==1.0.0 afwf-examples search-bookmarks --query '{query}' # After ~/.local/bin/uvx --from afwf==1.0.1 afwf-examples search-bookmarks --query '{query}'
This change goes into
info.plistin thescriptfield of each affected Script Filter node. Commit it so the repo stays in sync with the released version.Export and redistribute the updated
info.plistas a new.alfredworkflowbundle if you distribute the workflow to other users.
Dev vs Production Side by Side¶
Aspect |
Dev (local venv) |
Production (uvx) |
|---|---|---|
Script field |
|
|
Python environment |
Project’s |
uvx cache ( |
Code changes |
Immediate (no reinstall needed) |
Requires new PyPI release + version bump |
|
|
uvx-managed interpreter; |
Extras |
Installed via |
|
Keeping info.plist in Sync¶
info.plist in the repository should always reflect the dev invocation
paths so that cloning the repo and running mise run inst gives a working
workflow immediately. The production uvx paths live only in Alfred’s
installed copy of the workflow, updated manually when a new release is
published.
A useful convention: keep a comment or a separate info.plist.production
snippet in the repo documenting the production script strings, so that
updating Alfred after a release is a straightforward find-and-replace.