diff --git a/.github/workflows/ci.yml.jinja b/.github/workflows/ci.yml.jinja index 980f96cd..1f59a361 100644 --- a/.github/workflows/ci.yml.jinja +++ b/.github/workflows/ci.yml.jinja @@ -15,16 +15,16 @@ jobs: strategy: matrix: python-version: -{%- if version_between("3.8", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.8", min_py, max_py) %} - '3.8' {%- endif %} -{%- if version_between("3.9", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.9", min_py, max_py) %} - '3.9' {%- endif %} -{%- if version_between("3.10", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.10", min_py, max_py) %} - '3.10' {%- endif %} -{%- if version_between("3.11", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.11", min_py, max_py) %} - '3.11' {%- endif %} test: @@ -46,16 +46,16 @@ jobs: strategy: matrix: python-version: -{%- if version_between("3.8", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.8", min_py, max_py) %} - '3.8' {%- endif %} -{%- if version_between("3.9", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.9", min_py, max_py) %} - '3.9' {%- endif %} -{%- if version_between("3.10", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.10", min_py, max_py) %} - '3.10' {%- endif %} -{%- if version_between("3.11", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.11", min_py, max_py) %} - '3.11' {%- endif %} {%- if project_name == "Serious Scaffold Python" %} @@ -88,16 +88,16 @@ jobs: strategy: matrix: python-version: -{%- if version_between("3.8", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.8", min_py, max_py) %} - '3.8' {%- endif %} -{%- if version_between("3.9", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.9", min_py, max_py) %} - '3.9' {%- endif %} -{%- if version_between("3.10", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.10", min_py, max_py) %} - '3.10' {%- endif %} -{%- if version_between("3.11", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.11", min_py, max_py) %} - '3.11' {%- endif %} {%- endif %} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c17a9f61..3b579186 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: uses: actions/setup-python@v4 with: cache: pip - python-version: 3.x + python-version: '3.11' - run: env | sort - run: make dev-package - run: make build @@ -28,7 +28,7 @@ jobs: uses: actions/setup-python@v4 with: cache: pip - python-version: 3.x + python-version: '3.11' - run: env | sort - run: make dev-docs - run: make docs-all diff --git a/.github/workflows/release.yml.jinja b/.github/workflows/release.yml.jinja new file mode 100644 index 00000000..fd155b32 --- /dev/null +++ b/.github/workflows/release.yml.jinja @@ -0,0 +1,46 @@ +jobs: + deploy-package: + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + cache: pip + python-version: '{{ default_py }}' + - run: env | sort + - run: make dev-package + - run: make build + - name: Publish a Python distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: {{ '${{ secrets.PYPI_API_TOKEN }}' }} + deploy-pages: + permissions: + pages: write + id-token: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + cache: pip + python-version: '{{ default_py }}' + - run: env | sort + - run: make dev-docs + - run: make docs-all + - name: Upload pages artifact + uses: actions/upload-pages-artifact@v2 + with: + path: public + - id: deployment + name: Deploy to GitHub Pages + uses: actions/deploy-pages@v2 +name: Deploy Python Package & GitHub Pages +on: + release: + types: + - published diff --git a/.gitlab-ci.yml.jinja b/.gitlab-ci.yml.jinja index 43a20f38..11ed9823 100644 --- a/.gitlab-ci.yml.jinja +++ b/.gitlab-ci.yml.jinja @@ -3,7 +3,7 @@ stages: - lint_test - build_release default: - image: python:3.11 + image: python:{{ default_py }} lint: image: python:$PYTHON_VERSION interruptible: true @@ -14,16 +14,16 @@ lint: parallel: matrix: - PYTHON_VERSION: -{%- if version_between("3.8", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.8", min_py, max_py) %} - '3.8' {%- endif %} -{%- if version_between("3.9", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.9", min_py, max_py) %} - '3.9' {%- endif %} -{%- if version_between("3.10", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.10", min_py, max_py) %} - '3.10' {%- endif %} -{%- if version_between("3.11", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.11", min_py, max_py) %} - '3.11' {%- endif %} script: @@ -47,16 +47,16 @@ test: parallel: matrix: - PYTHON_VERSION: -{%- if version_between("3.8", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.8", min_py, max_py) %} - '3.8' {%- endif %} -{%- if version_between("3.9", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.9", min_py, max_py) %} - '3.9' {%- endif %} -{%- if version_between("3.10", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.10", min_py, max_py) %} - '3.10' {%- endif %} -{%- if version_between("3.11", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.11", min_py, max_py) %} - '3.11' {%- endif %} script: diff --git a/.readthedocs.yaml b/.readthedocs.yaml index e3c557fe..843b2f36 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,7 +6,7 @@ build: - make dev-docs os: ubuntu-22.04 tools: - python: '3' + python: '3.11' sphinx: fail_on_warning: true version: 2 diff --git a/.readthedocs.yaml.jinja b/.readthedocs.yaml.jinja new file mode 100644 index 00000000..e171bb4c --- /dev/null +++ b/.readthedocs.yaml.jinja @@ -0,0 +1,12 @@ +build: + jobs: + post_build: + - make reports + post_install: + - make dev-docs + os: ubuntu-22.04 + tools: + python: '{{ default_py }}' +sphinx: + fail_on_warning: true +version: 2 diff --git a/.vscode/settings.json b/.vscode/settings.json index a93b6d9b..42bd69fd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,6 +29,7 @@ "pathjoin", "pipenv", "pipx", + "pycache", "pydantic", "pytest", "Quickstart", @@ -74,15 +75,18 @@ "_exclude", "project_name", "project_description", - "repo_namespace", "author_name", + "organization_name", "author_email", + "repo_namespace", "repo_name", "package_name", "module_name", - "test_coverage_threshold", - "minimal_python_version", - "maximal_python_version", + "coverage_threshold", + "min_py", + "max_py", + "default_py", + "choices", "stages", "default", "lint", diff --git a/copier.yaml b/copier.yaml index 792304a7..e0043155 100644 --- a/copier.yaml +++ b/copier.yaml @@ -11,80 +11,89 @@ _exclude: - ~* project_name: default: Serious Scaffold Python - help: 'Project name in CamelCase:' + help: 'Enter the name of the project in CamelCase format:' type: str project_description: default: A development-focused Python project template with various integrations, configurations and modules. - help: 'Brief project description:' - type: str -repo_namespace: - default: serious-scaffold - help: 'Repo namespace, it should be the name of an user or an organization:' + help: 'Provide a brief description for the project:' type: str author_name: default: huxuan - help: 'Author name:' + help: 'Specify the name of the author:' + type: str +organization_name: + default: Serious Scaffold + help: 'Provide the name of the organization associated with the project:' type: str author_email: - default: i@{{ author_name }}.org - help: 'Author email:' + default: |- + {% if author_name == 'huxuan' and organization_name == 'Serious Scaffold' -%} + i@huxuan.org + {%- else -%} + {{ author_name }}@{{ organization_name|lower|replace(" ", "-") }}.com + {%- endif %} + help: 'Specify the email address of the author:' + type: str +repo_namespace: + default: '{{ organization_name|lower|replace(" ", "-") }}' + help: 'Indicate the GitHub Repository Owner or GitLab Namespace. This is typically the account name of the author or the organization:' type: str repo_name: default: '{{ project_name|lower|replace(" ", "-") }}' - help: 'Repo name:' + help: 'Provide a name for the repository:' type: str package_name: default: '{{ repo_name|regex_replace("-python$", "") }}' - help: 'Package name:' + help: 'Specify the name of the distributable package for the project (often used in "pip install "):' type: str module_name: default: '{{ package_name|lower|replace("-", "_") }}' - help: 'Module name:' + help: 'Specify the name of the primary module within the package (often used in "import "):' type: str -test_coverage_threshold: +coverage_threshold: default: 100 - help: 'Threshold for test coverage (0-100):' + help: 'Set the threshold for test coverage, ranging from 0 to 100:' type: int - validator: '{% if not 0 <= test_coverage_threshold <= 100 %}Invalid threshold{% endif %}' -minimal_python_version: + validator: '{% if not 0 <= coverage_threshold <= 100 %}Test Coverage threshold should be between 0 and 100{% endif %}' +min_py: choices: - '3.8' - '3.9' - '3.10' - '3.11' default: '3.8' - help: 'Minimal Python Version to support:' + help: 'Choose the minimal Python version the project should support:' type: str -maximal_python_version: +max_py: choices: '3.8': - validator: '{% from pathjoin("includes", "version_compare.jinja") import version_lt -%}{{ "Invalid" if version_lt("3.8", minimal_python_version) | bool }}' + validator: '{% from pathjoin("includes", "version_compare.jinja") import version_higher_than_validator %}{{ version_higher_than_validator("3.8", min_py) }}' value: '3.8' '3.9': - validator: '{% from pathjoin("includes", "version_compare.jinja") import version_lt -%}{{ "Invalid" if version_lt("3.9", minimal_python_version) | bool }}' + validator: '{% from pathjoin("includes", "version_compare.jinja") import version_higher_than_validator %}{{ version_higher_than_validator("3.9", min_py) }}' value: '3.9' '3.10': - validator: '{% from pathjoin("includes", "version_compare.jinja") import version_lt -%}{{ "Invalid" if version_lt("3.10", minimal_python_version) | bool }}' + validator: '{% from pathjoin("includes", "version_compare.jinja") import version_higher_than_validator %}{{ version_higher_than_validator("3.10", min_py) }}' value: '3.10' '3.11': value: '3.11' default: '3.11' - help: 'Maximal Python Version to support:' + help: 'Choose the maximal Python version the project should support:' type: str -default_python_version: +default_py: choices: '3.8': - validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between %}{{ "Invalid" if not version_between("3.8", minimal_python_version, maximal_python_version) | bool }}' + validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between_validator %}{{ version_between_validator("3.8", min_py, max_py) }}' value: '3.8' '3.9': - validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between %}{{ "Invalid" if not version_between("3.9", minimal_python_version, maximal_python_version) | bool }}' + validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between_validator %}{{ version_between_validator("3.9", min_py, max_py) }}' value: '3.9' '3.10': - validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between %}{{ "Invalid" if not version_between("3.10", minimal_python_version, maximal_python_version) | bool }}' + validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between_validator %}{{ version_between_validator("3.10", min_py, max_py) }}' value: '3.10' '3.11': - validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between %}{{ "Invalid" if not version_between("3.11", minimal_python_version, maximal_python_version) | bool }}' + validator: '{% from pathjoin("includes", "version_compare.jinja") import version_between_validator %}{{ version_between_validator("3.11", min_py, max_py) }}' value: '3.11' - default: '{{ maximal_python_version }}' - help: 'Default Python Version (e.g. for daily development, docs generation):' + default: '{{ max_py }}' + help: 'Choose the default Python version for development, documentation generation, and package build:' type: str diff --git a/includes/version_compare.jinja b/includes/version_compare.jinja index 97da4aab..0f45c83f 100644 --- a/includes/version_compare.jinja +++ b/includes/version_compare.jinja @@ -1,7 +1,23 @@ -{% macro version_lt(version1, version2) -%} -{{ version1.split(".") | map("int") | list < version2.split(".") | map("int") | list }} +{% macro version_higher_than(version1, version2) -%} + {{ "1" if version1.split(".") | map("int") | list >= version2.split(".") | map("int") | list }} +{%- endmacro %} + +{% macro version_higher_than_validator(version1, version2) -%} + {{ + "Invalid version. The version '%s' is not higher than '%s'." % (version1, version2) + if not version_higher_than(version1, version2) + }} {%- endmacro %} {% macro version_between(version, version_min, version_max) -%} -{{ (version_min.split(".") | map("int") | list <= version.split(".") | map("int") | list <= version_max.split(".") | map("int") | list ) }} + {{ + "1" if version_min.split(".") | map("int") | list <= version.split(".") | map("int") | list <= version_max.split(".") | map("int") | list + }} +{%- endmacro %} + +{% macro version_between_validator(version, version_min, version_max) -%} + {{ + "Invalid version. The version '%s' is not between '%s' and '%s'." % (version, version_min, version_max) + if not version_between(version, version_min, version_max) + }} {%- endmacro %} diff --git a/pyproject.toml.jinja b/pyproject.toml.jinja index 96e1d44c..e1903fe5 100644 --- a/pyproject.toml.jinja +++ b/pyproject.toml.jinja @@ -14,16 +14,16 @@ classifiers = [ "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", -{%- if version_between("3.10", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.10", min_py, max_py) %} "Programming Language :: Python :: 3.10", {%- endif %} -{%- if version_between("3.11", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.11", min_py, max_py) %} "Programming Language :: Python :: 3.11", {%- endif %} -{%- if version_between("3.8", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.8", min_py, max_py) %} "Programming Language :: Python :: 3.8", {%- endif %} -{%- if version_between("3.9", minimal_python_version, maximal_python_version) | bool %} +{%- if version_between("3.9", min_py, max_py) %} "Programming Language :: Python :: 3.9", {%- endif %} ] @@ -39,7 +39,7 @@ keywords = [ ] name = "{{ package_name }}" readme = "README.md" -requires-python = ">={{ minimal_python_version }}" +requires-python = ">={{ min_py }}" [project.scripts] {{ package_name }}-cli = "{{ module_name }}.cli:app" @@ -49,7 +49,7 @@ homepage = "https://github.com/{{ repo_namespace }}/{{ repo_name }}/" issue = "https://github.com/{{ repo_namespace }}/{{ repo_name }}/issues" [tool.coverage.report] -fail_under = 100 +fail_under = {{ coverage_threshold }} [tool.coverage.run] omit = [ diff --git a/src/{{ module_name }}/settings.py.jinja b/src/{{ module_name }}/settings.py.jinja index fbccfc79..b13e2b72 100644 --- a/src/{{ module_name }}/settings.py.jinja +++ b/src/{{ module_name }}/settings.py.jinja @@ -1,8 +1,8 @@ -{% from pathjoin("includes", "version_compare.jinja") import version_lt -%} +{% from pathjoin("includes", "version_compare.jinja") import version_higher_than -%} """Settings Module.""" import logging from logging import getLevelName -{%- if version_lt(minimal_python_version, "3.10") | bool %} +{%- if not version_higher_than(min_py, "3.10") %} from typing import Optional {%- endif %} @@ -18,7 +18,7 @@ class GlobalSettings(BaseSettings): class Settings(BaseSettings): """Project specific settings.""" -{%- if version_lt(minimal_python_version, "3.10") | bool %} +{%- if not version_higher_than(min_py, "3.10") %} # NOTE(huxuan): Pydantic cannot leverage future annotations at runtime prior to # Python 3.10, so `from __future__ import annotations` cannot be used here, and the