# Packaging & publishing AegisGitea-MCP is built with [`uv`](https://docs.astral.sh/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 ```bash uv build # -> dist/aegis_gitea_mcp--py3-none-any.whl # -> dist/aegis_gitea_mcp-.tar.gz ``` Smoke-test the local stdio server from the built wheel: ```bash GITEA_URL=https://git.hiddenden.cafe GITEA_TOKEN= \ uvx --from ./dist/aegis_gitea_mcp-*.whl aegis-gitea-mcp ``` ## Install from the Gitea registry **Stable** (`aegis-gitea-mcp`, published from `main`): ```bash 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): ```bash 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): - **`dev` push** → `aegis-gitea-mcp-dev` at `X.Y.Z.dev`. The CI run number is monotonic, so every merge to `dev` yields a unique pre-release. - **`main` push** → `aegis-gitea-mcp` at the plain `X.Y.Z` from `pyproject.toml`. Publishing uses `uv publish --check-url`, so a `main` push that did **not** bump the version is a clean no-op (the existing files are skipped) rather than a 409. ### Cutting a stable release 1. Bump `version` in `pyproject.toml` (e.g. `0.2.0` → `0.3.0`) on a branch off `dev`. 2. Open a PR into `dev` and merge it (this publishes a fresh `aegis-gitea-mcp-dev` build). 3. Promote `dev` → `main`. The push to `main` publishes the new stable `aegis-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 publish` step would be required and is left as a > commented stub in the workflow.