diff --git a/README.md b/README.md index 0352757..b463f5c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ uv tool install git+https://github.com/databricks/ucode ``` +Check your version with `ucode --version`. Between releases this looks like +`0.1.0+14.g93986a8` — the trailing `g` is the exact commit the build came +from, so include it when reporting a bug. + --- ## Usage diff --git a/pyproject.toml b/pyproject.toml index d93f47f..387c76e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,14 @@ [build-system] -requires = ["uv_build>=0.11.0"] -build-backend = "uv_build" +requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"] +build-backend = "hatchling.build" [project] name = "ucode" -version = "0.1.0" +# Version is derived from git tags at build time (see [tool.uv-dynamic-versioning]). +# On a tag it is the plain tag (e.g. `0.1.0`); between tags it carries the commit +# count and short hash (e.g. `0.1.0+14.g93986a8`) so a reported version maps back +# to an exact commit. +dynamic = ["version"] description = "Bootstrap Codex against a Databricks workspace with minimal setup." readme = "README.md" requires-python = ">=3.12" @@ -29,8 +33,22 @@ tracing = ["mlflow[databricks]>=3.4"] [project.scripts] ucode = "ucode.cli:main" -[tool.uv.build-backend] -module-name = "ucode" +[tool.hatch.build.targets.wheel] +packages = ["src/ucode"] + +[tool.hatch.version] +source = "uv-dynamic-versioning" + +[tool.uv-dynamic-versioning] +vcs = "git" +# On a tagged commit: plain tag (e.g. `0.1.0`). Between tags: append the commit +# count and short hash as a local segment (e.g. `0.1.0+14.g93986a8`) so a +# reported version points back to an exact commit for bug reports. +format-jinja = "{% if distance == 0 %}{{ base }}{% else %}{{ base }}+{{ distance }}.g{{ commit }}{% endif %}" +# Used only when git metadata is unavailable at build time (e.g. building from a +# source archive with no `.git`). The normal install paths clone the repo, so +# this is a last-resort so builds degrade instead of hard-failing. +fallback-version = "0.0.0" [dependency-groups] dev = [ diff --git a/src/ucode/cli.py b/src/ucode/cli.py index f541079..52047c7 100644 --- a/src/ucode/cli.py +++ b/src/ucode/cli.py @@ -875,6 +875,30 @@ def revert() -> int: app.add_typer(mcp_app, name="mcp", help="MCP servers exposed by ucode.") +def _version_callback(value: bool) -> None: + if value: + from ucode.telemetry import ucode_version + + print(ucode_version()) + raise typer.Exit() + + +@app.callback() +def _main( + version: Annotated[ + bool, + typer.Option( + "--version", + "-V", + help="Show the ucode version and exit.", + callback=_version_callback, + is_eager=True, + ), + ] = False, +) -> None: + """Configure and launch coding agents through Databricks AI Gateway.""" + + @mcp_app.command("web-search") def mcp_web_search_cmd() -> None: """Run the web_search MCP server over stdio. Invoked as a subprocess by Claude Code.""" diff --git a/tests/test_cli.py b/tests/test_cli.py index dc73a75..7d374f5 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -100,6 +100,23 @@ def test_configure_help_lists_agents_flag(self): assert "--workspaces" in output +class TestVersion: + @pytest.mark.parametrize("flag", ["--version", "-V"]) + def test_prints_version_and_exits(self, flag): + result = runner.invoke(app, [flag]) + assert result.exit_code == 0 + # Matches the derived version reported by importlib.metadata — either a + # real string like "0.1.0" / "0.1.0+2.g93986a8" or the "unknown" fallback. + assert _strip_ansi(result.output).strip() != "" + + def test_matches_telemetry_version(self): + from ucode.telemetry import ucode_version + + result = runner.invoke(app, ["--version"]) + assert result.exit_code == 0 + assert ucode_version() in _strip_ansi(result.output) + + def _patch_launch(tool: str): """Return a context-manager stack that makes _launch_tool a no-op.