Document the two publish channels (aegis-gitea-mcp from main, aegis-gitea-mcp-dev from dev), install commands for each, that both share the aegis_gitea_mcp module so only one installs per environment, and the merge-driven stable release flow (bump version -> PR into dev -> promote dev to main; re-pushing main at the same version is a --check-url no-op).
4.6 KiB
Packaging & publishing
AegisGitea-MCP is built with uv and published to
the self-hosted Gitea package registry on every merge. There are two channels:
| Channel | Package | Published on | Versioning |
|---|---|---|---|
| stable | aegis-gitea-mcp |
merge to main |
X.Y.Z |
| dev | aegis-gitea-mcp-dev |
merge to dev |
X.Y.Z.devN (N = CI run number, always unique) |
Both channels build from the same source and install the same import module,
aegis_gitea_mcp, with the same two console scripts. They differ only in the
distribution name and the version. Install one or the other in a given
environment, not both — they would collide on the module.
Distribution layout
Each channel ships one distribution with two console scripts and one optional extra (the stable and dev packages are identical here — only the dist name and version differ):
| Console script | Entry point | Requires |
|---|---|---|
aegis-gitea-mcp |
aegis_gitea_mcp.stdio_app:main |
core only |
aegis-gitea-mcp-server |
aegis_gitea_mcp.server_entry:main |
[server] extra |
- Core (default install):
httpx,pydantic,pydantic-settings,PyYAML,python-dotenv,structlog,mcp. Enough to run the local stdio server. [server]extra:fastapi,uvicorn[standard],PyJWT[crypto],python-multipart. The public HTTP/OAuth server.
The aegis-gitea-mcp-server entry point degrades gracefully: invoked without
the web stack it prints install 'aegis-gitea-mcp[server]' instead of a
ModuleNotFoundError traceback.
Build locally
uv build
# -> dist/aegis_gitea_mcp-<version>-py3-none-any.whl
# -> dist/aegis_gitea_mcp-<version>.tar.gz
Smoke-test the local stdio server from the built wheel:
GITEA_URL=https://git.hiddenden.cafe GITEA_TOKEN=<pat> \
uvx --from ./dist/aegis_gitea_mcp-*.whl aegis-gitea-mcp
Install from the Gitea registry
Stable (aegis-gitea-mcp, published from main):
uv pip install \
--index-url https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ \
aegis-gitea-mcp
# or run it one-off without installing into the environment:
uvx --index https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ aegis-gitea-mcp
Dev (aegis-gitea-mcp-dev, published from dev — newest pre-release build):
uv pip install \
--index-url https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ \
aegis-gitea-mcp-dev
(With pip, use --index-url the same way.) Both packages expose the same
aegis-gitea-mcp / aegis-gitea-mcp-server console scripts and the same
aegis_gitea_mcp import module, so install one or the other per environment.
Publishing channels
Publishing is merge-driven, not tag-driven. The publish workflow
(.gitea/workflows/publish.yml) triggers on a push to dev or main, runs
lint + tests first, then builds with uv and publishes to the Gitea PyPI
registry. The package name + version are patched into pyproject.toml at build
time only (never committed):
devpush →aegis-gitea-mcp-devatX.Y.Z.dev<run_number>. The CI run number is monotonic, so every merge todevyields a unique pre-release.mainpush →aegis-gitea-mcpat the plainX.Y.Zfrompyproject.toml. Publishing usesuv publish --check-url, so amainpush that did not bump the version is a clean no-op (the existing files are skipped) rather than a 409.
Cutting a stable release
- Bump
versioninpyproject.toml(e.g.0.2.0→0.3.0) on a branch offdev. - Open a PR into
devand merge it (this publishes a freshaegis-gitea-mcp-devbuild). - Promote
dev→main. The push tomainpublishes the new stableaegis-gitea-mcp X.Y.Z.
Re-pushing main at an unchanged version is harmless — --check-url skips the
already-published files.
Required CI secrets
The publish job reuses the existing REGISTRY_TOKEN Actions secret — the same
PAT (write:package) that docker.yml uses to push images — so no new secret is
needed. The token authenticates as its owning Gitea user, so GITHUB_ACTOR is the
username and the token is the password.
| Secret | Purpose |
|---|---|
REGISTRY_TOKEN |
PAT with write:package; used for both image and package pushes |
If the secret is absent the job fails loudly rather than publishing anonymously.
Publishing to public PyPI is intentionally not configured. A second, separately-gated
uv publishstep would be required and is left as a commented stub in the workflow.