Merge branch 'feat/ci-improvement'

Create custom docker for CI

See merge request Klagarge/mse2425-grp09!5
This commit is contained in:
Alec Schmidt
2025-03-26 17:04:59 +00:00
7 changed files with 138 additions and 34 deletions

View File

@@ -1,47 +1,49 @@
variables:
DOCKER_IMAGE: registry.forge.hefr.ch/klagarge/mse2425-grp09/python-pdm:latest
default:
image: python:3.9
image: $DOCKER_IMAGE
stages:
- build-docker
- lint
- build
- test
build job:
stage: build
script:
.setup_env: &setup_env
before_script:
- cd src
# - apt-get update -qy
# - apt-get install -y python3-dev python3-pip python3.12-venv curl
- python3 -V
# - pip3 install --break-system-packages -r requirements.txt
- curl -sSL https://pdm-project.org/install-pdm.py | python3 -
- export PATH=/root/.local/bin:$PATH
- pdm install
- cp -r /app/__pypackages__ .
- export "PYTHONPATH=/builds/Klagarge/mse2425-grp09/src:/builds/Klagarge/mse2425-grp09/src/__pypackages__/3.9/lib"
- export "PATH=/builds/Klagarge/mse2425-grp09/src/__pypackages__/3.9/bin:$PATH"
- export "FLASK_APP=app"
test job:
stage: test
<<: *setup_env
script:
# Set environment variables for the tests
- export FLASK_SECRET_KEY=$FLASK_SECRET_KEY
# launch tests
- export PYTHONPATH=.
- export FLASK_APP=app
- pdm run pytest tests --cov --cov-report term --cov-report html
artifacts:
paths:
- src/htmlcov/
paths:
- src/htmlcov/
lint job:
stage: lint
<<: *setup_env
dependencies: []
script:
- python3 -m pip install flake8
- flake8 src/
- pdm run flake8 --config=../tox.ini
allow_failure: true # Linter can fail, fixing it is for now outside of the projects scope
pages:
stage: build
stage: test
dependencies:
- build job
needs: ["build job"]
- test job
needs: ["test job"]
script:
- mv src/htmlcov/ public/
artifacts:
@@ -50,3 +52,21 @@ pages:
expire_in: 7 days
only:
- main
# This job runs only when Dockerfile changes
docker-build:
image: docker:latest
stage: build-docker
services:
- docker:dind
script:
- docker build -t $DOCKER_IMAGE -f Dockerfile .
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
- docker push $DOCKER_IMAGE
rules:
- if: $GITLAB_CI == 'false' # Only run in GitLab CI
when: never
- changes:
- Dockerfile
- src/pyproject.toml
- src/pdm.lock

20
Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
FROM python:3.10-slim
LABEL maintener="Rémi Heredero <remi.heredero@hevs.ch>"
RUN apt-get update && \
pip install --no-cache-dir -U pdm && \
rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.local/bin:$PATH" \
PDM_USE_VENV=false
WORKDIR /app
COPY src/pyproject.toml src/pdm.lock ./
RUN pdm config python.use_venv false && \
pdm install -G:all
ENV PYTHONPATH="/app/__pypackages__/3.9/lib" \
PATH="/app/__pypackages__/3.9/bin:$PATH"

View File

@@ -14,4 +14,7 @@
## Q1.3
- A linter is a tool to statically analyse code for readability and improving code quality. It is usually executed from a standalone tool.
- It is used to check for errors, vulns, code smells or general issues but also to enforce a coding style over the whole project.
- It is used to check for errors, vulns, code smells or general issues but also to enforce a coding style over the whole project.
## Q1.4
The minimum is to change the image on the CI to put an Alpine or a basic python image. Another option (which we implemented) is to create a custom Docker image that includes all the necessary dependencies for the application. This can significantly reduce the time required to set up the environment and speed up the CI/CD pipeline.

View File

@@ -5,3 +5,7 @@
- **Q2.1**: Every commit triggers the CI/CD pipeline. Find out a way to trigger the pipeline only if specific commits (e.g. commit in a development branch) are made. Where can this be configured. Describe your solution and implement it in your pipeline.
- **Q2.2**: Take the [CIS controls](./CIS_Controls_v8_Online.22.02.pdf) and give some examples (minimum 5) of controls from this standard that are not or not enough implemented in the calculator app. Provide a short description and a possible remediation. Implement at least two of the controls in the app / pipeline.
- **Q2.3 (optional)**: The linter from question 1.3 is a good start. It is only executed in your pipeline. But what if you would also integrate it directly in your local development environment (e.g. IDE)? Can you do the linting before you commit? Describe your solution and implement it in your (local) pipeline. Describe the advantages and disadvantages of this approach.
# Answers - Part 2
## Q2.1
Solution is to add a `rule` section to add condition to trigger the pipeline. It's what is implemented for the `docker-build` job. Another option is to use an `only` section to trigger the pipeline only if the change is made in a specific branch. It's what is implemented for the `pages` job.

69
src/pdm.lock generated
View File

@@ -2,10 +2,10 @@
# It is not intended for manual editing.
[metadata]
groups = ["default"]
groups = ["default", "lint", "test"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:e36fdc748f0c9135da773b2fbab7f45cc5c43e27fad6d39d2de23857da4c1a91"
content_hash = "sha256:f5c4e58e167316cb2440e9205a1e15474a3599463aa47c3c259b4009038166b0"
[[metadata.targets]]
requires_python = ">3.11"
@@ -41,7 +41,7 @@ name = "colorama"
version = "0.4.6"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
summary = "Cross-platform colored terminal text."
groups = ["default"]
groups = ["default", "test"]
marker = "sys_platform == \"win32\" or platform_system == \"Windows\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
@@ -53,7 +53,7 @@ name = "coverage"
version = "7.6.12"
requires_python = ">=3.9"
summary = "Code coverage measurement for Python"
groups = ["default"]
groups = ["default", "test"]
files = [
{file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"},
{file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"},
@@ -116,7 +116,7 @@ version = "7.6.12"
extras = ["toml"]
requires_python = ">=3.9"
summary = "Code coverage measurement for Python"
groups = ["default"]
groups = ["default", "test"]
dependencies = [
"coverage==7.6.12",
"tomli; python_full_version <= \"3.11.0a6\"",
@@ -189,6 +189,22 @@ files = [
{file = "dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9"},
]
[[package]]
name = "flake8"
version = "7.1.2"
requires_python = ">=3.8.1"
summary = "the modular source code checker: pep8 pyflakes and co"
groups = ["lint"]
dependencies = [
"mccabe<0.8.0,>=0.7.0",
"pycodestyle<2.13.0,>=2.12.0",
"pyflakes<3.3.0,>=3.2.0",
]
files = [
{file = "flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a"},
{file = "flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd"},
]
[[package]]
name = "flask"
version = "3.1.0"
@@ -229,7 +245,7 @@ name = "iniconfig"
version = "2.0.0"
requires_python = ">=3.7"
summary = "brain-dead simple config-ini parsing"
groups = ["default"]
groups = ["default", "test"]
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
@@ -320,12 +336,23 @@ files = [
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
]
[[package]]
name = "mccabe"
version = "0.7.0"
requires_python = ">=3.6"
summary = "McCabe checker, plugin for flake8"
groups = ["lint"]
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
[[package]]
name = "packaging"
version = "24.2"
requires_python = ">=3.8"
summary = "Core utilities for Python packages"
groups = ["default"]
groups = ["default", "test"]
files = [
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
@@ -336,18 +363,40 @@ name = "pluggy"
version = "1.5.0"
requires_python = ">=3.8"
summary = "plugin and hook calling mechanisms for python"
groups = ["default"]
groups = ["default", "test"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[[package]]
name = "pycodestyle"
version = "2.12.1"
requires_python = ">=3.8"
summary = "Python style guide checker"
groups = ["lint"]
files = [
{file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"},
{file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"},
]
[[package]]
name = "pyflakes"
version = "3.2.0"
requires_python = ">=3.8"
summary = "passive checker of Python programs"
groups = ["lint"]
files = [
{file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"},
{file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"},
]
[[package]]
name = "pytest"
version = "8.3.5"
requires_python = ">=3.8"
summary = "pytest: simple powerful testing with Python"
groups = ["default"]
groups = ["default", "test"]
dependencies = [
"colorama; sys_platform == \"win32\"",
"exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
@@ -366,7 +415,7 @@ name = "pytest-cov"
version = "6.0.0"
requires_python = ">=3.9"
summary = "Pytest plugin for measuring coverage."
groups = ["default"]
groups = ["default", "test"]
dependencies = [
"coverage[toml]>=7.5",
"pytest>=4.6",

View File

@@ -23,4 +23,12 @@ distribution = false
[tool.pdm.scripts]
flask.cmd = "flask run -p 5000 --debug"
flask.env = {FLASK_ENV = "development"}
shell.cmd = "sh"
shell.cmd = "sh"
[dependency-groups]
test = [
"pytest>=8.3.5",
"pytest-cov>=6.0.0",
]
lint = [
"flake8>=7.1.2",
]

View File

@@ -1,5 +1,5 @@
[flake8]
extend-ignore = E203
exclude = .git,__pycache__,.venv
exclude = .git,__pycache__,.venv,__pypackages__
max-complexity = 10
max-line-length = 120