diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1650b82a9abe25cb4a8175edb57a6f2af33d887d..0388ead7d8ab0d02b56bb9cb9ae585c64cd77a66 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,7 @@ -include: -- local: "{{cookiecutter.project_slug}}/.gitlab-ci.yml" - -variables: - GIT_SUBMODULE_STRATEGY: recursive +stages: + - prepare + - build + - test trigger_prepare: stage: prepare @@ -10,53 +9,43 @@ trigger_prepare: strategy: depend include: "{{cookiecutter.project_slug}}/.prepare.gitlab-ci.yml" -default: +# Generate template instance in my_awesome_app directory +build-template: + stage: build + image: $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG # Bootstrap Cookiecutter template to test provided ci pipeline template - before_script: + script: - python --version # For debugging - - git config --global user.name "unit test" - - git config --global user.email "info@astron.nl" + - git config --global user.email "ci-runner@example.com" + - git config --global user.name "CI Runner" + - git config --global init.defaultBranch main - cookiecutter --no-input --overwrite-if-exists --output-dir . . - cd my_awesome_app - -# Override semgrep-sast before script -sast: - before_script: - - python --version # For debugging - -# Override unit test before script -.run_unit_test_version_base: - before_script: - - pip install cookiecutter - - !reference [default, before_script] - - python --version # For debugging - - python -m pip install --upgrade pip - - pip install --upgrade tox twine - -# Override artifact directories -package_docs: - stage: package - artifacts: - expire_in: 1w - paths: - - my_awesome_app/docs/build/* - -# override package files before script -package_files: - before_script: - - pip install cookiecutter - - !reference [default, before_script] + - git init + - source ./setup.sh + - ls -lah + - tox --version + - pip install --upgrade tox + - tox -e fix + - rm -r .venv + - rm -r .tox + # cannot use needs, for artifacts on child pipeline so must regenerate template! artifacts: - expire_in: 1w paths: - - my_awesome_app/dist/* - -# Override artifact directories -run_unit_tests_coverage: - artifacts: - reports: - coverage_report: - coverage_format: cobertura - path: my_awesome_app/coverage.xml - paths: - - my_awesome_app/htmlcov/* + - my_awesome_app/* + - project.gitlab-ci.yml + +# Spawn pipeline using the gitlab-ci.yml from generated template instance +# use project.gitlab.ci.yml for necessary job overrides from this template instance +# (due to changes in directories and paths etc) +project-pipeline: + stage: test + trigger: + strategy: depend + include: + - artifact: my_awesome_app/.gitlab-ci.yml + job: build-template + - artifact: project.gitlab-ci.yml + job: build-template + variables: + PARENT_PIPELINE_ID: $CI_PIPELINE_ID diff --git a/cookiecutter.json b/cookiecutter.json index 1f29d355f8fcd56ebb3f1c5a3de8b49d748e60f6..8165b9764968afc310e18f5850f8b960c9c95a88 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -1,6 +1,7 @@ { "project_name": "My Awesome App", "project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_')|replace('.', '_')|trim() }}", - "project_url": "https://git.astron.nl/{{cookiecutter.project_slug}}", + "project_package": "{{ cookiecutter.project_name.lower()|replace(' ', '-')|replace('_', '-')|replace('.', '-')|trim() }}", + "project_url": "https://git.astron.nl/{{ cookiecutter.project_slug }}", "description": "An example package for CI/CD working group" } diff --git a/docker/ci-runner/Dockerfile b/docker/ci-runner/Dockerfile index 8b9621755a0653909c67cf1eda3ff2bfba87063c..aa795837c670f9e26b3c9dea4e7010007b91a1c8 100644 --- a/docker/ci-runner/Dockerfile +++ b/docker/ci-runner/Dockerfile @@ -1,5 +1,8 @@ -FROM python:3.11 +# This dockerfile is used throughout steps of the CI/CD pipeline +# It contains dependencies to execute the template and run tests +FROM python:3.13 RUN python -m pip install --upgrade pip -RUN pip install --upgrade cookiecutter tox twine cibuildwheel==2.13.1 cookiecutter +RUN python -m pip install --upgrade cibuildwheel>=2.23.0 cookiecutter tox twine RUN curl -sSL https://get.docker.com/ | sh + diff --git a/project.gitlab-ci.yml b/project.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..740497a5bdb6a0b706f980cba8867aa4f45d7ee9 --- /dev/null +++ b/project.gitlab-ci.yml @@ -0,0 +1,122 @@ + +# This file overrides all the jobs defined in {{cookiecutter.project_slug}}/.gitlab.ci-yml +# this is to ensure they depend on the template installation artifact of the root +# .gitlab-ci.yml from job `build-template` +# The generated gitlab-ci.yml from this `build-template` job is used for the actual +# trigger include to prevent including jobs that still contain template arguments + + +trigger_prepare: + rules: + - if: $CI_PIPELINE_SOURCE == "parent_pipeline" + when: never + +default: + # Bootstrap Cookiecutter template to test provided ci pipeline template + before_script: + - cd my_awesome_app + +run_lint: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + +sast: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + +dependency_scanning: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + +secret_detection: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + +.run_unit_test_version_base: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + +# Run all unit tests for Python versions except the base image +run_unit_tests: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + +# Run code coverage on the base image thus also performing unit tests +run_unit_tests_coverage: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + artifacts: + reports: + coverage_report: + coverage_format: cobertura + path: my_awesome_app/coverage.xml + paths: + - my_awesome_app/htmlcov/* + +package_files: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + artifacts: + expire_in: 1w + paths: + - my_awesome_app/dist/* + +package_docs: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + artifacts: + expire_in: 1w + paths: + - my_awesome_app/docs/build/* + +docker_build: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + - package_files + before_script: + - cd my_awesome_app + +run_integration_tests: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + - package_files + +publish_on_gitlab: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + - package_files + +publish_on_test_pypi: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + - package_files + +publish_on_pypi: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + - package_files + +publish_to_readthedocs: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template + - package_docs + +release_job: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: build-template diff --git a/{{cookiecutter.project_slug}}/.dockerignore b/{{cookiecutter.project_slug}}/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..141f90da2fac6459ee10150e4cd3e12aff08a48b --- /dev/null +++ b/{{cookiecutter.project_slug}}/.dockerignore @@ -0,0 +1,4 @@ +.tox +build +*.egg-info +.venv diff --git a/{{cookiecutter.project_slug}}/.gitignore b/{{cookiecutter.project_slug}}/.gitignore index 5513c1bf7cbca7a522e1ea44f306bda82a80975d..885470b5e93bf922433ae1741179f1c7ae07918a 100644 --- a/{{cookiecutter.project_slug}}/.gitignore +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -1,13 +1,19 @@ dist/* *.egg-info *.pyc +.tox +.venv .coverage coverage.xml htmlcov/* +build +dist -# Tox -.tox +# Documentation +docs/source/source_documentation +!docs/source/source_documentation/index.rst +docs/build # setuptools-scm src/{{cookiecutter.project_slug}}/_version.py diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 429482664900652365935d2c323b79eda4698a92..d08a6214f35dec222333e4062815c637b6ff8b71 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -2,8 +2,6 @@ default: image: name: $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG pull_policy: always - before_script: - - python --version # For debugging cache: paths: - .cache/pip @@ -14,6 +12,7 @@ stages: - lint - test - package + - images - integration - publish # publish instead of deploy @@ -21,36 +20,23 @@ stages: variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" - +# Job templates from Gitlab that scan for security issues or leaked secrets include: - template: Security/SAST.gitlab-ci.yml - template: Security/Dependency-Scanning.gitlab-ci.yml - template: Security/Secret-Detection.gitlab-ci.yml - -# Prepare image to run ci on +# Prepare image to run ci on, image will be used in most jobs trigger_prepare: stage: prepare trigger: strategy: depend include: .prepare.gitlab-ci.yml -run_black: - stage: lint - script: - - tox -e black - allow_failure: true - -run_flake8: +run_lint: stage: lint script: - - tox -e pep8 - allow_failure: true - -run_pylint: - stage: lint - script: - - tox -e pylint + - tox -e lint allow_failure: true sast: @@ -59,24 +45,15 @@ sast: pmd-apex, sobelow, spotbugs stage: test -dependency_scanning: - # override default before_script, job won't have Python available - before_script: - - uname - -secret_detection: - # override default before_script, job won't have Python available - before_script: - - uname - # Basic setup for all Python versions for which we don't have a base image .run_unit_test_version_base: before_script: - python --version # For debugging - python -m pip install --upgrade pip - - pip install --upgrade tox twine + - python -m pip install --upgrade tox twine # Run all unit tests for Python versions except the base image +# The base image is used for the highest Python version run_unit_tests: extends: .run_unit_test_version_base stage: test @@ -101,6 +78,8 @@ run_unit_tests_coverage: paths: - htmlcov/* +# Binary packages will be build for many architectures +# using scikitbuild-core and cibuildwheel package_files: stage: package tags: @@ -111,8 +90,9 @@ package_files: - dist/* script: - source scripts/setup-docker-host.sh - - cibuildwheel --platform linux --output-dir dist + - tox -e build-ci-linux +# Build the sphinx documentation package_docs: stage: package artifacts: @@ -122,6 +102,22 @@ package_docs: script: - tox -e docs +# Build a tiny docker image that contains the minimal dependencies to run this project +docker_build: + stage: images + image: docker:latest + needs: + - package_files + tags: + - dind + before_script: [] + script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker build -f docker/{{cookiecutter.project_slug}}/Dockerfile . --build-arg BUILD_ENV=copy --tag $CI_REGISTRY_IMAGE/{{cookiecutter.project_slug}}:$CI_COMMIT_REF_SLUG + # enable this push line once you have configured docker registry cleanup policy + # - docker push $CI_REGISTRY_IMAGE/{{cookiecutter.project_slug}}:$CI_COMMIT_REF_SLUG + + run_integration_tests: stage: integration allow_failure: true @@ -156,8 +152,7 @@ publish_on_test_pypi: - package_files when: manual rules: - - if: $CI_COMMIT_TAG - allow_failure: true + - if: '$CI_COMMIT_TAG && $CI_COMMIT_REF_PROTECTED == "true"' script: - echo "run twine for test pypi" # - | @@ -175,8 +170,7 @@ publish_on_pypi: - package_files when: manual rules: - - if: $CI_COMMIT_TAG - allow_failure: true + - if: '$CI_COMMIT_TAG && $CI_COMMIT_REF_PROTECTED == "true"' script: - echo "run twine for pypi" # - | @@ -195,11 +189,12 @@ publish_to_readthedocs: - package_docs when: manual rules: - - if: $CI_COMMIT_TAG + - if: '$CI_COMMIT_TAG && $CI_COMMIT_REF_PROTECTED == "true"' script: - echo "scp docs/* ???" - exit 1 +# automatically create a release on Gitlab for every tagged commit release_job: stage: publish image: registry.gitlab.com/gitlab-org/release-cli:latest diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c7c1b720cfc8d8d631b4c7c2640aebd0499b3a08 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -0,0 +1,26 @@ +# run several checks prior to creating a commit or pushing to a remote +# These can be skipped (but you shouldn't generally as the pipeline will fail) +# using `git push / commit --no-check` +# for more information see: https://pre-commit.com/ +default_stages: [ pre-commit, pre-push ] +default_language_version: + python: python3 +exclude: '^docs/.*\.py$' + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-yaml + - id: check-toml + - id: detect-private-key + - repo: local + hooks: + - id: tox-lint + name: tox-lint (local) + entry: tox + language: python + types: [file, python] + args: ["-e", "lint", "--"] diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md index 4e6e8c75c5bbc2a58e17e98bb27d8b7bf49143b0..323185a988534eeec5e851905ae0da7faf117f75 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -38,5 +38,11 @@ To automatically apply most suggested linting changes execute: ```tox -e format``` +The configuration for linting and tox can be found in `pyproject.toml` +For further references on tox or pyproject.toml visit: + +1. https://tox.wiki/en/latest/config.html#pyproject-toml-ini +2. https://packaging.python.org/en/latest/guides/writing-pyproject-toml/ + ## License This project is licensed under the Apache License Version 2.0 diff --git a/{{cookiecutter.project_slug}}/build-requirements.txt b/{{cookiecutter.project_slug}}/build-requirements.txt index 357a5ca22a4257b5aadfc73ae9ea9a3f97d12ef7..502b5f30232469f7a5614b019b541d998546db1e 100644 --- a/{{cookiecutter.project_slug}}/build-requirements.txt +++ b/{{cookiecutter.project_slug}}/build-requirements.txt @@ -1,3 +1,3 @@ -cibuildwheel >= 2.12.3 # BSD +cibuildwheel>=2.23.0 # BSD tox >= 4.0 # ? build >= 0.8.0 # MIT diff --git a/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile index 48d4586f1eb2c4755b370b9bb4c5fe079c97fed8..4494c2bc3b3819ffa2b4b4b0faabe62d7f2eaa11 100644 --- a/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile +++ b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile @@ -1,5 +1,7 @@ +# This dockerfile is used throughout steps of the CI/CD pipeline +# It contains dependencies to run tests FROM python:3.13 RUN python -m pip install --upgrade pip -RUN pip install --upgrade tox twine cibuildwheel==2.13.1 +RUN python -m pip install --upgrade cibuildwheel>=2.23.0 tox twine RUN curl -sSL https://get.docker.com/ | sh diff --git a/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile b/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f50ac747f63eb05bb97c30e1be597c020512566d --- /dev/null +++ b/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile @@ -0,0 +1,28 @@ +# This dockerfile creates a minimal docker image that can run the project. +# It can copy the result of `tox -e build` from the host to the container +# or build from source inside the container. +# Set `docker build --build-arg BUILD_ENV=copy` to copy from the host, rebuild otherwise +# contrarily, to the ci image it does not contain any dependencies used for testing. +ARG BUILD_ENV=no_copy + +# Build without copying from host inside docker +FROM python:3.13 AS build_no_copy +ADD ../../requirements.txt . +COPY ../.. /work +RUN rm -rf /work/dist +RUN python -m pip install --user tox +WORKDIR /work +RUN python -m tox -e build-local + +# Copy build package from host `dist` directory +FROM python:3.13 AS build_copy +COPY dist /work/dist + +# Dynamically set `build` from `build_no_copy` or `build_copy` depending on +# `BUILD_ENV` argument +FROM build_${BUILD_ENV} AS build + +# Copy build stage files into final python slim image and install +FROM python:3.13-slim +COPY --from=build /work/dist /dist +RUN python -m pip install /dist/*cp313*many*$(uname -m).whl diff --git a/{{cookiecutter.project_slug}}/docs/cleanup.py b/{{cookiecutter.project_slug}}/docs/cleanup.py new file mode 100644 index 0000000000000000000000000000000000000000..c88889067f1dc736a881d565ee02460f8cd05148 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/cleanup.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: Apache-2.0 + +""" +Remove generated source documentation files except for index.rst + +If a source file is created, documentation is generated and the source file is later +removed. The documentation for this source file will persists. + +This file ensures generated source documentation files, which are automatically +generated from source, are removed between every build. +""" + +import os + +file_dir = os.path.dirname(os.path.realpath(__file__)) + +clean_dir = os.path.join(file_dir, "source", "source_documentation") +print(f"Cleaning.. {clean_dir}/*") + +if not os.path.exists(clean_dir): + exit() + +for file_name in os.listdir(clean_dir): + file = os.path.join(clean_dir, file_name) + + if file_name == "index.rst": + continue + + print(f"Removing.. {file}") + os.remove(file) diff --git a/{{cookiecutter.project_slug}}/docs/cleanup.sh b/{{cookiecutter.project_slug}}/docs/cleanup.sh deleted file mode 100644 index aac4cef22a134a44b69e99369755a58507b3794f..0000000000000000000000000000000000000000 --- a/{{cookiecutter.project_slug}}/docs/cleanup.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -FILE_DIR=$(dirname -- "$(readlink -f -- "${0}")") - -echo "Cleaning.. ${FILE_DIR}/source/source_documentation/*" - -for f in "${FILE_DIR}"/source/source_documentation/* -do - - case $f in - */index.rst) true;; - *) echo "Removing.. ${f}"; rm "${f}";; - esac -done diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index db601f122b76f82a3f3037ee7524cfb28c4a75ff..f6dd9c3e0c614eeb13b51f7faffc911c84f3c279 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -11,13 +11,12 @@ name = "{{cookiecutter.project_slug}}" dynamic = ["version"] description="{{cookiecutter.description}}" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" classifiers = [ "Development Status :: 4 - Beta", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -34,7 +33,7 @@ sdist.include = ["src/{{cookiecutter.project_slug}}/_version.py"] write_to = "src/{{cookiecutter.project_slug}}/_version.py" [tool.cibuildwheel] -skip = "pp*" +skip = "*-manylinux_i686 pp*" # manylinux_i686 does not work on our runners currently build-verbosity = 1 [tool.cibuildwheel.macos] @@ -46,20 +45,95 @@ archs = ["x86_64", "i686"] [tool.cibuildwheel.windows] archs = ["AMD64", "x86"] -[tool.pylint] -ignore = "_version.py" - -# This does nothing until the issue below is resolved: -# https://github.com/PyCQA/flake8/issues/234 -[tool.flake8] -max-line-length = 88 -extend-ignore = ["E203"] -exclude = ["_version.py"] - -[tool.black] -line-length = 88 -extend-exclude = ''' -( - _version.py -) -''' + +[tool.coverage.run] +branch = true + + +[tool.ruff] +exclude = [ + ".venv", + ".git", + ".tox", + "dist", + "docs", + "*lib/python*", + "*egg", + "_version.py" +] + +[tool.ruff.lint] +# Expand basic rules with name and style checks roughly equivalent to flake8 + black + pylint +# Some additional functional errors such as `await` on none async or wrong return types / args. +# More info https://docs.astral.sh/ruff/rules/ +select = ["N", "A", "E", "F", "B", "W", "Q", "PL", "ASYNC", "S", "G", "RET", "ARG"] +ignore = ["E203"] + + +[tool.tox] +# Generative environment list to test all supported Python versions +requires = ["tox>=4.21", "tox-ignore-env-name-mismatch >= 0.2.0"] +env_list = ["fix", "coverage", "lint", "format", "py{13, 12, 11, 10}"] + +[tool.tox.env_run_base] +package = "editable" +deps = [ + "-r{toxinidir}/requirements.txt", + "-r{toxinidir}/tests/requirements.txt"] +set_env = { LANGUAGE = "en_US", LC_ALL = "en_US.UTF-8", PYTHONWARNINGS = "default::DeprecationWarning", DYNAMIC_VERSION_SOURCE= "{toxinidir}" } +commands = [["python", "--version"], ["python", "-m", "pytest", "tests/{posargs}"]] + +[tool.tox.env.fix] +description = "format the code base to adhere to our styles, and complain about what we cannot do automatically" +skip_install = true +deps = ["pre-commit-uv>=4.1.1"] +commands = [["pre-commit", "run", "--all-files", "--show-diff-on-failure"]] + +[tool.tox.env.coverage] +commands = [ + ["python", "--version"], + ["python", "-m", "pytest", "--cov-report", "term", "--cov-report", "xml", "--cov-report", "html", "--cov={{cookiecutter.project_slug}}", "tests/{posargs}"]] + +# Command prefixes to reuse the same virtualenv for all linting jobs. +[tool.tox.env.lint] +deps = [ + "ruff", + "-r{toxinidir}/tests/requirements.txt"] +commands = [ + ["python", "-m", "ruff", "--version"], + ["python", "-m", "ruff", "check", { replace = "posargs", default = ["src", "tests"], extend = true }] +] + +[tool.tox.env.format] +deps = [ + "ruff", + "-r{toxinidir}/tests/requirements.txt"] +commands = [ + ["python", "-m", "ruff", "format", "-v", { replace = "posargs", default = ["src", "tests"], extend = true }] +] + +[tool.tox.env.docs] +deps = [ + "-r{toxinidir}/requirements.txt", + "-r{toxinidir}/docs/requirements.txt"] +# unset LC_ALL / LANGUAGE from testenv, would fail sphinx otherwise +set_env = "" +changedir = "{tox_root}" +commands = [ + ["python", "docs/cleanup.py"], + ["sphinx-build", "-b", "html", "docs/source", "docs/build/html"] +] + +[tool.tox.env.build-local] +package = "wheel" +deps = ["-r{toxinidir}/build-requirements.txt"] +commands = [["python", "-m", "build"]] + +[tool.tox.env.build-ci-linux] +package = "wheel" +deps = ["-r{toxinidir}/build-requirements.txt"] +allowlist_externals = ["docker"] +pass_env = ["DOCKER_HOST", "DOCKER_TLS_VERIFY", "DOCKER_CERT_PATH"] +commands = [ + ["python", "-m", "cibuildwheel", "--platform", "linux", "--output-dir", "dist"] +] diff --git a/{{cookiecutter.project_slug}}/setup.sh b/{{cookiecutter.project_slug}}/setup.sh new file mode 100755 index 0000000000000000000000000000000000000000..b1a3a35ac02753f663a029cd320f7936aca93b9b --- /dev/null +++ b/{{cookiecutter.project_slug}}/setup.sh @@ -0,0 +1,34 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: Apache-2.0 +# + +# Substitute BASH_SOURCE if unset this is required for simple shells +# such as the one found in docker or alpine docker images. +# `#! /usr/bin/env bash` does not actually ensure the sourcing is executed +# using BASH +if [ -z ${BASH_SOURCE} ]; then + BASH_SOURCE=${(%):-%x} +fi + +# From BASH_SOURCE determine the absolute path, this is needed because $0 is +# unset when sourcing in some shells. +ABSOLUTE_PATH=$(realpath $(dirname ${BASH_SOURCE})) + +# Create a virtual environment directory if it doesn't exist +VENV_DIR="${ABSOLUTE_PATH}/.venv" +if [ ! -d "$VENV_DIR" ]; then + echo "Creating virtual environment..." + python3 -m venv "$VENV_DIR" +fi + +# Activate the virtual environment +source "$VENV_DIR/bin/activate" +python -m pip install pre-commit +python -m pip install "tox>=4.21.0" + +# Install git pre-commit pre-push hook if not already installed +if [ ! -f "${ABSOLUTE_PATH}/.git/hooks/pre-push" ]; then + pre-commit install --hook-type pre-push +fi diff --git a/{{cookiecutter.project_slug}}/tests/requirements.txt b/{{cookiecutter.project_slug}}/tests/requirements.txt index 82f1ee820e4b57fd3e720ad74078688cd87c2ead..b507faf8c6cc09660c41c58caa26d318e94cfd4d 100644 --- a/{{cookiecutter.project_slug}}/tests/requirements.txt +++ b/{{cookiecutter.project_slug}}/tests/requirements.txt @@ -1,8 +1,2 @@ -autopep8 >= 1.7.0 # MIT -black >= 22.0.0 # MIT -build >= 0.8.0 # MIT -flake8 >= 5.0.0 # MIT -pyproject-flake8 >= 5.0.4 # Unlicense -pylint >= 2.15.0 # GPLv2 pytest >= 7.0.0 # MIT pytest-cov >= 3.0.0 # MIT diff --git a/{{cookiecutter.project_slug}}/tests/test_cool_module.py b/{{cookiecutter.project_slug}}/tests/test_cool_module.py index 19e5cd2bd0bff16fa00560bdb678f9345e4e79f4..2cacc8e76f00c2d40f10bf9ff2bd28f61ca6b59a 100644 --- a/{{cookiecutter.project_slug}}/tests/test_cool_module.py +++ b/{{cookiecutter.project_slug}}/tests/test_cool_module.py @@ -1,3 +1,6 @@ +# Copyright (C) 2025 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: Apache-2.0 + """Testing of the Cool Module""" from unittest import TestCase diff --git a/{{cookiecutter.project_slug}}/tox.ini b/{{cookiecutter.project_slug}}/tox.ini deleted file mode 100644 index 37d15bad2be87979a4fdcde0a6a0b12de4ffb991..0000000000000000000000000000000000000000 --- a/{{cookiecutter.project_slug}}/tox.ini +++ /dev/null @@ -1,65 +0,0 @@ -[tox] -# Generative environment list to test all supported Python versions -envlist = py3{9,10,11,12,13},black,pep8,pylint -min_version = 4.3.3 -requires = - tox-ignore-env-name-mismatch >= 0.2.0 - -[testenv] -package = sdist # 'Source' package required for binary extension -use_develop = False # use_develop implies 'editable' package, not possible - -setenv = - PYTHONWARNINGS=default::DeprecationWarning - DYNAMIC_VERSION_SOURCE={toxinidir} -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/tests/requirements.txt -commands = - {envpython} --version - {envpython} -m pytest tests/{posargs} - -[testenv:coverage] -commands = - {envpython} --version - {envpython} -m pytest --cov-report term --cov-report xml --cov-report html --cov={{cookiecutter.project_slug}} tests/{posargs} - -# Use generative name and command prefixes to reuse the same virtualenv -# for all linting jobs. -[testenv:{pep8,black,pylint,format}] -usedevelop = False -envdir = {toxworkdir}/linting -commands = - pep8: {envpython} -m flake8 --version - pep8: {envpython} -m flake8 --exclude=_version.py --max-line-length 88 --extend-ignore=E203 src tests - black: {envpython} -m black --version - black: {envpython} -m black --check --diff src tests - pylint: {envpython} -m pylint --version - pylint: {envpython} -m pylint src tests - format: {envpython} -m autopep8 -v --max-line-length 88 -aa --in-place --recursive src - format: {envpython} -m autopep8 -v --max-line-length 88 -aa --in-place --recursive tests - format: {envpython} -m black -v src tests - -[testenv:docs] -allowlist_externals = - sh -; unset LC_ALL / LANGUAGE from testenv, would fail sphinx otherwise -setenv = - PYTHONWARNINGS=default::DeprecationWarning - DYNAMIC_VERSION_SOURCE={toxinidir} -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/docs/requirements.txt -changedir = {toxinidir} -commands = - sh docs/cleanup.sh - sphinx-build -b html docs/source docs/build/html - -[testenv:{build-local,build-ci-linux}] -deps = - -r{toxinidir}/tests/requirements.txt - -r{toxinidir}/build-requirements.txt -skip_install = True -commands = - build-local: {envpython} -m build - build-ci-linux: {envpython} -m cibuildwheel --platform linux --output-dir dist