diff --git a/.cz.yaml b/.cz.yaml new file mode 100644 index 0000000..078a2d8 --- /dev/null +++ b/.cz.yaml @@ -0,0 +1,7 @@ +commitizen: + major_version_zero: true + name: cz_conventional_commits + tag_format: $version + update_changelog_on_bump: true + version_provider: uv + version_scheme: semver2 diff --git a/.github/workflows/bump_version.yaml b/.github/workflows/bump_version.yaml index f583732..0287254 100644 --- a/.github/workflows/bump_version.yaml +++ b/.github/workflows/bump_version.yaml @@ -1,48 +1,25 @@ name: Bump version - on: push: branches: - master - main - workflow_dispatch: - inputs: - increment: - description: 'Version increment (major, minor, patch)' - required: false - type: choice - options: - - major - - minor - - patch - default: patch - permissions: - contents: write # 用于创建和推送标签 - pull-requests: write # 用于创建 PR - + contents: write # 用于创建和推送标签 + pull-requests: write # 用于创建 PR jobs: bump-version: - if: "!startsWith(github.event.head_commit.message, 'bump:')" + if: ${{ !startsWith(github.event.head_commit.message, 'bump:') }} runs-on: ubuntu-latest - name: "Bump version and create changelog with commitizen" + name: Bump version and create changelog with commitizen steps: - - name: Load secret - uses: 1password/load-secrets-action@v2 - with: - export-env: true - env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} - PERSONAL_ACCESS_TOKEN: op://shawndengdev/github_access_token/credential - - name: Check out uses: actions/checkout@v4 with: fetch-depth: 0 - token: '${{ env.PERSONAL_ACCESS_TOKEN }}' - + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: Create bump and changelog uses: commitizen-tools/commitizen-action@master with: - github_token: ${{ env.PERSONAL_ACCESS_TOKEN }} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} branch: master diff --git a/.github/workflows/deploy_docs.yaml b/.github/workflows/deploy_docs.yaml index 6e63b1f..c8faac0 100644 --- a/.github/workflows/deploy_docs.yaml +++ b/.github/workflows/deploy_docs.yaml @@ -1,33 +1,22 @@ name: Deploy Docs - on: push: tags: - - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 + - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 workflow_dispatch: - permissions: - contents: write # 用于部署到 GitHub Pages - + contents: write # 用于部署到 GitHub Pages jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - + - name: Install Task + uses: arduino/setup-task@v2 - name: Install uv uses: astral-sh/setup-uv@v5 - - - name: Install dependencies - run: uv sync --extra=docs - - name: Build and deploy documentation - run: uv run mkdocs gh-deploy --force + run: task deploy:gh-pages env: GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index 95227fb..0000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,89 +0,0 @@ -name: lint_and_unittest - -on: - push: - branches: - - main - - master - pull_request: - workflow_dispatch: - -permissions: - contents: read - pull-requests: write # 用于在 PR 中添加评论 - -jobs: - check: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install uv - uses: astral-sh/setup-uv@v5 - - - name: Install dependencies - run: uv sync --extra=dev - - - name: Run lint checks - id: lint - run: uvx nox -s lint - continue-on-error: true - - - name: Comment on PR (Lint) - if: github.event_name == 'pull_request' && steps.lint.outcome == 'failure' - uses: actions/github-script@v7 - with: - script: | - const output = `#### 🔍 Lint Check Failed - Please fix the linting issues before merging. - You can run \`nox -s lint\` locally to check and fix issues.`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - - name: Check lint result - if: steps.lint.outcome == 'failure' - run: exit 1 - - - name: Run tests - id: test - run: uvx nox -s test_all - continue-on-error: true - - - name: Upload coverage reports - if: success() - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml - flags: unittests - - - name: Comment on PR (Tests) - if: github.event_name == 'pull_request' && steps.test.outcome == 'failure' - uses: actions/github-script@v7 - with: - script: | - const output = `#### ❌ Tests Failed - Please fix the test failures before merging. - You can run \`nox -s test_all\` locally to debug the failures.`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - - name: Check test result - if: steps.test.outcome == 'failure' - run: exit 1 diff --git a/.github/workflows/lint_and_unittest.yaml b/.github/workflows/lint_and_unittest.yaml new file mode 100644 index 0000000..5bc130c --- /dev/null +++ b/.github/workflows/lint_and_unittest.yaml @@ -0,0 +1,27 @@ +name: lint_and_unittest +on: + push: + branches: + - main + - master + pull_request: + workflow_dispatch: +permissions: + contents: read +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install Task + uses: arduino/setup-task@v2 + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Install dependencies + run: task init + - name: Run lint checks + id: lint + run: task lint + - name: Run test all versions + run: task test:all diff --git a/.github/workflows/release_build.yaml b/.github/workflows/release_build.yaml index 9af2115..4927289 100644 --- a/.github/workflows/release_build.yaml +++ b/.github/workflows/release_build.yaml @@ -1,60 +1,31 @@ name: release-build - on: push: tags: - - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 - workflow_dispatch: - inputs: - version: - description: 'Version to release (e.g. 1.0.0)' - required: true - type: string - + - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 permissions: - contents: write # 用于创建 release - id-token: write # 用于发布到 PyPI - + contents: write # 用于创建 release + id-token: write # 用于发布到 PyPI jobs: release-build: runs-on: ubuntu-latest permissions: - contents: write # 用于创建 GitHub Release + contents: write # 用于创建 GitHub Release steps: - uses: actions/checkout@v4 - - - name: Load secret - uses: 1password/load-secrets-action@v2 - with: - export-env: true - env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} - PERSONAL_ACCESS_TOKEN: op://shawndengdev/github_access_token/credential - PYPI_TOKEN: op://shawndengdev/pypi_token/credential - - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - + - name: Install Task + uses: arduino/setup-task@v2 - name: Install uv uses: astral-sh/setup-uv@v5 - with: - version: ">=0.4.0" - - - name: Install dependencies - run: uv sync --extra dev - - - name: Build and test + - name: init environment and test run: | - uvx nox -s lint - uvx nox -s test - uvx nox -s build - + task init # 初始化项目环境 + task lint # 运行代码检查 + task test:all # 运行所有测试 - name: Publish to PyPI env: - UV_PUBLISH_TOKEN: ${{ env.PYPI_TOKEN }} - run: uv publish - + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} + run: task deploy:pypi - name: Release uses: softprops/action-gh-release@v2 with: @@ -63,4 +34,4 @@ jobs: dist/*.whl generate_release_notes: true env: - GITHUB_TOKEN: ${{ env.PERSONAL_ACCESS_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..dadf3a5 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.7.8 + hooks: + - id: uv-lock # Update the uv lockfile + - repo: https://github.com/google/yamlfmt + rev: v0.14.0 + hooks: + - id: yamlfmt + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.33.0 + hooks: + - id: check-github-workflows + args: [--verbose] + - repo: https://github.com/rhysd/actionlint + rev: v1.7.7 + hooks: + - id: actionlint # lint github actions diff --git a/.python-version b/.python-version deleted file mode 100644 index e4fba21..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.12 diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..d2bcd4e --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,36 @@ +include = ["pyproject.toml", "repo_scaffold/**/*.py", "tests/**/*.py"] +exclude = ["repo_scaffold/templates/**/*"] +line-length = 120 +target-version = "py312" + +[lint] +extend-ignore = [ + "D100", # Missing docstring in public module + "D104", # Missing docstring in public package + # airflow dags are not required to have docstrings + "D203", # 1 blank line required before class docstring + "D213" # Multi-line docstring summary should start at the second line +] +ignore = [ + "W191", # indentation contains tabs + "D401" # imperative mood +] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "D", # pydocstyle + "UP", # pyupgrade + "RUF", # Ruff-specific rules + "SIM" # flake8-simplify +] + +[lint.isort] +force-single-line = true +lines-after-imports = 2 + +[lint.pydocstyle] +convention = "google" diff --git a/.yamlfmt.yaml b/.yamlfmt.yaml new file mode 100644 index 0000000..f30d60b --- /dev/null +++ b/.yamlfmt.yaml @@ -0,0 +1,49 @@ +yaml_files: + - "**/*.yaml" + - "**/*.yml" + - ".yamlfmt" + - "!**/vendor/**" + - "!**/node_modules/**" +regex_exclude: + - ".*\\Taskfile\\.yml$" + - ".*\\mkdocs\\.yml$" +yaml_options: + # 文档末尾是否需要一个空行 + end_of_document: true + # 配置文档分隔符 + + document_start: false + # 行长度限制 + + line_length: 100 + # 缩进大小 + + indentation: 2 + # 是否保留引号 + + preserve_quotes: true + # 对齐方式 + + alignment: + # 是否启用键值对齐 + enable: true + # 键和冒号之间的最小间隔 + + key_value: 1 + # 对齐后冒号和值之间的空格数 + + colon_value: 1 + # 数组格式化 + + array: + # 嵌套数组是否应该与父级对齐 + indent_nested: true + # 数组项与前导符号 - 之间的空格数 + + item_spacing: 1 + # 是否移除文档注释 + + strip_comments: false + # 是否保留文件开头的注释 + + preserve_header: true diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..c990a15 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,79 @@ +# https://taskfile.dev +version: '3' +vars: + package_name: repo_scaffold + python_min_version: '3.12' + python_max_version: &dev_version '3.14' + # 默认的开发版本 + + python_dev_version: "3.12" +tasks: + init: + desc: Initialize the project + cmds: + - uv sync + - uvx pre-commit install + lint: + desc: Run linters + cmds: + - uvx ruff check --fix . # 先修复可以自动修复的问题 + - uvx ruff format . # 然后格式化 + - uvx ruff check . # 最后再检查一次确保没有遗留问题 + lint:add-noqa: + desc: Add noqa comments to files + cmds: + - uvx ruff check --fix --add-noqa . # 添加 noqa 注释以忽略特定行的检查 + - task: lint:pre-commit # 运行 pre-commit 钩子以确保代码风格一致 + - task: lint # 重新运行 ruff 检查以确保没有遗漏的错误 + lint:pre-commit: + desc: Run pre-commit hooks + cmds: + - uvx pre-commit run --all-files + lint:watch: + desc: Watch files and run linters + cmds: + - uvx ruff check --watch . # 监视文件变化并运行检查 + test: + desc: Run tests with dev version + cmds: + - task: test:version + vars: + version: '{{.python_dev_version}}' + test:all: + desc: Run tests with all Python versions + vars: + python_versions: + sh: | + python3 -c " + min_ver = '{{.python_min_version}}'.split('.') + max_ver = '{{.python_max_version}}'.split('.') + for minor in range(int(min_ver[1]), int(max_ver[1]) + 1): + print(f'{min_ver[0]}.{minor}') + " + cmds: + - for: {var: python_versions, split: "\n", as: version} + task: test:version + vars: + version: '{{.version}}' + test:version: + desc: Run tests with specific Python version + cmds: + - echo "Testing with Python {{.version}}..." + - uvx --python {{.version}} --with ".[dev]" pytest --cov={{.package_name}} --cov-report=xml --cov-report=term-missing -v tests/ + docs: + desc: Build and view documentation + cmds: + - uvx --with ".[docs]" mkdocs serve + export:deps: + cmds: + - uv pip compile pyproject.toml --no-deps --output-file requirements.txt + deploy:gh-pages: + desc: Deploy documentation to GitHub Pages + cmds: + - uvx --with ".[docs]" mkdocs gh-deploy --force + deploy:pypi: + desc: Deploy package to PyPI + cmds: + - uv build + - uv publish + diff --git a/mkdocs.yml b/mkdocs.yml index 7dd24ef..fe3bf9e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,11 +6,9 @@ site_author: ShawnDen-coder edit_uri: edit/main/docs/ repo_name: ShawnDen-coder/repo-scaffold copyright: Maintained by ShawnDen-coder. - nav: - Home: index.md - API Reference: reference/ - plugins: - search - gen-files: @@ -33,7 +31,6 @@ plugins: merge_init_into_class: true docstring_section_style: "spacy" show_if_no_docstring: false - theme: name: material palette: @@ -60,7 +57,6 @@ theme: - search.share - search.suggest - content.code.copy - markdown_extensions: - admonition - pymdownx.details @@ -76,7 +72,6 @@ markdown_extensions: - tables - toc: permalink: true - extra: social: - icon: fontawesome/brands/github diff --git a/noxfile.py b/noxfile.py deleted file mode 100644 index 5612001..0000000 --- a/noxfile.py +++ /dev/null @@ -1,226 +0,0 @@ -"""Nox automation file for repo-scaffold project. - -This module contains nox sessions for automating development tasks including: -- Code linting and formatting -- Unit testing with coverage reporting -- Package building -- Project cleaning -- Baseline creation for linting rules -- Documentation building - -Typical usage example: - nox -s lint # Run linting - nox -s test # Run tests with current Python version - nox -s test-all # Run tests with all supported Python versions - nox -s build # Build package - nox -s clean # Clean project - nox -s baseline # Create a new baseline for linting rules - nox -s docs # Build documentation - nox -s docs-serve # Serve documentation locally -""" -import nox -import shutil -from pathlib import Path - -# 支持的 Python 版本范围 -MIN_PYTHON = "3.12" -MAX_PYTHON = "3.12" - -# 生成版本列表 -PYTHON_VERSIONS = [MIN_PYTHON] - - -def install_with_uv(session: nox.Session, extras: list[str] | None = None) -> None: - """Helper function to install packages using uv. - - Args: - session: Nox session object for running commands - extras: Optional list of extra dependency groups to install (e.g. ["dev", "docs"]) - """ - session.install("uv") - if extras: - session.run("uv", "sync", *(f"--extra={extra}" for extra in extras)) - else: - session.run("uv", "sync") - - -@nox.session(python=PYTHON_VERSIONS[-1], reuse_venv=True) -def lint(session: nox.Session) -> None: - """Run code quality checks using ruff. - - Performs linting and formatting checks on the codebase using ruff. - Reports any issues found without auto-fixing. - - Args: - session: Nox session object for running commands - """ - # Install dependencies - install_with_uv(session, extras=["dev"]) - - # Run ruff checks - session.run("uv", "run", "ruff", "check", ".") - session.run("uv", "run", "ruff", "format", "--check", ".") - - -@nox.session(python=PYTHON_VERSIONS[-1], reuse_venv=True) -def test(session: nox.Session) -> None: - """Run the test suite with coverage reporting. - - Executes pytest with coverage reporting for the repo_scaffold package. - Generates both terminal and XML coverage reports. - - Args: - session: Nox session object for running commands - """ - # Install dependencies - install_with_uv(session, extras=["dev"]) - - # Run pytest with coverage - session.run( - "uv", - "run", - "pytest", - "--cov=repo_scaffold", - "--cov-report=term-missing", - "--cov-report=xml", - "-v", - "tests" - ) - - -@nox.session(python=PYTHON_VERSIONS) -def test_all(session: nox.Session) -> None: - """Run tests on all supported Python versions. - - This session runs the test suite against all supported Python versions. - It's useful for ensuring compatibility across different Python versions. - Also generates coverage report when running with the latest Python version. - - Args: - session: Nox session object for running commands - """ - # Install dependencies - session.install("uv") - install_with_uv(session, extras=["dev"]) - - # 运行测试 - session.run( - "uv", - "run", - "pytest", - "--cov=repo_scaffold", - "--cov-report=term-missing", - "--cov-report=xml", - "-v", - "tests" - ) - - -@nox.session(reuse_venv=True) -def build(session: nox.Session) -> None: - """Build the Python package. - - Creates a distributable package using build command. - - Args: - session: Nox session object for running commands - """ - install_with_uv(session, extras=["dev"]) - session.run("uv","build") - - -@nox.session(reuse_venv=True) -def clean(session: nox.Session) -> None: - """Clean the project directory. - - Removes build artifacts, cache directories, and other temporary files: - - build/: Build artifacts - - dist/: Distribution packages - - .nox/: Nox virtual environments - - .pytest_cache/: Pytest cache - - .ruff_cache/: Ruff cache - - .coverage: Coverage data - - coverage.xml: Coverage report - - **/*.pyc: Python bytecode - - **/__pycache__/: Python cache directories - """ - # 要清理的目录和文件 - paths_to_clean = [ - # 构建和分发 - "build", - "dist", - # 虚拟环境 - ".nox", - # 缓存 - ".pytest_cache", - ".ruff_cache", - # 覆盖率报告 - ".coverage", - "coverage.xml", - "site" - ] - - # 清理指定的目录和文件 - for path in paths_to_clean: - path = Path(path) - if path.exists(): - if path.is_dir(): - shutil.rmtree(path) - else: - path.unlink() - - # 清理 Python 缓存文件 - for pattern in ["**/*.pyc", "**/__pycache__"]: - for path in Path().glob(pattern): - if path.is_dir(): - shutil.rmtree(path) - else: - path.unlink() - - -@nox.session(reuse_venv=True) -def baseline(session: nox.Session) -> None: - """Create a new baseline for linting rules. - - This command will: - 1. Add # noqa comments to all existing violations - 2. Update the pyproject.toml with new extend-ignore rules - 3. Show a summary of changes made - - Args: - session: Nox session object for running commands - """ - # Install dependencies - install_with_uv(session, extras=["dev"]) - - # 运行 ruff 并自动修复所有问题 - session.run("uv", "run", "ruff", "check", ".", "--add-noqa") - session.run("uv", "run", "ruff", "format", ".") - - -@nox.session(reuse_venv=True) -def docs(session: nox.Session) -> None: - """Build the documentation. - - Uses MkDocs to build the documentation site. - - Args: - session: Nox session object for running commands - """ - session.install("uv") - install_with_uv(session, extras=["docs"]) - session.run("uv", "run", "mkdocs", "build") - - -@nox.session(reuse_venv=True) -def docs_serve(session: nox.Session) -> None: - """Serve the documentation locally. - - Starts a local server to preview the documentation site. - - Args: - session: Nox session object for running commands - """ - session.install("uv") - install_with_uv(session, extras=["docs"]) - session.run("uv", "run", "mkdocs", "serve") diff --git a/pyproject.toml b/pyproject.toml index 755a42e..0b1bb62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,12 +16,11 @@ dependencies = [ [project.optional-dependencies] dev = [ - "ruff>=0.9.6", - "nox>=2024.10.9", + "ruff>=0.9.7", "pytest>=8.3.4", "pytest-mock>=3.14.0", "pytest-cov>=6.0.0", - "commitizen>=4.1.0", + "commitizen>=3.12.0", ] docs = [ @@ -30,8 +29,8 @@ docs = [ "mkdocstrings>=0.24.0", "mkdocstrings-python>=1.7.5", "mkdocs-gen-files>=0.5.0", - "mkdocs-literate-nav>=0.6.1", "pymdown-extensions>=10.7", + "mkdocs-literate-nav>=0.6.1", ] [project.scripts] @@ -43,45 +42,3 @@ repo_scaffold = ["templates/**/*", "cookiecutter.json"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" - -[tool.ruff] -line-length = 120 -include = ["pyproject.toml", "repo_scaffold/*.py"] -exclude = ["repo_scaffold/templates/**/*"] - -[tool.ruff.lint] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "D" # pydocstyle -] - -ignore = [ - "W191", # indentation contains tabs - "D401" # imperative mood -] - -extend-ignore = [ - "D100", # Missing docstring in public module - "D104", # Missing docstring in public package -] - -[tool.ruff.lint.isort] -force-single-line = true -lines-after-imports = 2 - -[tool.ruff.lint.pydocstyle] -convention = "google" - - -[tool.commitizen] -name = "cz_conventional_commits" -tag_format = "$version" -version_scheme = "semver2" -version_provider = "pep621" -update_changelog_on_bump = true -major_version_zero = true diff --git a/repo_scaffold/cli.py b/repo_scaffold/cli.py index e08e0b0..1f630ad 100644 --- a/repo_scaffold/cli.py +++ b/repo_scaffold/cli.py @@ -29,7 +29,6 @@ import os from pathlib import Path from typing import Any -from typing import Dict import click from cookiecutter.main import cookiecutter @@ -43,7 +42,7 @@ def get_package_path(relative_path: str) -> str: Returns: str: Absolute path to the resource - """ # noqa: W293 + """ # 使用 files() 获取包资源 package_files = importlib.resources.files("repo_scaffold") resource_path = package_files.joinpath(relative_path) @@ -52,7 +51,7 @@ def get_package_path(relative_path: str) -> str: return str(resource_path) -def load_templates() -> Dict[str, Any]: +def load_templates() -> dict[str, Any]: """Load available project templates configuration. Reads template configurations from the cookiecutter.json file in the templates directory. @@ -74,7 +73,7 @@ def load_templates() -> Dict[str, Any]: json.JSONDecodeError: If the configuration file is not valid JSON """ config_path = get_package_path("templates/cookiecutter.json") - with open(config_path, "r", encoding="utf-8") as f: + with open(config_path, encoding="utf-8") as f: config = json.load(f) return config["templates"] @@ -104,7 +103,7 @@ def list(): python - template-python Description: template for python project ``` - """ # noqa: W293 + """ templates = load_templates() click.echo("\nAvailable templates:") for name, info in templates.items(): @@ -147,10 +146,10 @@ def create(template: str, output_dir: Path): ```bash $ repo-scaffold list ``` - """ # noqa: W293 + """ templates = load_templates() - # 如果没有指定模板,让 cookiecutter 处理模板选择 + # 如果没有指定模板,让 cookiecutter 处理模板选择 if not template: click.echo("Please select a template to use:") for name, info in templates.items(): @@ -177,7 +176,7 @@ def create(template: str, output_dir: Path): cookiecutter( template=template_path, output_dir=str(output_dir), - no_input=False, # 启用交互式输入,让 cookiecutter 处理所有选项 + no_input=False, # 启用交互式输入,让 cookiecutter 处理所有选项 ) diff --git a/repo_scaffold/templates/template-python/cookiecutter.json b/repo_scaffold/templates/template-python/cookiecutter.json index b89cce0..cc338ba 100644 --- a/repo_scaffold/templates/template-python/cookiecutter.json +++ b/repo_scaffold/templates/template-python/cookiecutter.json @@ -5,13 +5,13 @@ "github_username": "ShawnDen-coder", "project_slug": "{{ cookiecutter.repo_name.strip().lower().replace('-', '_') }}", "description": "A short description of the project.", - "min_python_version": ["3.9", "3.10", "3.11"], - "max_python_version": ["3.12"], + "min_python_version": ["3.9", "3.10", "3.11", "3.12", "3.13","3.14"], + "max_python_version": ["3.9", "3.10", "3.11", "3.12", "3.13","3.14"], "use_docker": ["yes", "no"], "include_cli": ["yes", "no"], "use_github_actions": ["yes", "no"], "__prompts__": { - "repo_name": "项目名称 (使用 - 分隔, 例如: my-awesome-project)", + "repo_name": "项目名称 (my-awesome-project)", "full_name": "你的全名", "email": "你的邮箱地址", "github_username": "你的 GitHub 用户名", diff --git a/repo_scaffold/templates/template-python/hooks/post_gen_project.py b/repo_scaffold/templates/template-python/hooks/post_gen_project.py index 7666e5c..d0ba07e 100644 --- a/repo_scaffold/templates/template-python/hooks/post_gen_project.py +++ b/repo_scaffold/templates/template-python/hooks/post_gen_project.py @@ -9,6 +9,17 @@ def remove_cli(): if os.path.exists(cli_file): os.remove(cli_file) +def remove_docker(): + """Remove GitHub Actions configuration if not needed.""" + file_name = [".dockerignore", "docker", ".github/workflows/docker_release.yaml"] + if "{{cookiecutter.use_github_actions}}" == "no": + for item in file_name: + if os.path.exists(item): + if os.path.isfile(item): + os.remove(item) + elif os.path.isdir(item): + shutil.rmtree(item) + def remove_github_actions(): """Remove GitHub Actions configuration if not needed.""" @@ -36,7 +47,7 @@ def init_project_depends(): os.chdir(project_dir) # 安装基础开发依赖 - subprocess.run(["uv", "sync", "--extra", "dev"], check=True) + subprocess.run(["uv", "sync"], check=True) if __name__ == "__main__": @@ -46,5 +57,8 @@ def init_project_depends(): if "{{cookiecutter.use_github_actions}}" == "no": remove_github_actions() remove_docs() + + if "{{cookiecutter.use_docker}}" == "no": + remove_docker() init_project_depends() \ No newline at end of file diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.cz.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.cz.yaml new file mode 100644 index 0000000..078a2d8 --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.cz.yaml @@ -0,0 +1,7 @@ +commitizen: + major_version_zero: true + name: cz_conventional_commits + tag_format: $version + update_changelog_on_bump: true + version_provider: uv + version_scheme: semver2 diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.dockerignore b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.dockerignore new file mode 100644 index 0000000..164a877 --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.dockerignore @@ -0,0 +1,8 @@ +__pycache__ +*.pyc +.git +.pytest_cache +docs/ +tests/ +mkdocs.yml +.venv \ No newline at end of file diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/bump_version.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/bump_version.yaml index 4df8654..6dd8370 100644 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/bump_version.yaml +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/bump_version.yaml @@ -1,48 +1,25 @@ name: Bump version - on: push: branches: - master - main - workflow_dispatch: - inputs: - increment: - description: 'Version increment (major, minor, patch)' - required: false - type: choice - options: - - major - - minor - - patch - default: patch - permissions: - contents: write # 用于创建和推送标签 - pull-requests: write # 用于创建 PR - + contents: write # 用于创建和推送标签 + pull-requests: write # 用于创建 PR jobs: bump-version: - if: "!startsWith(github.event.head_commit.message, 'bump:')" + if: {% raw %}${{ !startsWith(github.event.head_commit.message, 'bump:') }}{% endraw %} runs-on: ubuntu-latest - name: "Bump version and create changelog with commitizen" + name: Bump version and create changelog with commitizen steps: - - name: Load secret - uses: 1password/load-secrets-action@v2 - with: - export-env: true - env: - OP_SERVICE_ACCOUNT_TOKEN: {% raw %}${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}{% endraw %} - PERSONAL_ACCESS_TOKEN: op://shawndengdev/github_access_token/credential - - name: Check out uses: actions/checkout@v4 with: fetch-depth: 0 - token: {% raw %}'${{ env.PERSONAL_ACCESS_TOKEN }}'{% endraw %} - + token: {% raw %}${{ secrets.PERSONAL_ACCESS_TOKEN }}{% endraw %} - name: Create bump and changelog uses: commitizen-tools/commitizen-action@master with: - github_token: {% raw %}${{ env.PERSONAL_ACCESS_TOKEN }}{% endraw %} + github_token: {% raw %}${{ secrets.PERSONAL_ACCESS_TOKEN }}{% endraw %} branch: master diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/deploy_docs.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/deploy_docs.yaml index 210ac03..392402e 100644 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/deploy_docs.yaml +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/deploy_docs.yaml @@ -1,33 +1,22 @@ name: Deploy Docs - on: push: tags: - - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 + - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 workflow_dispatch: - permissions: - contents: write # 用于部署到 GitHub Pages - + contents: write # 用于部署到 GitHub Pages jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "{{cookiecutter.max_python_version}}" - + - name: Install Task + uses: arduino/setup-task@v2 - name: Install uv uses: astral-sh/setup-uv@v5 - - - name: Install dependencies - run: uv sync --extra=docs - - name: Build and deploy documentation - run: uv run mkdocs gh-deploy --force + run: task deploy:gh-pages env: GITHUB_TOKEN: {% raw %}${{ github.token }}{% endraw %} diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/docker_release.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/docker_release.yaml new file mode 100644 index 0000000..82fa11b --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/docker_release.yaml @@ -0,0 +1,51 @@ +name: docker release +on: + release: + types: [published] + workflow_dispatch: +env: + DOCKERHUB_IMAGE_NAME: shawndengdocker/homelab_airflow_dags + GITHUB_IMAGE_NAME: ghcr.io/shawnden-coder/homelab_airflow_dags +jobs: + push_to_registry: + name: Push Docker image to Docker Hub and GHCR + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: {% raw %}${{ secrets.DOCKERHUB_USERNAME }}{% endraw %} + password: {% raw %}${{ secrets.DOCKERHUB_TOKEN }}{% endraw %} + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: {% raw %}${{ github.actor }}{% endraw %} + password: {% raw %}${{ secrets.PERSONAL_ACCESS_TOKEN }}{% endraw %} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + {% raw %}${{ env.DOCKERHUB_IMAGE_NAME }}{% endraw %} + {% raw %}${{ env.GITHUB_IMAGE_NAME }}{% endraw %} + tags: | + {% raw %}type=raw,value=latest,enable={{is_default_branch}}{% endraw %} + type=ref,event=tag + type=ref,event=pr + type=sha,format=short + - name: Build and push Docker images + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: {% raw %}${{ steps.meta.outputs.tags }}{% endraw %} + labels: {% raw %}${{ steps.meta.outputs.labels }}{% endraw %} diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/lint.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/lint.yaml deleted file mode 100644 index c270b42..0000000 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/lint.yaml +++ /dev/null @@ -1,90 +0,0 @@ -name: lint_and_unittest - -on: - push: - branches: - - main - - master - pull_request: - workflow_dispatch: - -permissions: - contents: read - pull-requests: write # 用于在 PR 中添加评论 - -jobs: - check: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "{{cookiecutter.max_python_version}}" - - - name: Install uv - uses: astral-sh/setup-uv@v5 - - - name: Install dependencies - run: uv sync --extra=dev - - - name: Run lint checks - id: lint - run: uvx nox -s lint - continue-on-error: true - - - name: Comment on PR (Lint) - if: github.event_name == 'pull_request' && steps.lint.outcome == 'failure' - uses: actions/github-script@v7 - with: - script: | - const output = `#### 🔍 Lint Check Failed - Please fix the linting issues before merging. - You can run \`nox -s lint\` locally to check and fix issues.`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - - name: Check lint result - if: steps.lint.outcome == 'failure' - run: exit 1 - - - name: Run tests - id: test - run: uvx nox -s test_all - continue-on-error: true - - - name: Upload coverage reports - if: success() - uses: codecov/codecov-action@v4 - with: - token: {% raw %}${{ secrets.CODECOV_TOKEN }}{% endraw %} - file: ./coverage.xml - flags: unittests - - - name: Comment on PR (Tests) - if: github.event_name == 'pull_request' && steps.test.outcome == 'failure' - uses: actions/github-script@v7 - with: - script: | - const output = `#### ❌ Tests Failed - Please fix the test failures before merging. - You can run \`nox -s test_all\` locally to debug the failures.`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - - name: Check test result - if: steps.test.outcome == 'failure' - run: exit 1 - diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/lint_and_unittest.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/lint_and_unittest.yaml new file mode 100644 index 0000000..5bc130c --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/lint_and_unittest.yaml @@ -0,0 +1,27 @@ +name: lint_and_unittest +on: + push: + branches: + - main + - master + pull_request: + workflow_dispatch: +permissions: + contents: read +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install Task + uses: arduino/setup-task@v2 + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Install dependencies + run: task init + - name: Run lint checks + id: lint + run: task lint + - name: Run test all versions + run: task test:all diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/release_build.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/release_build.yaml index c755d75..083f64f 100644 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/release_build.yaml +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/release_build.yaml @@ -1,62 +1,31 @@ name: release-build - on: push: tags: - - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 - workflow_dispatch: - inputs: - version: - description: 'Version to release (e.g. 1.0.0)' - required: true - type: string - - + - '[0-9]+.[0-9]+.[0-9]+' # 匹配类似 1.0.0, 2.1.3 等格式的标签 permissions: - contents: write # 用于创建 release - id-token: write # 用于发布到 PyPI - + contents: write # 用于创建 release + id-token: write # 用于发布到 PyPI jobs: release-build: runs-on: ubuntu-latest permissions: - contents: write # 用于创建 GitHub Release + contents: write # 用于创建 GitHub Release steps: - uses: actions/checkout@v4 - - - name: Load secret - uses: 1password/load-secrets-action@v2 - with: - export-env: true - env: - OP_SERVICE_ACCOUNT_TOKEN: {% raw %}${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}{% endraw %} - PERSONAL_ACCESS_TOKEN: op://shawndengdev/github_access_token/credential - PYPI_TOKEN: op://shawndengdev/pypi_token/credential - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "{{ cookiecutter.max_python_version[0] }}" - + - name: Install Task + uses: arduino/setup-task@v2 - name: Install uv uses: astral-sh/setup-uv@v5 - with: - version: ">=0.4.0" - - - name: Install dependencies - run: uv sync --extra dev - - - name: Build and test + - name: init environment and test run: | - uvx nox -s lint - uvx nox -s test - uvx nox -s build - + task init # 初始化项目环境 + task lint # 运行代码检查 + task test:all # 运行所有测试 - name: Publish to PyPI env: - {% raw %}UV_PUBLISH_TOKEN: ${{ env.PYPI_TOKEN }}{% endraw %} - run: uv publish - + UV_PUBLISH_TOKEN: {% raw %}${{ secrets.PYPI_TOKEN }}{% endraw %} + run: task deploy:pypi - name: Release uses: softprops/action-gh-release@v2 with: @@ -65,4 +34,4 @@ jobs: dist/*.whl generate_release_notes: true env: - {% raw %}GITHUB_TOKEN: ${{ env.PERSONAL_ACCESS_TOKEN }}{% endraw %} + GITHUB_TOKEN: {% raw %}${{ secrets.PERSONAL_ACCESS_TOKEN }}{% endraw %} diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.pre-commit-config.yaml new file mode 100644 index 0000000..dadf3a5 --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.7.8 + hooks: + - id: uv-lock # Update the uv lockfile + - repo: https://github.com/google/yamlfmt + rev: v0.14.0 + hooks: + - id: yamlfmt + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.33.0 + hooks: + - id: check-github-workflows + args: [--verbose] + - repo: https://github.com/rhysd/actionlint + rev: v1.7.7 + hooks: + - id: actionlint # lint github actions diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.ruff.toml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.ruff.toml new file mode 100644 index 0000000..033bb1d --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.ruff.toml @@ -0,0 +1,32 @@ +include = ["pyproject.toml", "{{ cookiecutter.project_slug }}/**/*.py", "tests/**/*.py"] +line-length = 120 +target-version = {{ "py" ~ cookiecutter.min_python_version.replace(".", "") }} + +[lint] +extend-ignore = [ + "D100", # Missing docstring in public module + "D104", # Missing docstring in public package +] +ignore = [ + "W191", # indentation contains tabs + "D401" # imperative mood +] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "D", # pydocstyle + "UP", # pyupgrade + "RUF", # Ruff-specific rules + "SIM" # flake8-simplify +] + +[lint.isort] +force-single-line = true +lines-after-imports = 2 + +[lint.pydocstyle] +convention = "google" diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.yamlfmt.yaml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.yamlfmt.yaml new file mode 100644 index 0000000..9a34da8 --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.yamlfmt.yaml @@ -0,0 +1,46 @@ +yaml_files: + - "**/*.yaml" + - "**/*.yml" + - ".yamlfmt" + - "!**/vendor/**" + - "!**/node_modules/**" +yaml_options: + # 文档末尾是否需要一个空行 + end_of_document: true + # 配置文档分隔符 + + document_start: false + # 行长度限制 + + line_length: 100 + # 缩进大小 + + indentation: 2 + # 是否保留引号 + + preserve_quotes: true + # 对齐方式 + + alignment: + # 是否启用键值对齐 + enable: true + # 键和冒号之间的最小间隔 + + key_value: 1 + # 对齐后冒号和值之间的空格数 + + colon_value: 1 + # 数组格式化 + + array: + # 嵌套数组是否应该与父级对齐 + indent_nested: true + # 数组项与前导符号 - 之间的空格数 + + item_spacing: 1 + # 是否移除文档注释 + + strip_comments: false + # 是否保留文件开头的注释 + + preserve_header: true diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/README.md b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/README.md index 8d65129..d9a202b 100644 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/README.md +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/README.md @@ -8,5 +8,4 @@ ```bash pip install {{cookiecutter.project_slug}} -# or -poetry add {{cookiecutter.project_slug}} \ No newline at end of file +``` \ No newline at end of file diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/Taskfile.yml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/Taskfile.yml new file mode 100644 index 0000000..89fe876 --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/Taskfile.yml @@ -0,0 +1,130 @@ +# https://taskfile.dev +version: '3' + +vars: + package_name: {{ cookiecutter.project_slug }} + python_min_version: '{{ cookiecutter.min_python_version }}' + python_max_version: &dev_version '{{ cookiecutter.max_python_version }}' + # 默认的开发版本 + python_dev_version: *dev_version + +tasks: + init: + desc: Initialize the project + cmds: + - uv sync + - uvx pre-commit install + + lint: + desc: Run linters + cmds: + - uvx ruff check --fix . # 先修复可以自动修复的问题 + - uvx ruff format . # 然后格式化 + - uvx ruff check . # 最后再检查一次确保没有遗留问题 + + lint:add-noqa: + desc: Add noqa comments to files + cmds: + - uvx ruff check --fix --add-noqa . # 添加 noqa 注释以忽略特定行的检查 + - task: lint:pre-commit # 运行 pre-commit 钩子以确保代码风格一致 + - task: lint # 重新运行 ruff 检查以确保没有遗漏的错误 + + lint:pre-commit: + desc: Run pre-commit hooks + cmds: + - uvx pre-commit run --all-files + + lint:watch: + desc: Watch files and run linters + cmds: + - uvx ruff check --watch . # 监视文件变化并运行检查 + + test: + desc: Run tests with dev version + cmds: + - task: test:version + vars: + version: {% raw %}'{{.python_dev_version}}'{% endraw %} + + test:all: + desc: Run tests with all Python versions + vars: + python_versions: + sh: | + python3 -c " + min_ver = {% raw %}'{{.python_min_version}}'{% endraw %}.split('.') + max_ver = {% raw %}'{{.python_max_version}}'{% endraw %}.split('.') + for minor in range(int(min_ver[1]), int(max_ver[1]) + 1): + print(f'{min_ver[0]}.{minor}') + " + cmds: + - for: {% raw %}{var: python_versions, split: "\n", as: version}{% endraw %} + task: test:version + vars: + version: {% raw %}'{{.version}}'{% endraw %} + + test:version: + desc: Run tests with specific Python version + cmds: + - echo "Testing with Python {% raw %}{{.version}}{% endraw %}..." + - uvx --python {% raw %}{{.version}}{% endraw %} --with ".[dev]" pytest --cov={% raw %}{{.package_name}}{% endraw %} --cov-report=xml --cov-report=term-missing -v tests/ + + test:watch: + desc: Watch files and run tests + cmds: + - uvx --with ".[dev]" ptw --runner "pytest -vx" + + docs: + desc: Build and view documentation + cmds: + - uvx --with ".[docs]" mkdocs serve + + docs:build: + desc: Build documentation + cmds: + - uvx --with ".[docs]" mkdocs build + +{%- if cookiecutter.use_github_actions == 'yes' %} + deploy:gh-pages: + desc: Deploy documentation to GitHub Pages + cmds: + - uvx --with ".[docs]" mkdocs gh-deploy --force +{%- endif %} + + deploy:pypi: + desc: Deploy package to PyPI + cmds: + - uv build + - uv publish + + export:deps: + desc: Export dependencies to requirements.txt + cmds: + - uv pip compile pyproject.toml --no-deps --output-file requirements.txt + +{%- if cookiecutter.use_docker == 'yes' %} + docker:build: + desc: Build Docker image + cmds: + - docker build -t {{ cookiecutter.repo_name }}:latest . + + docker:run: + desc: Run Docker container + cmds: + - docker run --rm -it {{ cookiecutter.repo_name }}:latest + + docker-compose:up: + desc: Start services with docker-compose + cmds: + - docker-compose -f docker/docker-compose.yaml up + + docker-compose:down: + desc: Stop services with docker-compose + cmds: + - docker-compose -f docker/docker-compose.yaml down + + docker-compose:logs: + desc: View docker-compose logs + cmds: + - docker-compose -f docker/docker-compose.yaml logs -f +{%- endif %} \ No newline at end of file diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/docker/Dockerfile b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/docker/Dockerfile new file mode 100644 index 0000000..6b08c9f --- /dev/null +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/docker/Dockerfile @@ -0,0 +1 @@ +FROM python:{{cookiecutter.max_python_version}} diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/mkdocs.yml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/mkdocs.yml index abf4812..f13f9c0 100644 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/mkdocs.yml +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: {{cookiecutter.project_slug}} +site_name: '{{cookiecutter.project_slug}}' repo_url: https://github.com/{{cookiecutter.github_username}}/{{cookiecutter.project_slug}} site_url: https://{{cookiecutter.github_username}}.github.io/{{cookiecutter.project_slug}} site_description: {{cookiecutter.description}} diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/noxfile.py b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/noxfile.py deleted file mode 100644 index f11222f..0000000 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/noxfile.py +++ /dev/null @@ -1,231 +0,0 @@ -"""Nox automation file for {{cookiecutter.project_slug}} project. - -This module contains nox sessions for automating development tasks including: -- Code linting and formatting -- Unit testing with coverage reporting -- Package building -- Project cleaning -- Baseline creation for linting rules -- Documentation building - -Typical usage example: - nox -s lint # Run linting - nox -s test # Run tests with current Python version - nox -s test-all # Run tests with all supported Python versions - nox -s build # Build package - nox -s clean # Clean project - nox -s baseline # Create a new baseline for linting rules - nox -s docs # Build documentation - nox -s docs-serve # Serve documentation locally -""" -import nox -import shutil -from pathlib import Path - -# 支持的 Python 版本范围 -MIN_PYTHON = "{{cookiecutter.min_python_version}}" -MAX_PYTHON = "{{cookiecutter.max_python_version}}" - -# 生成版本列表 -PYTHON_VERSIONS = [ - f"3.{minor}" - for minor in range(int(MIN_PYTHON.split(".")[-1]), int(MAX_PYTHON.split(".")[-1]) + 1) -] - - -def install_with_uv(session: nox.Session, extras: list[str] | None = None) -> None: - """Helper function to install packages using uv. - - Args: - session: Nox session object for running commands - extras: Optional list of extra dependency groups to install (e.g. ["dev", "docs"]) - """ - session.install("uv") - if extras: - session.run("uv", "sync", *(f"--extra={extra}" for extra in extras)) - else: - session.run("uv", "sync") - - -@nox.session(python=PYTHON_VERSIONS[-1], reuse_venv=True) -def lint(session: nox.Session) -> None: - """Run code quality checks using ruff. - - Performs linting and formatting checks on the codebase using ruff. - Reports any issues found without auto-fixing. - - Args: - session: Nox session object for running commands - """ - # Install dependencies - install_with_uv(session, extras=["dev"]) - - # Run ruff checks - session.run("uv", "run", "ruff", "check", ".") - session.run("uv", "run", "ruff", "format", "--check", ".") - - -@nox.session(python=PYTHON_VERSIONS[-1], reuse_venv=True) -def test(session: nox.Session) -> None: - """Run the test suite with coverage reporting. - - Executes pytest with coverage reporting for the {{cookiecutter.project_slug}} package. - Generates both terminal and XML coverage reports. - - Args: - session: Nox session object for running commands - """ - # Install dependencies - install_with_uv(session, extras=["dev"]) - - # Run pytest with coverage - session.run( - "uv", - "run", - "pytest", - "--cov={{cookiecutter.project_slug}}", - "--cov-report=term-missing", - "--cov-report=xml", - "-v", - "tests", - ) - - -@nox.session(python=PYTHON_VERSIONS) -def test_all(session: nox.Session) -> None: - """Run tests on all supported Python versions. - - This session runs the test suite against all supported Python versions. - It's useful for ensuring compatibility across different Python versions. - Also generates coverage report when running with the latest Python version. - - Args: - session: Nox session object for running commands - """ - # Install dependencies - install_with_uv(session, extras=["dev"]) - - # 确定是否是最新的 Python 版本 - is_latest_python = session.python == PYTHON_VERSIONS[-1] - - # 构建测试命令 - test_args = ["-v", "tests"] - if is_latest_python: - test_args = [ - "--cov={{cookiecutter.project_slug}}", - "--cov-report=term-missing", - "--cov-report=xml", - ] + test_args - - # 运行测试 - session.run("uv", "run", "pytest", *test_args) - - -@nox.session(reuse_venv=True) -def build(session: nox.Session) -> None: - """Build the Python package. - - Creates a distributable package using build command. - - Args: - session: Nox session object for running commands - """ - install_with_uv(session, extras=["dev"]) - session.run("uv", "build") - - -@nox.session(reuse_venv=True) -def clean(session: nox.Session) -> None: - """Clean the project directory. - - Removes build artifacts, cache directories, and other temporary files: - - build/: Build artifacts - - dist/: Distribution packages - - .nox/: Nox virtual environments - - .pytest_cache/: Pytest cache - - .ruff_cache/: Ruff cache - - .coverage: Coverage data - - coverage.xml: Coverage report - - **/*.pyc: Python bytecode - - **/__pycache__/: Python cache directories - """ - # 要清理的目录和文件 - paths_to_clean = [ - # 构建和分发 - "build", - "dist", - # 虚拟环境 - ".nox", - # 缓存 - ".pytest_cache", - ".ruff_cache", - # 覆盖率报告 - ".coverage", - "coverage.xml", - "site" - ] - - # 清理指定的目录和文件 - for path in paths_to_clean: - path = Path(path) - if path.exists(): - if path.is_dir(): - shutil.rmtree(path) - else: - path.unlink() - - # 清理 Python 缓存文件 - for pattern in ["**/*.pyc", "**/__pycache__"]: - for path in Path().glob(pattern): - if path.is_dir(): - shutil.rmtree(path) - else: - path.unlink() - - -@nox.session(reuse_venv=True) -def baseline(session: nox.Session) -> None: - """Create a new baseline for linting rules. - - This command will: - 1. Add # noqa comments to all existing violations - 2. Update the pyproject.toml with new extend-ignore rules - 3. Show a summary of changes made - - Args: - session: Nox session object for running commands - """ - # Install dependencies - install_with_uv(session, extras=["dev"]) - - # 运行 ruff 并自动修复所有问题 - session.run("uv", "run", "ruff", "check", ".", "--add-noqa") - session.run("uv", "run", "ruff", "format", ".") - - -{% if cookiecutter.use_github_actions == "yes" %} -@nox.session(reuse_venv=True) -def docs(session: nox.Session) -> None: - """Build the documentation. - - Uses MkDocs to build the documentation site. - - Args: - session: Nox session object for running commands - """ - install_with_uv(session, extras=["docs"]) - session.run("uv", "run", "mkdocs", "build") - - -@nox.session(reuse_venv=True) -def docs_serve(session: nox.Session) -> None: - """Serve the documentation locally. - - Starts a local server to preview the documentation site. - - Args: - session: Nox session object for running commands - """ - install_with_uv(session, extras=["docs"]) - session.run("uv", "run", "mkdocs", "serve") -{% endif %} diff --git a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/pyproject.toml b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/pyproject.toml index 55e6dd3..c371ca7 100644 --- a/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/pyproject.toml +++ b/repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/pyproject.toml @@ -2,28 +2,25 @@ name = "{{cookiecutter.project_slug}}" version = "0.1.0" description = "{{cookiecutter.description}}" -authors = [ - {name = "{{cookiecutter.full_name}}", email = "{{cookiecutter.email}}"} -] +authors = [{name = "{{cookiecutter.full_name}}", email = "{{cookiecutter.email}}"}] license = "MIT" readme = "README.md" requires-python = ">={{cookiecutter.min_python_version}},<{{cookiecutter.max_python_version}}.99" dependencies = [ -{% if cookiecutter.include_cli == 'yes' %} +{%- if cookiecutter.include_cli == 'yes' %} "click>=8.1.8", -{% endif %} +{%- endif %} ] [project.optional-dependencies] dev = [ - "ruff>=0.9.7", # Update ruff version - "nox>=2024.10.9", + "ruff>=0.9.7", "pytest>=8.3.4", "pytest-mock>=3.14.0", "pytest-cov>=6.0.0", - "commitizen>=3.12.0", # 使用支持 Python 3.8 的版本 + "commitizen>=3.12.0", ] -{% if cookiecutter.use_github_actions == 'yes' %} +{%- if cookiecutter.use_github_actions == 'yes' %} docs = [ "mkdocs>=1.5.3", "mkdocs-material>=9.5.3", @@ -33,54 +30,13 @@ docs = [ "pymdown-extensions>=10.7", "mkdocs-literate-nav>=0.6.1", ] -{% endif %} +{%- endif %} -{% if cookiecutter.include_cli == 'yes' %} +{% if cookiecutter.include_cli == 'yes' -%} [project.scripts] {{cookiecutter.project_slug}} = "{{cookiecutter.project_slug}}.cli:cli" -{% endif %} +{%- endif %} [build-system] requires = ["hatchling"] build-backend = "hatchling.build" - -[tool.ruff] -line-length = 120 -include = ["pyproject.toml", "{{cookiecutter.project_slug}}/**/*.py"] -target-version = "py{{ cookiecutter.min_python_version | replace('.', '') }}" - -[tool.ruff.lint] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "D" # pydocstyle -] - -ignore = [ - "W191", # indentation contains tabs - "D401" # imperative mood -] - -extend-ignore = [ - "D100", # Missing docstring in public module - "D104", # Missing docstring in public package -] - -[tool.ruff.lint.isort] -force-single-line = true -lines-after-imports = 2 - -[tool.ruff.lint.pydocstyle] -convention = "google" - -[tool.commitizen] -name = "cz_conventional_commits" -tag_format = "$version" -version_scheme = "semver2" -version_provider = "pep621" -update_changelog_on_bump = true -major_version_zero = true diff --git a/tests/test_import.py b/tests/test_import.py index a48524f..c73355c 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -5,13 +5,14 @@ import repo_scaffold + def test_imports(): """Test import modules.""" - prefix = "{}.".format(repo_scaffold.__name__) # noqa + prefix = "{}.".format(repo_scaffold.__name__) # noqa iter_packages = pkgutil.walk_packages( - repo_scaffold.__path__, # noqa + repo_scaffold.__path__, prefix, ) for _, name, _ in iter_packages: module_name = name if name.startswith(prefix) else prefix + name - importlib.import_module(module_name) \ No newline at end of file + importlib.import_module(module_name) diff --git a/uv.lock b/uv.lock index 404426c..695f2e0 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,4 @@ version = 1 -revision = 1 requires-python = ">=3.12" [[package]] @@ -24,15 +23,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419 }, ] -[[package]] -name = "attrs" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, -] - [[package]] name = "babel" version = "2.17.0" @@ -128,18 +118,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] -[[package]] -name = "colorlog" -version = "6.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d3/7a/359f4d5df2353f26172b3cc39ea32daa39af8de522205f512f458923e677/colorlog-6.9.0.tar.gz", hash = "sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2", size = 16624 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/51/9b208e85196941db2f0654ad0357ca6388ab3ed67efdbfc799f35d1f83aa/colorlog-6.9.0-py3-none-any.whl", hash = "sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff", size = 11424 }, -] - [[package]] name = "commitizen" version = "4.2.1" @@ -228,36 +206,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bf/70/3ea48dc9e958d7d66c44c9944809181f1ca79aaef25703c023b5092d34ff/decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed", size = 7854 }, ] -[[package]] -name = "dependency-groups" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/57/cd53c3e335eafbb0894449af078e2b71db47e9939ce2b45013e5a9fe89b7/dependency_groups-1.3.0.tar.gz", hash = "sha256:5b9751d5d98fbd6dfd038a560a69c8382e41afcbf7ffdbcc28a2a3f85498830f", size = 9832 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/2c/3e3afb1df3dc8a8deeb143f6ac41acbfdfae4f03a54c760871c56832a554/dependency_groups-1.3.0-py3-none-any.whl", hash = "sha256:1abf34d712deda5581e80d507512664d52b35d1c2d7caf16c85e58ca508547e0", size = 8597 }, -] - -[[package]] -name = "distlib" -version = "0.3.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, -] - -[[package]] -name = "filelock" -version = "3.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, -] - [[package]] name = "ghp-import" version = "2.1.0" @@ -528,23 +476,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/d7/1d35cce198f76e8ae4010a71ff5acabe8b75aeb35f8c3d920e175a6476ca/mkdocstrings_python-1.15.0-py3-none-any.whl", hash = "sha256:77aced1bb28840d7d3510f77353319eeb450961880d87f9c53fdab331ba0120d", size = 449068 }, ] -[[package]] -name = "nox" -version = "2025.2.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argcomplete" }, - { name = "attrs" }, - { name = "colorlog" }, - { name = "dependency-groups" }, - { name = "packaging" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/22/84a2d3442cb33e6fb1af18172a15deb1eea3f970417f1f4c5fa1600143e8/nox-2025.2.9.tar.gz", hash = "sha256:d50cd4ca568bd7621c2e6cbbc4845b3b7f7697f25d5fb0190ce8f4600be79768", size = 4021103 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/ca/64e634c056cba463cac743735660a772ab78eb26ec9759e88de735f2cd27/nox-2025.2.9-py3-none-any.whl", hash = "sha256:7d1e92d1918c6980d70aee9cf1c1d19d16faa71c4afe338fffd39e8a460e2067", size = 71315 }, -] - [[package]] name = "packaging" version = "24.2" @@ -778,7 +709,7 @@ wheels = [ [[package]] name = "repo-scaffold" -version = "0.9.0" +version = "0.10.1" source = { editable = "." } dependencies = [ { name = "click" }, @@ -789,7 +720,6 @@ dependencies = [ [package.optional-dependencies] dev = [ { name = "commitizen" }, - { name = "nox" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-mock" }, @@ -808,7 +738,7 @@ docs = [ [package.metadata] requires-dist = [ { name = "click", specifier = ">=8.1.8" }, - { name = "commitizen", marker = "extra == 'dev'", specifier = ">=4.1.0" }, + { name = "commitizen", marker = "extra == 'dev'", specifier = ">=3.12.0" }, { name = "cookiecutter", specifier = ">=2.6.0" }, { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.5.3" }, { name = "mkdocs-gen-files", marker = "extra == 'docs'", specifier = ">=0.5.0" }, @@ -816,15 +746,13 @@ requires-dist = [ { name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.5.3" }, { name = "mkdocstrings", marker = "extra == 'docs'", specifier = ">=0.24.0" }, { name = "mkdocstrings-python", marker = "extra == 'docs'", specifier = ">=1.7.5" }, - { name = "nox", marker = "extra == 'dev'", specifier = ">=2024.10.9" }, { name = "pymdown-extensions", marker = "extra == 'docs'", specifier = ">=10.7" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.4" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0.0" }, { name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.14.0" }, { name = "ruff", specifier = ">=0.9.6" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9.6" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9.7" }, ] -provides-extras = ["dev", "docs"] [[package]] name = "requests" @@ -856,27 +784,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/e1/e265aba384343dd8ddd3083f5e33536cd17e1566c41453a5517b5dd443be/ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9", size = 3639454 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/e3/3d2c022e687e18cf5d93d6bfa2722d46afc64eaa438c7fbbdd603b3597be/ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba", size = 11714128 }, - { url = "https://files.pythonhosted.org/packages/e1/22/aff073b70f95c052e5c58153cba735748c9e70107a77d03420d7850710a0/ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504", size = 11682539 }, - { url = "https://files.pythonhosted.org/packages/75/a7/f5b7390afd98a7918582a3d256cd3e78ba0a26165a467c1820084587cbf9/ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83", size = 11132512 }, - { url = "https://files.pythonhosted.org/packages/a6/e3/45de13ef65047fea2e33f7e573d848206e15c715e5cd56095589a7733d04/ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc", size = 11929275 }, - { url = "https://files.pythonhosted.org/packages/7d/f2/23d04cd6c43b2e641ab961ade8d0b5edb212ecebd112506188c91f2a6e6c/ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b", size = 11466502 }, - { url = "https://files.pythonhosted.org/packages/b5/6f/3a8cf166f2d7f1627dd2201e6cbc4cb81f8b7d58099348f0c1ff7b733792/ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e", size = 12676364 }, - { url = "https://files.pythonhosted.org/packages/f5/c4/db52e2189983c70114ff2b7e3997e48c8318af44fe83e1ce9517570a50c6/ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666", size = 13335518 }, - { url = "https://files.pythonhosted.org/packages/66/44/545f8a4d136830f08f4d24324e7db957c5374bf3a3f7a6c0bc7be4623a37/ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5", size = 12823287 }, - { url = "https://files.pythonhosted.org/packages/c5/26/8208ef9ee7431032c143649a9967c3ae1aae4257d95e6f8519f07309aa66/ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5", size = 14592374 }, - { url = "https://files.pythonhosted.org/packages/31/70/e917781e55ff39c5b5208bda384fd397ffd76605e68544d71a7e40944945/ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217", size = 12500173 }, - { url = "https://files.pythonhosted.org/packages/84/f5/e4ddee07660f5a9622a9c2b639afd8f3104988dc4f6ba0b73ffacffa9a8c/ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6", size = 11906555 }, - { url = "https://files.pythonhosted.org/packages/f1/2b/6ff2fe383667075eef8656b9892e73dd9b119b5e3add51298628b87f6429/ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897", size = 11538958 }, - { url = "https://files.pythonhosted.org/packages/3c/db/98e59e90de45d1eb46649151c10a062d5707b5b7f76f64eb1e29edf6ebb1/ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08", size = 12117247 }, - { url = "https://files.pythonhosted.org/packages/ec/bc/54e38f6d219013a9204a5a2015c09e7a8c36cedcd50a4b01ac69a550b9d9/ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656", size = 12554647 }, - { url = "https://files.pythonhosted.org/packages/a5/7d/7b461ab0e2404293c0627125bb70ac642c2e8d55bf590f6fce85f508f1b2/ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d", size = 9949214 }, - { url = "https://files.pythonhosted.org/packages/ee/30/c3cee10f915ed75a5c29c1e57311282d1a15855551a64795c1b2bbe5cf37/ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa", size = 10999914 }, - { url = "https://files.pythonhosted.org/packages/e8/a8/d71f44b93e3aa86ae232af1f2126ca7b95c0f515ec135462b3e1f351441c/ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a", size = 10177499 }, +version = "0.11.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/0a/92416b159ec00cdf11e5882a9d80d29bf84bba3dbebc51c4898bfbca1da6/ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603", size = 4202289 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/cc/53eb79f012d15e136d40a8e8fc519ba8f55a057f60b29c2df34efd47c6e3/ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc", size = 10285597 }, + { url = "https://files.pythonhosted.org/packages/e7/d7/73386e9fb0232b015a23f62fea7503f96e29c29e6c45461d4a73bac74df9/ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3", size = 11053154 }, + { url = "https://files.pythonhosted.org/packages/4e/eb/3eae144c5114e92deb65a0cb2c72326c8469e14991e9bc3ec0349da1331c/ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa", size = 10403048 }, + { url = "https://files.pythonhosted.org/packages/29/64/20c54b20e58b1058db6689e94731f2a22e9f7abab74e1a758dfba058b6ca/ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012", size = 10597062 }, + { url = "https://files.pythonhosted.org/packages/29/3a/79fa6a9a39422a400564ca7233a689a151f1039110f0bbbabcb38106883a/ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a", size = 10155152 }, + { url = "https://files.pythonhosted.org/packages/e5/a4/22c2c97b2340aa968af3a39bc38045e78d36abd4ed3fa2bde91c31e712e3/ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7", size = 11723067 }, + { url = "https://files.pythonhosted.org/packages/bc/cf/3e452fbd9597bcd8058856ecd42b22751749d07935793a1856d988154151/ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a", size = 12460807 }, + { url = "https://files.pythonhosted.org/packages/2f/ec/8f170381a15e1eb7d93cb4feef8d17334d5a1eb33fee273aee5d1f8241a3/ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13", size = 12063261 }, + { url = "https://files.pythonhosted.org/packages/0d/bf/57208f8c0a8153a14652a85f4116c0002148e83770d7a41f2e90b52d2b4e/ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be", size = 11329601 }, + { url = "https://files.pythonhosted.org/packages/c3/56/edf942f7fdac5888094d9ffa303f12096f1a93eb46570bcf5f14c0c70880/ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd", size = 11522186 }, + { url = "https://files.pythonhosted.org/packages/ed/63/79ffef65246911ed7e2290aeece48739d9603b3a35f9529fec0fc6c26400/ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef", size = 10449032 }, + { url = "https://files.pythonhosted.org/packages/88/19/8c9d4d8a1c2a3f5a1ea45a64b42593d50e28b8e038f1aafd65d6b43647f3/ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5", size = 10129370 }, + { url = "https://files.pythonhosted.org/packages/bc/0f/2d15533eaa18f460530a857e1778900cd867ded67f16c85723569d54e410/ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02", size = 11123529 }, + { url = "https://files.pythonhosted.org/packages/4f/e2/4c2ac669534bdded835356813f48ea33cfb3a947dc47f270038364587088/ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c", size = 11577642 }, + { url = "https://files.pythonhosted.org/packages/a7/9b/c9ddf7f924d5617a1c94a93ba595f4b24cb5bc50e98b94433ab3f7ad27e5/ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6", size = 10475511 }, + { url = "https://files.pythonhosted.org/packages/fd/d6/74fb6d3470c1aada019ffff33c0f9210af746cca0a4de19a1f10ce54968a/ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832", size = 11523573 }, + { url = "https://files.pythonhosted.org/packages/44/42/d58086ec20f52d2b0140752ae54b355ea2be2ed46f914231136dd1effcc7/ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5", size = 10697770 }, ] [[package]] @@ -933,20 +861,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, ] -[[package]] -name = "virtualenv" -version = "20.29.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f1/88/dacc875dd54a8acadb4bcbfd4e3e86df8be75527116c91d8f9784f5e9cab/virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728", size = 4320272 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/fa/849483d56773ae29740ae70043ad88e068f98a6401aa819b5d6bee604683/virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a", size = 4301478 }, -] - [[package]] name = "watchdog" version = "6.0.0"