Repository navigation
Python 3.11-3.15 foundation: refreshed dependencies, modern toolchain, CI and trusted publishing (0.7.0) - #53
Conversation
- requires-python >=3.11, trove classifiers for 3.11-3.15, keywords, extra project URLs - bump version to 0.7.0 - raise dependency floors to releases that are current in Oct 2026 - typer-slim -> typer (typer-slim is now an empty shim that depends on typer) - drop the shellingham pin from the rich extra (typer depends on it) - pydantic-ai floor moves to 2.x (result.output / usage property API) - remove the dev extra and the extras dependency group, which duplicated the dev dependency group and the rich/ai extras - drop unused mkdocs-material-extensions, add types-pyyaml for mypy - regenerate uv.lock Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntic-ai - cli dev --run-cli: drop type=str from the flag. Click >= 8.2 converts the flag default through the type, so the handler received the truthy string 'False' and re-ran the CLI on every reload even without --run-cli. - cli build: print result.output on failure; Click >= 8.2 no longer mixes stderr into Result.stdout. - rich: import RichGroup from rich_click instead of the deprecated rich_click.rich_group module. - reloader: Thread.setDaemon() is deprecated, pass daemon=True. - ai: pydantic-ai 2.x renamed result.data -> result.output, made usage a property with input_tokens/output_tokens, and requires provider-prefixed model names (gemini-2.0-flash -> google:gemini-2.0-flash). - tests: replace CliRunner.isolated_filesystem() (deprecated in Click 8.5, removed in 9.0) with a tmp_path based fixture, and stop importing the test command under a name pytest tries to collect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ruff: target py311, select E/W/F/I/UP/B/C4/SIM/PIE/RUF, exclude generated code (cliffy/clis, examples/generated) - apply pyupgrade fixes: X | Y unions, builtin generics, collections.abc, typing.Self instead of typing_extensions / bound TypeVar, redundant open modes - sort imports, __all__ and __slots__ - close manifest file handles in 'cli update' and Transformer.load_manifest - chain the ImportError raised by import_module_from_path - drop a cast made redundant by click's TypeIs-typed _is_file_like - pytest: turn Deprecation/PendingDeprecation warnings into errors, with narrow ignores for the two known third-party offenders (pybash, shiv) - mypy: use types-pyyaml instead of ignoring the yaml module No behavior change intended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…publishing - test matrix 3.11-3.15 on actions/checkout v7, setup-uv v10.3.0, codecov v7 - trigger on pull_request and on pyproject.toml / uv.lock / Makefile / workflow changes; pushes only build main (PRs cover branches), stale runs are cancelled - install with uv sync --locked so a stale lockfile fails CI - drop the extras matrix axis: the old default dependency groups installed the extras in both variants, so it never tested a no-extras install - replace the twine/API-token publish workflow with uv build + uv publish using PyPI trusted publishing (OIDC); verifies tag == version and smoke-tests the wheel - docs workflow: install with uv from the lockfile, rebuild on mkdocs.yaml changes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h JSON schema - Makefile: no bare pip/python/cli calls, everything goes through uv; declare all phony targets; clean no longer walks into .venv; drop the no-op shell target - add .python-version (3.13, the newest interpreter every dependency ships wheels for on all platforms) and stop ignoring it - gitignore loaded CLIs, their metadata, test build outputs and ruff cache - limit the sdist to the package, tests and examples (2.0 MB -> 66 kB) - regenerate examples/cliffy_schema.json, which had drifted from the models - README: add supported Python versions badge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent loop warning - click>=8.2.1 and pydantic>=2.12 are what typer / pydantic-ai 2.0 already require; the suite passes on 3.11 with --resolution lowest-direct - mypy>=2.4 (1.18 segfaults on this codebase under Python 3.11) - pytest: ignore the asyncio.get_event_loop() DeprecationWarning raised from inside pydantic-ai's run_sync() on Python 3.12/3.13 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
click types the helper as TypeGuard in 8.2 and TypeIs in later releases, so mypy --strict passed or failed depending on the installed click. Branch on str / PathLike explicitly instead; runtime behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThis foundation release updates cliffy to Python 3.11–3.15 with refreshed locked dependencies and a modern strict-lint/type-check toolchain, applies compatibility fixes for current libraries, streamlines uv-based development and packaging, expands deterministic CI coverage, and introduces tag-validated PyPI trusted publishing plus updated docs deployment. Sequence diagram for trusted PyPI publishingsequenceDiagram
participant Maintainer
participant GitHub as GitHub Actions
participant Build as Build job
participant Publish as Publish job
participant PyPI
Maintainer->>GitHub: Publish release
GitHub->>Build: Checkout and uv build
Build->>Build: uv version --short
Build->>Build: uv run cli --version
Build-->>Publish: dist artifacts
Publish->>PyPI: Request OIDC trusted publishing
PyPI-->>Publish: Grant publisher identity
Publish->>PyPI: uv publish --trusted-publishing always
Flow diagram for locked multi-version CIflowchart TD
Trigger[PR or main changes] --> Matrix[Python 3.11-3.15 matrix]
Matrix --> Sync[uv sync --locked --all-extras]
Sync --> Quality[make lint]
Quality --> Tests[pytest]
Tests --> Coverage{main on Python 3.15?}
Coverage -->|yes| Upload[Upload coverage]
Coverage -->|no| Done[Job complete]
Upload --> Done
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 2 issues
Fixed security issues:
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path=".github/workflows/publish.yaml" line_range="57" />
<code_context>
+ - name: Install uv
+ uses: astral-sh/setup-uv@v10.3.0
+ - name: Publish
+ run: uv publish --trusted-publishing always dist/*
</code_context>
<issue_to_address>
**Partial uploads block workflow retries**
When a publish run fails after PyPI has accepted at least one distribution, if PyPI accepts one distribution from `dist/*` and a later upload fails, the release is left with only part of the build. Retrying the workflow attempts to upload the already accepted file again, which PyPI rejects as a duplicate, so the retry does not complete the release.
Make publishing resumable by detecting already-published files and retrying only missing distributions.
</issue_to_address>
### Comment 2
<location path="pyproject.toml" line_range="31" />
<code_context>
+ "Topic :: Software Development :: Code Generators",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: System :: Shells",
+ "Typing :: Typed",
+]
dependencies = [
</code_context>
<issue_to_address>
**Installed package loses type hints**
When a consumer type-checks code against an installed `cliffy` distribution, downstream type checkers treat `cliffy` as an untyped package, so callers lose its API annotations and strict mypy can reject imports despite the package advertising typed support.
Add a `cliffy/py.typed` marker and ensure it is included in the built wheel and sdist.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 2 findings to address first, and every published GitHub release can trigger an OIDC-authenticated upload to PyPI, so an overly broad publisher trust scope could make unintended releases available to users without a failing check. Reverting the workflow stops future uploads but cannot fully undo a package version already published or downstream installs of it.
Blocking findings: .github/workflows/publish.yaml:57, pyproject.toml:31
Summary
Brings cliffy up to date after ~20 months without changes. It now supports Python 3.11-3.15, and lint,
mypy --strictand all 188 tests pass on each of those versions against the refreshed lockfile. Every other modernization MR is meant to stack on this branch.What changed
Packaging (
pyproject.toml,uv.lock)requires-python >=3.11, classifiers for 3.11-3.15, keywords,license-files, extra project URLs, version0.7.0.click>=8.2.1,deepmerge>=2.0,jinja2>=3.1.6,packaging>=24.2,pybash>=0.3.5,pydantic>=2.12.0,pyyaml>=6.0.2,shiv>=1.0.8,typer>=0.24.0,watchdog>=6.0.0,rich-click>=1.9.0,pydantic-ai>=2.0.0. The suite also passes on 3.11 with--resolution lowest-direct.typer-slimis replaced bytyper:typer-slim0.24 is an empty shim that depends ontyper.uv.lockis regenerated with uv 0.13.jaykv/cliffyandjaykv.github.io/cliffy.github.com/cliffy-sh/cliffy301-redirects tojaykv/cliffy, andcliffy-sh.github.io/cliffyreturns 404.Compatibility fixes
cli dev --run-cli: removedtype=strfrom the flag. With Click >= 8.2 the handler received the truthy string'False', so the CLI re-ran on every reload even without--run-cli.cli build: printsresult.outputon failure, because Click >= 8.2 no longer mixes stderr intoResult.stdout.rich_click.rich_groupimport (deprecated) replaced byfrom rich_click import RichGroup.Thread.setDaemon()replaced bydaemon=True.result.data->result.output;result.usage().request_tokens/response_tokens->result.usage.input_tokens/output_tokens.ManifestOrCLI.convertno longer uses click's private_is_file_like, whose typing differs between click releases and mademypy --strictdepend on the installed click.CliRunner.isolated_filesystem()(deprecated in Click 8.5) replaced by atmp_path-basedisolated_fsfixture;Result.stdout->Result.outputin one assertion;test_commandimported under an alias so pytest stops trying to collect it.Modernization (no behavior change intended)
target-version = "py311", rulesE, W, F, I, UP, B, C4, SIM, PIE, RUF; generated code (cliffy/clis,examples/generated) is excluded.X | Yunions, builtin generics,collections.abc,typing.Self(no moretyping_extensionsimport), sorted imports.cli updateandTransformer.load_manifest.mypy --strictusestypes-pyyamlinstead of ignoring theyamlmodule.DeprecationWarningandPendingDeprecationWarninginto errors. Three narrow ignores cover third-party code: pybash's positionalre.subcount, shiv'sutcfromtimestamp, and pydantic-ai'sasyncio.get_event_loop()on 3.12/3.13.CI (
.github/workflows)python-app.yaml: matrix 3.11-3.15 onactions/checkout@v7,astral-sh/setup-uv@v10.3.0(setup-uv publishes no floating major tag) andcodecov/codecov-action@v7.pull_requestand on changes topyproject.toml,uv.lock,Makefileand the workflow itself. Pushes only buildmain. It installs withuv sync --locked, so a stale lockfile fails CI.extras: [true, false]matrix axis is removed. The old default dependency groups installed the extras in both variants, so it never tested a no-extras install.python-publish.yaml(twine +PYPI_API_TOKEN) is replaced bypublish.yaml:uv build, a tag == version check, a wheel smoke test, thenuv publish --trusted-publishing alwayswithid-token: writein apypienvironment.docs.yaml: installs from the lockfile with uv, and also rebuilds onmkdocs.yamlchanges.Repo hygiene
pip,pythonorcli), all targets are declared phony, andcleanno longer walks into.venv..python-versionadded (3.13) and no longer gitignored..gitignorenow coverscliffy/clis/,cliffy/metadata/, the test build directories and.ruff_cache/.examples/cliffy_schema.jsonregenerated. The committed file was already stale onmain; the models on this branch produce the same schema asmain's models.examples/generated/*not regenerated: the only difference is import order, which varies from run to run.Breaking changes
typer-slim->typer.richandshellinghamare now always installed.cliffy[dev]extra removed. It duplicated thedevdependency group; useuv sync. Theextrasdependency group is removed too; useuv sync --all-extras.cliffy[ai]now needs pydantic-ai >= 2.0. Model names must carry a provider prefix, so the default changed fromgemini-2.0-flashtogoogle:gemini-2.0-flash. Scripts passing-m gemini-...must add the prefix.cli dev --run-clipasses a realbooltoReloader.watchinstead of the strings'True'/'False'.PYPI_API_TOKEN.Maintainer action required
cliffy-> Publishing), add a GitHub trusted publisher: ownerjaykv, repositorycliffy, workflowpublish.yaml, environmentpypi. Releases will fail until this exists. ThePYPI_API_TOKENsecret can then be deleted.0.7.0orv0.7.0); the workflow checks this.How verified
Run locally on macOS arm64 with uv 0.13.0, each Python in its own environment synced with
uv sync --locked --all-extras:origin/mainwith the old lockfile on 3.11: 188 passed.--resolution lowest-direct): ruff,mypy --strictand 188 tests pass.uv buildand the wheel smoke test (cli --version->cli, version 0.7.0) pass;mkdocs buildwith only the docs group passes;uv lock --checkpasses.action.yml. They have not run on GitHub.🤖 Generated with Claude Code
Summary by Sourcery
Modernize cliffy for Python 3.11–3.15 and establish a current uv-based toolchain, CI pipeline, and trusted release process.
New Features:
Bug Fixes:
Enhancements:
Build:
CI:
Deployment:
Documentation:
Tests:
Chores: