The --env option swallowed the server name, so the stdio command is reordered. uvx now always names the project registry because the package name is not registered on public PyPI. README gains a Known limitations section, the READ_ONLY wording is corrected, packaging.md warns about --extra-index-url and the Le Chat admin requirement is mentioned.
113 lines
4.7 KiB
Markdown
113 lines
4.7 KiB
Markdown
# Packaging and publishing
|
|
|
|
AegisGitea MCP is built with [uv](https://docs.astral.sh/uv/) (hatchling backend) and published
|
|
to the self-hosted Gitea package registry on every merge. There are two channels:
|
|
|
|
| Channel | Package | Published on | Version |
|
|
|---|---|---|---|
|
|
| stable | `aegis-gitea-mcp` | push to `main` | `X.Y.Z` from `pyproject.toml` |
|
|
| dev | `aegis-gitea-mcp-dev` | push to `dev` | `X.Y.Z.dev<N>`, N is the CI run number |
|
|
|
|
Both build from the same source and install the same import module, `aegis_gitea_mcp`, and the
|
|
same console script, `aegis-gitea-mcp`. They differ only in the distribution name and version.
|
|
Install one or the other in an environment, not both, because they would collide on the module.
|
|
|
|
## What is in the package
|
|
|
|
One distribution, one console script:
|
|
|
|
| Console script | Entry point |
|
|
|---|---|
|
|
| `aegis-gitea-mcp` | `aegis_gitea_mcp.cli:main` (subcommands `serve`, `setup`, `version`; `serve` is the default) |
|
|
|
|
Dependencies are the `mcp` SDK (2.3 or newer, below 3), `httpx`, `pydantic`, `pydantic-settings`,
|
|
`jsonschema`, `cryptography` and `uvicorn`. There are no extras: both transports and per-user
|
|
OAuth are in the default install. The generated tool catalog (`catalog/tools.json`) ships inside
|
|
the package.
|
|
|
|
## Build locally
|
|
|
|
```bash
|
|
uv build
|
|
# dist/aegis_gitea_mcp-<version>-py3-none-any.whl
|
|
# dist/aegis_gitea_mcp-<version>.tar.gz
|
|
```
|
|
|
|
Smoke-test the built wheel over stdio:
|
|
|
|
```bash
|
|
GITEA_URL=https://git.example.com GITEA_TOKEN=<your-gitea-token> \
|
|
uvx --from ./dist/aegis_gitea_mcp-*.whl aegis-gitea-mcp
|
|
```
|
|
|
|
Dependencies are pinned in `uv.lock`; `uv sync` reproduces the environment.
|
|
|
|
## Install from the Gitea registry
|
|
|
|
Index URL: `https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/`
|
|
|
|
Stable:
|
|
|
|
```bash
|
|
# run it without installing into an environment (recommended):
|
|
uvx --index https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ aegis-gitea-mcp
|
|
|
|
# or install it into an environment:
|
|
uv pip install --index https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ aegis-gitea-mcp
|
|
```
|
|
|
|
Dev (newest pre-release build, package `aegis-gitea-mcp-dev`):
|
|
|
|
```bash
|
|
uvx --index https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ --prerelease allow aegis-gitea-mcp-dev
|
|
```
|
|
|
|
The registry holds only this project's packages and does not proxy PyPI, so it is added with
|
|
`--index` and the dependencies (`mcp`, `httpx`, ...) still come from PyPI. uv's default index
|
|
strategy (`first-index`) takes a package from the first index that has it and never mixes
|
|
versions across indexes, so with `--index` the name `aegis-gitea-mcp` is always taken from
|
|
this registry, even if somebody registers it on PyPI.
|
|
|
|
Do not use `--extra-index-url` with `pip`. It merges the indexes, and `pip` picks the highest
|
|
version from either: whoever publishes `aegis-gitea-mcp` on PyPI with a higher version number
|
|
wins (dependency confusion), and the package would run with your Gitea token in its
|
|
environment. With `pip`, fetch the wheel from this registry alone, then install it:
|
|
|
|
```bash
|
|
pip download --no-deps --index-url https://git.hiddenden.cafe/api/packages/Hiddenden/pypi/simple/ aegis-gitea-mcp -d wheels/
|
|
pip install wheels/aegis_gitea_mcp-*.whl
|
|
```
|
|
|
|
The name is not registered on public PyPI. Publishing there is intentionally not configured, but
|
|
the owner should reserve both names (`aegis-gitea-mcp`, `aegis-gitea-mcp-dev`) on PyPI so nobody
|
|
else can take them. Until then every install command above must name the index.
|
|
|
|
To run from a clone instead of a registry:
|
|
`uv run --directory /path/to/AegisGitea-MCP aegis-gitea-mcp serve`.
|
|
|
|
## Publishing
|
|
|
|
Publishing is merge-driven, not tag-driven. `.gitea/workflows/publish.yml` triggers on a push to
|
|
`dev` or `main`, runs the lint and test gates first, builds with `uv build`, and publishes with
|
|
`uv publish`. The package name and version are patched into `pyproject.toml` at build time only
|
|
and never committed.
|
|
|
|
- A push to `dev` publishes `aegis-gitea-mcp-dev` at `X.Y.Z.dev<run number>`, unique per run.
|
|
- A push to `main` publishes `aegis-gitea-mcp` at the plain `X.Y.Z`. `uv publish --check-url`
|
|
skips files that already exist, so a push that did not bump the version is a clean no-op.
|
|
|
|
### Cutting a stable release
|
|
|
|
1. Bump `version` in `pyproject.toml` on a branch off `dev`.
|
|
2. Open a pull request into `dev` and merge it. That publishes a fresh dev build.
|
|
3. Promote `dev` to `main`. The push to `main` publishes the stable version.
|
|
|
|
### CI secret
|
|
|
|
Publishing uses the `REGISTRY_TOKEN` Actions secret, a personal access token with
|
|
`write:package`, the same one the image build uses. The token authenticates as its owner, so the
|
|
job's actor is the username and the token is the password. If the secret is missing the job
|
|
fails instead of publishing anonymously.
|
|
|
|
Publishing to public PyPI is intentionally not configured.
|