├── CHANGELOG.md ├── docs ├── docs │ ├── CNAME │ ├── index.md │ ├── changelog.md │ ├── contributing.md │ └── code_of_conduct.md └── mkdocs.yml ├── .gitignore ├── project ├── CHANGELOG.md ├── tests │ ├── conftest.py │ ├── test_core.py.jinja │ └── __init__.py.jinja ├── {%-if build_doc-%}docs{%-endif-%} │ ├── docs │ │ ├── index.md │ │ ├── changelog.md │ │ ├── contributing.md │ │ └── code_of_conduct.md │ └── mkdocs.yml.jinja ├── {{_copier_conf.answers_file}}.jinja ├── CODE_OF_CONDUCT.md.jinja ├── src │ └── {{python_package_import_name}} │ │ └── __init__.py.jinja ├── noxfile.py ├── .gitignore ├── .github │ ├── ISSUE_TEMPLATE │ │ ├── feature_request.md │ │ └── bug_report.md.jinja │ └── workflows │ │ ├── {%-if build_doc-%}doc.yml{%-endif-%} │ │ ├── release.yml │ │ └── ci.yml ├── .pre-commit-config.yaml ├── news │ └── towncrier_template.md ├── README.md.jinja ├── tasks │ └── release.py ├── CONTRIBUTING.md.jinja └── pyproject.toml.jinja ├── tests ├── run-tests.ps1 ├── run-tests.sh ├── setup.sh └── setup.ps1 ├── CODE_OF_CONDUCT.md ├── changelogithub.config.json ├── .github └── workflows │ ├── doc.yml │ └── ci.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md └── copier.yml /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | -------------------------------------------------------------------------------- /docs/docs/CNAME: -------------------------------------------------------------------------------- 1 | copier-pdm.fming.dev 2 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | --8<-- "../README.md" 2 | -------------------------------------------------------------------------------- /docs/docs/changelog.md: -------------------------------------------------------------------------------- 1 | --8<-- "../CHANGELOG.md" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | tests/tmp/ 3 | site/ 4 | venv/ 5 | -------------------------------------------------------------------------------- /docs/docs/contributing.md: -------------------------------------------------------------------------------- 1 | --8<-- "../CONTRIBUTING.md" 2 | -------------------------------------------------------------------------------- /docs/docs/code_of_conduct.md: -------------------------------------------------------------------------------- 1 | --8<-- "../CODE_OF_CONDUCT.md" 2 | -------------------------------------------------------------------------------- /project/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | -------------------------------------------------------------------------------- /project/tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Configuration for the pytest test suite.""" 2 | -------------------------------------------------------------------------------- /project/{%-if build_doc-%}docs{%-endif-%}/docs/index.md: -------------------------------------------------------------------------------- 1 | --8<-- "../README.md" 2 | -------------------------------------------------------------------------------- /project/{%-if build_doc-%}docs{%-endif-%}/docs/changelog.md: -------------------------------------------------------------------------------- 1 | --8<-- "../CHANGELOG.md" 2 | -------------------------------------------------------------------------------- /project/{%-if build_doc-%}docs{%-endif-%}/docs/contributing.md: -------------------------------------------------------------------------------- 1 | --8<-- "../CONTRIBUTING.md" 2 | -------------------------------------------------------------------------------- /project/{%-if build_doc-%}docs{%-endif-%}/docs/code_of_conduct.md: -------------------------------------------------------------------------------- 1 | --8<-- "../CODE_OF_CONDUCT.md" 2 | -------------------------------------------------------------------------------- /project/tests/test_core.py.jinja: -------------------------------------------------------------------------------- 1 | import {{ python_package_import_name }} 2 | 3 | 4 | def test_import_package(): 5 | pass 6 | -------------------------------------------------------------------------------- /project/{{_copier_conf.answers_file}}.jinja: -------------------------------------------------------------------------------- 1 | # Changes here will be overwritten by Copier 2 | {{_copier_answers|to_nice_yaml}} 3 | -------------------------------------------------------------------------------- /tests/run-tests.ps1: -------------------------------------------------------------------------------- 1 | . tests/setup.ps1 2 | 3 | pdm install 4 | pdm run test 5 | 6 | Pop-Location 7 | 8 | Remove-Item -Recurse -Force $Dest 9 | -------------------------------------------------------------------------------- /tests/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source tests/setup.sh 4 | 5 | pdm install 6 | pdm run test 7 | 8 | popd 9 | rm -rf $DEST 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Everyone interacting in the copier-pdm project's codebases and issue trackers is expected to 2 | follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/). 3 | -------------------------------------------------------------------------------- /project/tests/__init__.py.jinja: -------------------------------------------------------------------------------- 1 | """Tests suite for `{{ python_package_import_name }}`.""" 2 | 3 | from pathlib import Path 4 | 5 | TESTS_DIR = Path(__file__).parent 6 | FIXTURES_DIR = TESTS_DIR / "fixtures" 7 | -------------------------------------------------------------------------------- /project/CODE_OF_CONDUCT.md.jinja: -------------------------------------------------------------------------------- 1 | Everyone interacting in the {{python_package_distribution_name}} project's codebases and issue trackers is expected to 2 | follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/). 3 | -------------------------------------------------------------------------------- /changelogithub.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": { 3 | "feat": { "title": "🚀 Features" }, 4 | "fix": { "title": "🐞 Bug Fixes" }, 5 | "doc": { "title": "📝 Documentation" }, 6 | "chore": { "title": "💻 Chores" } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /project/src/{{python_package_import_name}}/__init__.py.jinja: -------------------------------------------------------------------------------- 1 | """ 2 | {{ project_name }} 3 | 4 | {{ project_description }} 5 | :author: {{ author_fullname }} <{{ author_email }}> 6 | :license: {{ copyright_license }} 7 | """ 8 | -------------------------------------------------------------------------------- /project/noxfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import nox 4 | 5 | os.environ.update(PDM_IGNORE_SAVED_PYTHON="1", PDM_USE_VENV="1") 6 | 7 | 8 | @nox.session(python=("3.7", "3.8", "3.9", "3.10")) 9 | def test(session): 10 | session.run("pdm", "install", "-Gtest", external=True) 11 | session.run("pytest", "tests/") 12 | -------------------------------------------------------------------------------- /project/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | __pycache__/ 3 | dist/ 4 | build/ 5 | .coverage* 6 | *.egg-info/ 7 | pip-wheel-metadata/ 8 | .pytest_cache/ 9 | .mypy_cache/ 10 | 11 | docs/_build/ 12 | docs/site 13 | 14 | .venv/ 15 | venv/ 16 | env/ 17 | 18 | .pdm.toml 19 | __pypackages__/ 20 | 21 | .nox/ 22 | .tox/ 23 | 24 | .vscode/ 25 | .idea/ 26 | -------------------------------------------------------------------------------- /project/.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/doc.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "*.md" 9 | - docs/** 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build-doc: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - uses: pdm-project/setup-pdm@v2 19 | with: 20 | python-version: 3.8 21 | - name: Build pages 22 | run: | 23 | pip install mkdocs mkdocs-material 24 | cd docs && mkdocs build 25 | - name: Deploy 26 | uses: peaceiris/actions-gh-pages@v3 27 | with: 28 | github_token: ${{ secrets.GITHUB_TOKEN }} 29 | publish_dir: ./docs/site 30 | -------------------------------------------------------------------------------- /project/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v3.2.0 6 | hooks: 7 | - id: trailing-whitespace 8 | - id: end-of-file-fixer 9 | - id: check-added-large-files 10 | 11 | - repo: https://github.com/charliermarsh/ruff-pre-commit 12 | rev: 'v0.0.260' 13 | hooks: 14 | - id: ruff 15 | args: [--fix, --exit-non-zero-on-fix, --show-fixes] 16 | 17 | - repo: https://github.com/psf/black 18 | rev: 23.3.0 19 | hooks: 20 | - id: black 21 | 22 | - repo: https://github.com/pre-commit/mirrors-mypy 23 | rev: v1.1.1 24 | hooks: 25 | - id: mypy 26 | -------------------------------------------------------------------------------- /project/.github/workflows/{%-if build_doc-%}doc.yml{%-endif-%}: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | paths: 9 | - "*.md" 10 | - docs/** 11 | 12 | jobs: 13 | build-doc: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: pdm-project/setup-pdm@main 19 | with: 20 | python-version: 3.x 21 | cache: true 22 | - name: Build pages 23 | run: | 24 | pdm install -s doc 25 | cd docs && pdm run mkdocs build 26 | - name: Deploy 27 | uses: peaceiris/actions-gh-pages@v3 28 | with: 29 | github_token: ${{ secrets.GITHUB_TOKEN }} 30 | publish_dir: ./docs/site 31 | -------------------------------------------------------------------------------- /project/.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | release-pypi: 10 | name: release-pypi 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-python@v4 16 | with: 17 | python-version: '3.10' 18 | - name: Build artifacts 19 | run: | 20 | pip install build 21 | python -m build 22 | - name: Test Build 23 | run: | 24 | python3 -m venv fresh_env 25 | . fresh_env/bin/activate 26 | pip install dist/*.whl 27 | - name: Upload to Pypi 28 | run: | 29 | pip install twine 30 | twine upload --username __token__ --password ${{ secrets.PYPI_TOKEN }} dist/* 31 | -------------------------------------------------------------------------------- /project/.github/ISSUE_TEMPLATE/bug_report.md.jinja: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: unconfirmed 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Run command '...' 17 | 3. Scroll down to '...' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **System (please complete the following information):** 27 | - `{{ project_name }}` version: [e.g. 0.2.1] 28 | - Python version: [e.g. 3.8] 29 | - OS: [Windows/Linux] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /project/news/towncrier_template.md: -------------------------------------------------------------------------------- 1 | {% if top_line %} 2 | ## {{ top_line }} 3 | {% endif %} 4 | {% for section in sections %} 5 | {% if sections[section] %} 6 | {% for category, val in definitions.items() if category in sections[section] and category != 'trivial' %} 7 | 8 | ### {{ definitions[category]['name'] }} 9 | 10 | {% if definitions[category]['showcontent'] %} 11 | {% for text, values in sections[section][category]|dictsort(by='value') %} 12 | 13 | - {{ text }} {% if category != 'process' %}{{ values|sort|join(',\n ') }}{% endif %} 14 | 15 | {% endfor %} 16 | {% else %} 17 | 18 | - {{ sections[section][category]['']|sort|join(', ') }} 19 | {% endif %} 20 | {% if sections[section][category]|length == 0 %} 21 | 22 | No significant changes. 23 | {% else %} 24 | {% endif %} 25 | {% endfor %} 26 | {% else %} 27 | 28 | No significant changes. 29 | {% endif %} 30 | {% endfor %} 31 | -------------------------------------------------------------------------------- /project/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - "docs/**" 7 | - "news/**" 8 | - "*.md" 9 | push: 10 | branches: 11 | - master 12 | - main 13 | paths-ignore: 14 | - "docs/**" 15 | - "news/**" 16 | - "*.md" 17 | 18 | jobs: 19 | Testing: 20 | runs-on: ${{ matrix.os }} 21 | strategy: 22 | matrix: 23 | python-version: [3.7, 3.8, 3.9, '3.10'] 24 | os: [ubuntu-latest, macOS-latest, windows-latest] 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | - name: Set up PDM 29 | uses: pdm-project/setup-pdm@main 30 | with: 31 | python-version: ${{ matrix.python-version }} 32 | cache: true 33 | 34 | - name: Install dependencies 35 | run: pip install nox 36 | - name: Run Tests 37 | run: nox -s test-${{ matrix.python-version }} 38 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: "Copier PDM" 2 | site_description: "A Copier template for PDM projects" 3 | site_url: "https://copier-pdm.fming.dev" 4 | repo_url: "https://github.com/pdm-project/copier-pdm" 5 | repo_name: "pdm-project/copier-pdm" 6 | site_dir: "site" 7 | 8 | nav: 9 | - Home: 10 | - Overview: index.md 11 | - Changelog: changelog.md 12 | - Development: 13 | - Contributing: contributing.md 14 | - Code of Conduct: code_of_conduct.md 15 | 16 | theme: 17 | name: material 18 | palette: 19 | primary: deep purple 20 | accent: teal 21 | font: 22 | text: Open Sans 23 | code: Fira Code 24 | 25 | markdown_extensions: 26 | - admonition 27 | - pymdownx.emoji 28 | - pymdownx.magiclink 29 | - pymdownx.snippets: 30 | check_paths: true 31 | - pymdownx.superfences 32 | - pymdownx.tabbed 33 | - pymdownx.tasklist 34 | - toc: 35 | permalink: "#" 36 | 37 | plugins: 38 | - search 39 | -------------------------------------------------------------------------------- /tests/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -o pipefail 4 | 5 | DEST=tests/tmp 6 | 7 | rm -fr ${DEST} 8 | 9 | echo "///////////////////////////////////////////" 10 | echo " TAGGING TEMPLATE COPY" 11 | echo "///////////////////////////////////////////" 12 | template=$(mktemp -d) 13 | cp -rf . $template 14 | 15 | pushd $template 16 | git add -A . || true 17 | git commit -m "test" || true 18 | git tag 99.99.99 19 | popd 20 | 21 | echo "Template is located at ${template}" 22 | echo 23 | echo "///////////////////////////////////////////" 24 | echo " GENERATING PROJECT" 25 | echo "///////////////////////////////////////////" 26 | echo 27 | 28 | copier -f "${template}" "${DEST}" \ 29 | -d project_name="copier-pdm-testing" \ 30 | -d project_description='Testing this great template' \ 31 | -d author_fullname="Tester" \ 32 | -d author_username="tester" \ 33 | -d author_email="tester@example.org" 34 | 35 | pushd $DEST 36 | git init . 37 | git add -A . 38 | git commit -m "test" 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome, and they are greatly appreciated! 4 | Every little bit helps, and credit will always be given. 5 | 6 | ## Environment setup 7 | 8 | To run the test suites locally, you only need two tools, [copier](https://copier.readthedocs.io/) 9 | and `git`. 10 | 11 | ```bash 12 | python3 -m pip install --user pipx 13 | 14 | pipx install copier 15 | ``` 16 | 17 | ## Running tests 18 | 19 | To run the tests, run `bash tests/run-tests.sh` or `./tests/run-tests.ps1` depending on which platform you are on. 20 | 21 | ## Serving docs 22 | 23 | ```bash 24 | python3 -m venv venv 25 | . venv/bin/activate 26 | pip install mkdocs mkdocs-material 27 | mkdocs serve 28 | ``` 29 | 30 | You can also install `mkdocs` with `pipx` and 31 | inject `mkdocs-material` in its venv, 32 | this way you don't need to create one yourself: 33 | 34 | ```bash 35 | python3 -m pip install --user pipx 36 | pipx install mkdocs 37 | pipx inject mkdocs mkdocs-material 38 | mkdocs serve 39 | ``` 40 | -------------------------------------------------------------------------------- /project/{%-if build_doc-%}docs{%-endif-%}/mkdocs.yml.jinja: -------------------------------------------------------------------------------- 1 | site_name: "{{ project_name }}" 2 | site_description: "{{ project_description }}" 3 | site_url: "https://{{ repository_namespace }}.github.io/{{ repository_name }}" 4 | repo_url: "https://github.com/{{ repository_namespace }}/{{ repository_name }}" 5 | repo_name: "{{ repository_namespace }}/{{ repository_name }}" 6 | site_dir: "site" 7 | 8 | nav: 9 | - Home: 10 | - Overview: index.md 11 | - Changelog: changelog.md 12 | - Development: 13 | - Contributing: contributing.md 14 | - Code of Conduct: code_of_conduct.md 15 | 16 | theme: 17 | name: material 18 | palette: 19 | primary: deep purple 20 | accent: teal 21 | font: 22 | text: Open Sans 23 | code: Fira Code 24 | 25 | 26 | markdown_extensions: 27 | - admonition 28 | - pymdownx.emoji 29 | - pymdownx.magiclink 30 | - pymdownx.snippets: 31 | check_paths: true 32 | - pymdownx.superfences 33 | - pymdownx.tabbed 34 | - pymdownx.tasklist 35 | - toc: 36 | permalink: "#" 37 | 38 | plugins: 39 | - search 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Frost Ming 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /project/README.md.jinja: -------------------------------------------------------------------------------- 1 | # {{ project_name }} 2 | 3 | [![Tests](https://github.com/{{ repository_namespace }}/{{ repository_name }}/workflows/Tests/badge.svg)](https://github.com/{{ repository_namespace }}/{{ repository_name }}/actions?query=workflow%3Aci) 4 | [![pypi version](https://img.shields.io/pypi/v/{{ repository_name }}.svg)](https://pypi.org/project/{{ repository_name }}/) 5 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 6 | [![pdm-managed](https://img.shields.io/badge/pdm-managed-blueviolet)](https://pdm.fming.dev) 7 | 8 | {{ project_description }} 9 | 10 | ## Requirements 11 | 12 | {{ project_name }} requires Python {{ python_package_requires_python }} 13 | 14 | ## Installation 15 | 16 | It is recommended to install with `pipx`, if `pipx` haven't been installed yet, refer to the [pipx's docs](https://github.com/pipxproject/pipx) 17 | 18 | ```bash 19 | $ pipx install {{ python_package_distribution_name }} 20 | ``` 21 | 22 | Alternatively, install with `pip` to the user site: 23 | 24 | ```bash 25 | $ python -m pip install --user {{ python_package_distribution_name }} 26 | ``` 27 | -------------------------------------------------------------------------------- /tests/setup.ps1: -------------------------------------------------------------------------------- 1 | $Dest = "tests/tmp" 2 | $ErrorActionPreference = "Stop" 3 | function New-TemporaryDirectory { 4 | $parent = [System.IO.Path]::GetTempPath() 5 | [string] $name = [System.Guid]::NewGuid() 6 | Join-Path $parent "copier-pdm-$($name)" 7 | } 8 | 9 | if (Test-Path $Dest) { Remove-Item -Recurse -Force $Dest } 10 | 11 | Write-Output "///////////////////////////////////////////" 12 | Write-Output " TAGGING TEMPLATE COPY" 13 | Write-Output "///////////////////////////////////////////" 14 | Write-Output "" 15 | $Template = (New-TemporaryDirectory) 16 | Copy-Item -Force -Recurse . $Template 17 | 18 | Push-Location $Template 19 | git add -A . 20 | git commit -m "test" 21 | git tag 99.99.99 22 | Pop-Location 23 | 24 | Write-Output "Template is located at ${template}" 25 | Write-Output "" 26 | Write-Output "///////////////////////////////////////////" 27 | Write-Output " GENERATING PROJECT" 28 | Write-Output "///////////////////////////////////////////" 29 | Write-Output "" 30 | 31 | copier -f $Template $Dest ` 32 | -d project_name="copier-pdm-testing" ` 33 | -d project_description='Testing this great template' ` 34 | -d author_fullname="Tester" ` 35 | -d author_username="tester" ` 36 | -d author_email="tester@example.org" 37 | 38 | Push-Location $Dest 39 | git init . 40 | git add -A . 41 | git commit -m "test" 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Copier PDM 2 | 3 | Copier template for PDM projects. 4 | 5 | This copier template is mainly for my own usage, but feel free to try it out, or fork it! 6 | 7 | You need to install [Copier](https://copier.readthedocs.io/en/stable/) first. 8 | 9 | ## Features 10 | 11 | ### Package manager 12 | 13 | The template project uses [PDM](https://pdm.fming.dev) setup, with pre-defined `pyproject.toml` 14 | 15 | ### Documentation and changelog 16 | 17 | - Documentation is built with [MkDocs](https://github.com/mkdocs/mkdocs) 18 | ([Material theme](https://github.com/squidfunk/mkdocs-material) 19 | - Changelog is auto-generated from the fragments under `news` directory, with the power of [towncrier](https://pypi.org/project/towncrier/) 20 | 21 | ### Pre-commit and linter 22 | 23 | [pre-commit](https://pre-commit.com/) is used for both commit hook and linting, including the following hooks: 24 | 25 | - [ruff](https://github.com/charliermarsh/ruff) 26 | 27 | ### VSCode default settings 28 | 29 | The `.vscode/settings.json` will also be generated with the project, to enable Pylance auto-completions and test discovery in VSCode. 30 | 31 | ### Tests 32 | 33 | - Tests run with [pytest](https://pytest.org/) 34 | - Multi-environment testing powered by [nox](https://nox.thea.codes/) 35 | 36 | ## Requirements 37 | 38 | Make below requirements are met to use the copier template: 39 | 40 | - Python 3 41 | - Git 42 | - [Copier](https://copier.readthedocs.io/en/stable/) 43 | 44 | ## Quick Start 45 | 46 | ```bash 47 | copier "https://github.com/pdm-project/copier-pdm.git" 48 | ``` 49 | 50 | Or even shorter: 51 | 52 | ```bash 53 | copier "gh:pdm-project/copier-pdm" 54 | ``` 55 | 56 | See the [documentation](https://copier-pdm.fming.dev) for more details. 57 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - 'docs/**' 7 | - '*.md' 8 | push: 9 | branches: 10 | - main 11 | paths-ignore: 12 | - 'docs/**' 13 | - '*.md' 14 | tags: 15 | - '*' 16 | 17 | permissions: 18 | contents: write 19 | 20 | jobs: 21 | Testing: 22 | runs-on: ${{ matrix.os }} 23 | strategy: 24 | matrix: 25 | python-version: [3.7, '3.10', '3.11'] 26 | os: [ubuntu-latest, macOS-latest] 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | - name: Set up PDM 31 | uses: pdm-project/setup-pdm@main 32 | with: 33 | python-version: ${{ matrix.python-version }} 34 | - name: Set Variables 35 | id: set_variables 36 | run: | 37 | git config --global user.name "GitHub Action" 38 | git config --global user.email "action@github.com" 39 | git config --global init.defaultBranch main 40 | 41 | - name: Install dependencies 42 | run: python -m pip install -U copier 43 | - name: Setup Tests(Unix) 44 | run: bash tests/setup.sh 45 | if: runner.os != 'Windows' 46 | 47 | - name: Run Tests 48 | run: | 49 | pdm use -f ${{ matrix.python-version }} 50 | pdm install -d 51 | pdm run test 52 | working-directory: ${{ github.workspace }}/tests/tmp 53 | 54 | Release: 55 | needs: Testing 56 | runs-on: ubuntu-latest 57 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') 58 | steps: 59 | - uses: actions/checkout@v3 60 | with: 61 | fetch-depth: 0 62 | 63 | - uses: actions/setup-node@v3 64 | with: 65 | node-version: 16 66 | 67 | - run: npx changelogithub 68 | env: 69 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 70 | -------------------------------------------------------------------------------- /copier.yml: -------------------------------------------------------------------------------- 1 | # CONFIGURATION ------------------------- 2 | _subdirectory: project 3 | _templates_suffix: .jinja 4 | _skip_if_exists: 5 | - CHANGELOG.md 6 | 7 | # PROMPT -------------------------------- 8 | project_name: 9 | type: str 10 | help: Your project name 11 | 12 | project_description: 13 | type: str 14 | help: Your project description 15 | 16 | author_fullname: 17 | type: str 18 | help: Your full name 19 | 20 | author_email: 21 | type: str 22 | help: Your email 23 | 24 | author_username: 25 | type: str 26 | help: Your username (e.g. on GitHub) 27 | 28 | repository_namespace: 29 | type: str 30 | help: Your repository namespace 31 | default: "{{ author_username }}" 32 | 33 | repository_name: 34 | type: str 35 | help: Your repository name 36 | default: "{{ project_name|lower|replace('_', '-')|replace(' ', '-') }}" 37 | 38 | copyright_holder: 39 | type: str 40 | help: The name of the person/entity holding the copyright 41 | default: "{{ author_fullname }}" 42 | 43 | copyright_holder_email: 44 | type: str 45 | help: The email of the person/entity holding the copyright 46 | default: "{{ author_email }}" 47 | 48 | copyright_date: 49 | type: str 50 | help: The copyright date 51 | default: "2023" 52 | 53 | copyright_license: 54 | type: str 55 | help: Your project's license 56 | default: MIT 57 | choices: 58 | - AFL-3.0 59 | - Apache-2.0 60 | - Artistic-2.0 61 | - BSD-2-Clause 62 | - BSD-3-Clause 63 | - BSL-1.0 64 | - EPL-1.0 65 | - EPL-2.0 66 | - EUPL-1.0 67 | - EUPL-2.0 68 | - AGPL-3.0 69 | - GPL-2.0 70 | - GPL-3.0 71 | - LGPL-2.0-or-later 72 | - LGPL-3.0 73 | - ISC 74 | - MIT 75 | - MPL-2.0 76 | - NCSA 77 | - OFL-1.1 78 | - OSL-3.0 79 | - PostgreSQL 80 | - Unlicense 81 | - Zlib 82 | 83 | python_package_distribution_name: 84 | type: str 85 | help: Your Python package distribution name (for `pip install NAME`) 86 | default: "{{ project_name|lower|replace('_', '-')|replace(' ', '-') }}" 87 | 88 | python_package_import_name: 89 | type: str 90 | help: Your Python package import name (for `import NAME` in Python code) 91 | default: "{{ project_name|lower|replace('-', '_')|replace(' ', '_') }}" 92 | 93 | python_package_requires_python: 94 | type: str 95 | help: The python version range your project supports 96 | default: ">=3.7" 97 | 98 | build_doc: 99 | type: bool 100 | help: Do you want to include a doc site? 101 | default: no 102 | -------------------------------------------------------------------------------- /project/tasks/release.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import subprocess 3 | import sys 4 | from pathlib import Path 5 | 6 | import parver 7 | 8 | PROJECT_DIR = Path(__file__).parent.parent 9 | 10 | 11 | def get_current_version(): 12 | cmd = ["git", "describe", "--tags", "--abbrev=0"] 13 | return subprocess.check_output(cmd).decode("utf-8").strip() 14 | 15 | 16 | def bump_version(pre=None, major=False, minor=False, patch=True): 17 | if not any([major, minor, patch]): 18 | patch = True 19 | if len([v for v in [major, minor, patch] if v]) != 1: 20 | print( 21 | "Only one option should be provided among " "(--major, --minor, --patch)", 22 | file=sys.stderr, 23 | ) 24 | sys.exit(1) 25 | current_version = parver.Version.parse(get_current_version()) 26 | if not pre: 27 | version_idx = [major, minor, patch].index(True) 28 | version = current_version.bump_release(index=version_idx).replace( 29 | pre=None, post=None 30 | ) 31 | else: 32 | version = current_version.bump_pre(pre) 33 | version = version.replace(local=None, dev=None) 34 | return str(version) 35 | 36 | 37 | def release(dry_run=False, commit=True, pre=None, major=False, minor=False, patch=True): 38 | new_version = bump_version(pre, major, minor, patch) 39 | print(f"Bump version to: {new_version}") 40 | if dry_run: 41 | subprocess.check_call(["towncrier", "--version", new_version, "--draft"]) 42 | else: 43 | subprocess.check_call(["towncrier", "--yes", "--version", new_version]) 44 | subprocess.check_call(["git", "add", "."]) 45 | if commit: 46 | subprocess.check_call(["git", "commit", "-m", f"Release {new_version}"]) 47 | subprocess.check_call( 48 | ["git", "tag", "-a", new_version, "-m", f"v{new_version}"] 49 | ) 50 | subprocess.check_call(["git", "push"]) 51 | subprocess.check_call(["git", "push", "--tags"]) 52 | 53 | 54 | def parse_args(argv=None): 55 | parser = argparse.ArgumentParser("release.py") 56 | 57 | parser.add_argument("--dry-run", action="store_true", help="Dry run mode") 58 | parser.add_argument( 59 | "--no-commit", 60 | action="store_false", 61 | dest="commit", 62 | default=True, 63 | help="Do not commit to Git", 64 | ) 65 | group = parser.add_argument_group(title="version part") 66 | group.add_argument("--pre", help="Pre tag") 67 | group.add_argument("--major", action="store_true", help="Bump major version") 68 | group.add_argument("--minor", action="store_true", help="Bump minor version") 69 | group.add_argument("--patch", action="store_true", help="Bump patch version") 70 | 71 | return parser.parse_args(argv) 72 | 73 | 74 | if __name__ == "__main__": 75 | args = parse_args() 76 | release(args.dry_run, args.commit, args.pre, args.major, args.minor, args.patch) 77 | -------------------------------------------------------------------------------- /project/CONTRIBUTING.md.jinja: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome, and they are greatly appreciated! 4 | Every little bit helps, and credit will always be given. 5 | 6 | ## Environment setup 7 | 8 | Nothing easier! 9 | 10 | Fork and clone the repository: 11 | 12 | ```bash 13 | git clone https://github.com/{{ repository_namespace }}/{{ repository_name }} 14 | cd {{ repository_name }} 15 | ``` 16 | 17 | We use [pdm](https://pdm.fming.dev) to manage the project and dependencies, install PDM if it isn't done yet, then: 18 | 19 | ```bash 20 | pdm install 21 | ``` 22 | 23 | You now have the dependencies installed. 24 | 25 | You can run the tests with `pdm run test [ARGS...]`. 26 | 27 | ## Test against multiple Python versions 28 | 29 | This project uses [nox](https://nox.thea.codes/) as the test runner. See what sessions are list: 30 | 31 | ```bash 32 | nox --list 33 | ``` 34 | 35 | And run the test suite on specified Python versions: 36 | 37 | ```bash 38 | nox -s tests-3.8 39 | ``` 40 | 41 | !!! important "TIPS" 42 | `nox` and `pre-commit` in the following section are not list in the `dev-dependencies` of the project, 43 | because they can be installed separately to the system and used via the external executable. If you are willing to 44 | reproduce the development environment without external dependencies. Run `pdm add -d nox pre-commit` and the 45 | corresponding commands should be prefixed with `pdm run` as well. 46 | 47 | ## Development 48 | 49 | As usual: 50 | 51 | 1. create a new branch: `git checkout -b feature-or-bugfix-name` 52 | 1. edit the code and/or the documentation 53 | 54 | If you updated the documentation or the project dependencies: 55 | 56 | 1. run `pdm run doc` 57 | 1. go to http://localhost:8000 and check that everything looks good 58 | 59 | **Before committing:** 60 | 61 | 1. Make sure you submit a news entry under `news/` directory with the name pattern `..md` where `` should be one of: 62 | 1. `bugfix` for bug fixes 63 | 1. `feature` for features and improvements 64 | 1. `doc` for documentation improvements 65 | 1. `remove` for deprecations and removals 66 | 1. `dep ` for dependencies updates 67 | 1. `misc` for miscellany tasks 68 | 69 | 1. Install [pre-commit](https://pre-commit.com/) and hooks: 70 | ```bash 71 | pre-commit install 72 | ``` 73 | 1. Then linter task will be run each time when you commit something. Or you can run it manually: 74 | ```bash 75 | pdm run lint 76 | ``` 77 | 78 | If you are unsure about how to fix or ignore a warning, 79 | just let the continuous integration fail, 80 | and we will help you during review. 81 | 82 | Don't bother updating the changelog, we will take care of this. 83 | 84 | ## Pull requests guidelines 85 | 86 | Link to any related issue in the Pull Request message. 87 | 88 | During review, we recommend using fixups: 89 | 90 | ```bash 91 | # SHA is the SHA of the commit you want to fix 92 | git commit --fixup=SHA 93 | ``` 94 | 95 | Once all the changes are approved, you can squash your commits: 96 | 97 | ```bash 98 | git rebase -i --autosquash master 99 | ``` 100 | 101 | And force-push: 102 | 103 | ```bash 104 | git push -f 105 | ``` 106 | 107 | If this seems all too complicated, you can push or force-push each new commit, 108 | and we will squash them ourselves if needed, before merging. 109 | -------------------------------------------------------------------------------- /project/pyproject.toml.jinja: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["pdm-backend"] 3 | build-backend = "pdm.backend" 4 | 5 | [project] 6 | name = "{{ python_package_distribution_name }}" 7 | description = "{{ project_description }}" 8 | authors = [ 9 | {name = "{{ author_fullname }}", email = "{{ author_email }}"} 10 | ] 11 | license = {text = "{{ copyright_license }}"} 12 | readme = "README.md" 13 | requires-python = "{{ python_package_requires_python }}" 14 | dependencies = [] 15 | dynamic = ["version"] 16 | 17 | classifiers = [ 18 | "Development Status :: 3 - Alpha", 19 | "Programming Language :: Python :: 3" 20 | ] 21 | 22 | [project.urls] 23 | Repository = "https://github.com/{{ repository_namespace }}/{{ repository_name }}" 24 | Homepage = "https://github.com/{{ repository_namespace }}/{{ repository_name }}" 25 | 26 | [tool.pdm.version] 27 | source = "scm" 28 | 29 | [tool.pdm.build] 30 | package-dir = "src" 31 | 32 | [tool.pdm.dev-dependencies] 33 | test = [ 34 | "pytest>=6.1", 35 | ] 36 | dev = [ 37 | "towncrier>=19.2", 38 | "parver>=0.3", 39 | ] 40 | {% if build_doc %} 41 | doc = [ 42 | "mkdocs>=1.1", 43 | "mkdocs-material>=6.2", 44 | ] 45 | {% endif %} 46 | [tool.pdm.scripts] 47 | release = "python -m tasks.release" 48 | test = "pytest tests/" 49 | {% if build_doc %}doc = {shell = "cd docs && mkdocs serve", help = "Start the dev server for doc preview"}{% endif %} 50 | lint = "pre-commit run --all-files" 51 | 52 | [tool.black] 53 | line-length = 100 54 | target-version = ["py37", "py38", "py39", "py310"] 55 | 56 | [tool.ruff] 57 | line-length = 100 58 | select = [ 59 | "B", # flake8-bugbear 60 | "C4", # flake8-comprehensions 61 | "E", # pycodestyle 62 | "F", # pyflakes 63 | "PGH", # pygrep-hooks 64 | "RUF", # ruff 65 | "W", # pycodestyle 66 | "YTT", # flake8-2020 67 | ] 68 | extend-ignore = ["B018", "B019"] 69 | src = ["src"] 70 | extend-exclude = ["tests/fixtures"] 71 | target-version = "py37" 72 | 73 | [tool.ruff.mccabe] 74 | max-complexity = 10 75 | 76 | [tool.ruff.isort] 77 | known-first-party = ["{{python_package_import_name}}"] 78 | 79 | [tool.mypy] 80 | follow_imports = "silent" 81 | ignore_missing_imports = true 82 | disallow_incomplete_defs = true 83 | disallow_untyped_defs = true 84 | disallow_untyped_decorators = true 85 | namespace_packages = true 86 | mypy_path = "src" 87 | explicit_package_bases = true 88 | 89 | [tool.towncrier] 90 | package = "{{python_package_import_name}}" 91 | filename = "CHANGELOG.md" 92 | issue_format = "{{'[#{issue}]'}}(https://github.com/{{ repository_namespace }}/{{ repository_name }}/issues/{issue})" 93 | directory = "news/" 94 | start_string = "" 95 | title_format = "[v{version}](https://github.com/{{ repository_namespace }}/{{ repository_name }}/releases/tag/{version}) ({project_date})" 96 | template = "news/towncrier_template.md" 97 | underlines = "-~^" 98 | 99 | [[tool.towncrier.type]] 100 | directory = "feature" 101 | name = "Features & Improvements" 102 | showcontent = true 103 | 104 | [[tool.towncrier.type]] 105 | directory = "bugfix" 106 | name = "Bug Fixes" 107 | showcontent = true 108 | 109 | [[tool.towncrier.type]] 110 | directory = "doc" 111 | name = "Improved Documentation" 112 | showcontent = true 113 | 114 | [[tool.towncrier.type]] 115 | directory = "dep" 116 | name = "Dependencies" 117 | showcontent = true 118 | 119 | [[tool.towncrier.type]] 120 | directory = "removal" 121 | name = "Removals and Deprecations" 122 | showcontent = true 123 | 124 | [[tool.towncrier.type]] 125 | directory = "misc" 126 | name = "Miscellany" 127 | showcontent = true 128 | 129 | [tool.isort] 130 | profile = "black" 131 | atomic = true 132 | skip_glob = ["*/setup.py"] 133 | filter_files = true 134 | known_first_party = ["{{python_package_import_name}}"] 135 | 136 | [tool.pytest.ini_options] 137 | filterwarnings = [ 138 | "ignore::DeprecationWarning" 139 | ] 140 | --------------------------------------------------------------------------------