diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 241614390b38a3321f8fcf818592e76f9590ef3f..20272ac5a1a967adf4f32aa65d6778176d3cf38b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,6 +19,15 @@ build-template: - cookiecutter --no-input --overwrite-if-exists --output-dir . . - cd my_awesome_app - git init + - git config user.email "ci-runner@example.com" + - git config user.name "CI Runner" + - 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: paths: diff --git a/cookiecutter.json b/cookiecutter.json index 35e5d91c6ad2f6d25e5c40fd3d73251ac85909c4..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_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/project.gitlab-ci.yml b/project.gitlab-ci.yml index 2d6eb4238cb4eed070a86b57386656f0806b1f4b..740497a5bdb6a0b706f980cba8867aa4f45d7ee9 100644 --- a/project.gitlab-ci.yml +++ b/project.gitlab-ci.yml @@ -5,6 +5,7 @@ # 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" @@ -15,17 +16,7 @@ default: before_script: - cd my_awesome_app -run_black: - needs: - - pipeline: $PARENT_PIPELINE_ID - job: build-template - -run_flake8: - needs: - - pipeline: $PARENT_PIPELINE_ID - job: build-template - -run_pylint: +run_lint: needs: - pipeline: $PARENT_PIPELINE_ID job: build-template @@ -128,4 +119,4 @@ publish_to_readthedocs: release_job: needs: - pipeline: $PARENT_PIPELINE_ID - job: build-template \ No newline at end of file + 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 d5c650e6aee0559e7deb69be50a0dc534473d458..3f64dc50366ebf99b714795cd8bd590a452bc05a 100644 --- a/{{cookiecutter.project_slug}}/.gitignore +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -2,6 +2,7 @@ dist/* *.egg-info *.pyc .tox +.venv .coverage coverage.xml diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 337675520d9e8488af18c0e56178f643b637c926..b4e3610be0cd53f508c1915a5d45989564a1cbdb 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -36,22 +36,10 @@ trigger_prepare: strategy: depend include: .prepare.gitlab-ci.yml -run_black: +run_lint: stage: lint script: - - tox -e black - allow_failure: true - -run_flake8: - stage: lint - script: - - tox -e pep8 - allow_failure: true - -run_pylint: - stage: lint - script: - - tox -e pylint + - tox -e lint allow_failure: true # build_extensions: @@ -91,7 +79,7 @@ run_unit_tests: - tox -e py3${PY_VERSION} parallel: matrix: # use the matrix for testing - - PY_VERSION: [8, 9, 10, 11] + - PY_VERSION: [9, 10, 11, 12] # Run code coverage on the base image thus also performing unit tests run_unit_tests_coverage: diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ce46e6463265d2ad9704a24ae7f467cb7b50d8d9 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +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 eb72cc45252e58acb7ef6c402be0444c062aa212..25f215207307771b9fefe5658653fe4816828721 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -29,8 +29,16 @@ from {{cookiecutter.project_slug}} import cool_module cool_module.greeter() # prints "Hello World" ``` -## Contributing +## Development +### Development environment + +To setup and activte the develop environment run ```source ./setup.sh``` from within the source directory. + +If PyCharm is used, this only needs to be done once. +Afterward the Python virtual env can be setup within PyCharm. + +### Contributing To contribute, please create a feature branch and a "Draft" merge request. Upon completion, the merge request should be marked as ready and a reviewer should be assigned. diff --git a/{{cookiecutter.project_slug}}/bin/install-hooks/pre-commit.sh b/{{cookiecutter.project_slug}}/bin/install-hooks/pre-commit.sh new file mode 100755 index 0000000000000000000000000000000000000000..792a3aabef83dc4ebf0c94635ae1de0e7412c479 --- /dev/null +++ b/{{cookiecutter.project_slug}}/bin/install-hooks/pre-commit.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +if [ ! -f "setup.sh" ]; then + echo "pre-commit.sh must be executed with repository root as working directory!" + exit 1 +fi + +pre-commit install --hook-type pre-push diff --git a/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile index 6cb46437656aad64d8292c0a10f6edaffffba8e3..6063354805d6e2e35534ee5d09f80b51f856495b 100644 --- a/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile +++ b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12 +FROM python:3.13 RUN python -m pip install --upgrade pip RUN python -m pip install --upgrade tox twine diff --git a/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile b/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile index ceea7e721fc3acaa124184e709986e04dbd9a3dd..1f7eb275020d13ed45cb7b0983949c12161ff953 100644 --- a/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile +++ b/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile @@ -1,6 +1,6 @@ ARG BUILD_ENV=no_copy -FROM python:3.11 AS build_no_copy +FROM python:3.13 AS build_no_copy ADD ../../requirements.txt . COPY ../.. /work RUN rm -r /work/dist | true @@ -8,11 +8,11 @@ RUN python -m pip install --user tox WORKDIR /work RUN python -m tox -e build -FROM python:3.11 AS build_copy +FROM python:3.13 AS build_copy COPY dist /work/dist FROM build_${BUILD_ENV} AS build -FROM python:3.11-slim +FROM python:3.13-slim COPY --from=build /work/dist /dist RUN python -m pip install /dist/*.whl diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index 063c6b8c424a023260e012745fb447470329ade1..f9ef33d81867a69427453175611c432118349119 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "setuptools>=62.6", + "setuptools>=70.0", "setuptools_scm[toml]>=8.0", "wheel" ] @@ -9,5 +9,76 @@ build-backend = "setuptools.build_meta" [tool.setuptools_scm] version_file = "{{cookiecutter.project_slug}}/_version.py" -[tool.pylint] -ignore = "_version.py" +[tool.ruff] +exclude = [ + ".venv", + ".git", + ".tox", + "dist", + "docs", + "*lib/python*", + "*egg", + "_version.py" +] + +[tool.ruff.lint] +ignore = ["E203"] + +[tool.tox] +# Generative environment list to test all supported Python versions +requires = ["tox>=4.21"] +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" } +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 = ["{{cookiecutter.project_slug}}", "tests"], extend = true }] +] + +[tool.tox.env.format] +deps = [ + "ruff", + "-r{toxinidir}/tests/requirements.txt"] +commands = [ + ["python", "-m", "ruff", "format", "-v", { replace = "posargs", default = ["{{cookiecutter.project_slug}}", "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] +package = "wheel" +deps = ["build>=0.8.0"] +commands = [["python", "-m", "build"]] diff --git a/{{cookiecutter.project_slug}}/requirements.txt b/{{cookiecutter.project_slug}}/requirements.txt index 8f81bc285d0db9ea0945151fed0cd47eec4ee6a2..24ce15ab7ead32f98c7ac3edcd34bb2010ff4326 100644 --- a/{{cookiecutter.project_slug}}/requirements.txt +++ b/{{cookiecutter.project_slug}}/requirements.txt @@ -1,2 +1 @@ -importlib-metadata>=0.12, <5.0;python_version<"3.8" numpy diff --git a/{{cookiecutter.project_slug}}/setup.cfg b/{{cookiecutter.project_slug}}/setup.cfg index 5755b7a8b2f78bfd0d3f0d2651353b67220e5913..1755bbc3daeb8065aa1addc1b75f1bd0584858e6 100644 --- a/{{cookiecutter.project_slug}}/setup.cfg +++ b/{{cookiecutter.project_slug}}/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = {{cookiecutter.project_slug}} +name = {{cookiecutter.project_package}} description = {{cookiecutter.description}} long_description = file: README.md long_description_content_type = text/markdown @@ -15,11 +15,10 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 Topic :: Internet :: WWW/HTTP Topic :: Internet :: WWW/HTTP :: Dynamic Content Topic :: Scientific/Engineering @@ -28,10 +27,6 @@ classifiers = [options] include_package_data = true packages = find: -python_requires = >=3.8 +python_requires = >=3.10 install_requires = file: requirements.txt -[flake8] -max-line-length = 88 -extend-ignore = E203 -exclude=.venv,.git,.tox,dist,docs,*lib/python*,*egg,_version.py diff --git a/{{cookiecutter.project_slug}}/setup.py b/{{cookiecutter.project_slug}}/setup.py index b908cbe55cb344569d32de1dfc10ca7323828dc5..10fdaec810e96f0f1cbedb4a5ddf532c03f50dc4 100644 --- a/{{cookiecutter.project_slug}}/setup.py +++ b/{{cookiecutter.project_slug}}/setup.py @@ -1,3 +1,7 @@ +# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: Apache-2.0 + +""" Setuptools entry point """ import setuptools setuptools.setup() diff --git a/{{cookiecutter.project_slug}}/setup.sh b/{{cookiecutter.project_slug}}/setup.sh new file mode 100755 index 0000000000000000000000000000000000000000..4548910b6ba00d9f564aea2ac5ddfab50c1d77db --- /dev/null +++ b/{{cookiecutter.project_slug}}/setup.sh @@ -0,0 +1,30 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2023 ASTRON (Netherlands Institute for Radio Astronomy) +# SPDX-License-Identifier: Apache-2.0 +# + +# This file's directory is used to determine the station control directory +# location. +if [ -z ${BASH_SOURCE} ]; then + BASH_SOURCE=${(%):-%x} +fi + +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 [ ! -f "${ABSOLUTE_PATH}/.git/hooks/pre-push.legacy" ]; then + source "${ABSOLUTE_PATH}/bin/install-hooks/pre-commit.sh" +fi diff --git a/{{cookiecutter.project_slug}}/tests/requirements.txt b/{{cookiecutter.project_slug}}/tests/requirements.txt index f14d0b907ce977320ba250046dfab52a3101800a..b507faf8c6cc09660c41c58caa26d318e94cfd4d 100644 --- a/{{cookiecutter.project_slug}}/tests/requirements.txt +++ b/{{cookiecutter.project_slug}}/tests/requirements.txt @@ -1,7 +1,2 @@ -autopep8 >= 1.7.0 # MIT -black >= 22.0.0 # MIT -build >= 0.8.0 # MIT -flake8 >= 5.0.0 # MIT -pylint >= 2.15.0 # GPLv2 pytest >= 7.0.0 # MIT pytest-cov >= 3.0.0 # MIT diff --git a/{{cookiecutter.project_slug}}/tox.ini b/{{cookiecutter.project_slug}}/tox.ini deleted file mode 100644 index 5132bd5451807aea8ab5480b5c5671b5daf67c1a..0000000000000000000000000000000000000000 --- a/{{cookiecutter.project_slug}}/tox.ini +++ /dev/null @@ -1,58 +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] -usedevelop = True -package = wheel -wheel_build_env = .pkg - -setenv = - PYTHONWARNINGS=default::DeprecationWarning -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 -package = editable -envdir = {toxworkdir}/linting -commands = - pep8: {envpython} -m flake8 --version - pep8: {envpython} -m flake8 {{cookiecutter.project_slug}} tests - black: {envpython} -m black --version - black: {envpython} -m black --check --diff {{cookiecutter.project_slug}} tests - pylint: {envpython} -m pylint --version - pylint: {envpython} -m pylint {{cookiecutter.project_slug}} tests - format: {envpython} -m autopep8 -v -aa --in-place --recursive {{cookiecutter.project_slug}} - format: {envpython} -m autopep8 -v -aa --in-place --recursive tests - format: {envpython} -m black -v {{cookiecutter.project_slug}} tests - -[testenv:docs] -; unset LC_ALL / LANGUAGE from testenv, would fail sphinx otherwise -setenv = -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/docs/requirements.txt -changedir = {toxinidir} -commands = - {envpython} docs/cleanup.py - sphinx-build -b html docs/source docs/build/html - -[testenv:build] -usedevelop = False -deps = build -commands = {envpython} -m build