diff --git a/project_template/.github/workflows/cd.yml b/project_template/.github/workflows/cd.yml index addabaa..d291c04 100644 --- a/project_template/.github/workflows/cd.yml +++ b/project_template/.github/workflows/cd.yml @@ -1,36 +1,27 @@ name: CD on: - workflow_dispatch: - pull_request: - push: - branches: - - main release: types: - published jobs: dist: - needs: [pre-commit] - name: Distribution build + name: Build distribution runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Build sdist and wheel - run: pipx run build - - - uses: actions/upload-artifact@v3 + - uses: astral-sh/setup-uv@v5 with: + enable-cache: true + - run: uv build + - uses: actions/upload-artifact@v4 + with: + name: dist path: dist - - name: Check products - run: pipx run twine check dist/* - publish: needs: [dist] name: Publish to PyPI @@ -38,16 +29,13 @@ jobs: permissions: id-token: write runs-on: ubuntu-latest - if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: artifact + name: dist path: dist - - uses: pypa/gh-action-pypi-publish@release/v1 - if: github.event_name == 'release' && github.event.action == 'published' with: # Remove this line to publish to PyPI repository-url: https://test.pypi.org/legacy/ diff --git a/project_template/.github/workflows/ci.yml b/project_template/.github/workflows/ci.yml index 3ed5cdc..a08cdf0 100644 --- a/project_template/.github/workflows/ci.yml +++ b/project_template/.github/workflows/ci.yml @@ -1,54 +1,49 @@ name: CI on: - workflow_dispatch: - pull_request: push: - branches: - - main + branches: [main] + pull_request: + +concurrency: + group: {% raw %}${{ github.workflow }}-${{ github.ref }}{% endraw %} + cancel-in-progress: true jobs: - pre-commit: - name: Format + lint code + lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 with: - fetch-depth: 0 - - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - uses: pre-commit/action@v3.0.0 + enable-cache: true + - run: uv run ruff check --output-format=github . + - run: uv run ruff format --check . + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 with: - extra_args: --hook-stage manual --all-files + enable-cache: true + - run: uvx ty check - checks: - name: {% raw %}Run tests for Python ${{ matrix.python-version }} on ${{ matrix.runs-on }}{% endraw %} - runs-on: {% raw %}${{ matrix.runs-on }}{% endraw %} - needs: [pre-commit] + test: + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: ["{{ min_python_version }}", "3.12"] # test oldest and latest supported versions - runs-on: [ubuntu-latest, macos-latest] # can be extended to other OSes, e.g. [ubuntu-latest, macos-latest, windows-latest] - + python-version: [{% for v in range(min_python_version.split('.')[1]|int, 14) %}"3.{{ v }}"{% if not loop.last %}, {% endif %}{% endfor %}] steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-python@v4 + - uses: astral-sh/setup-uv@v5 with: python-version: {% raw %}${{ matrix.python-version }}{% endraw %} - allow-prereleases: true - - - name: Install package - run: python -m pip install .[dev] - - - name: Test package - run: >- - python -m pytest -ra --cov --cov-report=xml --cov-report=term - --durations=20 - - - name: Upload coverage report - uses: codecov/codecov-action@v3.1.4 + enable-cache: true + - run: uv sync --locked --group test + - run: uv run pytest --cov --cov-report=xml + - uses: codecov/codecov-action@v5 + if: {% raw %}matrix.python-version == '3.13'{% endraw %} + with: + files: coverage.xml diff --git a/project_template/.pre-commit-config.yaml b/project_template/.pre-commit-config.yaml index fc4f54a..950cc13 100644 --- a/project_template/.pre-commit-config.yaml +++ b/project_template/.pre-commit-config.yaml @@ -1,39 +1,31 @@ -ci: - autoupdate_commit_msg: "chore: update pre-commit hooks" - autofix_commit_msg: "style: pre-commit fixes" - repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: "v6.0.0" + rev: v5.0.0 hooks: - - id: check-added-large-files - - id: check-case-conflict - - id: check-merge-conflict - - id: check-symlinks + - id: trailing-whitespace + - id: end-of-file-fixer - id: check-yaml + - id: check-toml + - id: check-merge-conflict + - id: check-added-large-files - id: debug-statements - - id: end-of-file-fixer - - id: mixed-line-ending - - id: name-tests-test - args: ["--pytest-test-first"] - - id: requirements-txt-fixer - - id: trailing-whitespace + + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.24 + hooks: + - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.15.6" + rev: v0.15.6 hooks: - # first, lint + autofix - - id: ruff - types_or: [python, pyi, jupyter] - args: ["--fix", "--show-fixes"] - # then, format + - id: ruff-check + args: [--fix] - id: ruff-format - - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.19.1" + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.10.9 hooks: - - id: mypy - files: src - args: [] - additional_dependencies: - - pytest + - id: uv-lock + + # NOTE: ty pre-commit hook not yet available (astral-sh/ty#269). + # Add it here when Astral ships an official hook. diff --git a/project_template/pyproject.toml b/project_template/pyproject.toml index 211918a..3214db3 100644 --- a/project_template/pyproject.toml +++ b/project_template/pyproject.toml @@ -50,6 +50,12 @@ test = [ lint = [ "ruff>=0.15", ] +{%- if include_docs %} +docs = [ + "mkdocs-material>=9.0", + "mkdocstrings[python]>=0.27", +] +{%- endif %} [project.urls] Homepage = "{{ url }}" @@ -77,67 +83,40 @@ port.exclude_lines = [ 'if typing.TYPE_CHECKING:', ] -{% if typing != "no_typing" -%} -[tool.mypy] -files = ["src", "tests"] -python_version = "{{ min_python_version }}" -show_error_codes = true -warn_unreachable = true -disallow_untyped_defs = false -disallow_incomplete_defs = false -check_untyped_defs = true -{% if typing == "loose" -%} -strict = false -{% elif typing == "strict" -%} -strict = true -enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +[tool.ty.environment] +python-version = "{{ min_python_version }}" -[[tool.mypy.overrides]] -module = "{{ python_name }}.*" -disallow_untyped_defs = true -disallow_incomplete_defs = true -{%- endif %} -{%- endif %} +[tool.ty.rules] +unused-ignore-comment = "warn" + +[tool.ty.src] +include = ["src", "tests"] [tool.ruff] +line-length = 88 +target-version = "py{{ min_python_version | replace('.', '') }}" src = ["src"] -exclude = [] -line-length = 88 # how long you want lines to be - -[tool.ruff.format] -docstring-code-format = true # code snippets in docstrings will be formatted [tool.ruff.lint] select = [ - "E", "F", "W", # flake8 - "B", # flake8-bugbear - "I", # isort - "ARG", # flake8-unused-arguments - "C4", # flake8-comprehensions - "EM", # flake8-errmsg - "ICN", # flake8-import-conventions - "ISC", # flake8-implicit-str-concat - "G", # flake8-logging-format - "PGH", # pygrep-hooks - "PIE", # flake8-pie - "PL", # pylint - "PT", # flake8-pytest-style - "RET", # flake8-return - "RUF", # Ruff-specific - "SIM", # flake8-simplify - "UP", # pyupgrade - "YTT", # flake8-2020 - "EXE", # flake8-executable + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # Pyflakes + "I", # isort + "UP", # pyupgrade + "B", # flake8-bugbear + "SIM", # flake8-simplify + "C4", # flake8-comprehensions + "RUF", # Ruff-specific rules ] -ignore = [ - "PLR", # Design related pylint codes - "ISC001", # Conflicts with formatter -] -unfixable = [ - "F401", # Would remove unused imports - "F841", # Would remove unused variables -] -flake8-unused-arguments.ignore-variadic-names = true # allow unused *args/**kwargs -{%- if typing != "no_typing" -%} -isort.required-imports = ["from __future__ import annotations"] -{%- endif %} +ignore = ["E501"] + +[tool.ruff.lint.per-file-ignores] +"tests/**/*.py" = ["S101", "D"] + +[tool.ruff.lint.isort] +known-first-party = ["{{ python_name }}"] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" diff --git a/project_template/{% if include_docs %}docs{% endif %}/index.md b/project_template/{% if include_docs %}docs{% endif %}/index.md new file mode 100644 index 0000000..8ff520c --- /dev/null +++ b/project_template/{% if include_docs %}docs{% endif %}/index.md @@ -0,0 +1,5 @@ +# {{ project_name }} + +{{ project_short_description }} + + diff --git a/project_template/{% if include_docs %}docs{% endif %}/reference.md b/project_template/{% if include_docs %}docs{% endif %}/reference.md new file mode 100644 index 0000000..e78b1c4 --- /dev/null +++ b/project_template/{% if include_docs %}docs{% endif %}/reference.md @@ -0,0 +1,3 @@ +# API Reference + +::: {{ python_name }} diff --git a/project_template/{% if include_docs %}mkdocs.yml{% endif %} b/project_template/{% if include_docs %}mkdocs.yml{% endif %} new file mode 100644 index 0000000..21a5c22 --- /dev/null +++ b/project_template/{% if include_docs %}mkdocs.yml{% endif %} @@ -0,0 +1,26 @@ +site_name: "{{ project_name }}" +site_url: "https://{{ org }}.github.io/{{ project_name }}" +repo_url: "{{ url }}" + +theme: + name: material + features: + - content.code.copy + - navigation.sections + - search.highlight + +plugins: + - search + - mkdocstrings: + handlers: + python: + paths: [src] + +nav: + - Home: index.md + - API Reference: reference.md + +markdown_extensions: + - admonition + - pymdownx.highlight + - pymdownx.superfences