build: derive package version from git via setuptools-scm

Replace hatchling and the static version with setuptools + setuptools-scm,
following Kaya's pre-uv packaging setup: dynamic version, generated
_version.py, and release/* tag prefix. The Docker builder now includes git
and the .git directory so the image is versioned from the checkout. CI
checks out full history and tags for the tag-triggered builds.
This commit is contained in:
2026-10-22 04:03:08 +00:00
parent 9185b5fa05
commit 5db454f8c8
8 changed files with 40 additions and 9 deletions
+5 -1
View File
@@ -1,4 +1,3 @@
.git/
.gitignore
.venv/
__pycache__/
@@ -15,3 +14,8 @@ Dockerfile
!README.md
xmcp.yml
xmcp.example.yml
# Generated at build time
src/xmcp/_version.py
*.egg-info/
dist/
+4
View File
@@ -11,6 +11,10 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v6
with:
# setuptools-scm needs tags and history to derive the package version.
fetch-depth: 0
fetch-tags: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea container registry
+6
View File
@@ -10,3 +10,9 @@ xmcp.yml
xmcp.local.yml
.env
.env.*
# Generated version metadata and build artifacts
src/xmcp/_version.py
*.egg-info/
build/
dist/
+6 -1
View File
@@ -6,6 +6,9 @@
FROM python:3.14-alpine3.24 AS builder
COPY --from=ghcr.io/astral-sh/uv:0.13.1 /uv /uvx /bin/
# setuptools-scm derives the package version from git history and tags.
RUN apk add --no-cache git
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=never
@@ -17,9 +20,11 @@ COPY pyproject.toml uv.lock .python-version ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-install-project
# Then install the project itself as a regular wheel.
# Then install the project itself as a regular wheel. The git metadata is
# required by setuptools-scm to derive the package version.
COPY README.md ./
COPY src ./src
COPY .git ./.git
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-editable
+5
View File
@@ -220,6 +220,11 @@ git push origin release/0.2.0
This publishes `gitea.woggioni.net/woggioni/xmcp:0.2.0` and
`gitea.woggioni.net/woggioni/xmcp:latest`.
The Python package version is derived from the pushed `release/*` tag with
[setuptools-scm](https://setuptools-scm.readthedocs.io): `release/0.2.0`
produces package version `0.2.0`. Builds from untagged commits carry a PEP 440
development version (for example `0.2.1.dev3+gabc1234`).
## Project layout
```
+11 -5
View File
@@ -1,6 +1,6 @@
[project]
name = "xmcp"
version = "0.2.0"
dynamic = ["version"]
description = "MCP server generated from an OpenAPI specification, built with FastMCP"
readme = "README.md"
requires-python = ">=3.14"
@@ -14,8 +14,14 @@ dependencies = [
xmcp = "xmcp.server:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
requires = ["setuptools>=61.0", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"
[tool.hatch.build.targets.wheel]
packages = ["src/xmcp"]
[tool.setuptools.packages.find]
where = ["src"]
[tool.setuptools_scm]
version_file = "src/xmcp/_version.py"
[tool.setuptools_scm.tag]
prefix = "release/"
+3 -1
View File
@@ -1,3 +1,5 @@
"""xmcp: an MCP server generated from an OpenAPI specification with FastMCP."""
__version__ = "0.2.0"
from ._version import __version__
__all__ = ["__version__"]
Generated
-1
View File
@@ -1231,7 +1231,6 @@ wheels = [
[[package]]
name = "xmcp"
version = "0.2.0"
source = { editable = "." }
dependencies = [
{ name = "fastmcp" },