diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0c49f25..3c5891e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,5 +1,7 @@ name: Lint - +permissions: + contents: read + on: push: branches: [main] @@ -22,12 +24,12 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: "3.14" - name: Install dependencies run: | uv sync - uv pip install ruff mypy pytest + uv add ruff mypy pytest - name: Run ruff check run: uv run ruff check src/ tests/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8a00909..cc09a02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,7 @@ name: Tests +permissions: + contents: read + pull-requests: write on: push: @@ -12,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"] steps: - uses: actions/checkout@v4 @@ -33,9 +36,15 @@ jobs: - name: Run tests with coverage run: uv run pytest --cov=toon_format --cov-report=xml --cov-report=term --cov-report=html --cov-fail-under=85 + - name: Run linting + run: uv run ruff check . + + - name: Run type checking + run: uv run mypy src/toon_format + - name: Upload coverage reports as artifact uses: actions/upload-artifact@v4 - if: matrix.python-version == '3.12' + if: matrix.python-version == '3.14' with: name: coverage-reports path: | @@ -45,7 +54,7 @@ jobs: - name: Coverage comment on PR uses: py-cov-action/python-coverage-comment-action@v3 - if: matrix.python-version == '3.12' && github.event_name == 'pull_request' + if: matrix.python-version == '3.14' && github.event_name == 'pull_request' with: GITHUB_TOKEN: ${{ github.token }} MINIMUM_GREEN: 90 diff --git a/PUBLISHING.md b/PUBLISHING.md index fa2c639..7fbd8f1 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -15,7 +15,7 @@ Create accounts on both platforms: The publish workflow uses GitHub environments with trusted publishing (no API tokens needed!). -#### Set up environments in GitHub: +#### Set up environments in GitHub 1. Go to your repository Settings > Environments 2. Create two environments: @@ -26,9 +26,10 @@ The publish workflow uses GitHub environments with trusted publishing (no API to - Required reviewers (for production releases) - Wait timer (optional delay before deployment) -#### Configure Trusted Publishers: +#### Configure Trusted Publishers **For TestPyPI:** + 1. Log in to https://test.pypi.org 2. Go to Account Settings > Publishing 3. Add a new pending publisher: @@ -39,6 +40,7 @@ The publish workflow uses GitHub environments with trusted publishing (no API to - Environment: `testpypi` **For PyPI:** + 1. Log in to https://pypi.org 2. Go to Account Settings > Publishing 3. Add a new pending publisher: @@ -61,13 +63,18 @@ The publish workflow uses GitHub environments with trusted publishing (no API to 2. **Update changelog** (if exists) or create release notes 3. **Run tests locally**: + + The `tox` package will run tests for all supported Python versions. Run this with `uv run tox`. + ```bash + uv run tox uv run pytest uv run ruff check . uv run mypy src/toon_format ``` 4. **Build and test locally**: + ```bash # Clean previous builds rm -rf dist/ build/ *.egg-info @@ -88,6 +95,7 @@ The publish workflow uses GitHub environments with trusted publishing (no API to ### Step 2: Commit and Tag 1. **Commit version changes**: + ```bash git add pyproject.toml src/toon_format/__init__.py git commit -m "Bump version to X.Y.Z" @@ -131,7 +139,7 @@ Before publishing to production PyPI, test on TestPyPI: ### Step 5: Verify the Release -1. **Check PyPI**: https://pypi.org/project/toon_format/ +1. **Check PyPI**: [https://pypi.org/project/toon_format/](https://pypi.org/project/toon_format/) 2. **Test installation**: ```bash pip install toon_format @@ -147,6 +155,7 @@ Before publishing to production PyPI, test on TestPyPI: ### Build fails with "metadata missing" This is usually a configuration issue in `pyproject.toml`. Verify: + - All required fields are present (name, version, description, etc.) - Project URLs are properly formatted - Author email is valid @@ -154,6 +163,7 @@ This is usually a configuration issue in `pyproject.toml`. Verify: ### Trusted publishing fails If the trusted publisher configuration fails: + 1. Verify the environment name matches exactly 2. Check that the repository owner and name are correct 3. Ensure the workflow file path is correct (`publish.yml`) @@ -162,6 +172,7 @@ If the trusted publisher configuration fails: ### Package already exists on PyPI PyPI doesn't allow overwriting published versions. You must: + 1. Increment the version number 2. Create a new tag 3. Publish the new version @@ -174,7 +185,7 @@ Follow [Semantic Versioning](https://semver.org/): - **MINOR version** (0.X.0): New functionality, backward compatible - **PATCH version** (0.0.X): Bug fixes, backward compatible -### Agreed Roadmap (from Discussion #18): +### Agreed Roadmap (from Discussion #18) - **0.8.x** - Initial code set, tests, documentation, migration from toon-llm - **0.9.x** - Serializer, spec compliance, publishing to PyPI (test and prod) @@ -182,6 +193,7 @@ Follow [Semantic Versioning](https://semver.org/): - **1.0.0** - Official stable release 🎉 Examples: + - `0.9.0-beta.1` - First beta release for testing - `0.9.0-beta.2` - Second beta with fixes - `0.9.0` - First minor release with new features @@ -192,7 +204,8 @@ Examples: Before each release, verify: -- [ ] All tests pass (`uv run pytest`) +- [ ] All tests pass for the latest stable Python version (`uv run pytest`) +- [ ] All tests pass for **all** supported Python versions (`uv run tox`) - [ ] Linting passes (`uv run ruff check .`) - [ ] Type checking passes (`uv run mypy src/toon_format`) - [ ] Version updated in `pyproject.toml` and `src/toon_format/__init__.py` diff --git a/pyproject.toml b/pyproject.toml index 8c8824b..1531740 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,9 +6,13 @@ readme = "README.md" authors = [ { name = "Johann Schopplich", email = "hello@johannschopplich.com" } ] -requires-python = ">=3.8" +requires-python = ">=3.10" dependencies = [ - "typing-extensions>=4.0.0; python_version < '3.10'", + "mypy>=1.18.2", + "pytest>=9.0.0", + "ruff>=0.14.4", + "tox>=4.25.0", + "tox-uv>=1.13.1", ] license = { text = "MIT" } keywords = ["toon", "serialization", "llm", "data-format", "token-efficient"] @@ -17,8 +21,6 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -65,7 +67,7 @@ show_missing = true skip_covered = false [tool.ruff] -target-version = "py38" +target-version = "py310" line-length = 100 [tool.ruff.lint] @@ -83,7 +85,7 @@ quote-style = "double" indent-style = "space" [tool.mypy] -python_version = "3.9" +python_version = "3.10" warn_return_any = false warn_unused_configs = true disallow_untyped_defs = false diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..0349f28 --- /dev/null +++ b/tox.ini @@ -0,0 +1,71 @@ +[tox] +requires = + tox>=4 +envlist = py310, py311, py312, py313, py314, py314t, coverage, lint, format-check, type +skip_missing_interpreters = + True + +[testenv] +deps = + pytest>=8.4.2 + pytest-cov>=6.3.0 + pytest-asyncio>=1.1.0 + pytest-mock>=3.15.0 + pytest-timeout>=2.4.0 + pytest-xdist>=3.8.0 +commands = + pytest + +[testenv:py] +description = run tests with the current interpreter (used by CI) +commands = + pytest + +[testenv:fix] +description = run code formatter and linter (auto-fix) +skip_install = true +deps = + pre-commit-uv>=4.1.1 +commands = + pre-commit run --all-files --show-diff-on-failure + +[testenv:lint] +description = run linter via ruff +skip_install = true +deps = + ruff>=0.14.2 +commands = + ruff check {posargs:src tests} + +[testenv:format] +description = run formatter via ruff +skip_install = true +deps = + ruff>=0.14.2 +commands = + ruff format {posargs:src tests} + +[testenv:format-check] +description = check code formatting via ruff +skip_install = true +deps = + ruff>=0.14.2 +commands = + ruff format --check {posargs:src tests} + +[testenv:type] +description = run type checker via mypy +deps = + mypy +commands = + mypy {posargs:src} + +[testenv:dev] +runner = uv-venv-lock-runner +description = dev environment +extras = + dev + test + type +commands = + uv pip tree