diff --git a/.gitignore b/.gitignore index 8701434..693392d 100644 --- a/.gitignore +++ b/.gitignore @@ -243,3 +243,4 @@ secrets*.json *.db remote.json requirements.txt +/documentations/seminararbeiten/DevOps/seminararbeit/dev-ops.pdf diff --git a/README.md b/README.md index e1ee93e..60fce14 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,5 @@ # aki_prj23_transparenzregister -[![python](https://img.shields.io/badge/Python-3.11-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org) -[![Actions status](https://github.com/astral-sh/ruff/workflows/CI/badge.svg)](https://github.com/astral-sh/ruff/actions) -[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) -[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) -[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) -[![Documentation Status](https://readthedocs.org/projects/mypy/badge/?version=stable)](https://mypy.readthedocs.io/en/stable/?badge=stable) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) - ## Contributions See the [CONTRIBUTING.md](CONTRIBUTING.md) about how code should be formatted and what kind of rules we set ourselves. @@ -53,7 +45,7 @@ The sqlite db is alternative to the postgres section. Alternatively, the secrets can be provided as environment variables. One option to do so is to add a `.env` file with the following layout: -``` +```dotenv PYTHON_POSTGRES_USERNAME=postgres PYTHON_POSTGRES_PASSWORD=postgres PYTHON_POSTGRES_HOST=postgres @@ -66,12 +58,14 @@ PYTHON_MONGO_PASSWORD=password PYTHON_MONGO_PORT=27017 PYTHON_MONGO_DATABASE=transparenzregister -PYTHON_SQLITE_PATH=PathToSQLite3.db # An overwrite path to an sqllite db +# An overwrite path to an sqlite db +PYTHON_SQLITE_PATH=PathToSQLite3.db PYTHON_DASH_LOGIN_USERNAME=some-login-to-webgui PYTHON_DASH_LOGIN_PW=some-pw-to-login-to-webgui -PYTHON_INGEST_SCHEDULE=12 # Every x hours +# Every x hours +PYTHON_INGEST_SCHEDULE=12 CR=ghcr.io/fhswf/aki_prj23_transparenzregister TAG=latest diff --git a/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/DevOps Loop image.png b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/DevOps Loop image.png new file mode 100644 index 0000000..44f92ad Binary files /dev/null and b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/DevOps Loop image.png differ diff --git a/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/costOfChangeTraditional.gif b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/costOfChangeTraditional.gif new file mode 100644 index 0000000..a9330c9 Binary files /dev/null and b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/costOfChangeTraditional.gif differ diff --git a/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/dev-ops.md b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/dev-ops.md new file mode 100644 index 0000000..515eefa --- /dev/null +++ b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/dev-ops.md @@ -0,0 +1,1097 @@ +## DevOps & CI/CD + +DevOps und CI/CD sind verwandte Themengebiete mit starken Überschneidungen. + +### Definition: DevOps + +DevOps, eine Kombination aus *Development* und *Operations*, bezeichnet die enge Zusammenarbeit zwischen Entwicklern und IT-Operatoren. +Das Ziel ist es, den Produktlebenszyklus durch frühzeitige Berücksichtigung von Betriebsanforderungen zu beschleunigen. +Die Zusammenarbeit umfasst folgende Aspekte: + +- Implementierung der Automatisierung von Entwicklungs-, Test- und Deployment-Prozessen, um manuelle Fehler zu reduzieren und Effizienz zu steigern. +- Umsetzung von kontinuierlicher Integration (CI) und kontinuierlicher Bereitstellung (CD) für schnelle und zuverlässige Softwareauslieferung. +- Kurze Feedback-Schleifen, um schnell auf Änderungen reagieren und Probleme zeitnah beheben zu können. +- Förderung einer Kultur der Zusammenarbeit und des Lernens zur Unterstützung des Wissensaustauschs und kontinuierlicher Prozessverbesserungen. + +Typische DevOps-Tools sind: + +- Docker: Eine Plattform zur Erstellung, zum Versand und Betrieb von Anwendungen in isolierten Umgebungen (Containern). +- Docker-Compose: Ein Tool für Docker, das Multi-Container Docker-Anwendungen vereinfacht und als Code definiert. +- Ansible: Ein Open-Source-Automatisierungstool für Konfigurationsmanagement, Anwendungsdeployment und vieles mehr. +- Kubernetes: Ein Open-Source-System zur Orchestrierung von containerisierten Anwendungen. +- Terraform: Ein Infrastruktur-als-Code-Tool zum sicheren und effizienten Aufbau, Ändern und Versionieren von Infrastruktur. +- Configuration as Code: Ein Ansatz, bei dem Konfigurationen ähnlich wie Code in Versionskontrollsystemen gespeichert und versioniert werden. + +Der typische DevOps-Arbeitsfluss wird im folgenden Diagramm dargestellt: +![](DevOps Loop image.png) +[Quelle: Atlassian](https://www.atlassian.com/de/devops) + +### Definition CI/CD + +CI/CD steht für kontinuierliche Integration (*Continuous Integration*) und kontinuierliches Deployment (*Continuous Deployment*). +Dieser Ansatz in der Softwareentwicklung fokussiert auf die Entwicklung kleiner, in sich geschlossener Softwareteile. +Diese werden schnell in einen oder mehrere Hauptstränge integriert, meist über eine geeignete Git Branching-Strategie. +In unserem Projekt, das sich auf Entwicklung konzentriert, erfolgt die Integration ausschließlich in den Main Branch. + +Jede Integration in den Main Branch wird möglichst vollständig automatisch getestet, bevor sie freigegeben wird. +Tests können unterschiedlich gestaltet sein, von Unit-Tests bis zu statischen Codeanalysen. +Das bekannteste Framework für Unit-Tests in Python ist [pytest](https://docs.pytest.org/). +Zusätzlich werden oft statische Codeanalysen wie [ruff](https://docs.astral.sh/ruff/) oder [mypy](https://mypy-lang.org/) eingesetzt. + +Der Code wird oft vor der Integration einem verpflichtenden Review unterzogen. +Review-Vorgaben lassen sich in Werkzeugen wie GitHub, GitLab und Gitea konfigurieren. + +Dieses Vorgehen ermöglicht eine kleinschrittige, stetige Verbesserung und eine relativ einfache sowie kostengünstige Integration von Veränderungen in die Codebasis. +Durch den geringen Umfang der Veränderungen und das automatische Testen können Fehler vermieden oder frühzeitig gefunden und einfach repariert werden. + +### Dependency Management in Python + +Für das Dependency Management in Python gibt es verschiedene Werkzeuge. +Je nach Anwendungsfall und Erstellungszeitpunkt eines Projekts kommen unterschiedliche Tools zum Einsatz. +Hier einige Beispiele: + +#### `requirements.txt` + +Eine `requirements.txt` ist eine Textdatei, die die Abhängigkeiten eines Projekts auflistet. +Diese können dann komplett mit dem Befehl `pip install -r requirements.txt` installiert werden. +Das Erstellen und Warten der Datei kann jedoch problematisch sein. +Die `requirements.txt` listet Abhängigkeiten zeilenweise mit Versionsspezifikationen auf. +Dabei ist es üblich, sowohl direkte als auch indirekte Abhängigkeiten zu listen. +Die Datei wird entweder manuell erstellt oder schreibt die aktuell installierte Abhängigkeitssammlung nieder. +Die aktuell installierten packages werden dabei mit `pip freeze > requirements.txt` aufgezeichnet. +Dabei gibt es aber keinerlei garantien das die so aufgezeichneten Abhängigkeiten auch zueinander passen. +Indirekte Abhängigkeiten sind die Abhängigkeiten von Abhängigkeiten. + +Trotz ihrer Einfachheit wird die `requirements.txt` oft als Export- oder Zwischenprodukt genutzt. + +Eine beispielhafte `requirements.txt` für die beliebte Bibliothek `pandas` könnte wie folgt aussehen: + +```requirements +meson-python==0.13.1 +meson==1.2.1 +wheel +Cython==3.0.5 # Note: sync with setup.py, environment.yml and asv.conf.json +numpy>1.22.4,<=2.0.0.dev0 +versioneer[toml] +``` + +Es ist wichtig zu wissen, dass eine `requirements.txt` noch komplexer ausfallen kann, beispielsweise mit Hashwerten der + Abhängigkeiten und Bedingungen für deren Gültigkeit sowie Verweisen auf zusätzliche Paketquellen. +Diese Faktoren erschweren die Wartung der `requirements.txt`. +Der Name `requirements.txt` ist dabei eine starke Konvention, aber nicht verpflichtend. + +#### `pip-tools` + +`pip-tools` ist ein Python-Paket, das das manuelle Erstellen und Pflegen von Abhängigkeiten vereinfacht. +Es nutzt eine `requirements.in`, in der nur die direkten Abhängigkeiten aufgeführt werden. +Mit dem Befehl `pip-compile` kann aus der `requirements.in` eine aktuelle `requirements.txt` generiert werden, die alle Abhängigkeiten erfüllt. +Diese ist einfacher zu warten als eine rohe `requirements.txt`. +`pip-tools` bietet zudem einen Befehl, der installierte Pakete mit einer `requirements.txt` synchronisiert. +Dies ermöglicht ein einfacheres Arbeiten mit Paketen als mit `pip` allein. +Der Vorteil zeigt sich besonders in Teamumgebungen oder bei der Entwicklung auf mehreren Geräten. + +Da `requirements.in` und `requirements.txt` nur starke Konventionen sind, können zusätzlich zu den Anwendungsabhängigkeiten + auch Entwicklungsabhängigkeiten gepflegt werden. + +Indirekte Abhängigkeiten können bei mehreren direkten Abhängigkeiten anders ausfallen, was zu unerwartetem Verhalten der Software führen kann. +Ein Lösungsansatz hierfür ist die Erweiterung von `pip-tools` namens `pip-tools-multi`. +Üblicherweise werden die `requirements.txt` Dateien mit den gelösten Abhängigkeiten im Git verwaltet. + +#### `pip-compile-multi` + +`pip-tools-multi` ermöglicht es, Lösungen für mehrere Gruppen direkter Abhängigkeiten zu definieren. +So kann eine `base.in` die direkten Laufzeitabhängigkeiten einer Software auflisten. +Inhalte anderer `*.in`-Dateien können vererbt werden. +Ein Bündel von `*.in`-Dateien erhält eine Gesamtlösung, aus der diverse `*.txt`-Dateien im `requirements.txt`-Stil automatisch generiert werden. +Damit lassen sich zusätzliche Pakete für Testen, Linten und Entwickeln pflegen, ohne die Laufzeitumgebungspakete zu beeinflussen. +Die generierten `*.txt`-Dateien mit den gelösten Abhängigkeiten werden üblicherweise im Git verwaltet. + +#### `poetry` + +Das neueste Werkzeug zur Verwaltung von Python-Abhängigkeiten ist `poetry`. +Aktuell ist Poetry "Stand der Technik" und wird in vielen aktiv entwickelten Projekten genutzt. + +##### `pyproject.toml` + +Anders als die bisher vorgestellten Lösungen nutzt `poetry` die `pyproject.toml` zur Verwaltung von Abhängigkeiten. +Die `pyproject.toml` wurde in [PEP 518](https://peps.python.org/pep-0518/) vorgestellt und in den PEPs [517](https://peps.python.org/pep-0517/), + [621](https://peps.python.org/pep-0621/) und [660](https://peps.python.org/pep-0660/) weiter ausgeführt. +Ziel der `pyproject.toml` ist es, eine einzelne Datei zu definieren, in der alle Konfigurationen eines Python-Projekts enthalten sind, + einschließlich Build- und Abhängigkeitssystem. + +#### Abhängigkeitsverwaltung mit `poetry` + +Der Befehl `poetry new project-name` erstellt eine `Poetry`-Projektstruktur. +Diese umfasst `pyproject.toml`, `README.md`, ein Paket und einen Testordner. + +Mit `poetry init` lässt sich ein bestehendes Projekt um eine `poetry`-Sektion erweitern. + +Dadurch entsteht in der `pyproject.toml` eine grundlegende Konfiguration. +Für Ergänzungen und detaillierte Optionen verweise ich auf die umfangreiche Dokumentation unter [python-poetry.org](https://python-poetry.org/docs). + +Die `pyproject.toml` enthält nun Sektionen, die Laufzeit-, Test- und Entwicklungsabhängigkeiten in frei definierbaren Gruppen deklarieren. + +Ein Beispiel hierfür könnte wie folgt aussehen: + +```toml +[tool.poetry] +authors = ["AKI Projektgruppe 23"] +classifiers = [] +description = "Some describing Text!" +documentation = "https://some-url.eu/" +homepage = "https://some-url.eu/" +keywords = ["deutschland", "economy", "transparenzregister", "dataintegration", "handelsregister"] +maintainers = [] +name = "aki-prj23-transparenzregister" +packages = [{include = "aki_prj23_transparenzregister", from = "src"}] +readme = "README.md" +repository = "https://github.com/fhswf/aki_prj23_transparenzregister" +version = "0.1.0" + +[tool.poetry.dependencies] +pandas = "^2.0.0" + +[tool.poetry.group.develop.dependencies] +black = {extras = ["jupyter"], version = "*"} +jupyterlab = "*" +pre-commit = "*" + + +[tool.poetry.group.lint.dependencies] +black = "*" +mypy = "*" +pandas-stubs = "*" +pip-audit = "*" +pip-licenses = "*" +ruff = "*" + +[tool.poetry.group.test.dependencies] +pytest = "^7.4.2" +``` + +Mit den Befehlen `poetry update` oder `poetry lock` wird aus der `pyproject.toml` eine `poetry.lock`-Datei generiert. +Diese Datei erfüllt eine ähnliche Funktion wie die `requirements.txt`, zeichnet aber zusätzlich den Abhängigkeitsbaum auf welcher in der `poetry.lock` mit aufgezeichnet wird. +So bietet sie eine übersichtliche Verwaltung der direkten und indirekten Abhängigkeiten. + +[Die vollständige Dokumentation der Poetry Sektion über Abhängigkeiten der Pyproject.toml kann auf https://python-poetry.org/docs/managing-dependencies/ gefunden werden.](https://python-poetry.org/docs/managing-dependencies/) + +#### Poetry cheat sheet + +1. `poetry install` (`--with`, `--without`, `--only`)\ + **Funktionalität**: Installiert die Abhängigkeiten, die in der pyproject.toml Datei aufgelistet sind. + - `--with`: Ermöglicht das explizite Hinzufügen von optionalen Gruppen von Abhängigkeiten zur Installation. + - `--without`: Schließt bestimmte Gruppen von Abhängigkeiten von der Installation aus. + - `--only`: Installiert ausschließlich die angegebenen Abhängigkeitsgruppen und ignoriert alle anderen. + +2. `poetry update`\ + **Funktionalität**: Aktualisiert die Abhängigkeiten eines Projekts auf ihre neuesten verfügbaren Versionen, basierend auf den Einschränkungen, die in der pyproject.toml-Datei definiert sind. + Details: + - Bei der Ausführung überprüft poetry update, ob es neuere Versionen der in pyproject.toml definierten Paketabhängigkeiten gibt, die mit den spezifizierten Versionseinschränkungen kompatibel sind. + - Es aktualisiert die `poetry.lock`-Datei, um diese neuen Versionen widerzuspiegeln, wodurch sichergestellt wird, dass bei zukünftigen Installationen mit poetry install genau diese Versionen verwendet werden. + - Man kann spezifische Pakete für die Aktualisierung auswählen, indem man sie als Argumente hinzufügt (z.B. poetry update flask aktualisiert nur Flask und seine Abhängigkeiten). + Keine zusätzlichen Optionen: Der Befehl hat keine weiteren Modifikatoren oder Optionen. + - Je nachdem wie die Version festgelegt wurde, werden Updates auf [*patches* (`~1.2.3` => `>=1.2.3 < 1.3.0`)](https://python-poetry.org/docs/dependency-specification/#tilde-requirements), oder [*minor* (`^1.2.3` => `>=1.2.3 <2.0.0`)](https://python-poetry.org/docs/dependency-specification/#caret-requirements) Versions begrenzt . + +3. `poetry add` (`--group`)\ + **Funktionalität**: Fügt eine neue Abhängigkeit zum Projekt hinzu. + - Option --group: Mit dieser Option kann die Abhängigkeit einer spezifischen Gruppe zugeordnet werden, z.B. development oder testing. Dies ist nützlich, um Abhängigkeiten, die nur in bestimmten Umgebungen benötigt werden, separat zu verwalten. + - Versionsrestriktionen können mit einem `@` an die Abhängigkeit angefügt werden. + Diese sind Standardmäßig die aktuelle Version bis zur nächsten Hauptversion in der Zirkumflex-Schreibweise (`^1.2.3`). + +4. `poetry remove`\ + **Funktionalität**: Entfernt eine bestehende Abhängigkeit aus dem Projekt. + +5. `poetry build`\ + **Funktionalität**: Erstellt das Paket für das Projekt. + Dies umfasst typischerweise die Erstellung von wheel- und sdist-Archiven. + Keine zusätzlichen Optionen: Dieser Befehl kompiliert das Projekt in ein Format, + das auf PyPI oder anderen Paket-Indizes veröffentlicht werden kann. + +6. `poetry publish`\ + **Funktionalität**: Veröffentlicht das Paket auf einem Paket-Index wie PyPI. + Wichtige Optionen: + - `--repository`: Gibt das Repository an, auf das das Paket hochgeladen werden soll. + - `--username`, `--password`: Für die Authentifizierung bei einem privaten Repository. + - `--build`: Führt automatisch poetry build vor dem Veröffentlichen aus. + +7. `poetry lock`\ + **Funktionalität**: Locked die dependencies wie `poetry update` aber ohne diese Direkt zu installieren. + +[Die vollständige Dokumentation aller Befehle kann auf https://python-poetry.org/docs/cli/ gefunden werden.](https://python-poetry.org/docs/cli/) + +#### Cython + +Poetry unterstützt das Bauen von Cython-Modulen, + obwohl diese Unterstützung nur inoffiziell ist und keine offizielle Dokumentation existiert. +In unserem Projekt ist der Einsatz von Cython jedoch nicht vorgesehen. + +#### Extras + +`poetry` bietet die Möglichkeit, Extras zu definieren. +Extras sind Abhängigkeitsgruppen, die bei Bedarf installiert werden. +Dabei wird die Gesamtlösung der Abhängigkeiten über alle Gruppen hinweg erstellt. +Die direkten und indirekten Abhängigkeiten werden nur installiert, wenn eine spezifische Extragruppe bei der Installation eines Python Wheels angefordert wird. +Bei der untenstehenden Konfiguration würde `pip install ` lediglich `pandas` als Abhängigkeit installieren. +Der Befehl `pip install [ai]` würde zusätzlich `tensorflow` und alle dessen indirekten Abhängigkeiten installieren. + +```toml +[tool.poetry.dependencies] +pandas = "^2.0.0" +tensorflow = "^2.10.0" + +[tool.poetry.extras] +ai = ["tensorflow"] +``` + +Natürlich können mehrere Extragruppen konfiguriert werden. + +#### Script Entry Points + +In der `pyproject.toml` können Einstiegspunkte für das Skript definiert werden. +Diese werden bei der nächsten Ausführung von `poetry install` erstellt. +Auch ein kompiliertes Python-Wheel erstellt diese Einstiegspunkte während der Installation. +Die Definitionen für Einstiegspunkte können wie folgt aussehen: + +```toml +[tool.poetry.scripts] +start-program = "package.module:method" +``` + +Mehrere Einstiegspunkte können in der Konfiguration definiert werden. +Nach der Installation über `poetry install` oder die Installation eines fertigen `wheels` steht der `start-program`-Einstiegspunkt zur Verfügung. +Dieser führt die Methode `method` aus dem Paket `package` und dem Modul `module` aus. +Ist das Python-`script`-Verzeichnis im PATH gelistet, steht das Kommando auch global zur Verfügung. + +### Linter + +Der Begriff `Lint` leitet sich vom englischen Wort für "Fussel" ab und bezeichnet die statische Codeanalyse. +Hierbei wird der Programmcode auf Schwachstellen, Fehler, Sicherheitslücken oder Verstöße gegen Codekonventionen analysiert, ohne ihn auszuführen. + +In Python, einer nicht kompilierten und schwach typisierten Sprache, werden viele Fehler erst bei der Ausführung sichtbar. +In Compilierten und streng typisierten Programmiersprachen werden viele Probleme schon in der Compilezeit sichtbar. +Einige Fehler treten nur unter seltenen Umständen auf. +Daher ist die strukturelle Analyse des Codes in Python besonders wichtig. + +Es gibt verschiedene Programme für Python, die unterschiedliche Analysen durchführen. +Im Folgenden werden die aktuell wichtigsten Werkzeuge vorgestellt. + +#### `mypy` + +`mypy` ist ein von der Python Foundation gewartetes Werkzeug, das die Typisierung in Python statisch überprüft. +Obwohl Python eine schwach typisierte Programmiersprache ist, überwiegen in größeren Projekten oft die Nachteile dieser Eigenschaft. +Die Einführung einer Typisierung wird dann sinnvoller, insbesondere wenn Teammitglieder mit Funktionen und Klassen arbeiten, die von anderen entwickelt wurden. +Das Fehlerpotenzial durch unzulässige oder ungetestete Zuweisungen kann durch optionale Typisierung in Python verringert werden, besonders effektiv in Kombination mit einem Linter wie `mypy`. +`mypy` führt eine statische Code-Analyse durch und identifiziert widersprüchliche Zuweisungen sowie unzulässige Argumente, wobei Lösungshinweise oder Fehlerbeschreibungen bereitgestellt werden. + +##### Typisierung in Python + +In Mypy erhalten Variablenzuweisungen, wenn bestimmbar, den Typ der ersten Zuweisung. +Der Stil der Typisierung hat sich in fast jeder der letzten Python-Versionen geändert, daher wird hier nur auf die Typisierung wie in Python 3.11 implementiert eingegangen. +Die folgenden beiden Zuweisungen sind sowohl in der Zuweisung als auch im Typ für Mypy identisch: + +**Variablenzuweisung**: +```python +u_number = 5 +t_number: int = 5 +``` + +Ein Funktionskopf kann beispielhaft wie folgt typisiert werden: + +**Funktionskopf**: +```python +def some_funtion_name( + arg1: int, + arg2: list[int | SomeClass | None] = None + ) -> OtherClass: + # ... + # do something + # ... + return OtherClass() +``` + +Typisierung bietet neben der Dokumentation weitere Vorteile. +Sie unterstützt die Autovervollständigung in IDEs wie PyCharm, Visual Studio Code oder Jupyter. +Zudem enthalten Typisierungen Informationen, die von Language Models (LLMs) genutzt werden können. +Diese Modelle bearbeiten, vervollständigen, dokumentieren oder erklären Code unter Berücksichtigung der Typisierung. +Vor allem aber gibt die Typisierung werkzeugen wie `mypy` anhaltspunkte darüber welche Typen für einen Wert bzw. Rückgabewert erwartet werden. +Dies kann dan über statische Analysen abgeglichen werden. + +`mypy` ist nicht immer in der Lage, Typen korrekt zu inferieren. +Dies kann an unvollständigem Code liegen. +Ist der Code jedoch korrekt, kann der Kommentar `# type: ignore` verwendet werden, um die Typüberprüfung für einzelne Zeilen zu unterdrücken. +Die Unterdrückung der Typisierungsprüfung sollte nur erfolgen, wenn man sich sicher ist, dass dies notwendig ist. + +#### `ruff` + +`ruff` ist eine Neuentwicklung in Rust, basierend auf verschiedenen anderen Werkzeugen. +Es folgt eine unvollständige Liste wichtiger Linter, die in `ruff` reimplementiert wurden: +- `flake8`: Dieses Werkzeug überprüft Quellcode auf die Einhaltung des PEP 8-Stils, Programmierfehler und Komplexität. Es kombiniert verschiedene Tools wie PyFlakes, pycodestyle und McCabe. +- `isort`: Sortiert Importe in Python-Dateien. Isort automatisiert die Sortierung und Trennung von Importen in Abschnitte sowie deren alphabetische Anordnung zur Verbesserung der Lesbarkeit. +- `pylint`: Ein umfassendes Werkzeug, das nach Programmierfehlern sucht, den Code-Stil zu standardisieren versucht und die Refaktorisierung von Code vorschlägt. Pylint bietet detaillierte Berichte über potenzielle Codeprobleme. +- `bandit`: Speziell für die Sicherheitsprüfung von Python-Code entwickelt. Es durchsucht den Code nach häufigen Sicherheitslücken und warnt bei potenziell unsicheren Konstrukten. +- `flake8`-Plugins: `ruff` implementiert auch einige gängige flake8-Plugins. Diese Plugins erweitern flake8 um zusätzliche Prüfungen und Funktionen, die über die Standardfunktionen hinausgehen. + +Im Gegensatz zu `flake8`, `bandit` und `pylint` kann `ruff` einfache Korrekturen selbstständig durchführen. +Dies führt dazu, dass Nutzern manche Regeln möglicherweise nicht bewusst werden, aber sie werden nicht von unwichtigen Problemen aufgehalten. +`ruff` wird in der `pyproject.toml` konfiguriert, wo einzelne Regelsätze und individuelle Regeln aktiviert oder deaktiviert werden können. +Die Beschleunigung gegenüber bekannten Lintern beträgt etwa 1000%, was auch das Problem der Pluginhölle und der Mehrfachausführung verschiedener Tools beseitigt. + +Wichtig zu wissen ist, dass `ruff` Dateien isoliert analysiert, im Gegensatz zu anderen Programmen. +Dies führt manchmal zu Fehlern, wie dem Nichterkennen vererbter Docstrings über Dateigrenzen hinweg, was zu fehlerhaften Erkennungen führen kann. + +Tools wie `ruff` sind besonders nützlich für Python-Einsteiger, da sie viele stilistische Details im Code verfeinern und Verbesserungspotentiale hervorheben. +So werden Styleguides wie PEP-8 nach und nach erschlossen, ohne Nutzer mit umfangreichen Informationen zu überfordern. + +`ruff` Fehlercodes können durch den `# noqa` Kommentar lokal unterdrückt werden. +Es ist jedoch besser, die Unterdrückung spezifisch mit `# noqa: F1, F2` durchzuführen, um z. B. die Fehlercodes F1 und F2 zu unterdrücken. +Die alleinige Verwendung von `# noqa` unterdrückt alles und ist daher **nicht** empfohlen. +Die Fehlercodes werden bei der Ausführung von `ruff` angezeigt. + +#### `black` + +`black` ist ein Linter, der sich ausschließlich um die menschliche Lesbarkeit des Codes kümmert. +Er formatiert Whitespace nach immer gleichen Mustern. +Dadurch sieht der Code konsistent aus, was mehrere Ziele erfüllt: + +1. Der Programmierer muss sich nicht um die Formatierung kümmern. +2. Andere Programmierer müssen sich nicht an unterschiedliche Formatierungen gewöhnen. +3. `black` lässt sich kaum konfigurieren, sodass alle damit formatierten Projekte gleich aussehen. Die somit erreichte konsistenz reduziert den mentalen Overhead beim Lesen und erhöht somit das Verständnis. + +Für einen tieferen Einblick in die Philosophie von `black` empfiehlt sich [der Vortrag des Autors auf der PyCon](https://www.youtube.com/watch?v=esZLCuWs_2Y). +Black hat sich als inoffizieller Standard für die Python-Codeformatierung etabliert. + +#### pip-audit + +`pip-audit` ist ein OWASP-Abhängigkeitsscanner für Python. +Er scannt installierte oder in einer `requirements.txt` festgelegte Abhängigkeiten auf bekannte Sicherheitslücken. +Es ist redundant, wenn GitHubs Dependabot verwendet wird. +Eine kostenpflichtige Alternative ist das `safety`-Tool, das schneller Sicherheitslücken aktualisiert und möglichkeiten zur Korrektur durch automatische Updates bietet. +Auch `safety` ist redundant, wenn Dependabot genutzt wird. + +#### pip-licenses + +`pip-licenses` listet und analysiert die Lizenzen der verwendeten Abhängigkeiten. +Es kann überprüfen, ob nur Software mit erlaubten oder nicht verbotenen Lizenzen verwendet wurde. +Die Liste der erlaubten und verbotenen Lizenzen ist frei konfigurierbar. +Für akademische Projekte ist dies weniger wichtig und daher nicht implementiert, wird aber der Vollständigkeit halber erwähnt. +In einem Corporate/Compliance Kontext ist dies aber natürlich von Bedeutung. + +### pytest + +Um die anfängliche Funktionalität und Stabilität von Programmteilen sicherzustellen, ist es sinnvoll, Tests zu schreiben, auch im Hinblick auf spätere Modifikationen! +Diese vergleichen die Rückgabewerte von Funktionen mit Erwartungswerten. +Python wird mit einem integrierten Test-Framework ausgeliefert, oft wird jedoch `pytest` aufgrund der einfacher Syntax verwendet. + +Hier ein einfaches Beispiel für Code und Test: + +```python + +def addition(a: int, b: int) -> int: + """Die Funktion welche zu testen ist.""" + return a + b + + +def test_addition() -> None: + """Ein Test für die Funktion addition.""" + assert addition(1, 5) == 6 +``` + +Tests werden üblicherweise im `tests`-Verzeichnis angelegt. +`pytest` durchsucht dieses Verzeichnis nach Dateien, die auf `*_test.py` enden, und registriert darin alle Funktionen, die mit `test` beginnen, als Tests. +Diese Tests werden dann nacheinander ausgeführt. +Nicht alle Tests sind so einfach wie das oben dargestellte Beispiel. +Oft müssen mehrere Schritte ausgeführt werden. +Es ist jedoch ratsam, Funktionen unabhängig und kleinschrittig in einzelnen Tests zu überprüfen. +Das führt zu besserer Übersichtlichkeit, besonders wenn Tests fehlschlagen da so eine Gesamtübersicht über das Fehlerverhalten vorliegt. + +Einige Abhängigkeiten bieten eigene assert-Funktionen. +Zum Beispiel enthält `pandas` Funktionen wie `pandas.testing.assert_frame_equal`, `pandas.testing.assert_series_equal` und `pandas.testing.assert_index_equal`, die `pandas`-Objekte vergleichen und verschiedene Assertions durchführen. +Meine persönliche Präferenz ist es jedoch, wenn möglich, `pandas`-Objekte in Dictionaries zu konvertieren und diese dann mit `assert` zu vergleichen, da dabei fehlschlagende Vergleiche aussagekräftiger sind. +Dies ist jedoch eine persönliche Präferenz. +Auch `numpy` enthält ein `testing`-Unterpaket. + +Globale Konfigurationen oder Code, der vor dem ersten Test ausgeführt werden soll, können in der `conftest.py` im `tests`-Ordner abgelegt werden. + +#### Setup und Teardown + +Manche Tests erfordern einen Aufbau (`setup`) und ein nachfolgendes Aufräumen (`teardown`). +Dies wird auch als "setup and teardown" bezeichnet. +In `pytest` erfolgt dies über Fixtures. + +Fixtures sind Funktionen, die mit dem `pytest.fixture`-Decorator versehen werden. +Dieser wandelt eine Funktion in eine Fixture um. +Zusätzlich können Einstellungen vorgenommen werden, wie beispielsweise die Nutzungsdauer und der Geltungsbereich der Fixture. + +Beispiele für die Nutzung von Fixtures sind: +- Erstellung und Löschung von Dateisystemen. +- Erstellung von Datenbank-Sessions oder Datenbanken (SQLite). +- Überschreiben von Funktionen und Umgebungsvariablen. +- *Mocken* von Netzwerkverbindungen/Sessions. + +Eine Fixture teilt sich in einen Setup-, einen Teardown-Teil und `yield` auf. +Im Setup-Teil werden Vorbereitungen für ein Testelement durchgeführt. +Im Teardown-Teil wird dieses Element aufgeräumt, falls notwendig. +`yield` trennt Setup und Teardown. +`yield` übergibt ein Objekt oder einen Wert an den Test und pausiert die Ausführung der Fixture, bis die Tests im Scope abgeschlossen sind. + + +Der Teardown wird nach Abschluss der Tests durchgeführt. +Das garantiert, dass auch bei fehlschlagenden oder nicht durchführbaren Tests aufgeräumt wird. + +Wird kein Teardown benötigt, wird konventionell `return` anstelle von `yield` verwendet. + +Fixtures können auf anderen Fixtures aufbauen. + +Eine Fixture wird verwendet, indem der Methodenname als Argument einem Test hinzugefügt wird oder `autouse=True` im Decorator gesetzt wird. +Alternativ kann der `pytest.mark.usefixtures("fixturename")`-Decorator genutzt werden. + +```python +from typing import Generator + +import pytest + +from sqlalchemy.orm import Session + +@pytest.fixture(autouse=False, scope="function") +def create_test_sql() -> Generator[Session, None, None]: + # create_test_sql_table + # create sql connection + yield sql_session + # delete sql connection + # delete sql tables + +def test_sql_table(create_test_sql: Session) -> None: + # executes a test on the test db + assert create_test_sql.query(HelloWorldTable).get("hello") == "world" +``` + +Ein Test kann mehrere unabhängige Fixtures nutzen. +Fixtures, die für alle Tests verfügbar sein sollen, werden in der `conftest.py` im `tests`-Ordner abgelegt. +Eine in der `conftest.py` definierte Fixture muss in Tests nicht importiert werden. + +Für schnelle und performante Datenbanktests wird empfohlen, diese als In-Memory-Datei anzulegen. +Dies geschieht bei SQLite mit dem Pfad `sqlite:\\\:memory:`. + +Fixtures, die den `yield`-Operator verwenden, haben den Rückgabetyp `Generator[SomethingYielded, None, None]`. +Fixtures ähneln Kontextmanagern. + +#### Mock + +In manchen Situationen müssen Softwareteile für effiziente Tests überschrieben werden. +Beispiele hierfür sind Netzwerkverbindungen und redundante, zeitaufwendige Berechnungen. + +Mocks werden in Fixtures oder innerhalb von Kontextmanagern verwendet. +Dies kan auch in Fixtures kombiniert werden, um Wiederverwendbarkeit zu fördern. +Natürlich ist es sinnvoll mehrere abhängige Mocks in einer Fixture zusammenzufassen. + +##### Beispiel `pytest-mock` + +In manchen Fällen ist es nicht ausreichend, nur das Ergebnis einer Anfrage zu überprüfen. +Stattdessen muss untersucht werden, welche internen Funktionen aufgerufen werden. +`pytest-mock` kann hierbei verwendet werden. +Mit `pytest-mock` lassen sich Funktionsaufrufe und deren Ergebnisse aufzeichnen und anschließend analysieren. +Dies ermöglicht beispielsweise Aussagen darüber, ob Caching-Operationen erfolgreich waren. + +```python +class Foo(object): + def bar(self, v): + return v * 2 + +def test_spy_method(mocker) -> None: + foo = Foo() + spy = mocker.spy(foo, 'bar') + assert foo.bar(21) == 42 + + spy.assert_called_once_with(21) + assert spy.spy_return == 42 +``` + +##### Beispiel `requests-mock` + +Bei der Verwendung einer Anfrage (`request`) innerhalb eines Tests hängt der Test von der korrekten Serverantwort ab. +Es ist meist sinnvoller, solche Anfragen zu überschreiben. +Dieser Ansatz ist zuverlässiger, deterministischer und schneller. +Interaktionen mit anderen Komponenten eines Softwarekomplexes werden in Integrationstests behandelt nicht in Unit-Tests. + +```python +import requests +import requests_mock + +def test_mock_requests() -> None: + with requests_mock.Mocker() as m: + m.get('http://test.com', text='data') + assert requests.get('http://test.com').status_code == 200 +``` + +##### Beispiel `MonkeyPatch` + +Pytest's `MonkeyPatch` kann genutzt werden, um viele Werte temporär zu überschreiben, wie zum Beispiel Umgebungsvariablen und Funktionen. +Das folgende Beispiel zeigt, wie eine Umgebungsvariable für die Dauer eines Tests überschrieben wird. + +```python +import os + +def test_partial(monkeypatch): + assert not os.getenv("SOME_ENV_VAR") + monkeypatch.setenv("SOME_ENV_VAR", "SOME_VALUE") + assert os.getenv("SOME_ENV_VAR") == "SOME_VALUE" +``` + +#### `parametrize` + +Oft ist es sinnvoll, einen Test mit verschiedenen Werten durchzuführen. +Dafür ist eine For-Schleife ungeeignet, da ein Test bei der ersten fehlschlagenden Assertion abbricht. +Eine Möglichkeit, die Code-Duplikation vermeidet, ist die Auslagerung der Schleife mit dem `pytest.mark.parametrize`-Decorator. + +```python +import pytest + +@pytest.mark.parametrize( + ("a", "b"), + [(2, 4), (3, 9), (4, 6)], +) +def test_sqr(a: int, b: int) -> None: + assert a**2 == b + + +def test_sqr_wrong() -> None: + for a, b in [(2, 4), (3, 9), (4, 6)]: + assert a**2 == b +``` + +Der `pytest`-Decorator erwartet zuerst ein Tupel der Argumentnamen und als Zweites eine Liste der Wertetupel welche später Entpackt werden. +Bei nur einem Parameter kann dieser direkt als String benannt werden. +Mehrere `parametrize`-Decorator können verwendet werden, um ein mehrdimensionales, unabhängiges Parameterfeld aufzuspannen. +`parametrize` kann zusammen mit Fixtures verwendet werden. + +#### `pytest.raises` + +Manchmal ist es notwendig zu prüfen, ob ein Fehler korrekt ausgelöst wird, anstatt auf einen Rückgabewert zu achten. +`pytest` bietet dafür den `pytest.raises` Kontext an. +Dieser prüft, ob ein bestimmter Fehler ausgelöst wird und ermöglicht das druchführen weiterer Validierungen in diesem Kontext. +```python +import pytest + +def i_raise_an_error() -> None: + raise ValueError("Happy to provide an example!") + +def test_value_error_raise(): + with pytest.raises(ValueError, match="Happy"): + i_raise_an_error() +``` + +Der Beispielcode überprüft, ob eine Funktion einen ValueError auslöst. +Zusätzlich wird geprüft, ob der Fehler die Zeichenkette "Happy" enthält. +Schlägt der Test fehl, liegt es daran, dass der Fehler nicht wie erwartet ausgelöst wird oder das Wort "Happy" nicht enthält. + +#### Code-Coverage + +Beim Testen ist es wichtig, einen Überblick darüber zu haben, welche Codebereiche bereits getestet sind und welche noch nicht. +Eine Möglichkeit, dies zu tun, ist die Verwendung von Code-Coverage. +Dieses Tool zählt, wie oft jede Codezeile beim Testen ausgeführt wird und überprüft, welcher Verzweigung im Code gefolgt wird. +Das Coverage lässt sich als Metrik (meist in Prozent) darüber welcher anteil des Codes beim Testen ausgeführt wurde darstellen oder visuell aufbereiten, um aufzuzeigen, wo noch Tests fehlen. +In einigen IDEs wird dies durch farbliche Hervorhebungen umgesetzt. +Es ist nicht unüblich, die Code-Coverage über Tools wie SonarQube anzuzeigen und Änderungen zu verfolgen. + +Es ist wichtig, die Grenzen dieser Metriken und Markierungen zu kennen: +1. Code-Coverage zählt die Ausführungen pro Codezeile, nicht die sinnvolle Prüfung von Rückgabewerten oder Funktionsergebnissen. +2. Code-Coverage existiert auch, wenn nur die äußerste Methode getestet wird, aber es ist ökonomischer, erst innere Funktionen einzeln zu testen. +3. Je kleiner der zu testende Codeabschnitt, desto schneller der Test. +4. Jede Abzweigung benötigt mindestens einen weiteren Test. Verschachtelte Abzweigungen sind multiplikativ. + Im Englischen spricht man dabei vom *Condition Coverage* oder *Branch Coverage*. +5. Eine Code-Zeile ist ein Befehl. Ein Befehl über mehrere Zeilen gilt als eine Zeile für die Coverage-Analyse. + +Verzerrte Metriken erschweren ein aussagekräftiges Bild über die Testqualität. + +Im Test-Driven Development werden Tests vor oder während der Funktionsentwicklung geschrieben. +Obwohl dies die Lehrbuchvariante ist, wird es selten so praktiziert. +Wichtig ist jedoch, dass Tests so schnell wie möglich erstellt werden. +Das Erstellen von Tests zur Sicherstellung der Funktionalität bei Änderungen wird empfohlen. +Je früher ein Test erstellt wird, desto früher bring dieser nutzen. +Wird ein Test erst nachträglich erstellt, ist der Nutzen am geringsten. + +Wird eine Funktion oder Klasse in einem Jupyter-Notebook entwickelt empfiehlt sich das `ipytest` package. +Dieses erlaubt die Nutzung von pytest innerhalb von Jupyter-Notebooks. + +Die meisten Softwareprojekte haben eine untere Grenze für das Code-Coverage für neuen oder modifizierten Code. +Ein allgemeiner Standardwert liegt bei 80 %. +Das bedeutet, dass Code nur dann dem `main`-Branch hinzugefügt werden darf, wenn mindestens 80 % des Codes beim Testen ausgeführt werden. + +Normalerweise wird die Code-Coverage in einem Werkzeug wie SonarQube mitgeschrieben und visualisiert. +GitHub bietet dazu keine native Möglichkeit an. +Es gibt über den Marketspace dazu Werkzeuge, welche aber meist auf Organisationsebene eingebunden werden müssen. +Und das Aufsetzen von SonarQube für die Dauer des Projektes wurde hier als nicht sinnvoll angesehen. + +### Sphinx + +Sphinx ist ein weit verbreitetes Werkzeug zur Erstellung von Python-Dokumentationen. +Viele Werkzeuge verwenden `sphinx` mit einem ReadTheDocs-Layout für ihre Projektdokumentationen. +`sphinx` unterstützt zahlreiche andere Layouts. +Dies ist auch für unsere Projektdokumentation sinnvoll. + +Sphinx-Konfigurationen sind nicht Teil der `pyproject.toml`. + +Zum Erstellen von Sphinx-Projekten existiert ein CLI-Interface. +Dieses kann mit dem Befehl `sphinx-quickstart` aufgerufen werden. + +Es wird ein Verzeichnis für die Dokumentation erstellt, bei uns ist dies das `documentations`-Verzeichnis. +Dort befinden sich eine `makefile` für Linux und eine `make.bat` für Windows. +Beide erzeugen verschiedene Ausgabeformate. +Relevant für uns sind die Formate `html` und `latexpdf`. + +Neben diesen liegt die `index.rst`, die als Startseite der Dokumentation dient und eine `conf.py`. + +#### `index.rst` + +In der `index.rst` können Inhaltsverzeichnisse und API-Dokumentationen referenziert werden. +Inhaltsverzeichnisse listen Dokumente auf, optional unter Verwendung von `glob`. +Das erste Inhaltsverzeichnis wird durchnummeriert, das zweite nicht. + + +```rst +Fancy Dockumentation +==================== + +Welcome. +Below you will find two Table of Content on of the generell documentation. +a second Table of Contents (TOC) containing the API docs for `my_fancy_package` + +.. toctree:: + :glob: + :maxdepth: 3 + :caption: Main Content + :numbered: + + some-other-file + a-folder-full-of-files/* + +.. toctree:: + :maxdepth: 6 + :caption: Modules + + modules + +.. automodule:: my_fancy_package + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + :autodoc_member_order: +``` + +#### Plugins + +Sphinx ist eine Pluginhölle. +Sogar plugins können plugins haben. So zum Beispiel der `myst_parser`. +Hier ist eine Liste der verwendeten Plugins. + +1. **sphinx.ext.autodoc**: Dieses Plugin automatisiert die Dokumentation von Python-Quellcode. Es extrahiert Dokumentation aus den Docstrings der Module, Klassen, Funktionen und Methoden. +2. **nbsphinx**: Ermöglicht die Integration von Jupyter Notebooks in Sphinx-Dokumentation. Es konvertiert Notebooks in Sphinx-Dokumente, wobei die Inhalte der Notebooks direkt gerendert werden. +3. **myst_parser**: Ein Parser, der Markdown-Dateien in Sphinx verarbeitet. Er unterstützt erweiterte Markdown-Features und ermöglicht die Verwendung von Sphinx-spezifischen Direktiven in Markdown. +4. **sphinx.ext.napoleon**: Dieses Plugin ermöglicht es Sphinx, Docstrings zu parsen, die im NumPy- oder Google-Stil verfasst sind. Es macht die Integration solcher Docstrings in die generierte Dokumentation einfacher und übersichtlicher. +5. **sphinx_copybutton**: Fügt einen "Kopieren"-Button zu Codeblöcken in der Sphinx-Dokumentation hinzu. Dies erleichtert es dem Benutzer, Code-Snippets zu kopieren. +6. **sphinx_autodoc_typehints**: Dieses Plugin fügt automatisch Typenhinweise aus Python-Funktionen und -Methoden in die Sphinx-Dokumentation ein. Es hilft, die Dokumentation klarer und informativer zu gestalten. +7. **sphinx.ext.intersphinx**: Ermöglicht das Verlinken zu Dokumentationen anderer Projekte, die mit Sphinx erstellt wurden. Dieses Plugin unterstützt das Erstellen von Querverweisen zwischen verschiedenen Sphinx-Dokumentationen. +8. **sphinx.ext.autosectionlabel**: Generiert automatisch Labels für jede Sektionstitel in der Dokumentation. Dies vereinfacht das Verlinken innerhalb der Dokumentation. +9. **sphinx.ext.viewcode**: Fügt Links zu den Quellcode-Dateien neben den dokumentierten Python-Objekten hinzu. Dies ermöglicht es dem Benutzer, direkt vom Dokument zum entsprechenden Quellcode zu springen. +10. **IPython.sphinxext.ipython_console_highlighting**: Bietet Syntax-Highlighting für IPython-Interaktionskonsolen in Sphinx-Dokumentationen. Dies verbessert die Lesbarkeit von IPython-Codebeispielen. +11. **sphinxcontrib.mermaid**: Erlaubt die Integration von Mermaid-Diagrammen in Sphinx-Dokumentationen. Mermaid ist ein Tool zur Erstellung von Diagrammen und Flussdiagrammen mittels Textbeschreibungen. +12. **notfound.extension**: Zeigt eine benutzerdefinierte 404-Seite in der HTML-Dokumentation, wenn eine Seite nicht gefunden wird. Dies verbessert die Benutzererfahrung bei fehlenden Seiten. +13. **sphinxcontrib.drawio**: Integriert draw.io-Diagramme in Sphinx-Dokumentation. Dies ermöglicht es, komplexe Diagramme direkt in die Dokumentation einzubinden. +14. **sphinx_git**: Ein Plugin für Sphinx, das es ermöglicht, Informationen aus einem Git-Repository in Dokumentationen einzufügen. Dies kann zum Anzeigen von Versionsinformationen oder zur Verfolgung von Änderungen verwendet werden. + +Besonders hervorzuheben finde ich die Interaktionen der verschiedenen Plugins. +`sphinx_autodoc_typehints` integriert wunderbar die Links welche von `sphinx.ext.intersphinx` bereitgestellt werden. +Dies macht einen Einstieg in Sphinx lieder sehr mühsam. + +#### `conf.py` + +Die `conf.py` ist eine Datei im `documentation`-Verzeichnis. +Sie konfiguriert Elemente wie Titel, Versionsnummern und Plugins. + +Einstellungen wie Typisierungsstil und Verlinkungen zu Fremddokumentationen sind hier definierbar. +So können beispielsweise Rückgabewerte vom Typ `pd.DataFrame` mit der `pandas`-Dokumentation verlinkt werden. + +Einmal erstellte Konfigurationen können oft größtenteils von Projekt zu Projekt übernommen werden. +Auch die Pfade für `custom.css` und das Standard-HTML-Theme werden hier festgelegt. + +#### API-DOCS + +Sphinx kann aus Docstrings eine API-Dokumentation erstellen. +Vor dem Bauen mit `make` muss dazu der Befehl `sphinx-apidoc` im `documentation`-Verzeichnis ausgeführt werden. + +#### Docstrings - Google-Style +In diesem Projekt wurde sich zu Anfang geeinigt, das Google-Style docstrings verwendet werden sollen. +[Die volle Dokumentation dazu findet sich hier.](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) + + +### GitHub + +Die FH-SWF hat uns ein GitHub-Repository bereitgestellt. +Dies dient als zentraler Ort zur Versionierung des Projekts und zum Abgleich der Entwicklungsstände des Teams. +Eine detaillierte Erklärung von Git würde hier zu weit führen. + +#### Pull Requests + +Git-Repositories ermöglichen die Entwicklung einzelner Entwicklungsstränge (Branches) lokal, die dann zentral synchronisiert werden. +Ein abgeschlossener Entwicklungsstrang wird auf den Default-Branch zurückgeführt. +Die richtige Größe solcher Entwicklungen zu finden, ist herausfordernd, da große und langwierige Entwicklungen schwer zu integrieren sind. + +Bei Git-Projekten wird in der Regel ein formeller Antrag, ein sogenannter Pull Request (PR), gestellt. +In anderen Systemen wird dies als Merge Request bezeichnet. +Es wurde festgelegt, dass Änderungen, die dem `main`-Branch hinzugefügt werden sollen, von mindestens einer Person korrektur gelesen werden müssen. +Die Genehmigung eines Pull Requests hängt von der positiven Bewertung ab. +Änderungsvorschläge sind normal und wünschenswert, um die Qualität des Projekts zu steigern. +Es ist üblich, ein Teammitglied, das mit dem speziellen Thema vertraut ist, um ein Code Review zu bitten. +Dies erfolgt über den entsprechenden Button im Verlauf des Pull Requests. +![request-review.png](request-review.png) + +Es wurde auch festgelegt, dass nur Pull Requests gemerged werden können, für die alle Tests erfolgreich waren. +Dies lässt sich durch Branch-Protection-Regeln in den Repository-Einstellungen unter dem Tab Branches festlegen. +![Pull_request.PNG](presentation/Pull_request.PNG) + +#### Actions + +GitHub Actions ist die Lösung von GitHub für das Erstellen und Durchführen von CI/CD-Pipelines. +Arbeitsflüsse (`workflows`) werden definiert und bei Bedarf ausgeführt. +`workflows` werden im `.github\workflows`-Verzeichnis als `*.yaml`-Dateien definiert. + +Hier ein Beispiel für einen einfachen `workflow`: + +```yaml +name: Python-Lint # the name of the workflow + +on: # defines when an actin should be triggered (more detailed specifications are possible) + push: + pull_request: + +jobs: # lists the jobs that should be run in the workflow + ruff: # the id of a job + runs-on: ubuntu-latest # defines on what kind of maschine the job should be executed. latest can only be used if the specific version does not matter + # needs: something # defines if another job needs to be executed before (is needed) + steps: # defines the steps of the workflow + - uses: actions/checkout@v4 # uses an action (dokumentation on: github.com/actions/checkout) + - uses: chartboost/ruff-action@v1 # uses an action (dokumentation on: github.com/chartboost/ruff-action) + with: # Defines arguments for an action + version: 0.1.9 # An argument as requested by an action + - name: Finishline # name of the step + run: | # Runs a script with multiple lines + echo All done! + echo Really everything is done! +``` + +Ein komplexerer Workflow kümmert sich um das Ausführen von `pytest` und das Mitschreiben des Code-Coverages: + +```yaml +name: Test & Build + +on: + push: + pull_request: + types: [reopened, opened] + +jobs: + test: + name: Pytest + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + - name: Install poetry + run: pipx install poetry + - name: Set up python + id: setup-python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: poetry + - run: poetry install --without develop,doc,lint --all-extras + - name: Run test suite + run: | + poetry run pytest --junit-xml=unit-test-results.xml \ + --cov-report "xml:coverage.xml" --cov=src --disable-warnings tests/ + - name: Archive code coverage results + uses: actions/upload-artifact@v4 + with: + name: code-coverage-report + path: | + coverage.xml + .coverage + if-no-files-found: error + - name: Archive unit test results + uses: actions/upload-artifact@v4 + with: + name: test-report + path: | + unit-test-results.xml + if-no-files-found: error +``` + +Der obenstehende Ausschnitt aus dem Workflow, der sich auf das Bauen und Testen des Projekts konzentriert, illustriert ausschließlich das Testen mit `pytest`. +Dieser Abschnitt veranschaulicht einige Feinheiten bei der Erstellung von `workflows` für Python. +Eine Schritt-für-Schritt-Beschreibung verdeutlicht den Prozess: + +- Der `workflow` wird bei jedem Push nach GitHub sowie bei der Erstellung und Wiedereröffnung geschlossener PRs ausgeführt. +- Der hier gezeigte einzige `job` trägt den Namen Pytest. +- Er wird auf der neuesten Ubuntu-Version ausgeführt. +- Nach 10 Minuten erreicht der Pytest `job` ein Timeout. +- Der gepushte oder PR-Branch wird ausgecheckt, was überschrieben werden kann. +- Anschließend wird `poetry` mittels `pipx` installiert. + `pipx` ist eine Variante von `pip`, die Python-Software nicht als Python-Abhängigkeit, sondern als ausführbares Python-Skript installiert. +- Python wird eingerichtet. + Die `PATH`-Variable wird auf die erforderliche Python-Installation gesetzt. + Falls die notwendige Python-Version nicht vorinstalliert ist, wird sie von der Aktion nachinstalliert. + Es ist zu beachten, dass keine Linux-ARM-Distributionen unterstützt werden. + Nur wenn die Python-Installation konfiguriert wird, können die zugehörigen Caches genutzt werden. + Hier kommt der Poetry-Cache zum Einsatz. + Für jedes `poetry`-Projekt im Repository wird ein separater Cache angelegt, was den Prozess erheblich beschleunigt. + Caches sind auch für andere Abhängigkeitsverwaltungen als Poetry verfügbar, jedoch nur, wenn die Python-Version explizit definiert wird. +- `poetry` installiert selektiv die erforderlichen Abhängigkeitsgruppen für das Testen. +- `pytest` führt die Tests durch und erstellt Protokolle in verschiedenen Formaten, darunter ein Unittest-Protokoll und ein Code-Coverage-Protokoll. +- Die Berichte werden für nachfolgende Aktionen bereitgestellt und können von den Nutzern heruntergeladen werden. + +Die Workflows werden für Pushes und PRs aus dem entsprechenden Branch heraus ausgeführt. + +Bei Pushen und PRs werden die Workflows aus dem entsprechenden Branch verwendet. +GitHub Actions führen alle `jobs` parallel aus, im Gegensatz zu sequentiellen Abläufen bei anderen CI/CD-Tools wie z.B. Jenkins. +GitHub Actions ermöglichen parametrisierte Matrix Builds. + +Sie müssen in den Repository-Einstellungen unter `Actions` aktiviert werden. +Eine vollständige Schemabeschreibung für GitHub `workflows` findet sich [hier](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions). + +#### Implementierte Workflows + +Für dieses Projekt wurden folgende Workflows implementiert: + +- **Python-Lint**: + - Führt verschiedene Linter aus, um die aktuelle Version des Projekts auf Konformität zu prüfen. + - Das Linting wird bei jedem Push und bei jedem Öffnen eines Pull Requests ausgeführt. + - Der Bericht listet Fehler auf. + ![Lint-error.PNG](presentation\Lint-error.PNG) + - Zu den Lintern gehören `black`, `ruff` und `mypy`. + - Die Abhängigkeitsvalidierung umfasst: + - Export der `requirements.txt`. + - `pip-audit` für Laufzeit-Abhängigkeiten. + - `pip-licenses`, nur als Bericht für Laufzeit-Abhängigkeiten. + +- **Test & Build**: + - Testet und baut die Software. + - Alle Berichte werden als Artefakte bereitgestellt. + ![test-and-build.PNG](test-and-build.PNG) + - Pull Requests werden mit Angaben zum Code-Coverage kommentiert. + ![Coverage.PNG](presentation\Coverage.PNG) + - `pytest` wird für Tests ausgeführt. + - Das Code-Coverage wird analysiert, um das Mergen bei unzureichender Abdeckung zu verweigern (nur bei Pull Requests). + - Das Code-Coverage wird visualisiert, um eine Übersicht über die Testabdeckung auch ohne passende IDE zu bieten. + - Parallel zur Analyse werden eine Python-Wheel (`*.whl`) und ein Python-Projekt (`*.tar.gz`) erstellt. + - Mit dem Python Wheel wird das Docker-Image gebaut. + - Das Image wird unter dem Tag `main` bereitgestellt, wenn es vom `main`-Branch gebaut wird. + - Für verschiedene Docker-Container gibt es spezifische Abhängigkeitsdefinitionen in unserem Poetry-Python-Projekt, die pro `target` in der Dockerfile nachinstalliert werden. + +- **Documentation-Action**: + - Baut die Dokumentation mit `sphinx` und lädt sie als GitHub Pages hoch. + - Der Dokumentationsbau erstellt eine HTML-Webseite mit Sphinx. + - Die Dokumentation wird nur in Pull Requests oder auf dem `main`-Branch bereitgestellt und kann heruntergeladen werden. + - Die Bereitstellung der Dokumentation erfolgt nur auf dem `main`-Branch und wird als externe Webseite auf den GitHub Pages veröffentlicht. + + +#### Self-Hosted Act-Runner + +Zu Beginn des Projekts erhielten wir die Information, dass die FH-SWF keine Cloud-Ressourcen zur Verfügung stellen kann. +Deshalb wurde anfänglich versucht, einen Runner lokal auf einem Raspberry Pi 4 zu installieren. +Die Einrichtung gemäß der GitHub-Anleitung war relativ einfach, stieß jedoch aufgrund der nicht unterstützten ARM32/ARM64-Architektur bei der Nutzung von Python auf Probleme. +1. Die `actions/setup-python`-Aktion unterstützt ARM für Linux nicht. Ein Fallback auf eine installierte Python-Version ist jedoch möglich. +2. Viele Python-Wheels sind nicht für ARM gebaut und müssen lokal kompiliert werden, besonders wenn sie Cython- oder Rust-Komponenten enthalten. +3. Ohne Angabe der Python-Version unterstützt die `actions/setup-python`-Aktion keinen Python-Poetry-Cache. +4. Einige KI-Modelle lassen sich auf dem Raspberry Pi nicht ausführen, da er nicht genügend RAM hat. +5. Geringe Parallelisierung und langsame Prozessoren führen zu langen Wartezeiten bei Tests und Linting-Ergebnissen. +6. Das Risiko, von GitHub aufgrund zu vieler Downloads gesperrt zu werden, ist geringer. +7. GitHub Actions sind für Open-Source-Projekte kostenlos, weshalb es relativ wenig Dokumentation zum Hosting eigener Runner gibt. + +Letztendlich wurde uns jedoch die Nutzung von GitHub Actions über den FH-SWF-Account ermöglicht, sodass der self-hosted Runner nicht zum Einsatz kommen musste. +Zur Zeit Nutze ich persönlich den Gitea's Act Runner welcher ein Fork von GitHubs self-hosted Runner ist. +Es ist also möglich diesen zu Nutzen. +Dies hat aber eindeutige Grenzen und ist bei weitem nicht so komfortabel, ist aber mit nicht ARM32/64 Geräten gut möglich. + +Die Anleitung von GitHub zu self-hosted Runners ist [hier zu finden](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners). + +#### Erwartete Artefakte + +Die derzeit konzipierten Pipelines erstellen folgende Artefakte: +- Ein Python Wheel. +- Test Coverage. +- Einen Bericht über die verwendeten Laufzeitabhängigkeiten. + - Eine `requirements.txt`. + - Einen Bericht über die genutzten Lizenzen, erstellt von `pip-license`. + - Einen Bericht über potenzielle Sicherheitslücken in den Abhängigkeiten, erstellt von `pip-audit`. + +#### Dependabot + +Dependabot, der GitHub OWASP-Scanner, ermöglicht es, den Standard-Branch eines Projekts regelmäßig auf Sicherheitslücken in Abhängigkeiten zu überprüfen. +Abhängig von der Konfiguration kann Dependabot Handlungsempfehlungen aussprechen oder automatisch Pull Requests erstellen, um Sicherheitslücken zu schließen. +Für unser Projekt ist der Einsatz von Dependabot aktuell nicht geplant. +Dies liegt daran, dass es mit `pip-audit` redundant wäre und erst nach Abschluss der aktiven Entwicklungsphase des Projekts Vorteile bietet. +Dependabot kann Routineaufgaben übernehmen, insbesondere, wenn GitHub Actions implementiert sind und vorgeschlagene Änderungen sofort testen. +Dependabot unterstützt eine Vielzahl von Programmiersprachen und Tools zur Abhängigkeitsverwaltung. +Es ermöglicht nicht nur Scans von Python-Abhängigkeiten, sondern auch das Scannen und Aktualisieren von GitHub Actions. + +Natürlich wäre es einfach möglich nur Dependabot zu nutzen. Da ich aber im beruflichen Kontext `pip-audit` verwende, war dies einfacher. + +### pre-commit + +Oft in der Softwareentwicklung gezeigt wird das Kostendiagramm für Fehler, das die zunehmenden Kosten von Fehlern über die Zeit illustriert. +![costOfChangeTraditional.gif](costOfChangeTraditional.gif) + +Es verdeutlicht, dass die Kosten für das Beheben eines Fehlers mit der Zeit ansteigen. +`pre-commit`, ein Python-Tool, adressiert dieses Problem. +Es hängt sich in die `git-hooks`-Mechanik ein und validiert geänderte Dateien vor dem Committen. +Durch den selektiven Einsatz von Lintern und Formatierern wird sichergestellt, dass nur qualitativ hochwertiger Code committet wird. +Nur die Hooks, die auf die aktuell geänderten Dateiformate zutreffen, werden ausgeführt. +Hilfestellungen und automatische Fixes bieten bei Bedarf schnelle Lösungen und schlagen eine überarbeitete Version vor. +Diese überarbeitete Version kann dann mit der `gestagten` Version abgeglichen und übernommen werden. +Meist garantieren die Hooks eine unveränderte Funktionalität. +Ich persönlich hatte an dieser Stelle bisher noch nie Probleme. + +Programmierer müssen sich mit Themen wie Importreihenfolge und Whitespace-Formatierung auseinandersetzen. +Dadurch ist auch bei diesen Details die Qualität gewährleistet öhne die Kosten zu erhöhen. +Typisierungsfehler können dem Programmierer schon während des Committens aufgezeigt werden. +Dies trägt dazu bei, dass potenzielle Fehler schon vor dem Testing, aber direkt beim oder nach dem Codieren auffallen. +Das beschleunigt den Programmierprozess und senkt damit wieder die Kosten. +Auch wenn wir in einem akademischen Projekt keine Kosten in EUR haben ist die benötigte Zeit natürlich immer noch ein Faktor. + +Um `pre-commit` zu nutzen, müssen neben Git und Python auch das `pre-commit`-Paket installiert werden. +Nach der Installation wird das `pre-commit`-Skript im `.git/hooks/`-Verzeichnis eingehängt, was durch den Befehl `pre-commit install` geschieht. +Das `pre-commit`-Skript im `.git/hooks/`-Verzeichnis wird dann vor jedem Commit ausgeführt. +Scheitert einer der Tests oder werden Änderungen vorgeschlagen, wird der Commit abgebrochen. +`pre-commit` wird durch eine `.pre-commit-config.yaml` im Root-Verzeichnis eines Projekts konfiguriert. + +Es gibt im git-workflow viele verschiedene Stellen um `hooks` einzuhängen. +In dieser Dokumentation wird nur auf die Validierung/Formattierung vor dem Committen eingegangen. + +Hier ein Beispiel einer `.pre-commit-config.yaml`: +```yaml +default_language_version: + python: python3.11 + +default_stages: +- pre-commit + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: end-of-file-fixer + exclude: (.txt$|.ipynb$) +``` + +In diesem Beispiel wird Python 3.11 als Standardversion festgelegt und die Hooks werden standardmäßig nur im `pre-commit`-Stadium verwendet. +Es wird ein Git-Repository als Quelle für eine Reihe von Hooks definiert. +Der Hook `end-of-file-fixer` wird ausgeführt und dabei werden `*.txt` und `*.ipynb`-Dateien ausgeschlossen. +Mehrere Repos und Hooks pro Repo können definiert werden. +Der Befehl `pre-commit autoupdate` aktualisiert die Versionsdefinitionen. +Neue Repos oder andere Versionen werden bei der ersten Ausführung einmalig installiert. + + +Hier eine übersicht der für dieses Projekt verwenden hooks: + +1. **end-of-file-fixer**: Stellt sicher, dass Dateien mit einer leeren Zeile am Ende enden. +2. **trailing-whitespace**: Entfernt überflüssige Leerzeichen am Ende von Zeilen. +3. **check-yaml**: Überprüft die syntaktische Korrektheit von YAML-Dateien. +4. **check-json**: Überprüft die syntaktische Korrektheit von JSON-Dateien. +5. **check-toml**: Überprüft die syntaktische Korrektheit von TOML-Dateien. +6. **check-xml**: Überprüft die syntaktische Korrektheit von XML-Dateien. +7. **check-ast**: Überprüft Python-Dateien auf Syntaxfehler. +8. **check-added-large-files**: Verhindert das versehentliche Hinzufügen großer Dateien zum Repository. +9. **name-tests-test**: Stellt sicher, dass Testdateien mit `test` beginnen. +10. **detect-private-key**: Erkennt und verhindert das Hinzufügen von privaten Schlüsseln zum Repository. +11. **check-case-conflict**: Verhindert Probleme mit Dateinamen, die sich nur in der Groß-/Kleinschreibung unterscheiden. +12. **check-symlinks**: Überprüft symbolische Verknüpfungen auf Gültigkeit. +13. **check-docstring-first**: Stellt sicher, dass Dateien mit einem Docstring beginnen. +14. **mixed-line-ending**: Vereinheitlicht die Zeilenendungen in Dateien. +15. **destroyed-symlinks**: Erkennt zerstörte symbolische Verknüpfungen. +16. **debug-statements**: Erkennt und meldet Debug-Anweisungen in Python-Code. +17. **pretty-format-json**: Formatiert JSON-Dateien in einem einheitlichen Stil. +18. **no-commit-to-branch**: Verhindert das direkte Commiten in geschützte Branches. Bei uns `main`. +19. **ruff**: Ein schneller Python-Linter und Code-Formatter (hier vorgestellt). +20. **black**: Ein Python-Code-Formatter (hier vorgestellt). +21. **black-jupyter**: Formatiert Jupyter Notebooks mit Black (hier vorgestellt). +22. **pretty-format-ini**: Formatiert INI-Dateien in einem einheitlichen Stil. +23. **pretty-format-yaml**: Formatiert YAML-Dateien in einem einheitlichen Stil. +24. **pretty-format-toml**: Formatiert TOML-Dateien in einem einheitlichen Stil. +25. **mypy**: Ein statischer Typenprüfer für Python (hier vorgestellt). +26. **md-toc**: Erzeugt automatisch ein Inhaltsverzeichnis für Markdown-Dateien. +27. **poetry-check**: Überprüft die Konsistenz einer Poetry-Projektdatei. +28. **poetry-install**: Installiert Abhängigkeiten mit Poetry. +29. **validate-html**: Überprüft die Korrektheit von HTML-Dateien. +30. **check-github-workflows**: Überprüft GitHub Workflow-Dateien auf Korrektheit. +31. **auto-walrus**: Automatisiert die Verwendung des Walrus-Operators in Python. Um ein Bewusstsein zu schaffen, wann dieser Verwendet werden kann. + +Auf `prettier` wurde verzichtet, da es sich um ein Node.js-Paket handelt, das die Installation von Node.js bei allen Teammitgliedern erfordert. +`prettier` könnte für das Projekt benötigte `*.css`-Dateien formatieren. + +Einige der Hooks kamen nicht oder nur selten zum Einsatz. +Sie waren Teil der verwendeten Repositories und wurden nur ausgeführt, wenn sie für eine spezifische Datei benötigt wurden. +Somit gab es keinen Nachteil darin, selten verwendete Hooks zu unterstützen. +Langsame Hooks wie `mypy` sind hinsichtlich der Ausführungszeit kritischer. +Allerdings bietet `mypy` aufgrund seiner statischen Typ-Prüfung einen besonders hohen Mehrwert. + +Bei der Nutzung von `pre-commit` neben `poetry` empfehle ich das `poetry-pre-commit-plugin`. +Dieses Plugin führt `pre-commit install` aus, wenn `pre-commit` über `poetry` installiert wird. +So wird verhindert, dass dieser einfache und schnelle Schritt nach dem `git clone` vergessen wird. + +`pre-commit`-Konfigurationen lassen sich fast vollständig von einem Projekt auf ein anderes übertragen. +Die einzige Ausnahme bildet die `mypy`-Definition, da hier auch `*-stubs`/`*-typing`-Abhängigkeiten hinterlegt werden müssen. +`pre-commit` installiert jedes Hook-Repository in einer eigenen virtuellen Umgebung. + +Das Commit-Log unter Verwendung von `pre-commit` kann folgendermaßen aussehen: +![Pre-commit.PNG](presentation\Pre-commit.PNG) + +[Die vollständige Dokumentation von pre-commit finden Sie hier.](https://pre-commit.com) + +[Den offiziellen Katalog über die verwendbaren Hooks finden Sie hier.](https://pre-commit.com/hooks.html) + +Pre-commit Hooks haben oft eine große Überschneidung mit der zentralen Pipeline auf einem Build-Server. +Dies ist beabsichtigt. +Ein Pre-commit Hook ermöglicht schnelle Rückmeldungen, die nicht direkt in der IDE angezeigt werden. +CI/CD-Pipelines benötigen in der Regel mehr Zeit, da sie das gesamte Projekt bearbeiten. +Sie lassen sich nicht so einfach umgehen, da Pre-Commit Pipelines eher suggestiv wirken. +Der Overhead für `autofixes`, die von einer `pre-commit` Hook-Kollektion stammen, ist in einer zentralen CI/CD-Pipeline unerwünscht. +Die durch `pre-commit` Hooks durchgesetzten Regeln sind oft weichere Konventionen, die automatische Fixes bereitstellen. +Diese können durch einfaches und oft nur einmaliges Deaktivieren bewusst umgangen werden. +Andererseits vermeidet man aufwendige Verifikationen, wie Unit-Testing, in Pre-Commit Hooks, da diese zu langsam sind und Entwickler unnötig aufhalten könnten. + +Diese Unterschiede lassen sich auch zusammen fassen als: +pre-commit Hooks erleichtern Entwicklern das Einhalten bestimmter Standards, während CI/CD-Pipelines die Einhaltung von Projektstandards garantieren. + +### pre-commit ci + +[`pre-commit.ci`](https://pre-commit.ci/) ist eine CI/CD-Lösung, die in GitHub integriert werden kann und dort `pre-commit` als Linter ausführt. +Da die Ergebnisse in hohem Maße gecacht werden, ist dies extrem schnell. +Diese Lösung ist jedoch bei nicht öffentlichen Projekten kostenpflichtig und kam daher in diesem Projekt nicht zum Einsatz. + +Ich weiß es zu schätzen, dass einige der in `pre-commit` verwendeten Linter in `pre-commit.ci` explizit umgangen werden können, ohne eine formelle Ausnahme zu definieren. +Leider ist [`pre-commit.ci`](https://pre-commit.ci/) derzeit nur für GitHub verfügbar. + +### Zustimmung vom Team +Da das DevOps- und CI/CD-Setup zumindest initial für ein Projekt vorhanden sein muss, um Mehrwert zu erzeugen und nicht nur Mehrarbeit, +wurden alle Werkzeuge in einem Development-Branch zusammengestellt und konfiguriert. + +Obwohl CI/CD stark formalisiertes maschinelles Testen beinhaltet, funktioniert es nicht ohne das Buy-In des Entwicklungsteams. +Daher wurde bei der Präsentation dieser Werkzeuge die Zustimmung des Teams für dieses Vorgehen eingeholt. + +Die Zustimmung wurde erteilt, und ein erster Entwurf der Pipelines und Tools wurde in Betrieb genommen. + +### Fazit + +Es gibt zahlreiche hilfreiche Werkzeuge für die Softwareentwicklung. +Allein im Bereich CI/CD für Python den Überblick zu behalten und zu entscheiden, was nützlich ist oder sein könnte, ist eine Frage der Erfahrung. +Die Einschätzung der damit verbundenen Kosten gehört ebenfalls dazu. +Es erscheinen ständig neue Werkzeuge. +In anderen Projekten habe ich beispielsweise bisher mit `flake8` gearbeitet. +`ruff`, das weniger als ein Jahr alt und noch in den Versionen `0.0.*` ist, übertrifft ältere Linter deutlich. +Ich bin zuversichtlich, dass die aktuelle Konfiguration der Werkzeuge prinzipiell funktionieren wird. +Sie muss jedoch kontinuierlich mit neueren Werkzeugen abgeglichen und deren praktischer Nutzen evaluiert werden. +Die Evaluation wird sehr von der Wahrnehmung dieser Werkzeuge abhängen. +Entscheidend ist, ob sie als Hilfestellungen oder als Einschränkungen empfunden werden. +Ich habe versucht möglichst selbstreparierende Werkzeuge auszuwählen, sodass ich dabei aber zuversichtlich bin. diff --git a/documentations/seminararbeiten/DevOps/Action-Summary.PNG b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Action-Summary.PNG similarity index 100% rename from documentations/seminararbeiten/DevOps/Action-Summary.PNG rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Action-Summary.PNG diff --git a/documentations/seminararbeiten/DevOps/Action.PNG b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Action.PNG similarity index 100% rename from documentations/seminararbeiten/DevOps/Action.PNG rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Action.PNG diff --git a/documentations/seminararbeiten/DevOps/Coverage.PNG b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Coverage.PNG similarity index 100% rename from documentations/seminararbeiten/DevOps/Coverage.PNG rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Coverage.PNG diff --git a/documentations/seminararbeiten/DevOps/Lint-error.PNG b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Lint-error.PNG similarity index 100% rename from documentations/seminararbeiten/DevOps/Lint-error.PNG rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Lint-error.PNG diff --git a/documentations/seminararbeiten/DevOps/Pre-commit.PNG b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Pre-commit.PNG similarity index 100% rename from documentations/seminararbeiten/DevOps/Pre-commit.PNG rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Pre-commit.PNG diff --git a/documentations/seminararbeiten/DevOps/Pull_request.PNG b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Pull_request.PNG similarity index 100% rename from documentations/seminararbeiten/DevOps/Pull_request.PNG rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Pull_request.PNG diff --git a/documentations/seminararbeiten/DevOps/Seminarpraesentation.ipynb b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Seminarpraesentation.ipynb similarity index 100% rename from documentations/seminararbeiten/DevOps/Seminarpraesentation.ipynb rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/Seminarpraesentation.ipynb diff --git a/documentations/seminararbeiten/DevOps/bohems-law.png b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/bohems-law.png similarity index 100% rename from documentations/seminararbeiten/DevOps/bohems-law.png rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/bohems-law.png diff --git a/documentations/seminararbeiten/DevOps/project-organisation-and-dev-ops.md b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/project-organisation-and-dev-ops.md similarity index 100% rename from documentations/seminararbeiten/DevOps/project-organisation-and-dev-ops.md rename to documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/presentation/project-organisation-and-dev-ops.md diff --git a/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/prompts.md b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/prompts.md new file mode 100644 index 0000000..80b26ec --- /dev/null +++ b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/prompts.md @@ -0,0 +1,11 @@ +Bitte überarbeite den folgenden Text. +Halte dich inhaltlich an die Vorgaben. +Korrigiere aber bitte, Grammatik, Zeichensetzung und Stil. +Bitte kürze / teile die Sätze wenn möglich. + + +Bitte gib mir die überarbeitete version in Markdown (md) aus. +Dieses sollte in einem md codeblock angezeigt werden. + +Bitte fange für jeden Satz eine neue Zeile an. +Sollten Fragen offen bleiben schreibe mir bitte einen kurzen Verweise auf den md block. diff --git a/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/request-review.png b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/request-review.png new file mode 100644 index 0000000..98dc41a Binary files /dev/null and b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/request-review.png differ diff --git a/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/test-and-build.PNG b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/test-and-build.PNG new file mode 100644 index 0000000..29c7ac5 Binary files /dev/null and b/documentations/Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/test-and-build.PNG differ diff --git a/documentations/index.rst b/documentations/index.rst index b6b1768..5ee24b6 100644 --- a/documentations/index.rst +++ b/documentations/index.rst @@ -38,7 +38,7 @@ Diese sind, um Industriestandards zu entsprechen, auf Englisch gehalten. :caption: Seminararbeiten :numbered: - seminararbeiten/DevOps/Seminarpraesentation.ipynb + Ergebnisse/Zwischenbericht_und_Praesentation/PhHo/dev-ops seminararbeiten/Datenspeicherung/00_Datenspeicherung .. toctree:: @@ -50,13 +50,6 @@ Diese sind, um Industriestandards zu entsprechen, auf Englisch gehalten. Ergebnisse/Abschlussbericht_und_Praesentation/PhHo/4-4-2-database-generator -.. .. toctree:: - :glob: - :maxdepth: 1 - :caption: UI Mock Ups - -.. mock_up/**/* - .. toctree:: :glob: :maxdepth: 1