diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 59e5de7..2335fd5 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -7,3 +7,12 @@
- [Aufgabe 3](./aufgaben/aufgabe_3.md)
- [Aufgabe 4](./aufgaben/aufgabe_4.md)
- [Aufgabe 5](./aufgaben/aufgabe_5.md)
+
+# Selfhosting von Open-Source-Entwicklertools
+
+- [Einleitung](./studienarbeit/1_einleitung.md)
+- [GitLab](./studienarbeit/2_gitlab.md)
+- [Gitea](./studienarbeit/3_gitea.md)
+- [Sourcehut](./studienarbeit/4_sourcehut.md)
+- [OneDev](./studienarbeit/5_onedev.md)
+- [Anhang: Sourcehut-Setup](./studienarbeit/sourcehut_setup.md)
diff --git a/src/studienarbeit/1_einleitung.md b/src/studienarbeit/1_einleitung.md
new file mode 100644
index 0000000..93b1d53
--- /dev/null
+++ b/src/studienarbeit/1_einleitung.md
@@ -0,0 +1,161 @@
+# Selfhosting von Open-Source-Entwicklertools
+
+Um effizient mit anderen Entwicklern an Open-Source-Projekten arbeiten zu können, muss
+man den Quellcode seines Projekts veröffentlichen, Aufgaben koordinieren und neue
+Codebeiträge entgegen nehmen.
+
+Hierbei kommt fast immer eine Git-Hosting-Plattform zum Einsatz. Diese erlaubt es einem,
+sein Repository zu veröffentlichen und mit anderen Personen über das Internet
+zusammenzuarbeiten. Andere Nutzer und Entwickler können Softwarefehler melden,
+Vorschläge für neue Features machen und neuen Code zum Projekt beitragen.
+
+Der meistgenutzte Git-Hosting-Anbieter ist GitHub. Die Plattform wurde von Tom
+Preston-Werner, Chris Wanstrath, P. J. Hyett and Scott Chacon gegründet und ging 2008
+ans Netz. 2018 wurde die Firma von Microsoft übernommen.
+
+Der große Vorteil von GitHub ist, dass der Dienst für öffentliche Projekte vollkommen
+kostenlos ist. Zudem hat der Dienst mit 100 Millionen Nutzern die mit Abstand größte
+Community an potenziellen Beitragenden.
+
+GitHub bietet auch einen Continuous-Integration-Dienst namens GitHub Actions an, mit dem
+man sein Projekt automatisch testen und veröffentlichen lassen kann. Im Gegensatz zu
+vielen Konkurrenzanbietern setzt GitHub hierbei kein Nutzungslimit bei öffentlichen
+Repositories.
+
+Allerdings hat die Nutzung von GitHub auch seine Nachteile. Zum einen ist man bei der
+Nutzung eines fremden Webdiensts, insbesondere wenn dieser kostenlos ist und man
+deswegen keinen Vertrag mit dem Anbieter abgeschlossen hat, komplett von dessen
+Entscheidungen und Nutzungsbedingungen abhängig. Der Anbieter kann den Dienst
+einstellen, kostenpflichtig machen, Features entfernen oder bestimmte Nutzer
+ausschließen.
+
+Beispielsweise wurde der russische Webentwickler Nikolay Kuchumov wegen eines einzigen
+beleidigenden Issues
+[2020 auf GitHub gesperrt](https://medium.com/@catamphetamine/how-github-blocked-me-and-all-my-libraries-c32c61f061d3).
+Dabei wurden alle seine Repositories von der Plattform entfernt. Noch absurder: GitHub
+verlangte eine Kopie seines Personalausweis um die Sperre wieder aufzuheben.
+
+Es gibt auch einige Fälle von Open Source-Projekten, die von GitHub wegen angeblichen
+Verstößen gegen das amerikanische Urheberrecht (DMCA) gesperrt wurden. Das prominenteste
+Beispiel hierfür ist der Video-Downloader
+[youtube-dl](github.blog/2020-11-16-standing-up-for-developers-youtube-dl-is-back/).
+Auch das Repository der alternativen Smartwatch-App
+[Gadgetbridge](https://www.heise.de/news/Pebble-Gadgetbridge-unter-Beschuss-3740625.html)
+wurde zeitweise von GitHub gesperrt. Eine selbstgehostete Plattform ist dagegen viel
+schwieriger zu sperren.
+
+Ein weiterer Grund für einige Entwickler, die GitHub-Plattform zu verlassen, ist die
+Einführung von GitHub Copilot. Hierbei handelt es sich um ein künstliche Intelligenz,
+die einem Programmierer automatisch Code-Vorschläge geben kann. Dessen Sprachmodell
+wurde mit dem Code aus öffentlichen GitHub-Repositories trainiert. Kritiker sehen
+hierbei eine Urheberrechtsverletzung, da der AI-generierte Code Schnipsel aus den
+Trainingsdaten enthalten kann, die dann ohne Erwähnung des Urhebers in andere Projekte
+eingefügt werden.
+
+Insbesondere bei privaten oder firmeninternen Projekten kann auch der Datenschutz ein
+Grund sein, auf Webanwendungen von Fremdanbietern zu verzichten und die Daten auf
+eigenen Servern zu speichern. Da GitHub nicht quelloffen ist und nur mit einer
+kostspieligen Enterprise-Lizenz selbst gehostet werden kann, ist is für solche Fälle
+attraktiv, eine Open-Source-Anwendung einzusetzen.
+
+Mittlerweile gibt es mehrere Open-Source-Projekte, die Alternativen zu GitHub entwickelt
+hat. Im Gegensatz zu proprietären Webdiensten lassen sich diese Anwendungen auf eigenen
+Servern installieren, sodass man die volle Autonomie über sein System behält.
+
+In dieser Arbeit werde ich vier von ihnen testen und vergleichen.
+
+## Testkriterien
+
+Ich habe jede Plattform auf meinem Rechner installiert und nach folgenden Kritierien
+getestet:
+
+### 0\. Über das Projekt
+
+Zuerst werde ich einige grundlegende Informationen über die entsprechende Anwendung
+präsentieren.
+
+- In welcher Programmiersprache ist die Anwendung geschrieben?
+- Wie lange existiert das Projekt bereits? Ist es eine Abspaltung eines anderen
+ Projekts?
+- Wie viele Beitragende hat das Projekt?
+- Wie sieht die Architektur der Anwendung aus? Welche Dienste werden benötigt?
+
+### 1\. Installation und Einrichtung
+
+Hier gebe ich eine kurze Anleitung zur Installation und Einrichtung der Anwendung. Ich
+werde hierbei bewerten, wie einfach die Installation vonstatten geht.
+
+Monolithisch (d.h. aus einem Dienst) aufgebaute Anwendungen sind in der Regel einfacher
+einzurichten, bieten aber weniger Flexibilität bei der Skalierung für viele User.
+
+### 2\. Systemanforderungen
+
+Hierbei geht es nicht nur um die unterstützten Betriebssysteme und CPU-Architekturen,
+sondern auch um den Bedarf an Rechenleistung und Arbeitsspeicher. Im Idealfall soll die
+Software auch auf preisgünstigen Single Board-Computern und virtualisierten Servern
+lauffähig sein, sodass sich jeder ein selbst gehostetes Setup leisten kann und nicht aus
+Kostengründen auf proprietäre Anbieter angewiesen ist.
+
+Getestet wurden CPU-Auslastung und Arbeitsspeicherverbrauch auf einem Rechner mit einem
+Ryzen 5700G-Prozessor (16 Threads) und 32GB RAM. Die angegebene CPU-Auslastung bezieht
+sich auf die ausgelasteten Threads (wie in htop angezeigt), d.h. 100% entspricht einem
+voll ausgelastetem Thread, 16.000% einem voll ausgelasteten Prozessor.
+
+Neben dem Ressourcenverbrauch im Leerlauf habe ich auch die CPU-Auslastung unter Last
+gemessen. Hierfür habe ich das Repository des Linux-Kernels importiert und mir die
+Versionsgeschichte und die Blame-Ansicht der Makefile im Stammverzeichnis anzeigen
+lassen.
+
+### 3\. Bedienung
+
+Wie einfach ist die Weboberfläche zu bedienen? Ist es möglich, schnell durch
+Repositories zu navigieren? Gibt es eine Suchfunktion, um beispielsweise eine bestimmte
+Funktion im Code zu finden?
+
+Zudem habe ich dir Unterstützung für verschiedene Dateiformate getestet. Alle
+Plattformen können Code, Markdown und Textdateien darstellen. Binärdateien können auf
+diese Weise natürlich nicht dargestellt werden, weswegen einige Plattformen spezielle
+Viewer für bestimmte Dateitypen mitbringen. Um dies zu testen, habe ich ein Repository
+mit Dateien in folgenden Formaten angelegt.
+
+- Bilder (\*.jpg, \*.svg)
+- Tabellen (\*.csv, \*.xlsx, \*.ods)
+- Text (\*.rst, \*.tex, \*.docx, \*.odt)
+- PDF-Dokumente
+- 3D-Modelle (\*.stl)
+- Jupyter-Notebooks (\*.ipynb)
+
+Zum Vergleich: GitHub unterstützt alle diese Dateitypen mit Ausnahme von LaTEX- und
+MS/Open Office-Dokumenten.
+
+### 4\. Import bestehender Projekte
+
+Eine gute GitHub-Alternative sollte es dem Entwickler ermöglichen, bestehende Projekte
+von anderen Providern zu importieren. Hierbei soll nicht nur das Repository, sondern
+auch z.B. Issues übertragen werden können.
+
+### 5\. Zusatzfeatures
+
+Bietet die Anwendung zusätzlich zu Git-Repositories, Issues und Pull-Requests noch
+weitere Features an?
+
+Beispiele hierfür wären das Hosting von statischen Websites direkt aus Repositories
+heraus, ein Repository für Softwarepakete verschiedener Programmiersprachen oder
+Projektmanagement-Tools.
+
+### 6\. Continuous Integration
+
+Continuous Integration (CI), also das automatische Testen von neuem Code ist ein fester
+Teil des Worflows vieler Entwickler. Hat die Plattform einen CI-Dienst eingebaut oder
+lässt sie sich mit anderen CI-Diensten integrieren?
+
+### 7\. Öffentliche Instanzen
+
+Nicht jeder möchte den Aufwand betreiben, seine Git-Hosting-Plattform selbst zu hosten.
+Deswegen ist es vom Vorteil, wenn es die Möglichkeit gibt, anstelle dessen einer
+öffentlichen Instanz beizutreten.
+
+## Quellen
+
+- [Software Freedom Conservancy](https://sfconservancy.org/GiveUpGitHub/): Give up
+ GitHub
diff --git a/src/studienarbeit/2_gitlab.md b/src/studienarbeit/2_gitlab.md
new file mode 100644
index 0000000..a25ccd9
--- /dev/null
+++ b/src/studienarbeit/2_gitlab.md
@@ -0,0 +1,278 @@
+# GitLab
+
+2011, drei Jahre nach dem Start von GitHub veröffentlichten Dmytro Zaporozhets und Sytse
+Sijbrandij die erste Version von GitLab. Damit ist GitLab die erste und bekannteste
+Open-Source-Alternative zu GitHub.
+
+GitLab Community Edition ist unter der MIT-Lizenz veröffentlicht. Die Firma hinter
+GitLab verfolgt ein "Open Core"-Geschäftsmodell, d.h. einige fortgeschrittene Features
+sind nicht in der Community Edition enthalten und erfordern die proprietären Versionen
+"Premium" bzw. "Ultimate". Diese sind nur in Form eines Abonnements zum Preis von 29
+(Premium) bzw. 99USD (Ultimate) pro Nutzer und Monat erhältlich. Open-Source-Projekte
+und Bildungseinrichtungen können die Ultimate-Version kostenfrei nutzen.
+
+Im Kern ist GitLab eine Ruby-on-Rails-Webanwendung. Allerdings sind einige Funktionen
+der Plattform (bspw. SSH oder Git-Operationen) an zusätzliche Services ausgelagert. Die
+meisten dieser Services sind in Go implementiert. Das Webfrontend wurde mit Vue.js
+implementiert.
+
+
+
+- **Workhorse** Reverse Proxy, der HTTP-Anfragen an Git oder die Hauptanwendung
+ weiterleitet
+- **Gitaly** Microservice zur Speicherung der Git-Repositories und Verarbeitung von
+ Git-Anfragen
+- **GitLab Shell** SSH-Server
+- **Sidekiq** Task Queue zur Ausführung von Aufgaben im Hintergrund
+
+GitLab verwendet eine PostgreSQL-Datenbank zur Speicherung seiner Daten. Zudem kommt
+eine Redis-Instanz als Cache und Event Queue zum Einsatz.
+
+Getestet wurde GitLab in der Version 16.1.1.
+
+## Installation
+
+Um die Installation der Anwendung trotz ihres Aufbaus aus verschiedenen Services zu
+vereinfachen, stellt GitLab ein Omnibus-Paket zur verfügung. Hierbei handelt es sich um
+ein Linux-Softwarepaket, das die gesamte GitLab-Anwendung mit allen Services (inklusive
+NGINX, Redis und Postgres) beinhalten. Die Pakete sind für die meisten
+Linux-Distributionen (Ubuntu, Debian, Alma Linux, CentOS, OpenSUSE, Raspberry Pi OS)
+verfügbar.
+
+Um Gitlab als Omnibus-Paket zu installieren, muss man zuerst das Gitlab-Repository zu
+seinem System hinzufügen. Hierfür kann man dieses Skript verwenden:
+.
+
+Danach kann man GitLab mit diesem Befehl installieren und starten:
+
+```
+GITLAB_ROOT_PASSWORD="" EXTERNAL_URL="http://gitlab.example.com" apt install gitlab-ce
+```
+
+Der Konfigurationsprozess dauert mehrere Minuten. Wenn der Webserver gestartet ist, kann
+man die Weboberfläche aufrufen und sich anmelden (Benutzername: `root`).
+
+GitLab stellt auch ein Docker-Image (`gitlab/gitlab-ce`) zur Verfügung, welches das
+Omnibus-Paket beinhaltet. Der Einrichtungsprozess ist der gleiche wie bei einer bare
+metal-Installation, mit dem Unterschied dass die Konfiguration durch die
+Umgebungsvariable `GITLAB_OMNIBUS_CONFIG` erfolgt.
+
+[Hier](./assets/gitlab/docker-compose.yml) ist die Docker-Compose-Datei, die ich für
+meine Testinstallation verwendet habe.
+
+Für Unternehmen und große Organisationen mit Tausenden Nutzern bietet GitLab in ihrer
+Dokumentation auch Referenzarchitekturen für hochverfügbare Systemen mit mehreren
+Servern.
+
+## Systemanforderungen
+
+GitLab bietet Pakete für eine
+[Vielzahl von Linux-Distributionen](https://docs.gitlab.com/ee/administration/package_information/supported_os.html),
+sowohl für Intel/AMD als auch für ARM-Prozessoren. Das offizielle Docker-Image ist
+jedoch nur für die arm64-Architektur verfügbar.
+
+GitLab hat unter allen getesteten Plattformen den höchsten Ressourcenverbrauch.
+
+Im Leerlauf bewegt sich die CPU-Last auf meinem Testsystem zwischen 3 und 30 Prozent.
+Der Arbeitsspeicherverbrauch beträgt ganze 6GB.
+
+Aufwändige Git-Operationen können Lastspitzen über 100% für eine Dauer von bis zu 5
+Sekunden pro Anfrage verursachen.
+
+GitLab erwähnt in ihrer Dokumentation, dass man die Konfiguration anpassen kann, um den
+Ressourcenverbrauch etwas zu senken. Nachdem ich diese Zeilen zur Konfigurationsdatei
+hinzugefügt habe, sank der Speicherbedarf auf 2.8GB und die CPU-Auslastung auf 1-12%.
+
+```
+# Reduce the number of running workers to the minimum in order to reduce memory usage
+puma['worker_processes'] = 2
+sidekiq['max_concurrency'] = 9
+# Turn off monitoring to reduce idle cpu and disk usage
+prometheus_monitoring['enable'] = false
+```
+
+Für eine Installation auf Single Board-Computern und kleinen V-Servern ist der
+Speicherverbrauch jedoch weiterhin zu hoch. GitLab gibt als Mindestanforderung 4
+CPU-Kerne und 4GB RAM an.
+
+## Bedienung
+
+Gitlab's Weboberfläche wurde mit dem VueJS-Framework realisiert. Die gesamte
+Weboberfläche wird somit clientseitig mittels JavaScript gerendert. Dies erlaubt eine
+äußerst flüssige Navigation durch die Webseite, da die Seite nicht bei jedem
+Navigationsschritt neu geladen wird. Der Nachteil dieser Lösung: Javascript ist für die
+Darstellung der Webseite zwingend erforderlich.
+
+GitLab bietet die Möglichkeit, den Code eines Repositories zu durchsuchen. Die
+Verwendung einer externen Elasticsearch-Instanz für die Suche und die Möglichkeit, die
+gesamte Gitlab-Instanz zu durchsuchen, ist allerdings den kostenpflichtigen Versionen
+vorbehalten.
+
+GitLab unterstützt in der Standardkonfiguration die meisten Dateiformate unter den
+getesteten Plattformen. Auf der Weboberfläche können Bilder, CSV-Tabellen, PDF-Dokumente
+und stl-Modelle betrachtet werden, alternative Markupsprachen wie Restructured Text
+werden ebenfalls unterstützt. Es gibt sogar die Möglichkeit, GeoJSON-Dateien (Listen von
+GPS-Standorten) auf einer OpenStreetMap-Karte darzustellen.
+
+Eine Besonderheit der Gitea-Weboberfläche stellt die Web-IDE dar. GitLab verfügt über
+eine modifizierte Version von Visual Studio Code, mit der man direkt im Browser an
+seinen Projekten arbeiten kann.
+
+
+
+## Import bestehender Projekte
+
+GitLab erlaubt den Projektimport von GitHub, Bitbucket, FogBugz, Gitea sowie von anderen
+GitLab-Instanzen.
+
+Die Importfunktion ist standardmäßig deaktiviert und muss erst in der _Admin Area_
+aktiviert werden (_Settings_ \> _General_ \> _Visibility and access controls_ \> _Import
+sources_).
+
+Für den Import von GitHub-Projekten muss man sich mit einem Access Token anmelden und
+bekommt anschließend eine Übersicht aller Repositories angezeigt. Hier kann man
+auswählen, welche Repositories übertragen werden sollten. Der Import umfasst auch
+Issues, Pull Requests und Releases.
+
+
+
+Als einzige Plattform im Test bietet GitLab die Möglichkeit, ein Repository mit
+sämtlichen gespeicherten Daten als zip-Datei zu exportieren. Dies erlaubt nicht nur den
+Import auf anderen Instanzen sondern bietet dem Nutzer auch eine Backup-Option.
+
+## Zusatzfeatures
+
+GitLab hat von allen getesteten Plattformen die meisten Features. Einige Features sind
+jedoch der proprietären Premium/Ultimate Edition vorbehalten, die ich nicht getestet
+habe.
+
+Die Projektmanagement-Features von GitLab sind die besten im Test. GitLab erlaubt das
+Erstellen von Issues mit Labels, Verantwortlichen, Fälligkeitsterminen und geleisteter
+Arbeitszeit. Zudem können Issues zu Meilensteinen hinzugefügt werden.
+
+Issue Boards erlauben einen schnellen Überblick über den alle gerade bearbeiteten
+Issues. Es lassen sich Spalten erstellen, die Issues mit einem bestimmten Tag auflisten.
+Erstellt man also z.B. zwei Tags: "Implementierung" und "Review" und legt in seinem
+Board zwei Spalten hierfür an, erhält man eine Übersicht über den Fortschritt aller
+Issues. Issues lassen sich zwischen Spalten verschieben, wodurch sich auch die Tags des
+Issues ändern.
+
+
+
+Ein interessantes Feature ist die Möglichkeit, zu einem Issue mehrere Tasks
+hinzuzufügen. Tasks sind Sub-Issues, die ihre eigene Beschreibung und Diskussion haben,
+aber unter einem Issue zusammengefasst sind. Auf diese Weise lassen sich große Features
+in kleinere Arbeitsschritte aufteilen, die von verschiedenen Teammitgliedern erledigt
+werden können.
+
+
+
+Viele fortgeschrittene Projektmanagement-Features sind allerdings der Enterprise Edition
+vorbehalten. Hierzu gehört z.B. das Anlegen von Boards mit Filtern (z.B. für einen
+bestimmten Meilenstein) oder Aufwandsschätzungen.
+
+Um Projekte zu dokumentieren bietet GitLab ein auf Git und Markdown basierendes Wiki.
+Zudem gibt es mit "Snippets" einen Pastebin, der ebenfalls Git zur Speicherung der
+Codeschnipsel verwendet.
+
+Es gibt auch die Möglichkeit, mit GitLab statische Webseiten zu hosten. Die Seiten
+können mit GitLab CI gebaut und unter einer persönlichen Subdomain veröffentlicht
+werden.
+
+GitLab bietet auch eine Paketregistry für Bibliotheken verschiedener Programmiersprachen
+(z.B. Maven, npm, Python) an. Die Unterstützung für einige Programmiersprachen wie Ruby
+ist allerdings noch experimentell. \*.deb-Pakete sowie Docker-Images können ebenfalls
+mit GitLab gehostet werden.
+
+Zur einfachen Bedienung mit der Kommandozeile bietet GitLab das Tool `glab` an. Damit
+lassen sich beispielsweise Issues und Pull Requests erstellen und bearbeiten, Snippets
+hochladen und CI-Build starten. Es gibt auch ein offizielles VS-Code-Plugin, das
+Pull-Requests, Issues und CI-Builds in den Texteditor integriert.
+
+## Continous Integration
+
+Gitlab verfügt über ein eingebautes CI-System, um Software automatisiert zu testen und
+zu veröffentlichen. Das System kann Builds in Docker-Containern, auf einem
+Kubernetes-Cluster, in einer VirtualBox-Maschine oder ohne Virtualisierung ausführen.
+Hierfür verwendet Gitlab ein verteiltes System aus Runnern. Die Runner kommunizieren
+über das Internet mit der Gitlab-Instanz und können so neue Buildaufträge
+entgegennehmen.
+
+Um einen Runner einzurichten, muss man zuerst die erforderliche Konfigurationsdatei
+erstellen. Hierfür startet man den Runner mit dem Befehl `register`:
+
+```
+docker run --rm -it -v $(pwd)/runner-config:/etc/gitlab-runner gitlab/gitlab-runner register
+```
+
+Daraufhin fragt der Runner nach der URL der Gitlab-Instanz und einem
+Registrierungstoken. Den Token kann man sich entweder in den Repository-Einstellungen
+oder in der Admin-Oberfläche generieren lassen (je nachdem, ob man den Runner nur zu
+einem Projekt oder der gesamten Instanz hinzufügen möchte). Anschließend muss man
+festlegen, wie der Runner die Builds ausführen sollte. In meinem Test habe ich den
+`docker`-Executor gewählt.
+
+Wenn die Konfiguration abgeschlossen ist, kann man den Runner mit diesem Befehl starten
+
+```
+docker run --rm -it -v $(pwd)/runner-config:/etc/gitlab-runner gitlab/gitlab-runner
+```
+
+
+
+Anschließend kann man für sein Repository einen Build definieren. Hierfür muss man eine
+Datei mit dem Namen `.gitlab-ci.yml` im Wurzelverzeichnes des Repositories erstellen, in
+der die einzelnen Schritte des Builds beschrieben werden. GitLab stellt ein Repository
+mit Beispielen für verschiedene Programmiersprachen und Frameworks zur Verfügung, um den
+Einstieg zu erleichtern.
+
+```yml
+image: "rust:latest"
+
+test:cargo:
+ script:
+ - rustc --version && cargo --version # Print version info for debugging
+ - rustup component add rustfmt clippy
+ - cargo fmt --all --check
+ - cargo clippy --all --features=rss -- -D warnings
+ - cargo test --features=rss --workspace
+```
+
+## Öffentliche Instanzen
+
+Gitlab betreibt selbst eine öffentliche Instanz der proprietären Ultimate-Version unter
+[gitlab.com](https://gitlab.com/explore/).
+
+Daneben gibt es eine Vielzahl von communitybetriebenen GitLab-Instanzen, die die freie
+Community Edition verwenden. Viele große Open-Source-Projekte und Organisationen hosten
+eine eigene GitLab-Instanz, wie zum Beispiel [Debian](https://salsa.debian.org),
+[Framasoft](https://framagit.org/public/projects/) oder [KDE](https://invent.kde.org).
+
+## Fazit
+
+GitLab ist zu Recht seit mehr als zehn Jahren die führende quelloffene
+GitHub-Alternative. Mittlerweile bietet GitLab mehr Features als die Konkurrenz von
+Microsoft.
+
+Trotz seiner komplexen Architektur lässt sich GitLab relativ einfach und schnell
+installieren, da das Omnibus-Paket die einzelnen Dienste automatisch konfiguriert.
+
+Insbesondere Unternehmen und große Organisationen werden die professionellen Features
+und den kommerziellen Support zu schätzen wissen.
+
+Darüber hinaus bietet Gitlab selbst in der Open-Source-Version die besten
+Projektmanagement-Features im Test. Wer also keine separate Projektmanagement-Anwendung
+einsetzen möchte, ist mit Gitlab ebenfalls gut beraten.
+
+Wer seine Plattform allerdings auf einem kleinen Single Board Computer oder V-Server
+betreiben möchte, sollte sich dagegen nach einer leichtgewichtigeren Alternative
+umsehen.
+
+## Quellen
+
+- Dokumentation
+- Repository
+- Architekturübersicht
+
+- Liste von öffentlichen GitLab-Instanzen
+
diff --git a/src/studienarbeit/3_gitea.md b/src/studienarbeit/3_gitea.md
new file mode 100644
index 0000000..bffb94b
--- /dev/null
+++ b/src/studienarbeit/3_gitea.md
@@ -0,0 +1,327 @@
+# Gitea
+
+Das Gitea-Projekt begann 2016 als Fork der Git-Hosting-Plattform Gogs. Lunny Xiao, einer
+der Beitragenden von Gogs, war damit unzufrieden, dass der Gründer des Projekts, Joe
+Chen, das Projekt alleine betreuen und keine weiteren Maintainer ernennen wollte.
+
+2022 gründete Lunny Xiao die Firma Gitea Limited. Die Firma bietet Unternehmen Hosting-
+und Supportdienstleistungen für Gitea an. Da einige Mitglieder der Community einem
+kommerziell geleiteten Open-Source-Projekt misstrauen, entstand ein neuer Fork names
+Forgejo. Forgejo steht unter der Leitung von Codeberg, einem Berliner Verein, der seit
+2018 eine öffentliche Gitea-Instanz betreibt. Abgesehen von Design- und Namensänderungen
+unterscheidet sich Forgejo von Gitea momentan kaum.
+
+Gitea ist in Go geschrieben und besteht aus einer einzigen ausführbaren Datei.
+Standardmäßig verwendet Gitea eine interne SQLite-Datenbank, alternativ werden auch
+externe Datenbanken (MySQL, MariaDB, Postgres, MS SQL) unterstützt.
+
+Die Weboberfläche wied serverseitig mit Templates gerendert. Einige Websitekomponenten
+wie z.B. Drop-Down-Menüs erfordern JavaScript.
+
+Getestet wurde die Version 1.20.0-rc2.
+
+## Installation
+
+Gitea stellt kompilierte Binaries für alle gängigen Betriebssysteme unter
+ zur Verfügung. Von dort aus lässt sich die Anwendung
+einfach herunterladen und starten.
+
+Alternativ gibt es auch [offizielle](https://pkgs.org/download/gitea) und
+[inoffizielle](https://gitea.com/gitea/awesome-gitea/src/branch/main/README.md#user-content-packages)
+Pakete für die meisten Linux-Distributionen.
+
+Darüber hinaus stellt Gitea auch ein Docker-Image (`gitea/gitea`) zur Verfügung.
+
+Die Einrichtung ist extrem simpel. Beim ersten Start wird diese Konfigurationsseite
+angezeigt, auf der man die grundlegenden Einstellungen (z.B. Datenbank-URL,
+Admin-Passwort) vornehmen kann. Nach der Bestätigung ist Gitea in wenigen Sekunden
+bereit zur Verwendung.
+
+
+
+Dieser Konfigurationsassistent wird jedoch nur beim ersten Start angezeigt. Alle
+weiteren Konfigurationsänderungen müssen durch die
+`app.ini`-[Konfigurationsdatei](https://docs.gitea.com/1.20/administration/config-cheat-sheet)
+erfolgen. Gitea verfügt zwar über eine Admin-Oberfläche, diese dient allerdings
+hauptsächlich der Nutzerverwaltung und erlaubt keine Konfigurationsänderungen.
+
+## Systemanforderungen
+
+Gitea ist auf jedem Betriebssystem und jeder CPU-Architektur lauffähig, die von der
+Programmiersprache Go unterstützt wird. Binaries für MacOS (amd64/aarch64), FreeBSD
+(amd64), Windows (i386/amd64) und Linux (i386/amd64/arm5/arm6/aarch64) werden offiziell
+zum Download angeboten. Das offizielle Docker-Image ist für die amd64- und
+aarch64-Architektur verfügbar.
+
+Im Leerlauf benötigt der Server ca. 150MB Arbeitsspeicher und verursacht unter 0.1%
+CPU-Auslastung. Damit ist Gitea die mit Abstand leichtgewichtigste Plattform in Test und
+eignet sich perfekt für den Einsatz auf schwächerer Hardware.
+
+## Bedienung
+
+Die Weboberfläche von Gitea wird serverseitig mit Hilfe von Templates gerendert
+(klassische Multi-Page-Anwendung). Dies hat den Vorteil, dass die Seite auch ohne
+JavaScript dargestellt werden kann (wenn auch mit eingeschränkter Bedienbarkeit).
+
+Obwohl bei jedem Navigationsschritt durch ein Repository die gesamte Seite neu geladen
+wird, läuft die Navigation durch Ordner sehr flüssig.
+
+Optisch und funktional ist die Benutzerschnittstelle stark an GitHub angelehnt. Wer also
+vorher GitHub verwendet hat, findet sich auf Gitea schnell zurecht
+
+Der Code in Gitea-Repositories kann durchsucht werden, allerdings ist diese Funktion
+standardmäßig deaktiviert. Um sie zu aktivieren, müssen diese Zeilen zur
+Konfigurationsdatei hinzugefügt werden:
+
+```
+[indexer]
+REPO_INDEXER_ENABLED=true
+```
+
+Standardmäßig verwendet Gitea die Bibliothek [Bleve](https://blevesearch.com/) für die
+Suche, es kann jedoch auch eine externe Elasticsearch-Instanz verwendet werden.
+
+
+
+Gitea kann standardmäßig Bilder, CSV-Dateien, PDFs und Markdown-Dateien auf der
+Weboberfläche darstellen. Andere Markupsprachen wie ReStructured Text werden
+standardmäßig nicht unterstützt. Allerdings bietet Gitea als einzige getestete Plattform
+die Möglichkeit, zusätzliche Renderer in der Konfigurationsdatei zu definieren.
+Beispielsweise kann Gitea Pandoc verwenden, um RST, LaTEX oder Word-Dokumente in HTML zu
+konvertieren und anzuzigen. Es ist auch möglich, mit etwas Bastelei einen Viewer für STL
+oder Excel-Dateien zu installieren.
+
+## Import bestehender Projekte
+
+Gitea bietet die Möglichkeit, Projekte von GitHub, Gitlab, Gogs, OneDev, GitBucket und
+Codebase zu importieren. Neben den Repositories können auch Issues, Pull-Requests und
+Releases importiert werden.
+
+Der Versuch, zwei meiner GitHub-Projekte mit Issues und Pull-Requests zu übertragen,
+scheiterte jedoch mit einer Fehlermeldung.
+
+## Zusatzfeatures
+
+Gitea bietet grundlegende Features zum Projektmanagement. Issues können erstellt, mit
+Labels versehen und bestimmten Entwicklern zugewiesen werden. Es können
+Fälligkeitstermine für Issues festgelegt und die Arbeitszeit hinterlegt werden.
+
+Dazu kommt die Möglichkeit, Issues zu Meilensteinen hinzuzufügen und somit
+beispielsweise den Arbeitsfortschritt für eine neue Version zu verfolgen.
+
+Darüber hinaus können in Gitea Kanbanboards erstellt werden. Allerdings sind diese
+Boards nicht in das restliche Issue-System integriert und deswegen umständlich zu
+nutzen. Issues müssen beispielsweise manuell den Boards hinzugefügt werden. Während
+GitLab Labels verwendet, um die Issues den Spalten zuzuordnen ist in Gitea außerhalb des
+Boards nicht ersichtlich, welchen Status ein Issue hat. Ein Issue, das in die
+Done-Spalte verschoben wird, wird nicht automatisch geschlossen. Umgekehrt führt das
+Schließen eines Issues nicht zu einer Platzierung in der Done-Spalte.
+
+Dokumentation für Gitea-Repositories kann in einem simplen, auf Markdown basierenden
+Wiki veröffentlicht werden.
+
+Das Hosting statischer Webseiten unterstützt Gitea nicht, weswegen das Team von Codeberg
+hierfür eine [eigene Lösung](https://codeberg.org/Codeberg/pages-server) entwickelt hat.
+
+Gitea bietet eine integrierte Registry für Pakete aller gängigen Programiersprachen
+(z.B. Python, npm, Rust). Wer eine Softwarebibliothek entwickelt, kann diese auf seiner
+Gitea-Instanz veröffentlichen, sodass andere Entwickler sie nutzen können. Zudem
+unterstützt die Gitea-Registry Linux-Pakete (.deb und .rpm) sowie Docker-Images.
+
+Gitea unterstützt als einzige getestete Plattform agit. Dies ist ein alternativer
+Git-Workflow, der von Alibaba entwickelt wurde. Agit erlaubt es Entwicklern, Pull
+Requests zu erstellen ohne zuvor einen Fork des Projekts anzulegen. Hierbei wird die
+Änderung mit diesem Befehl in einen versteckten Branch des Reposiories gepusht, worauf
+Gitea automatisch eine PR erstellt.
+
+Dieser Befehl erfordert im Gegensatz zum normalen push keine Schreibrechte im
+entsprechenden Repository.
+
+```
+git push origin HEAD:refs/for/main -o topic="feat/user-search" -o title="Add user search"
+```
+
+Es existiert auch ein Git-Plugin für agit namens
+[`git-repo`](https://git-repo.info/en/2020/03/agit-flow-and-git-repo/), damit man nicht
+jedes Mal diesen langen Befehl eintippen muss, sondern den Beschreibungstext direkt in
+die Kommandozeile tippen kann.
+
+Gitea lässt sich auch mit dem Tool [`tea`](https://gitea.com/gitea/tea) auf der
+Kommandozeile bedienen. Auf diese Weise lassen sich Issues erstellen und lesen oder Pull
+Requests erstellen und abrufen.
+
+Gitea bietet dem Administrator vollen Zugriff auf seinen Webserver und das
+Templatesystem. Dadurch kann man Templates und Stylesheets durch eigens angepasste
+Versionen ersetzen und die Weboberfläche nach seinen Bedürfnissen anpassen. Dies ist
+insbesondere für große Organisationen und Firmen interessant, die ihre
+Git-Hosting-Plattform nach ihrem Corporate Design gestalten möchten.
+
+Ein Beispiel für eine stark angepasste Gitea-Oberfläche ist die Instanz des
+[Blender-Projekts](https://projects.blender.org/), die sich nahtlos in die restliche
+Projektseite einfügt.
+
+## Continous Integration
+
+### Woodpecker
+
+Gitea verfügte lange nicht über einen eingebauten CI-Server. Allerdings gibt es eine
+Schnittstelle, um externe CI-Systeme anzubinden.
+
+Ich habe einige CI-Systeme getestet, die mit Gitea kompatibel sind.
+[Woodpecker](https://woodpecker-ci.org/) ist meiner Meinung nach eines der besten
+Systeme. Bei Woodpecker handelt es sich um einen Fork des Drone CI-Projekts nach deren
+Wechsel von der Apache License zu einer nichtkommerziellen Lizent.
+
+Woodpecker erlaubt den Login mit seinem Gitea-Account und zeigt dem Entwickler auf der
+Startseite eine Liste aller Repositories an. Von dort aus lässt sich die CI für
+bestimmte Repositories aktivieren. Daraufhin konfiguriert Woodpecker die Webhooks des
+Repositories, sodass die Builds automatisch bei Aktualisierungen gestartet werden.
+
+Woodpecker verwendet sogenannte Agents, die mittels GRPC mit dem zentralen Server
+kommunizieren und Buildaufträge empfangen. Die Agents verwenden Docker, um die Builds in
+Containern auszuführen.
+
+
+
+Die Builds werden in einer yml-Datei mit dem Namen `.woodpecker.yml` im
+Wurzelverzeichnes des Repositories definiert. Jeder Buildschritt wird in einem eigenen
+Docker-Container ausgeführt.
+
+```yaml
+pipeline:
+ test:
+ image: rust:latest
+ commands:
+ - rustup component add rustfmt clippy
+ - cargo fmt --all --check
+ - cargo clippy --all --features=rss -- -D warnings
+ - cargo test --features=rss --workspace
+```
+
+Die Installation von Woodpecker ist etwas komplizierter, da Woodpecker sowohl einen
+HTTP-Server als auch einen GRPC-Server bereitstellt. Möchte man Agents auf anderen
+Servern betreiben, sollte auch der GRPC-Server mit TLS gesichert sein. Hierfür kann man
+einen Reverse Proxy (z.B Traefik) verwenden. Allerdings beinhaltet die offizielle
+Dokumentation keine Anleitung hierfür.
+
+[Hier](./assets/woodpecker/docker-compose.yml) ist die Docker-Compose-Datei, die ich für
+meine Setup (mit Traefik) verwendet habe. Vor der Verwendung müssen noch die folgenden
+Konfigurationsvariablen bearbeitet werden:
+
+- `WOODPECKER_HOST` URL der Woodpecker-Instanz
+- `WOODPECKER_GITEA_URL` URL der Gitea-Instanz
+- `WOODPECKER_GITEA_CLIENT` / `WOODPECKER_GITEA_SECRET` OAuth-Zugangsdaten (in Gitea das
+ Einstellungsmenü aufrufen und unter _Create a new OAuth2 Application_ einen neuen
+ Client erstellen. Redirect URI: `/authorize`)
+- `WOODPECKER_AGENT_SECRET` ist das Passwort für alle mit der Instanz verbundenen
+ Agents. Hierfür sollte man sich einfach einen langen Zufallsstring erzeugen,
+ beispielsweise mit `openssl rand -hex 32`.
+
+### Gitea Actions
+
+Mit dem vorletzten Release 1.19 bekam Gitea jedoch ein eigenes CI-System: Gitea Actions.
+Hierbei wurde nicht nur der Name an die Konkurrenz von Microsoft angelehnt. Das System
+basiert auf [act](https://github.com/nektos/act), einer freien Implementation von GitHub
+Actions. Ursprünglich wurde act zum lokalen Testen von GitHub Actions entwickelt. Gitea
+hat die Anwendung stattdessen als Basis für ihr CI-System verwendet.
+
+Standardmäßig sind Gitea Actions in den Einstellungen deaktiviert. Um sie verwenden zu
+können, muss man diese Zeilen zur Konfigurationsdatei hinzufügen:
+
+```
+[actions]
+ENABLED=true
+```
+
+Anschließend muss man wie bei Gitlab und Woodpecker einen Runner einrichten, der die
+Builds mittels Docker ausführt. Der Runner kann als Binary von der
+[Gitea-Download-Seite](https://dl.gitea.com/act_runner/) heruntergeladen werden. Es ist
+auch möglich, den Runner in einem Docker-Container zu betreiben.
+
+Nachdem man den Runner heruntergeladen hat, muss man ihn konfigurieren. Dafür führt man
+den Befehl `act-runner register` auf. Anschließend muss man die Adresse der Instanz und
+einen Registrierungstoken, den man in der Admin-Oberfläche unter _Actions / Runners_
+erzeugen kann.
+
+Anschließend muss man noch die Actions im gewünschten Repository aktivieren, in dem man
+in den Einstellungen den Haken bei _Enable Repository Actions_ setzt.
+
+Die Builds werden als yaml-Dateien im Verzeichnis `.gitea/workflows` definiert. Obwohl
+Gitea die gleiche Syntax wie Github Actions verwendet, sind die Workflows nicht 1:1
+kompatibel. Beispielsweise lädt Gitea vordefinierte Actions wie `actions/checkout` nicht
+von [github.com/actions](https://github.com/actions) sondern von
+[gitea.com/actions](https://gitea.com/actions) herunter, sodass einige Actions nicht
+verfügbar sind.
+
+Außerdem führt GitHub seine Builds in einer virtuellen Maschine mit einer Vielzahl an
+vorinstallierten Programmiersprachen und Entwicklertools aus. Da das Image dieser
+Maschine sehr groß ist, benutzt Gitea stattdessen standardmäßig den
+Debian+NodeJS-Container `node:16-bullseye`. Das Image enthält neben Node.js auch Python,
+gcc, make und curl. Wer andere Programmiersprachen benutzt, muss jedoch Anwendungen
+nachinstallieren (wie beispielsweise Rust im unteren Beispiel). Es ist jedoch möglich,
+andere Docker-Images zu verwenden.
+
+```yaml
+name: Test
+on: [push]
+
+jobs:
+ Test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v3
+ - name: Setup Rust
+ run: |
+ curl https://sh.rustup.rs -sSf | sh -s -- -y
+ source "$HOME/.cargo/env"
+ - name: Test
+ run: |
+ cargo clippy
+ cargo test
+```
+
+
+
+Ein wichtiges Feature, das Gitea Actions momentan noch fehlt ist die Möglichkeit,
+Workflows manuell zu starten. Da Gitea Actions noch relativ neu ist, gehe ich jedoch
+davon aus, dass dieses Feature in Zukunft implementiert wird.
+
+## Öffentliche Instanzen
+
+Die von den Maintainern des Projekts betriebene Instanz unter
+[gitea.com](https://gitea.com/explore/repos) verfügt über 5600 Repositories.
+
+Eine noch größere Instanz betreibt der Berliner Verein Codeberg e.V. Auf
+[codeberg.org](https://codeberg.org) werden 30900 öffentliche Repositories gehostet.
+Zusätzlich zu ihrer Gitea-Instanz bietet Codeberg auch einen Webhosting-Dienst unter
+[\*.codeberg.page](codeberg.page) an. Codeberg betreibt auch einen Woodpecker-CI-Server,
+für dessen Verwendung die Nutzer allerdings manuell freigeschaltet werden müssen.
+
+Da Gitea sehr einfach und günstig zu hosten ist, wird es von einigen Open
+Source-Projekten auch als sekundäre Plattform zusätzlich zu GitHub verwendet.
+Beispielsweise betreibt das Team hinter dem alternativen YouTube-Frontend Invidious eine
+[Gitea-Instanz](https://gitea.invidious.io/iv-org), um vor eventuellen Takedowns des
+Repositories auf GitHub sicher zu sein
+
+## Fazit
+
+Während GitLab meiner Meinung nach die beste Lösung für große Organisationen und
+Unternehmen darstellt, ist Gitea das perfekte System für einzelne Entwickler, kleine
+Teams und Hobbybastler. Durch seinen sparsamen Ressourcenverbrauch ist Gitea auf fast
+jeder Hardware lauffähig und die Einrichtung funktioniert einfach und schnell.
+
+Darüber hinaus lässt sich Gitea mit eigenen Themes und Templates individuell anpassen
+und zu seiner persönlichen Website machen.
+
+Die Projektmanagement-Features von Gitea sind allerdings eher rudimentär. Wer
+Kanbanboards benötigt, sollte Gitlab oder OneDev in Betracht ziehen oder hierfür eine
+externe Software verwenden.
+
+## Quellen
+
+- Projektseite
+- Repository
+- Start von Codeberg (damals TeaHub)
+ ,
+
diff --git a/src/studienarbeit/4_sourcehut.md b/src/studienarbeit/4_sourcehut.md
new file mode 100644
index 0000000..026ec8e
--- /dev/null
+++ b/src/studienarbeit/4_sourcehut.md
@@ -0,0 +1,292 @@
+# Sourcehut
+
+Eine Git-Hosting-Plattform ohne Pull-Requests, einer spartanischen Weboberfläche und
+einem mächtigen Buildsystem - so lässt sich Sourcehut am besten zusammenfassen.
+
+
+
+SourceHut begann im
+[Oktober 2016](https://git.sr.ht/~sircmpwn/meta.sr.ht/commit/40dcb38349af3f67845857c4ac9003911a653624)
+als Hobbyprojekt von Drew DeVault. Drew wollte eine alternative zu den etablierten
+Git-Hosting-Diensten entwickeln, die den klassischen E-Mail-basierten Git-Workflow
+unterstützt anstatt das Funktionsprinzip von GitHub zu kopieren.
+
+[2018](https://drewdevault.com/2018/11/15/sr.ht-general-availability.html) war die
+Alpha-Version der Software öffentlich unter verfügbar.
+
+Die Plattform besteht aus einzelnen Komponenten für die verschiedenen Features:
+
+- meta.sr.ht (Login und Benutzerverwaltung)
+- hub.sr.ht (Projektverwaltung)
+- git.sr.ht / hg.sr.ht (Git/Mercurial Hosting)
+- todo.sr.ht (Issues)
+- lists.sr.ht (Mailinglisten)
+- man.sr.ht (Wiki)
+- paste.sr.ht (Pastebin)
+- builds.sr.ht (Continuous Integration)
+- pages.sr.ht (Statisches Webhosting)
+
+Jede dieser Komponentent besteht wiederum aus 2-3 Diensten: dem in Python/Flask
+implementierten Webfrontend, einem in Go geschriebenen API-Server und eventuell noch
+zusätzliche Dienste wie eine Task Queue.
+
+Jede Komponente legt ihre Daten in einer eigenen PostgreSQL-Datenbank ab. Zusätzlich
+wird noch eine gemeinsame Redis-Instanz als Cache und Event Queue verwendet.
+
+Hier ist eine Übersicht über die einzelnen Dienste:
+
+
+
+## Installation
+
+Da Sourcehut aus mehreren Services für die einzelnen Features besteht, ist die
+Einrichtung mit deutlich mehr Aufwand verbunden. Die Anleitung zur Installation findet
+sich auf der [Dokumentationsseite](https://man.sr.ht/installation.md) des Projekts.
+Allerdings ist diese Anleitung nicht vollständig und ich musste an einigen Stellen im
+Quellcode der Software nachschlagen, wie bestimmte Features zu konfigurieren sind.
+
+Erschwerend kommt hinzu, dass Sourcehut an vielen Stellen keine aussagekräftigen
+Fehlermeldungen liefert. Beispielsweise hatte ich das Problem, dass builds.sr.ht keine
+VMs starten konnte, da diese standardmäßig mit 4GB RAM konfiguriert sind - zu viel für
+meine Test-VM. Der einzige Fehler, der in diesem Fall auf der Webseite angezeigt wird,
+ist der, dass das Build-System innerhalb einer bestimmten Zeit keine Verbindung zur VM
+aufbauen konnte. Der OOM-Fehler beim Start wurde weder geloggt noch an den Nutzer
+weitergegeben.
+
+Auf der Dokumentationsseite wird erwähnt, dass sich das Projekt noch im Alpha-Stadium
+befindet, es ist also zu hoffen, dass die Dokumentation in Zukunft verbessert wird.
+
+Ich habe sämtliche Schritte, die zur Installation meiner Testinstanz erforderlich waren,
+im [Anhang](sourcehut_setup.md) aufgelistet.
+
+## Systemanforderungen
+
+Sourcehut ist auf jedem System lauffähig, das Python 3.10 und Go unterstützt. Allerdings
+sind die Pakete momentan nur für die amd64-Architektur verfügbar. Wer Sourcehut also auf
+einem Raspberry Pi oder ARM-Server installieren will, muss die Pakete selbst
+kompilieren.
+
+Die CPU-Auslastung im Leerlauf ist gering (ca. 1%). Bei aufwändigen Git-Operationen
+sieht die Sache jedoch anders aus. Wenn man sich z.B. die Änderungshistorie der Makefile
+des Linux-Kernels anzeigen lässt, läuft git.sr.ht 30 Sekunden lang mit 100%
+CPU-Auslastung, bis Nginx die Anfrage wegen Zeitüberschreitung abbricht. Dieses Problem
+lässt sich auch auf der offiziellen Instanz reproduzieren
+([Link zum Testen](https://git.sr.ht/~gregkh/linux-stable/log/master/item/Makefile)).
+
+Zum Vergleich: Die Ausführung des Befehls `git --no-pager log Makefile | head -n 1000`
+benötigte auf meinem Testsystem nur 100ms.
+
+Beim Arbeitsspeicherverbrauch macht sich die Microservice-Architektur negativ bemerkbar:
+jeder Python-Dienst reserviert 90-100MB, die in Go geschriebenen API-Server kommen mit
+20-30MB RAM aus. Da die gesamte Sourcehut-Installation aus 14 Python- und 8 Go-Services
+besteht, summiert sich der Arbeitsspeicherbedarf auf ca. 1,5GB.
+
+## Bedienung
+
+Sourcehut's Weboberfläche ist spartanisch designt und kommt fast ohne JavaScript aus.
+Dadurch ist die Webseite ohne Probleme in alten Geräten und Browsern benutzbar.
+
+Die Repository-Ansicht ist klar an GitWeb angelehnt und zeigt die Dateien ähnlich wie
+der `ls -l`-Befehl zusammen mit Größe und Berechtigungen an.
+
+Genau wie GitWeb (und ls) platziert Sourcehut Ordner nicht vor Dateien, was das
+Auffinden bestimmter Ordner unter vielen Dateien erschwert.
+
+Eine Suchfunktion gibt es nicht, dadurch ist es schwierig, auf der Webseite durch große
+Projekte zu navigieren.
+
+Hinzu kommt, dass die Webseite nur Code und Markdown-Dokumente darstellen kann. Bilder,
+PDF-Dokumente oder andere Markupsprachen wie RST werden nicht unterstützt.
+
+
+
+Die einzelnen Komponenten von Sourcehut sind eigenständige Webanwendungen. Dies hat den
+Vorteil, dass man nur einen Teil von Sourcehut installieren kann, wenn man
+beispielsweise den Build-Service oder die Mailinglisten nicht benötigt. Allerdings macht
+diese Aufteilung die Bedienung an vielen Stellen hakelig. Es gibt zwar eine zentrale
+Projektverwaltung (hub.sr.ht), die Repositories, Issue-Tracker und Mailinglisten zu
+bestimmten Projekten zuordnet. Allerdings ist diese Verlinkung eine Einbahnstraße: von
+der Projekseite auf dem Hub kann man alle zugehörigen Repositories, Issue-Tracker und
+Mailinglisten aufrufen, befindet man sich aber bspw. in einem Repository, kann man das
+zugehörige Projekt nicht ohne weiteres ermitteln.
+
+ Projektansicht im SourceHut Hub
+
+Sourcehut unterscheidet sich von GitHub und anderen Code-Hosting-Plattformen vor allem
+dadurch, dass es zur Kollaboration zwischen Entwicklern keine Pull-Requests verwendet.
+Stattdessen basiert das System auf den "klassischen" Git-Workflow, also dem Austausch
+von Patches via E-Mail. Projekteigentümer können Mailinglisten einrichten und darüber
+Patches empfangen.
+
+Dies hat den Vorteil, dass außenstehende Entwickler sich nicht auf der Plattform
+registrieren müssen und ausschließlich via E-Mail mit den Maintainern des Projekts
+kommunizieren können.
+
+Sourcehut bietet Entwicklern auch die Möglichkeit, über die Weboberfläche Patches zu
+erstellen und zu versenden. Dazu muss man das Repository zuerst auf der Plattform
+klonen, die eigenen Commits hochladen und diese anschießend in der Weboberfläche
+auswählen.
+
+
+
+Auch wenn dieser Workflow sicherlich Vorteile mit sich bringt, muss man feststellen,
+dass dies für die meisten Entwickler ungewohnt und umständlich ist. Entwickler, die
+bisher nur mit GitHub und ähnlichen Plattformen gearbeitet haben, werden beim Versuch,
+zu einem Projekt beizutragen, eventuell auf Hindernisse stoßen.
+
+Es gibt auch ein CLI-Tool namens [hut](https://git.sr.ht/~emersion/hut), um die
+verschiedenen Sourcehut-Dienste mit der Kommandozeile zu bedienen. Das Tool erlaubt das
+Erstellen von Issues, Hochladen von Releases, Starten von Builds, Veröffentlichen von
+Webseiten, Hinzufügen von SSH-Keys und einiges mehr.
+
+## Import bestehender Projekte
+
+Die Sourcehut-Weboberfläche erlaubt auf lediglich den Import von öffentlichen
+Git-Repositories per clone. Es gibt keine Möglichkeit zum Import von Issues und
+Pull-Requests werden wie oben erwähnt nicht unterstützt.
+
+## Zusatzfeatures
+
+Wie bereits beschrieben besteht Sourcehut aus einer Vielzahl von Diensten, den
+Entwicklern neben Git-Hosting und Issue Tracking diverse Zusatzfeatures bieten.
+
+man.sr.ht ist ein auf Git basierendes Wiki, das in Markdown geschriebene
+Dokumentationstexte darstellen kann. Allerdings bietet es kaum Features: der einzige
+Unterschied zum Betrachten von Markdown-Dateien in git.sr.ht ist das
+Seiteninhaltsverzeichnis, das man.sr.ht oben anfügt. Es gibt kein Inhaltsverzeichnis
+über die gesamte Dokumentation, genauso wenig wie eine Suchfunktion.
+
+paste.sr.ht ist ein Pastebin zum Speichern und Veröffentlichen von kleineren Texten,
+Skripten und Codeausschnitten. Man kann Pastes öffentlich oder privat speichern und es
+gibt auch die Möglichkeit, Textdateien mit dem `hut`-CLI-Tool hochzuladen.
+
+pages.sr.ht ist ein Webhosting-Dienst, der es einem erlaubt, statische Webseiten unter
+einer persönlichen Subdomain zu veröffentlichen. Hierfür muss man sämtliche Dateien als
+`tar.gz`-Archiv komprimieren und anschließend mit dem `hut`-Tool hochladen.
+
+Das Issue-System von Sourcehut (todo.sr.ht) ist jedoch sehr rudimentär. Es gibt außer
+Labels und der Möglichkeit, verantwortliche Personen zuzuweisen keine Zusatzfunktionen,
+die das Projektmanagement erleichtern. Für größere Projekte ist daher die Verwendung
+einer zusätzlichen Projektmanagement-Software wie Redmine nötig.
+
+## Continous Integration
+
+builds.sr.ht ist ein sehr leistungsfähiges und einfach zu verwendender CI-System.
+
+Builds können über die Weboberfläche, eine API oder durch Git-Pushes und auf der
+Mailigliste eingegangene Patches initiiert werden. Zum Ausführen der Builds werden
+virtuelle Maschinen (mit QEMU/KVM) gestartet, wodurch die Build-Umgebung vom Server
+isoliert ist.
+
+QEMU erlaubt es auch, andere CPU-Architekturen zu emulieren und so beispielsweise
+Embedded Linux-Anwendungen zu testen.
+
+
+
+Sourcehut Builds werden durch
+[yaml-Manifests](https://man.sr.ht/builds.sr.ht/manifest.md) beschrieben. Die Syntax ist
+sehr simpel gehalten. Manifests bestehen aus dem Namen des VM-Images, den benötigten
+Paketen und Repositories und einem oder mehreren Shell-Skripten.
+
+```yaml
+image: alpine/latest
+packages:
+ - cargo
+sources:
+ - https://code.thetadev.de/ThetaDev/short-uuid.git
+tasks:
+ - test: |
+ cd short-uuid
+ cargo test
+```
+
+Es gibt auch die Möglichkeit, für den Build benötige Secrets wie API-Keys und
+SSH-Schlüssel zu hinterlegen. Die Secrets werden auf der Build-VM in Dateien abgelegt,
+von wo aus sie in Buildskripten verwendet werden können (z.B.
+`cargo publish --token "$(<~/.cargo-token)"`). Zudem gibt es die Möglichkeit, SSH- und
+GPG-Schlüssel automatisch zu importieren.
+
+Ein besonderes Feature von builds.sr.ht ist die Möglichkeit, fehlgeschlagene Builds zu
+analysieren. Schlägt ein Build fehl, läuft die entsprechende VM noch 10 Minuten weiter.
+In dieser Zeit kann man sich per SSH mit dem Build-Server verbinden und kann Befehle in
+der entsprechenden Build-VM ausführen. Auf diese Weise kann man Logdateien inspizieren
+oder den Build mit Konfigurationsänderungen neu starten, ohne jedes Mal das
+Buildmanifest zu ändern und neu zu übermitteln.
+
+
+
+Allerdings bietet Sourcehut Builds nicht die Möglichkeit, zu spezifizieren, wann ein
+Build ausgeführt werden soll. Builds werden immer dann gestartet, wenn ein Branch in
+einem Repository aktualisiert wird, in dem sich eine `.builds.yml`-Datei befindet.
+Eingehende Patches in der Mailingliste können ebenfalls einen Build startet.
+
+Es gibt jedoch keine Möglichkeit, Builds nur dann zu starten, wenn ein bestimmter Branch
+oder bestimmte Dateien aktualisiert wurden. Dies kann beispielsweise in Monorepos
+(Repositories mit mehreren Projekten) zu unnötig lange dauernden Builds führen.
+
+## Öffentliche Instanzen
+
+Die öffentliche Sourcehut-Instanz, die von Drew DeVault und seiner Firma betrieben wird,
+befindet sich unter .
+
+sr.ht ist der einige mir bekannte Git-Hosting-Anbieter, der ein kostenpflichtiges
+Abonnement für das Erstellen eigener Projekte erfordert. Solange sich Sourcehut im
+Alpha-Stadium befindet, ist das Abo allerdings optional (außer für die Nutzung des
+Build-Service). Das Abonnement kostet 20€ im Jahr, man kann auch auf freiwilliger Basis
+bis zum 100€ bezahlen, um das Projekt zu unterstützen.
+
+Die kostenpflichtigen Mitgliedschaften erlauben Sourcehut, finanziell unabhängig zu sein
+und nicht den Entscheidungen von Investoren unterworfen zu sein. Sourcehut
+veröffentlicht seine jährlichen Einnahmen und Ausgaben auf ihrem Blog und gibt an, seit
+2019 Profit zu erwirtschaften.
+
+Bekannte Projekte, die auf sr.ht gehostet werden sind das alternative Instagram-Frontend
+[Bibliogram](https://sr.ht/~cadence/bibliogram/),
+[microblog.pub](https://sr.ht/~tsileo/microblog.pub/), eine persönliche
+Microblogging-Anwendung mit ActivityPub-Unterstützung und natürlich
+[Sourcehut](https://sr.ht/~sircmpwn/sourcehut/) selbst. Insgesamt hostet sr.ht ca. 7100
+Projekte.
+
+Es gibt auch Open-Source-Projekte, die ihren Code auf anderen Plattformen hosten und nur
+das Build-System von Sourcehut nutzen. Beispielsweise benutzt die Linux-Distribution
+postmarketOS [Sourcehut Builds](https://build.postmarketos.org/) zum Bau ihrer Pakete.
+
+Wichtig ist noch zu erwähnen, dass die offizielle Sourcehut-Instanz keine Projekte
+erlaubt, die mit
+[Blockchain oder Kryptowährungen](https://sourcehut.org/blog/2022-10-31-tos-update-cryptocurrency/)
+zu tun haben. Sourcehut begründet dies damit, dass Kryptowährungen häufig für Betrug und
+andere Verbrechen eingesetzt werden und große Mengen an Ressourcen verschwenden.
+
+Abgesehen von der offiziellen Instanz gibt es sehr wenige weiterem öffentlichen
+Sourcehut-Instanzen, was wohl auch dem komplizierten Einrichtungsprozess geschuldet ist.
+
+Es existiert eine Instanz unter [gnu.org](https://sourcehut.gnu.org), die sich
+allerdings im Entwicklermodus befindet und über keine öffentlichen Projekte verfügt. Die
+größte alternative Sourcehut-Instanz, die ich gefunden habe, ist
+. Die Instanz wird von einer Webentwicklungsfirma
+betrieben, beherbergt 9 öffentliche Projekte und erlaubt keine Registrierung von
+Außenstehenden.
+
+## Fazit
+
+Das Highlight von sr.ht ist meiner Meinung nach der CI-Dienst. Während die meisten
+anderen CI-Dienste an Repositories gekoppelt sind, erlaubt es Sourcehut, einfach ein
+Build-Manifest mit dem CLI-Tool oder dem Formular auf der Website abzuschicken und
+ausführen zu lassen. Die Möglichkeit fehlgeschlagene Builds zu analysieren ist ebenfalls
+einzigartig.
+
+Allerdings ist die Einrichtung von Sourcehut sehr aufwändig und einige grundlegende
+Features wie eine Suchfunktion fehlen weswegen ich das System in seinem momentanen
+Zustand nicht empfehlen kann.
+
+Hinzu kommt, dass Sourcehut trotz seiner spartanischen Weboberfläche und seinem
+reduziertem Funktionsumfang das System mit dem zweithöchsten Ressourcenverbrauch im Test
+war.
+
+Da sich Sourcehut wie gesagt noch im Alpha-Stadium befindet, lohnt es sich jedoch das
+Projekt weiter zu verfolgen.
+
+## Quellen
+
+- Projektseite
+- Sourcehut Blog
diff --git a/src/studienarbeit/5_onedev.md b/src/studienarbeit/5_onedev.md
new file mode 100644
index 0000000..8394d68
--- /dev/null
+++ b/src/studienarbeit/5_onedev.md
@@ -0,0 +1,232 @@
+# OneDev
+
+Abschließen möchte ich meinen Test mit einer weitgehend unbekannten Plattform, die erst
+2019 als Hobbyprojekt von zwei Entwicklern veröffentlicht wurde, sich aber in Sachen
+Funktionalität nicht vor den anderen Plattformen verstecken muss: OneDev.
+
+
+
+OneDev ist in Java geschrieben und verwendet das Webframework Apache Wicket. Das Projekt
+wird hauptächlich von Robin Shen und Steve Luo entwickelt, insgesamt gibt es ca. 30
+Beitragende. Das Projekt ist unter der MIT-Lizenz veröffentlicht.
+
+Der
+[erste Commit](https://code.onedev.io/onedev/server/~commits/6be8e3f97c97e44cf6b5a9b8360c299cc55388f6)
+erfolgte im April 2012, die erste in Git getaggte Version (2.0.0) wurde 2019
+veröffentlicht. Rechnet man mit dem Datum des ersten Release, ist OneDev die jüngste der
+vier Plattformen, die ich getestet habe.
+
+OneDev besteht aus einer einzigen Java-Anwendung und erfordert bei Verwendung der
+eingebetteten HSQL-Datenbank keine zusätzlichen Dienste zum Betrieb. Es werden auch
+externe Datenbanken (MySQL, MariaDB, Postgres, MS SQL, Oracle) unterstützt.
+
+Getestet wurde die Version 8.5.2.
+
+## Installation
+
+Die OneDev-Entwickler stellen Container-Images für OneDev zur Verfügung
+(`docker.io/1dev/server`), mit denen sich OneDev einfach mittels Docker installieren
+lässt. Alternativ gibt es auch die Möglichkeit, die Java-Anwendung direkt auf seinem
+System zu installieren oder OneDev in einem Kubernetes-Cluster zu betreiben.
+
+Für den Installationsprozess gibt es eine Schritt-für-Schritt-Anleitung auf der
+Dokumentationsseite des Projekts. Allerdings bin ich hiervon abgewichen, da in der
+Installationsanleitung empfohlen wird, den Docker-Socket (`/var/run/docker.sock`) im
+OneDev-Container als Volume einzubinden.
+
+Dies ist nur dann erforderlich, wenn der eingebaute _Server Docker Executor_ anstelle
+eines externen Agents für CI-Jobs verwendet wird.
+
+Da Container mit Docker-Zugriff effektiv über Root-Rechte auf dem Hostsystem verfügen,
+ist dies jedoch mit einem Sicherheitsrisiko verbunden. Deswegen empfehle ich, den
+eingebauten Agent nicht zu nutzen und stattdessen den seperaten Agent-Container
+(`docker.io/1dev/agent`) zu verwenden. Der CI-Agent sollte bei einem Produktionssetup
+auf einem anderen Server bzw. einer anderen VM laufen, auf der sich keine wertvollen
+oder sensiblen Daten befinden - insbesondere wenn nicht vertrauenswürdige Nutzer Zugriff
+auf das CI-System haben.
+
+[Hier](./assets/onedev/docker-compose.yml) ist die Docker-Compose-Datei, die ich für
+meine Testinstallation verwendet habe.
+
+Nach dem Start des Servers kann man auf der Weboberfläche den ersten Benutzeraccount
+anlegen und die URL des Servers konfigurieren.
+
+Anschließend muss noch das CI-System eingerichtet werden. Hierzu öffnet man den
+Menüpunkt _Administration_ \> _Agents_, klickt auf _Tokens_ und generiert sich einen
+neuen Agent Token. Dieser Token muss dem Agent als Umgebungsvariable übergeben werden
+(also z.B. in die Docker-Compose-Datei eintragen). Nach dem Start des Agents muss dieser
+in der Tabelle sichtbar sein.
+
+
+
+Danach öffnet man den Menüpunkt _Job Executors_ und erstellt einen _Remote Docker
+Executor_. Damit ist die Einrichtung des CI-Systems abgeschlossen und man kann den
+ersten Build starten (siehe im Abschnitt
+[Continuous Integration](#continous-integration)).
+
+Standardmäßig erlaubt OneDev nur angemeldeten Benutzern den Zugriff auf die Instanz. Um
+seine Projekte öffentlich verfügbar zu machen, muss noch die Option _Security Settings_
+\> _Enable Anonymous Access_ aktiviert werden.
+
+## Systemanforderungen
+
+OneDev ist auf jedem System mit Java-Unterstützung lauffähig. Das offizielle
+Docker-Image wird für die 64bit Interl/AMD und ARM-Architektur angeboten.
+
+Der Server benötigte auf meinem System ca. 1.2GB Arbeitsspeicher, der CI-Agent ca.
+180MB. Die CPU-Auslastung im Leerlauf beträgt auf meinem System (AMD Ryzen 5700G) ca.
+1%, mit gelegentlichen Spitzen auf 5-10%.
+
+Aufwändige Git-Operationen (History/Blame in großen Repositories) können Lastspitzen von
+bis zu 100% mit einer Dauer von max. 2 Sekunden verursachen. Bei Nutzung der
+Suchfunktion habe ich bis zu 50% Auslastung gemessen.
+
+## Bedienung
+
+OneDev verfügt über eine modern gestaltete Weboberfläche, die mit dem Webframework
+Apache Wicket realisiert wurde. Das Framework lädt beim Navigieren nur die Teile der
+Website vom Server, die sich durch den entsprechenden Navigationsschritt geändert haben
+(also z.B. die Dateiliste).
+
+Dadurch geht das Navigieren durch die Repositories sehr flüssig vonstatten, ohne dass
+die gesamte Seite neu geladen werden muss.
+
+Ein Alleinstellungsmerkmal von OneDev ist die Suchfunktion. OneDev verwendet nicht nur
+eine Textsuchmaschine, sondern analysiert den Code im Repository und bietet so die
+Möglichkeit, nach Klassen und Funktionen zu suchen. Die Suchergebnisse werden während
+dem Tippen angezeigt. Auf diese Weise kann man schnell durch komplexe Softwareprojekte
+navigieren.
+
+
+
+Die Suche kann auch über ein Kontextmenü in der Code-Ansicht aufgrufen werden. Auf diese
+Weise kann man nach Definitionen oder Verwendungen von Funktionen suchen. Es gibt auch
+eine Übersicht aller Klassen und Funktionen einer Datei auf der rechten Seite.
+
+Die Code-Analyse ist für die Sprachen Java, JavaScript, C, C++, CSharp, Go, PHP, Python,
+CSS, SCSS, LESS und R verfügbar. Rust wird leider nicht unterstützt.
+
+
+
+OneDev kann auf der Weboberfläche Code, Markdown-Dokumente und Bilder darstellen. Es
+gibt keine Unterstützung für alternative Markupsprachen wie RST.
+
+## Import bestehender Projekte
+
+OneDev bietet einen Import-Assistenten mit Unterstützung für GitHub, Gitlab, BitBucket,
+und Gitea. Neben dem Git-Repository werden auch Metadaten und Issues übertragen.
+
+Der Importer erlaubt es auch, nach einer Authentifizierung per API-Key sämtliche
+Projekte eines Nutzers oder einer Organisation zu übertragen, was beim Umstieg viel Zeit
+sparen kann.
+
+Es gibt jedoch nicht die Möglichkeit, Projekte aus anderen OneDev-Instanzen zu
+übertragen.
+
+## Zusatzfeatures
+
+OneDev verfügt über umfangreiche Tools zum Projektmanagement. Es gibt die Möglichkeit,
+Issues zu Meilensteinen zuzuordnen (z.B. _Sprint 14_ oder _Version 2.0_). Zudem verfügt
+OneDev über Issue Boards, um Issues je nach Status in mehreren Spalten anzuzeigen.
+
+
+
+Issues können bestimmten Entwicklern zugewiesen werden und es gibt die Möglichkeit,
+Abhängigkeiten zwischen Issues zu definieren (z.B. Issue A muss vor Issue B erledigt
+werden). Was allerdings fehlt ist die Möglichkeit, einem Issue mehrere Labels
+zuzuordnen.
+
+OneDev bietet auch die Möglichkeit, statische Webseiten zu hosten. Hierfür ist im
+Gegensatz zu Gitlab Pages keine separate Domain nötig. Stattdessen wird die Webseite im
+Unterverzeichnis `~site` des Repositorys (z.B. `https://example.com/user/repo/~site`)
+veröffentlicht.
+
+Als einzige getestete Plattform verfügt OneDev über ein Plugin-System. Auf diese Weise
+lassen sich neue Features implementieren, ohne die Software verändern zu müssen.
+Beispielsweise gibt es ein Plugin zum
+[Import von Issues](https://github.com/DevCharly/onedev-plugin-import-redmine/) aus der
+Projektmanagement-Anwendung Redmine.
+
+Weitere Zusatzfeatures wie Paketmanagement bietet OneDev nicht. Darüber hinaus fehlt die
+Möglichkeit, Releases seiner Software hochzuladen und mit Git-Tags zu verknüpfen. Dieses
+Feature beherrschen ansonsten alle getesteten Plattformen.
+
+## Continous Integration
+
+OneDev bietet ein eingebautes CI-System um Projekte zu kompilieren und zu testen. Die
+Jobs können in Docker-Containern, Kubernetes-Pods oder bare metal ausgeführt werden.
+
+Jobs werden in einer yml-Datei definiert, die als `.onedev-buildspec.yml` im
+Wurzelverzeichnis des Repositories abgelegt werden muss.
+
+OneDev bietet jedoch auch eine Weboberfläche, um Builds zu erstellen und zu bearbeiten.
+Auf diese Weise muss man nicht zuerst die yaml-Syntax erlernen, um das CI-System zu
+nutzen. Über die Weboberfläche lassen sich bestimmte Aktionen (z.B. "Befehle ausführen",
+"Docker Image bauen", "Webseite veröffentlichen") auswählen und konfigurieren.
+
+
+
+```yml
+version: 23
+jobs:
+ - name: test
+ steps:
+ - !CheckoutStep
+ name: checkout
+ cloneCredential: !DefaultCredential {}
+ withLfs: false
+ withSubmodules: false
+ condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL
+ - !CommandStep
+ name: build
+ runInContainer: true
+ image: rust:latest
+ interpreter: !DefaultInterpreter
+ commands:
+ - rustup component add rustfmt clippy
+ - cargo fmt --all --check
+ - cargo clippy --all --features=rss -- -D warnings
+ - cargo test --features=rss --workspace
+ useTTY: false
+ condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL
+ triggers:
+ - !BranchUpdateTrigger {}
+ - !PullRequestUpdateTrigger {}
+ retryCondition: never
+ maxRetries: 3
+ retryDelay: 30
+ timeout: 3600
+```
+
+Die Builds können durch Branch Updates oder neue Pull Requests ausgelöst werden. Es gibt
+auch die Möglichkeit, Builds zeitgesteuert oder manuell zu starten.
+
+## Öffentliche Instanzen
+
+Das OneDev-Team betreibt ihre eigene Instanz unter
+[code.onedev.io](https://code.onedev.io). Allerdings verbietet diese Instanz das
+erstellen eigener Projekte und dient nur der Entwicklung und Demonstration der
+Plattform.
+
+Darüber hinaus habe ich außer einer kleinen privaten Instanz names
+["Winux Projects"](https://projects.winux.it/) keine öffentlichen Instanzen gefunden,
+was sicher auch daran liegt, dass das Projekt momentan noch relativ unbekannt ist.
+
+## Fazit
+
+Obwohl OneDev noch relativ jung und weitgehend unbekannt ist, kann es funktional
+durchaus mit den Konkurrenten mithalten. Insbesondere die intelligente Suchfunktion und
+das einfach zu bedienende CI-System haben mir im Test sehr gut gefallen.
+
+Allerdings ist OneDev immer noch ein Hobbyprojekt von 2 Entwicklern und hat keine Firma
+oder größere Organisation hinter sich. Wer OneDev beispielsweise in einem Unternehmen
+einsetzen möchte, muss die Einrichtung und den Support selbst übernehmen, da es im
+Gegensatz zu GitLab und Gitea keine öffentlichen Instanzen oder SaaS-Angebote gibt.
+
+Wer sich hieran nicht stört, erhält mit OneDev eine leichtgewichtige, einfach zu
+installierende und moderne Git-Hosting-Plattform.
+
+## Quellen
+
+- Projektseite
+- Repository
diff --git a/src/studienarbeit/assets/gitea/actions.png b/src/studienarbeit/assets/gitea/actions.png
new file mode 100644
index 0000000..3a6d89c
Binary files /dev/null and b/src/studienarbeit/assets/gitea/actions.png differ
diff --git a/src/studienarbeit/assets/gitea/homepage.png b/src/studienarbeit/assets/gitea/homepage.png
new file mode 100644
index 0000000..27cf819
Binary files /dev/null and b/src/studienarbeit/assets/gitea/homepage.png differ
diff --git a/src/studienarbeit/assets/gitea/search.png b/src/studienarbeit/assets/gitea/search.png
new file mode 100644
index 0000000..bccbde9
Binary files /dev/null and b/src/studienarbeit/assets/gitea/search.png differ
diff --git a/src/studienarbeit/assets/gitea/setup.png b/src/studienarbeit/assets/gitea/setup.png
new file mode 100644
index 0000000..b259dc3
Binary files /dev/null and b/src/studienarbeit/assets/gitea/setup.png differ
diff --git a/src/studienarbeit/assets/gitea/woodpecker.png b/src/studienarbeit/assets/gitea/woodpecker.png
new file mode 100644
index 0000000..cc72833
Binary files /dev/null and b/src/studienarbeit/assets/gitea/woodpecker.png differ
diff --git a/src/studienarbeit/assets/gitlab/architecture.png b/src/studienarbeit/assets/gitlab/architecture.png
new file mode 100644
index 0000000..dcdb9d7
Binary files /dev/null and b/src/studienarbeit/assets/gitlab/architecture.png differ
diff --git a/src/studienarbeit/assets/gitlab/docker-compose.yml b/src/studienarbeit/assets/gitlab/docker-compose.yml
new file mode 100644
index 0000000..10c3fb3
--- /dev/null
+++ b/src/studienarbeit/assets/gitlab/docker-compose.yml
@@ -0,0 +1,21 @@
+version: "3.6"
+services:
+ web:
+ image: "gitlab/gitlab-ce:latest"
+ restart: unless-stopped
+ environment:
+ GITLAB_OMNIBUS_CONFIG: |
+ external_url 'http://localhost:8001'
+ nginx['listen_https'] = false
+ nginx['listen_port'] = 80
+ nginx['redirect_http_to_https'] = false
+ letsencrypt['enable'] = false
+ GITLAB_ROOT_PASSWORD: secret1234
+ ports:
+ - "8001:80"
+ - "2222:22"
+ volumes:
+ - "./config:/etc/gitlab"
+ - "./logs:/var/log/gitlab"
+ - "./data:/var/opt/gitlab"
+ shm_size: "256m"
diff --git a/src/studienarbeit/assets/gitlab/import.png b/src/studienarbeit/assets/gitlab/import.png
new file mode 100644
index 0000000..91318ff
Binary files /dev/null and b/src/studienarbeit/assets/gitlab/import.png differ
diff --git a/src/studienarbeit/assets/gitlab/issue.png b/src/studienarbeit/assets/gitlab/issue.png
new file mode 100644
index 0000000..3fb10e8
Binary files /dev/null and b/src/studienarbeit/assets/gitlab/issue.png differ
diff --git a/src/studienarbeit/assets/gitlab/kanban.png b/src/studienarbeit/assets/gitlab/kanban.png
new file mode 100644
index 0000000..ba830d6
Binary files /dev/null and b/src/studienarbeit/assets/gitlab/kanban.png differ
diff --git a/src/studienarbeit/assets/gitlab/runners.png b/src/studienarbeit/assets/gitlab/runners.png
new file mode 100644
index 0000000..7af0ac4
Binary files /dev/null and b/src/studienarbeit/assets/gitlab/runners.png differ
diff --git a/src/studienarbeit/assets/gitlab/web_ide.png b/src/studienarbeit/assets/gitlab/web_ide.png
new file mode 100644
index 0000000..315d302
Binary files /dev/null and b/src/studienarbeit/assets/gitlab/web_ide.png differ
diff --git a/src/studienarbeit/assets/onedev/add_agent.png b/src/studienarbeit/assets/onedev/add_agent.png
new file mode 100644
index 0000000..de4ef57
Binary files /dev/null and b/src/studienarbeit/assets/onedev/add_agent.png differ
diff --git a/src/studienarbeit/assets/onedev/build.png b/src/studienarbeit/assets/onedev/build.png
new file mode 100644
index 0000000..5edff29
Binary files /dev/null and b/src/studienarbeit/assets/onedev/build.png differ
diff --git a/src/studienarbeit/assets/onedev/ci_editor.png b/src/studienarbeit/assets/onedev/ci_editor.png
new file mode 100644
index 0000000..215d921
Binary files /dev/null and b/src/studienarbeit/assets/onedev/ci_editor.png differ
diff --git a/src/studienarbeit/assets/onedev/code_navigation.png b/src/studienarbeit/assets/onedev/code_navigation.png
new file mode 100644
index 0000000..b4d6315
Binary files /dev/null and b/src/studienarbeit/assets/onedev/code_navigation.png differ
diff --git a/src/studienarbeit/assets/onedev/docker-compose.yml b/src/studienarbeit/assets/onedev/docker-compose.yml
new file mode 100644
index 0000000..c35eabe
--- /dev/null
+++ b/src/studienarbeit/assets/onedev/docker-compose.yml
@@ -0,0 +1,19 @@
+version: "3"
+services:
+ onedev:
+ image: 1dev/server:latest
+ ports:
+ - 6610:6610
+ - 6611:6611
+ volumes:
+ - ./data:/opt/onedev
+ agent:
+ image: 1dev/agent:latest
+ environment:
+ - "serverUrl=http://onedev:6610"
+ - "agentToken=3922e711-8a7f-4dd0-8b68-fd3ca891ffff"
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ - ./work:/agent/work
+ depends_on:
+ - onedev
diff --git a/src/studienarbeit/assets/onedev/homepage.png b/src/studienarbeit/assets/onedev/homepage.png
new file mode 100644
index 0000000..e577248
Binary files /dev/null and b/src/studienarbeit/assets/onedev/homepage.png differ
diff --git a/src/studienarbeit/assets/onedev/kanban.png b/src/studienarbeit/assets/onedev/kanban.png
new file mode 100644
index 0000000..027163e
Binary files /dev/null and b/src/studienarbeit/assets/onedev/kanban.png differ
diff --git a/src/studienarbeit/assets/onedev/search.png b/src/studienarbeit/assets/onedev/search.png
new file mode 100644
index 0000000..1df4329
Binary files /dev/null and b/src/studienarbeit/assets/onedev/search.png differ
diff --git a/src/studienarbeit/assets/sourcehut/failed_build.png b/src/studienarbeit/assets/sourcehut/failed_build.png
new file mode 100644
index 0000000..71e8dd2
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/failed_build.png differ
diff --git a/src/studienarbeit/assets/sourcehut/homepage.png b/src/studienarbeit/assets/sourcehut/homepage.png
new file mode 100644
index 0000000..10d0d46
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/homepage.png differ
diff --git a/src/studienarbeit/assets/sourcehut/hub.png b/src/studienarbeit/assets/sourcehut/hub.png
new file mode 100644
index 0000000..faabd0e
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/hub.png differ
diff --git a/src/studienarbeit/assets/sourcehut/oauth.png b/src/studienarbeit/assets/sourcehut/oauth.png
new file mode 100644
index 0000000..3ae6499
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/oauth.png differ
diff --git a/src/studienarbeit/assets/sourcehut/oauth_new_client.png b/src/studienarbeit/assets/sourcehut/oauth_new_client.png
new file mode 100644
index 0000000..efdbdc7
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/oauth_new_client.png differ
diff --git a/src/studienarbeit/assets/sourcehut/repo.png b/src/studienarbeit/assets/sourcehut/repo.png
new file mode 100644
index 0000000..1f7629c
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/repo.png differ
diff --git a/src/studienarbeit/assets/sourcehut/send_patchset.png b/src/studienarbeit/assets/sourcehut/send_patchset.png
new file mode 100644
index 0000000..43ca636
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/send_patchset.png differ
diff --git a/src/studienarbeit/assets/sourcehut/sourcehut.dio b/src/studienarbeit/assets/sourcehut/sourcehut.dio
new file mode 100644
index 0000000..df756ce
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut/sourcehut.dio
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/studienarbeit/assets/sourcehut/sourcehut.png b/src/studienarbeit/assets/sourcehut/sourcehut.png
new file mode 100644
index 0000000..8d7c4e7
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/sourcehut.png differ
diff --git a/src/studienarbeit/assets/sourcehut/sourcehut_arch.svg b/src/studienarbeit/assets/sourcehut/sourcehut_arch.svg
new file mode 100644
index 0000000..eb31e5c
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut/sourcehut_arch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/studienarbeit/assets/sourcehut/successful_build.png b/src/studienarbeit/assets/sourcehut/successful_build.png
new file mode 100644
index 0000000..edceae8
Binary files /dev/null and b/src/studienarbeit/assets/sourcehut/successful_build.png differ
diff --git a/src/studienarbeit/assets/sourcehut_setup/config.ini b/src/studienarbeit/assets/sourcehut_setup/config.ini
new file mode 100644
index 0000000..09f1155
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut_setup/config.ini
@@ -0,0 +1,523 @@
+[sr.ht]
+# The name of your network of sr.ht-based sites
+site-name=sourcehut
+#
+# The top-level info page for your site
+site-info=https://sourcehut.org
+#
+# {{ site-name }}, {{ site-blurb }}
+site-blurb=the hacker's forge
+#
+# If this != production, we add a banner to each page
+environment=production
+#
+# Contact information for the site owners
+owner-name=Drew DeVault
+owner-email=sir@cmpwn.com
+#
+# The source code for your fork of sr.ht
+source-url=https://git.sr.ht/~sircmpwn/srht
+#
+# Link to your instance's privacy policy. Uses the sr.ht privacy policy as the
+# default, which describes the information collected by the upstream SourceHut
+# code.
+privacy-policy=
+#
+# A key used for encrypting session cookies. Use `srht-keygen service` to
+# generate the service key. This must be shared between each node of the same
+# service (e.g. git1.sr.ht and git2.sr.ht), but different services may use
+# different keys. If you configure all of your services with the same
+# config.ini, you may use the same service-key for all of them.
+service-key=9654c9ec7bedab6321a7f29f8b1b607ba023ddb9e248a67e10c49122e59a335f
+#
+# A secret key to encrypt internal messages with. Use `srht-keygen network` to
+# generate this key. It must be consistent between all services and nodes.
+network-key=iZnfEOgNVuCw6uGPQx1LE6eKVd20GXnd9IgCD3wxKJA=
+#
+# The redis host URL. This is used for caching and temporary storage, and must
+# be shared between nodes (e.g. git1.sr.ht and git2.sr.ht), but need not be
+# shared between services. It may be shared between services, however, with no
+# ill effect, if this better suits your infrastructure.
+redis-host=redis://localhost:6379
+#
+# Optional email address for reporting security-related issues.
+security-address=
+#
+# The global domain of the site. If unset, the global domain will be determined
+# from the service URL: each service is assumed to be a sub-domain of the global
+# domain, i.e. of the form `meta.globaldomain.com`.
+global-domain=codeforge.int
+
+[mail]
+# Outgoing SMTP settings
+smtp-host=
+smtp-port=
+smtp-from=user@example.org
+#
+# Default: starttls
+# Options: starttls, tls, insecure
+smtp-encryption=tls
+#
+# Default: plain
+# Options: plain, none
+smtp-auth=plain
+# user / password are required if smtp-auth is plain
+smtp-user=user@example.org
+smtp-password=pass
+
+pgp-privkey=/etc/sr.ht/gpg/privkey.gpg
+pgp-pubkey=/etc/sr.ht/gpg/pubkey.gpg
+pgp-key-id=E77640E3C9BA474C9C0791547298D360EE7DB9B4
+
+[webhooks]
+#
+# base64-encoded Ed25519 key for signing webhook payloads. This should be
+# consistent between all services.
+#
+# Use the `srht-keygen webhook` command to generate this key. Put the private
+# key here and distribute the public key to anyone who would want to verify
+# webhook payloads from your service.
+private-key=FMCFMBL9HX3OHkYFoDUetJzxNfHw0vwWgLFKACXdoCY=
+
+[meta.sr.ht]
+#
+# URL meta.sr.ht is being served at (protocol://domain)
+origin=http://meta.codeforge.int
+# Address and port to bind the debug server to
+# debug-host=127.0.0.1
+# debug-port=5000
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://meta:1234@localhost/meta.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# The redis connection used for the webhooks worker
+webhooks=redis://localhost:6379/1
+#
+# If "yes", the user will be sent the stock sourcehut welcome emails after
+# signup (requires cron to be configured properly). These are specific to the
+# sr.ht instance so you probably want to patch these before enabling this.
+welcome-emails=no
+
+#
+# Origin URL for the API
+# By default, the API port is 100 more than the web port
+api-origin=http://localhost:5100
+
+[meta.sr.ht::api]
+#
+# Maximum complexity of GraphQL queries. The higher this number, the more work
+# that API clients can burden the API backend with. Complexity is equal to the
+# number of discrete fields which would be returned to the user. 200 is a good
+# default.
+max-complexity=200
+
+#
+# The maximum time the API backend will spend processing a single API request.
+#
+# See https://golang.org/pkg/time/#ParseDuration
+max-duration=3s
+
+#
+# Set of IP subnets which are permitted to utilize internal API
+# authentication. This should be limited to the subnets from which your
+# *.sr.ht services are running.
+#
+# Comma-separated, CIDR notation.
+internal-ipnet=127.0.0.0/8,::1/128,192.168.0.0/16,10.0.0.0/8
+
+[meta.sr.ht::settings]
+#
+# If "no", public registration will not be permitted.
+registration=yes
+#
+# Where to redirect new users upon registration
+onboarding-redirect=http://codeforge.int
+
+[meta.sr.ht::aliases]
+#
+# You can add aliases for the client IDs of commonly used OAuth clients here.
+#
+# Example:
+# git.sr.ht=12345
+
+[meta.sr.ht::billing]
+#
+# "yes" to enable the billing system
+enabled=no
+#
+# Get your keys at https://dashboard.stripe.com/account/apikeys
+stripe-public-key=
+stripe-secret-key=
+
+[meta.sr.ht::auth]
+#
+# What authentication method to use.
+# builtin: use sr.ht builtin authentication
+# unix-pam: use Unix PAM authentication
+auth-method=builtin
+
+[hub.sr.ht]
+#
+# URL hub.sr.ht is being served at (protocol://domain)
+origin=http://codeforge.int
+#
+# Address and port to bind the debug server to
+# debug-host=127.0.0.1
+# debug-port=5014
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://hub:1234@localhost/hub.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# hub.sr.ht's OAuth client ID and secret for meta.sr.ht
+# Register your client at meta.example.org/oauth
+oauth-client-id=712f709b315e4441
+oauth-client-secret=a186072ee9900662841188857777f44f
+
+[git.sr.ht]
+#
+# URL git.sr.ht is being served at (protocol://domain)
+origin=http://git.codeforge.int
+#
+# Address and port to bind the debug server to
+#debug-host=0.0.0.0
+#debug-port=5001
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://git:1234@localhost/git.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# The redis connection used for the webhooks worker
+webhooks=redis://localhost:6379/1
+#
+# A post-update script which is installed in every git repo.
+post-update-script=/usr/bin/gitsrht-update-hook
+#
+# git.sr.ht's OAuth client ID and secret for meta.sr.ht
+# Register your client at meta.example.org/oauth
+oauth-client-id=33e27e614a5a6c99
+oauth-client-secret=e128d76adb42d64290381facab1a9db1
+#
+# Path to git repositories on disk
+repos=/var/lib/git/
+#
+# Configure the S3 bucket and prefix for object storage. Leave empty to disable
+# object storage. Bucket is required to enable object storage; prefix is
+# optional.
+#s3-bucket=
+#s3-prefix=
+#
+# Required for preparing and sending patchsets from git.sr.ht
+outgoing-domain=
+
+#
+# Origin URL for the API
+# Only needed if not run behind a reverse proxy, e.g. for local development.
+# By default, the API port is 100 more than the web port
+#api-origin=http://localhost:5101
+
+[git.sr.ht::api]
+#
+# Maximum complexity of GraphQL queries. The higher this number, the more work
+# that API clients can burden the API backend with. Complexity is equal to the
+# number of discrete fields which would be returned to the user. 200 is a good
+# default.
+max-complexity=200
+
+#
+# The maximum time the API backend will spend processing a single API request.
+#
+# See https://golang.org/pkg/time/#ParseDuration
+max-duration=3s
+
+#
+# Set of IP subnets which are permitted to utilize internal API
+# authentication. This should be limited to the subnets from which your
+# *.sr.ht services are running.
+#
+# Comma-separated, CIDR notation.
+internal-ipnet=127.0.0.0/8,::1/128,192.168.0.0/16,10.0.0.0/8
+
+[git.sr.ht::dispatch]
+#
+# The authorized keys hook uses this to dispatch to various handlers
+# The format is a program to exec into as the key, and the user to match as the
+# value. When someone tries to log in as this user, this program is executed
+# and is expected to omit an AuthorizedKeys file.
+#
+# Uncomment the relevant lines to enable the various sr.ht dispatchers.
+/usr/bin/gitsrht-keys=git:git
+/usr/bin/buildsrht-keys=builds:builds
+
+[todo.sr.ht]
+#
+# URL todo.sr.ht is being served at (protocol://domain)
+origin=http://todo.codeforge.int
+#
+# Address and port to bind the debug server to
+#debug-host=0.0.0.0
+#debug-port=5003
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://todo:1234@localhost/todo.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# todo.sr.ht's OAuth client ID and secret for meta.sr.ht
+# Register your client at meta.example.org/oauth
+oauth-client-id=215b592aa1e401b6
+oauth-client-secret=05f0d7ba973c6e4f2e0b5c95f00510f4
+#
+# Outgoing email for notifications generated by users
+notify-from=user@example.org
+#
+# The redis connection used for the webhooks worker
+webhooks=redis://localhost:6379/1
+#
+# Origin URL for the API
+# Only needed if not run behind a reverse proxy, e.g. for local development.
+# By default, the API port is 100 more than the web port
+#api-origin=http://localhost:5103
+
+[todo.sr.ht::mail]
+#
+# Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
+# Alternatively, specify IP:PORT and an SMTP server will be run instead.
+sock=/tmp/todo.sr.ht-lmtp.sock
+#
+# The lmtp daemon will make the unix socket group-read/write for users in this
+# group.
+sock-group=postfix
+#
+# Fill this in with the name of the domain to which emails should be sent.
+# Leave blank to disable email submission.
+posting-domain=
+
+[lists.sr.ht]
+#
+# URL lists.sr.ht is being served at (protocol://domain)
+origin=http://lists.codeforge.int
+#
+# Address and port to bind the debug server to
+#debug-host=0.0.0.0
+#debug-port=5006
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://lists:1234@localhost/lists.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# The redis connection used for the webhooks worker
+webhooks=redis://localhost:6379/1
+#
+# The redis connection used for the Celery worker (configure this on both the
+# master and workers)
+redis=redis://localhost:6379/0
+#
+# The domain that incoming email should be sent to. Forward mail sent here to
+# the LTMP socket.
+posting-domain=lists.codeforge.int
+#
+# lists.sr.ht's OAuth client ID and secret for meta.sr.ht
+# Register your client at meta.example.org/oauth
+oauth-client-id=95b6e656251e01f9
+oauth-client-secret=c6a53f7ac6e242dcbae198b738ce9c9a
+#
+# Trusted upstream SMTP server generating Authentication-Results header fields
+msgauth-server=mail.sr.ht.local
+#
+# If "no", prevents non-admins from creating new lists
+allow-new-lists=yes
+#
+# Origin URL for the API
+# Only needed if not run behind a reverse proxy, e.g. for local development.
+# By default, the API port is 100 more than the web port
+#api-origin=http://localhost:5106
+
+[lists.sr.ht::worker]
+# Protocol used by the daemon. Either lmtp or smtp. By default ltmp if using
+# unix socket and smtp if using tcp socket.
+protocol=lmtp
+#
+# Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
+# Alternatively, specify IP:PORT will run the server using tcp.
+sock=/tmp/lists.sr.ht-lmtp.sock
+#
+# The lmtp daemon will make the unix socket group-read/write for users in this
+# group.
+sock-group=postfix
+#
+# Comma-delimited list of Content-Types to reject. Messages with Content-Types
+# included in this list are rejected. Multipart messages are always supported,
+# and each part is checked against this list.
+#
+# Uses fnmatch for wildcard expansion.
+reject-mimetypes=
+#
+# Link to include in the rejection message where senders can get help
+# correcting their email.
+reject-url=https://man.sr.ht/lists.sr.ht/etiquette.md
+
+[lists.sr.ht::api]
+#
+# Maximum complexity of GraphQL queries. The higher this number, the more work
+# that API clients can burden the API backend with. Complexity is equal to the
+# number of discrete fields which would be returned to the user. 200 is a good
+# default.
+max-complexity=200
+
+#
+# The maximum time the API backend will spend processing a single API request.
+#
+# See https://golang.org/pkg/time/#ParseDuration
+max-duration=90s
+
+#
+# Set of IP subnets which are permitted to utilize internal API
+# authentication. This should be limited to the subnets from which your
+# *.sr.ht services are running.
+#
+# Comma-separated, CIDR notation.
+internal-ipnet=127.0.0.0/8,::1/128,192.168.0.0/16,10.0.0.0/8
+
+[lists.sr.ht::redirects]
+#
+# Redirects for migrating old mailing lists to new ones. This just sets up the
+# redirect for incoming emails.
+#
+# old-address=~example/new-name
+
+[man.sr.ht]
+#
+# URL man.sr.ht is being served at (protocol://domain)
+origin=http://man.codeforge.int
+#
+# Address and port to bind the debug server to
+#debug-host=0.0.0.0
+#debug-port=5004
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://man:1234@localhost/man.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# man.sr.ht's OAuth client ID and secret for meta.sr.ht
+# Register your client at meta.example.org/oauth
+oauth-client-id=96319d48a50cdfe5
+oauth-client-secret=e405e716b052402c88035c6ab429e6d3
+
+[builds.sr.ht]
+#
+# URL builds.sr.ht is being served at (protocol://domain)
+origin=http://builds.codeforge.int
+#
+# Address and port to bind the debug server to
+#debug-host=0.0.0.0
+#debug-port=5002
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://builds:1234@localhost/builds.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# The redis connection used for the Celery worker (configure this on both the
+# master and workers)
+redis=redis://localhost:6379/0
+#
+# builds.sr.ht's OAuth client ID and secret for meta.sr.ht
+# Register your client at meta.example.org/oauth
+oauth-client-id=ecfda530d17ae5c3
+oauth-client-secret=68176355b4de8865163748e0a1ec1551
+#
+# Script used to launch on ssh connnection. /usr/bin/master-shell on master,
+# /usr/bin/runner-shell for workers.
+# If master and worker are on the same system set to /usr/bin/runner-shell
+shell=/usr/bin/runner-shell
+#
+# Set to "yes" to allow nonpaying users to submit builds
+allow-free=yes
+#
+# Origin URL for the API
+# Only needed if not run behind a reverse proxy, e.g. for local development.
+# By default, the API port is 100 more than the web port
+#api-origin=http://localhost:5102
+
+#
+# These config options are only necessary for systems running a build runner
+[builds.sr.ht::worker]
+#
+# Name of this build runner (with HTTP port if not 80)
+name=runner.codeforge.int
+#
+# Path to write build logs
+buildlogs=/var/log/builds
+#
+# Path to the build images
+images=/var/lib/images
+#
+# In production you should NOT put the build user in the docker group. Instead,
+# make a scratch user who is and write a sudoers or doas.conf file that allows
+# them to execute just the control command, then update this config option. For
+# example:
+#
+# doas -u docker /var/lib/images/control
+#
+# Assuming doas.conf looks something like this:
+#
+# permit nopass builds as docker cmd /var/lib/images/control
+#
+# For more information about the security model of builds.sr.ht, visit the wiki:
+#
+# https://man.sr.ht/builds.sr.ht/installation.md
+controlcmd=sudo -u docker /var/lib/images/control
+#
+# Max build duration. See https://golang.org/pkg/time/#ParseDuration
+timeout=45m
+#
+# Http bind address for serving local build information/monitoring
+bind-address=127.0.0.1:8080
+#
+# Build trigger email
+trigger-from=user@example.org
+#
+# Configure the S3 bucket and prefix for object storage. Leave empty to disable
+# object storage. Bucket is required to enable object storage; prefix is
+# optional.
+s3-bucket=
+s3-prefix=
+
+[paste.sr.ht]
+#
+# URL paste.sr.ht is being served at (protocol://domain)
+origin=http://paste.codeforge.int
+#
+# Address and port to bind the debug server to
+#debug-host=0.0.0.0
+#debug-port=5011
+#
+# Configures the SQLAlchemy connection string for the database.
+connection-string=postgresql://paste:1234@localhost/paste.sr.ht?sslmode=disable
+#
+# Set to "yes" to automatically run migrations on package upgrade.
+migrate-on-upgrade=yes
+#
+# paste.sr.ht's OAuth client ID and secret for meta.sr.ht
+# Register your client at meta.example.org/oauth
+oauth-client-id=b930097f0d5666fb
+oauth-client-secret=ee749b58b1ff808f3bc095a7e5ea4f9a
+#
+# Origin URL for the API
+# Only needed if not run behind a reverse proxy, e.g. for local development.
+# By default, the API port is 100 more than the web port
+#api-origin=http://localhost:5111
diff --git a/src/studienarbeit/assets/sourcehut_setup/nginx/graphql.conf b/src/studienarbeit/assets/sourcehut_setup/nginx/graphql.conf
new file mode 100644
index 0000000..cc990ca
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut_setup/nginx/graphql.conf
@@ -0,0 +1,20 @@
+real_ip_header X-Forwarded-For;
+real_ip_recursive on;
+proxy_set_header Host $host;
+proxy_set_header X-Forwarded-Proto https;
+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+if ($request_method = 'OPTIONS') {
+ add_header 'Access-Control-Allow-Origin' '*';
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+ add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
+ add_header 'Access-Control-Max-Age' 1728000;
+ add_header 'Content-Type' 'text/plain; charset=utf-8';
+ add_header 'Content-Length' 0;
+ return 204;
+}
+
+add_header 'Access-Control-Allow-Origin' '*';
+add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
+add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
diff --git a/src/studienarbeit/assets/sourcehut_setup/nginx/nginx_services.conf b/src/studienarbeit/assets/sourcehut_setup/nginx/nginx_services.conf
new file mode 100644
index 0000000..0ac98c6
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut_setup/nginx/nginx_services.conf
@@ -0,0 +1,223 @@
+# Meta
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name meta.codeforge.int;
+
+ location / {
+ proxy_pass http://127.0.0.1:5000;
+ include web.conf;
+ }
+
+ location /.well-known/oauth-authorization-server {
+ proxy_pass http://127.0.0.1:5000;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5100;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/metasrht;
+ expires 30d;
+ }
+}
+
+# Hub
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name codeforge.int;
+
+ location / {
+ proxy_pass http://127.0.0.1:5014;
+ include web.conf;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5114;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/hubsrht;
+ expires 30d;
+ }
+}
+
+# Git
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name git.codeforge.int;
+
+ location / {
+ proxy_pass http://127.0.0.1:5001;
+ include web.conf;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5101;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/gitsrht;
+ expires 30d;
+ }
+
+ location = /authorize {
+ proxy_pass http://127.0.0.1:5001;
+ proxy_pass_request_body off;
+ proxy_set_header Content-Length "";
+ proxy_set_header X-Original-URI $request_uri;
+ }
+
+ location ~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$ {
+ auth_request /authorize;
+ root /var/lib/git;
+ fastcgi_pass unix:/run/fcgiwrap/fcgiwrap.sock;
+ fastcgi_param SCRIPT_FILENAME /usr/libexec/git-core/git-http-backend;
+ fastcgi_param PATH_INFO $uri;
+ fastcgi_param GIT_PROJECT_ROOT $document_root;
+ fastcgi_read_timeout 500s;
+ include fastcgi_params;
+ gzip off;
+ }
+}
+
+# Man
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name man.codeforge.int;
+
+ client_max_body_size 100M;
+
+ location / {
+ proxy_pass http://127.0.0.1:5004;
+ include web.conf;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5104;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/mansrht;
+ expires 30d;
+ }
+}
+
+# Todo
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name todo.codeforge.int;
+
+ client_max_body_size 100M;
+
+ location / {
+ proxy_pass http://127.0.0.1:5003;
+ include web.conf;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5103;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/todosrht;
+ expires 30d;
+ }
+}
+
+# Lists
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name lists.codeforge.int;
+
+ client_max_body_size 100M;
+
+ location / {
+ proxy_pass http://127.0.0.1:5006;
+ include web.conf;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5106;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/listssrht;
+ expires 30d;
+ }
+}
+
+# Paste
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name paste.codeforge.int;
+
+ client_max_body_size 10M;
+
+ location / {
+ proxy_pass http://127.0.0.1:5011;
+ include web.conf;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5111;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/pastesrht;
+ expires 30d;
+ }
+}
+
+# Builds
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name builds.codeforge.int;
+
+ client_max_body_size 100M;
+
+ location / {
+ proxy_pass http://127.0.0.1:5002;
+ include web.conf;
+ }
+
+ location /query {
+ proxy_pass http://127.0.0.1:5102;
+ include graphql.conf;
+ }
+
+ location /static {
+ root /usr/lib/$python/site-packages/buildsrht;
+ expires 30d;
+ }
+}
+
+# Runner (proxy only local requests)
+server {
+ include sourcehut.conf;
+ include port80.conf;
+ server_name runner.codeforge.int;
+
+ location / {
+ proxy_pass http://127.0.0.1:8080;
+ include web.conf;
+
+ allow 127.0.0.1;
+ deny all;
+ }
+}
diff --git a/src/studienarbeit/assets/sourcehut_setup/nginx/port80.conf b/src/studienarbeit/assets/sourcehut_setup/nginx/port80.conf
new file mode 100644
index 0000000..4e2f720
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut_setup/nginx/port80.conf
@@ -0,0 +1,10 @@
+listen 80;
+listen [::]:80;
+
+location ^~ /.well-known {
+ root /var/www;
+}
+
+location = /robots.txt {
+ root /var/www;
+}
diff --git a/src/studienarbeit/assets/sourcehut_setup/nginx/sourcehut.conf b/src/studienarbeit/assets/sourcehut_setup/nginx/sourcehut.conf
new file mode 100644
index 0000000..dec2564
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut_setup/nginx/sourcehut.conf
@@ -0,0 +1,3 @@
+set $python "python3.10";
+set_real_ip_from 127.0.0.0/16;
+set_real_ip_from 173.195.146.128/25;
diff --git a/src/studienarbeit/assets/sourcehut_setup/nginx/web.conf b/src/studienarbeit/assets/sourcehut_setup/nginx/web.conf
new file mode 100644
index 0000000..cee7b79
--- /dev/null
+++ b/src/studienarbeit/assets/sourcehut_setup/nginx/web.conf
@@ -0,0 +1,5 @@
+real_ip_header X-Forwarded-For;
+real_ip_recursive on;
+proxy_set_header Host $http_host;
+proxy_set_header X-Forwarded-Proto https;
+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/src/studienarbeit/assets/woodpecker/docker-compose.yml b/src/studienarbeit/assets/woodpecker/docker-compose.yml
new file mode 100644
index 0000000..add0cd6
--- /dev/null
+++ b/src/studienarbeit/assets/woodpecker/docker-compose.yml
@@ -0,0 +1,48 @@
+version: "3"
+services:
+ woodpecker-server:
+ image: woodpeckerci/woodpecker-server:next
+ restart: unless-stopped
+ networks:
+ - proxy
+ environment:
+ - WOODPECKER_OPEN=true
+ - WOODPECKER_HOST=https://ci.thetadev.de
+ - WOODPECKER_GITEA=true
+ - WOODPECKER_GITEA_URL=https://code.thetadev.de
+ - WOODPECKER_GITEA_CLIENT=48060801-dc04-4361-b0a9-eefbfc6f29d0
+ - WOODPECKER_GITEA_SECRET=gto_wefjljwg4ouio34goi4ui34ui34uighjrfvnerrejrekwhgui34g4
+ - WOODPECKER_AGENT_SECRET=f81909f8e70371af65a6e1c6e77d35d4677c3ed242dcadde032fb27a9c5f9c35
+ labels:
+ - "traefik.enable=true"
+ - "traefik.docker.network=proxy"
+ - "traefik.http.routers.woodpecker.entrypoints=websecure"
+ - "traefik.http.routers.woodpecker.rule=Host(`ci.thetadev.de`)"
+ - "traefik.http.routers.woodpecker.service=woodpecker"
+ - "traefik.http.services.woodpecker.loadbalancer.server.port=8000"
+
+ # gRPC service
+ - "traefik.http.routers.woodpecker-grpc.entrypoints=websecure"
+ - "traefik.http.routers.woodpecker-grpc.rule=Host(`ci-grpc.thetadev.de`)"
+ - "traefik.http.routers.woodpecker-grpc.service=woodpecker-grpc"
+ - "traefik.http.services.woodpecker-grpc.loadbalancer.server.port=9000"
+ - "traefik.http.services.woodpecker-grpc.loadbalancer.server.scheme=h2c"
+
+ woodpecker-agent:
+ image: woodpeckerci/woodpecker-agent:next
+ command: agent
+ restart: unless-stopped
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ - /etc/localtime:/etc/localtime:ro
+ environment:
+ - "WOODPECKER_SERVER=ci-grpc.thetadev.de:443"
+ - "WOODPECKER_AGENT_SECRET=f81909f8e70371af65a6e1c6e77d35d4677c3ed242dcadde032fb27a9c5f9c35"
+ - "WOODPECKER_MAX_PROCS=2"
+ - "WOODPECKER_HOSTNAME=ocloud"
+ - WOODPECKER_GRPC_SECURE=true
+ - WOODPECKER_GRPC_VERIFY=true
+
+networks:
+ proxy:
+ external: true
diff --git a/src/studienarbeit/sourcehut_setup.md b/src/studienarbeit/sourcehut_setup.md
new file mode 100644
index 0000000..113dbd8
--- /dev/null
+++ b/src/studienarbeit/sourcehut_setup.md
@@ -0,0 +1,512 @@
+# Anhang: Installation von Sourcehut
+
+Da ich im Internet keine vollständige Installationsanleitung für Sourcehut gefunden
+habe, beschreibe ich hier den vollständigen Installationsvorgang für meine Testinstanz.
+
+Ich habe Sourcehut wie vom Entwickler empfohlen auf einer Alpine-Linux-VM installiert.
+Die Sourcehut-Pakete werden auch für Arch und Debian (Sid) angeboten.
+
+Alle aufgelisteten Befehle müssen als Root-User ausgeführt werden, sofern es nicht
+anders beschrieben ist.
+
+## Sourcehut-Repo
+
+Zuerst muss das Sourcehut-Repository zum Paketmanager des Systems hinzugefügt werden.
+Die URLs für verschiedene Distributionen sind in der Dokumentation des Projekts
+angegeben.
+
+
+
+```
+echo "https://mirror.sr.ht/alpine/v3.17/sr.ht" > /etc/apk/repositories
+
+wget -q -O /etc/apk/keys/alpine@sr.ht.rsa.pub https://mirror.sr.ht/alpine/alpine@sr.ht.rsa.pub
+
+apk update
+```
+
+## Grundlegende Dienste
+
+Zu Beginn müssen die grundlegenden Dienste eingerichtet werden: die Postgres-Datenbank,
+Redis und ein Nginx-Webserver, der als Reverse Proxy zwischen dem Nutzer und den
+einzelnen Diensten steht.
+
+Beim Einrichten der Dienste ist zu beachten, dass Alpine Linux OpenRC anstelle des
+systemd-Init-Systems verwendet und alle neu installierten Dienste manuell aktiviert und
+gestartet werden müssen.
+
+```
+apk add postgresql redis nginx
+
+rc-service postgresql setup
+rc-update add postgresql
+rc-service postgresql start
+
+rc-update add nginx
+rc-update add redis
+rc-service nginx start
+rc-service redis start
+```
+
+Wenn Postgres läuft, kann man die Datenbanken und Nutzer für die einzelnen Dienste
+erstellen.
+
+```
+su postgres
+psql
+CREATE USER meta WITH PASSWORD '1234';
+CREATE DATABASE "meta.sr.ht";
+ALTER DATABASE "meta.sr.ht" OWNER TO meta;
+GRANT ALL PRIVILEGES ON DATABASE "meta.sr.ht" TO meta;
+
+CREATE USER hub WITH PASSWORD '1234';
+CREATE DATABASE "hub.sr.ht";
+ALTER DATABASE "hub.sr.ht" OWNER TO hub;
+GRANT ALL PRIVILEGES ON DATABASE "hub.sr.ht" TO hub;
+
+CREATE USER git WITH PASSWORD '1234';
+CREATE DATABASE "git.sr.ht";
+ALTER DATABASE "git.sr.ht" OWNER TO git;
+GRANT ALL PRIVILEGES ON DATABASE "git.sr.ht" TO git;
+
+CREATE USER man WITH PASSWORD '1234';
+CREATE DATABASE "man.sr.ht";
+ALTER DATABASE "man.sr.ht" OWNER TO man;
+GRANT ALL PRIVILEGES ON DATABASE "man.sr.ht" TO man;
+
+CREATE USER todo WITH PASSWORD '1234';
+CREATE DATABASE "todo.sr.ht";
+ALTER DATABASE "todo.sr.ht" OWNER TO todo;
+GRANT ALL PRIVILEGES ON DATABASE "todo.sr.ht" TO todo;
+
+CREATE USER lists WITH PASSWORD '1234';
+CREATE DATABASE "lists.sr.ht";
+ALTER DATABASE "lists.sr.ht" OWNER TO lists;
+GRANT ALL PRIVILEGES ON DATABASE "lists.sr.ht" TO lists;
+
+CREATE USER paste WITH PASSWORD '1234';
+CREATE DATABASE "paste.sr.ht";
+ALTER DATABASE "paste.sr.ht" OWNER TO paste;
+GRANT ALL PRIVILEGES ON DATABASE "paste.sr.ht" TO paste;
+
+CREATE USER builds WITH PASSWORD '1234';
+CREATE DATABASE "builds.sr.ht";
+ALTER DATABASE "builds.sr.ht" OWNER TO builds;
+GRANT ALL PRIVILEGES ON DATABASE "builds.sr.ht" TO builds;
+```
+
+Alle Sourcehut-Dienste werden über eine einzige Datei unter `/etc/sr.ht/config.ini`
+konfiguriert. Sourcehut stellte jedoch keine vollständige Beispielkonfiguration bereit,
+weswegen ich meine Konfiguration aus den einzelnen Repositories zusammenkopieren musste.
+
+-
+-
+-
+-
+-
+-
+-
+-
+
+[Beispielkonfiguration](./assets/sourcehut_setup/config.ini)
+
+Folgende Optionen müssen angepasst werden:
+
+- Die Domains/URLs der Webseiten (in meiner Beispielkonfiguration `*.codeforge.int`).
+- Datenbank-URLs/Passwörter (Option: `connection-string`)
+- GPG-Keys (ein GPG-Schlüsselpaar ohne Passwort erstellen und unter `mail/pgp-privkey`,
+ `mail/pgp-pubkey` und `mail/pgp-key-id` angeben)
+- `service-key`, `network-key`, `webhooks/private-key` (siehe meta.sr.ht)
+- OAuth-Client-IDs/Secrets (siehe meta.sr.ht)
+
+Anschließend kann man Nginx so konfigurieren, dass Web-Anfragen an die einzelnen Dienste
+weitergeleitet werden können. Eine Nginx-Beispielkonfiguration findet sich in
+[diesem Repository](https://git.sr.ht/~sircmpwn/sr.ht-nginx/tree) auf Sourcehut.
+
+[Hier](./assets/sourcehut_setup/nginx/nginx_services.conf) befindet sich die angepasste
+Version (ohne HTTPS), die ich für meine Testinstallation verwendet habe.
+
+Noch ein Hinweis für Testinstallationen mit internen Domains: Die konfigurierten
+Domains/URL müssen auf der Maschine, auf der Sourcehut läuft, erreichbar sein. Eventuell
+müssen diese Domains also in die /etc/hosts-Datei eingetragen werden. Ansonsten
+funktioniert die OAuth-Authentifizierung sowie das CI-System nicht.
+
+## Sourcehut-Dienste
+
+Jede einzelne Sourcehut-Anwendung (git.sr.ht, builds.sr.ht, todo.sr.ht, etc) besteht aus
+mehreren Diensten. Dazu zählen das Python-Webfrontend, ein in Go geschriebener
+API-Server und eventuell noch zusätzliche Dienste wie eine Task Queue.
+
+Die Einrichtung einer Sourcehut-Anwendung erfordert folgende Schritte
+
+1. Installation des Pakets
+2. Initialisierung der Datenbank mit dem Skript `-initdb`
+3. Starten der Dienste
+
+Bei einigen Diensten sind noch zusätzliche Konfigurationsanpassungen notwendig.
+
+### meta.sr.ht
+
+- Web-UI: Port 5000
+- API: Port 5100
+
+meta.sr.ht ist der Dienst zur Nutzerverwaltung und Authentisierung und muss deswegen
+zuerst installiert werden.
+
+```
+# Paket installieren
+apk add meta.sr.ht
+
+# Datenbank initialisieren
+metasrht-initdb
+
+# Schlüssel erzeugen und in die Konfigurationsdatei eintragen
+# [sr.ht] > service-key
+srht-keygen service
+# [sr.ht] > network-key
+srht-keygen network
+# [webhooks] > private-key
+srht-keygen webhook
+
+# Dienste starten
+rc-update add meta.sr.ht
+rc-update add meta.sr.ht-api
+rc-update add meta.sr.ht-webhooks
+
+rc-service meta.sr.ht start
+rc-service meta.sr.ht-api start
+rc-service meta.sr.ht-webhooks start
+
+# Admin-Benutzer erstellen
+metasrht-manageuser -t admin -e thetadev@example.com thetadev
+
+# Cronjob hinzufügen
+crontab -e
+0 0 * * * metasrht-daily
+```
+
+Die einzelnen Sourcehut-Dienste verwenden OAuth zur Authentifizierung der Nutzer. Jeder
+Dienst muss hierfür über eine eigene Client-Konfiguration verfügen. Diese lässt sich
+über das (Legacy) OAuth-Dashboard unter `meta./oauth` erstellen.
+
+
+
+Als _Base Redirect URI_ trägt man die URL des Dienstes mit dem Pfad `oauth/callback`
+ein. Beispiel: `https://git.codeforge.int/oauth/callback`.
+
+
+
+Die Client-ID und das Client-Secret, das auf der nächsten Seite angezeigt wird, können
+nun in die Konfigurationsdatei unter `[.sr.ht]/oauth-client-id` und
+`[.sr.ht]/oauth-client-secret` eingetragen werden.
+
+Standardmäßig verlangt Sourcehut nach einer manuellen Einwilligung für die Nutzung eines
+OAuth-Diensts. Um die internen Sourcehut-Dienste als vertrauenswürdig zu markieren und
+den Einwilligungsbildschirm für neue Nutzer zu deaktivieren, muss man eine manuelle
+Änderung an der Datenbank vornehmen und das Feld `preauthorized` für die entsprechenden
+OAuth-Clients auf `true` setzen.
+
+```
+su postgres
+psql
+
+\c "meta.sr.ht"
+UPDATE oauthclient SET preauthorized=true;
+```
+
+### hub.sr.ht
+
+hub.sr.ht fungiert als Startseite für Sourcehut. Hier kann man Projekte erstellen,
+suchen und aufrufen.
+
+- Web-UI: Port 5014
+- API: Port 5114
+
+```
+# hub.src.ht (Port: 5014, API-Port: 5114)
+apk add hub.sr.ht
+
+hubsrht-initdb
+
+rc-update add hub.sr.ht
+rc-update add hub.sr.ht-api
+
+rc-service hub.sr.ht-api start
+rc-service hub.sr.ht start
+```
+
+### git.sr.ht
+
+git.sr.ht ist der eigentliche Git-Hosting-Dienst. Sourcehut bietet auch einen Dienst für
+das Versionskontrollsystem Mercurial an, diesen habe ich jedoch nicht getestet.
+
+- Web-UI: Port 5001
+- API: Port 5101
+
+```
+# Abhängigkeiten für git-http server installieren
+apk add git-sr.ht fcgiwrap git-daemon
+
+gitsrht-initdb
+```
+
+Diese Zeilen müssen zur `/etc/ssh/sshd_config` hinzugrfügt werden. Dadurch kann der
+SSH-Server Nutzer anhand der auf Sourcehut hinterlegten SSH-Zugangsdaten
+authentifizieren.
+
+```
+AuthorizedKeysCommand=/usr/bin/gitsrht-dispatch "%u" "%h" "%t" "%k"
+AuthorizedKeysCommandUser=root
+PermitUserEnvironment SRHT_*
+```
+
+Da SSH den Login von Nutzern mit nicht gesetztem Passwort verbietet, muss der Account
+`git` mit einem beliebigen Passwort versehen werden.
+
+Anschließend muss noch eine Logdatei für das Skript `gitsrht-shell`, dass bei einer
+SSH-Verbindung aufgerufen wird, erstellt werden.
+
+```
+touch /var/log/gitsrht-shell
+chown git:git /var/log/gitsrht-shell
+```
+
+Nachdem alles konfiguriert wurde, kann man den Dienst starten und das Skript
+`gitsrht-periodic` in den crontab eintragen.
+
+```
+rc-update add fcgiwrap
+rc-update add git.sr.ht
+rc-update add git.sr.ht-api
+rc-update add git.sr.ht-webhooks
+
+rc-service fcgiwrap start
+rc-service git.sr.ht-webhooks start
+rc-service git.sr.ht-api start
+rc-service git.sr.ht start
+
+# Cronjob hinzufügen:
+*/20 * * * * gitsrht-periodic
+```
+
+### man.sr.ht
+
+- Web-UI: Port 5004
+- API: Port 5104
+
+man.sr.ht ist ein Git-gestütztes Wiki. Der Dienst hängt von git.sr.ht zur Verwaltung der
+Repositories ab.
+
+```
+apk add man.sr.ht
+
+mansrht-initdb
+
+rc-update add man.sr.ht
+rc-update add man.sr.ht-api
+
+rc-service man.sr.ht-api start
+rc-service man.sr.ht start
+```
+
+### paste.sr.ht
+
+- Web-UI: Port 5011
+- API: Port 5111
+
+paste.sr.ht ist ein simpler Pastebin, mit dem man Codeschnipsel speichern und
+veröffentlichen kann.
+
+```
+apk add paste.sr.ht
+pastesrht-initdb
+
+rc-update add paste.sr.ht
+rc-update add paste.sr.ht-api
+
+rc-service paste.sr.ht-api start
+rc-service paste.sr.ht start
+```
+
+### builds.sr.ht
+
+- Web-UI: Port 5002
+- API: Port 5102
+
+builds.sr.ht ist der Continuous-Integration-Dienst von Sourcehut.
+
+Da der Dienst KVM zur Virtualisierung verwendet, muss auf dem Hostsystem _Nested
+virtualization_ aktiviert werden, sollte der Dienst selbst in einer KVM-gestützten
+virtuellen Maschine installiert werden:
+
+
+builds.sr.ht besteht aus zwei Komponenten: dem Master-Server und dem Worker. Der
+Master-Server verwaltet die Builds, die Worker führen sie aus. Aus Sicherheitsgründen
+wird empfohlen, die Worker auf einem seperaten System zu installieren. Bei meiner
+Testinstallation habe ich jedoch beide Komponenten auf dem selben System installiert.
+
+Zuerst installiert man die erforderlichen Pakete und initialisiert die Datenbank:
+
+```
+# Master-Server
+apk add builds.sr.ht
+# Worker-Server
+builds.sr.ht-worker builds.sr.ht-images
+
+buildsrht-initdb
+```
+
+Der Nutzer `builds` muss genauso wie der git-Nutzer mit einem beliebigen Password
+versehen werden, um den SSH-Zugriff auf fehlgeschlagene Builds zu ermöglichen.
+
+Anschließend kann man den Master-Server starten
+
+```
+rc-update add builds.sr.ht
+rc-update add builds.sr.ht-api
+
+rc-service builds.sr.ht-api start
+rc-service builds.sr.ht start
+```
+
+Der Worker benutzt standardmäßig eine QEMU-Installation in einem Docker-Container, um
+die Build-VMs zu betreiben. Deswegen muss man zuerst Docker installieren und starten.
+
+Zusätzlich sollte man einen neuen Benutzer mit dem Namen `docker` anlegen und der
+Docker-Gruppe hinzufügen. Dann konfiguriert man sudo so, dass der `builds`-Nutzer das
+`/var/lib/images/control`-Skript zum Start der Buildmaschinen als `docker`-Nutzer
+aufrufen kann. Auf diese Weise muss man dem Build-Nutzer keine vollen Root- oder
+Docker-Rechte geben.
+
+```
+apk add docker
+rc-update add docker
+rc-service docker start
+
+sudo adduser -SDH docker docker
+
+visudo
+# Diese Zeile in die sudoers-Datei eintragen
+builds ALL=(docker) NOPASSWD: /var/lib/images/control
+```
+
+```
+cd /var/lib/images
+docker build -t qemu qemu/Dockerfile .
+```
+
+Als nächstes muss man die VM-Images bauen. Im Ordner `/var/lib/images` befinden sich
+Skripte für eine Vielzahl von Betriebssystemen und CPU-Architekturen.
+
+Ich habe zum Testen das Alpine 3.17-Image in einem Docker-Container gebaut. Dazu musste
+ich zunächst eine kleine Annpassung am Buildskript vornehmen. Das Skript unter
+`/var/lib/images/alpine/genimg` enthält den Befehl `modprobe nbd max_part=16`, der in
+einem Docker-Container nicht ausgeführt werden kann. Dieser Befehl muss deshalb
+auskommentiert werden und stattdessen vorher auf dem Hostsystem ausgeführt werden.
+
+Anschließend kann man das Image bauen:
+
+```
+modprobe nbd max_part=16
+docker run -it --rm --privileged -v /var/lib/images:/images alpine:3.17
+
+# Im Docker-Container
+cd /images/alpine/3.17
+apk add qemu-img sfdisk syslinux e2fsprogs
+./genimg
+```
+
+Mit der Datei `/etc/image-control.conf` kann der Worker konfiguriert werden
+(zugewiesener Arbeitsspeicher, Methode der Virtualisierung).
+
+```
+MEMORY=4096
+default_means=docker
+```
+
+Nachdem der Worker konfiguriert wurde, kann man ihn mit dem Skript
+`/var/lib/images/control` testen.
+
+Hinweis: wenn der Container nicht startet, sollte man prüfen, ob das System genug RAM
+zum Start der VM zur Verfügung hat.
+
+```
+./control alpine/latest boot x86_64 12345
+docker ps # Prüfen, ob der Container gestartet wurde
+# Über die SSH-Verbindung kann man nun Befehle auf der Build-VM ausführen
+ssh -p 12345 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no build@localhost
+./control alpine/latest cleanup 12345
+```
+
+Jetzt kann der Worker gestartet werden:
+
+```
+# Start worker
+rc-update add builds.sr.ht-worker
+rc-service builds.sr.ht-worker start
+```
+
+### todo.sr.ht
+
+- Web-UI: Port 5003
+- API: Port 5103
+
+```
+apk add todo.sr.ht
+```
+
+Im Gegensatz zu den anderen Diensten konnte ich die Datenbank für den todo-Dienst nicht
+mit `todosrht-initdb` erfolgreich initialisieren. Offenbar fehlten in diesem Skript
+bestimmte Datenbankobjekte, sodass die Anwendung Fehler lieferte. Stattdessen habe ich
+die `schema.sql`-Datei aus dem sourcehut-Repository verwendet.
+
+```
+wget "https://git.sr.ht/~sircmpwn/todo.sr.ht/blob/master/schema.sql"
+psql -U todo -d todo.sr.ht -f schema.sql
+todo-migrate stamp head
+```
+
+Anschließend können die Dienste gestartet werden.
+
+Hinweis: Der LMTP-Dienst ist dafür da, E-Mails zu empfangen, sodass man auf Issues per
+Mail antworten kann. Hierfür ist ein Mailserver nötig, weswegen ich diesen Dienst in
+meiner Testinstallation nicht vollständig konfigurieren konnte.
+
+```
+rc-update add todo.sr.ht
+rc-update add todo.sr.ht-api
+rc-update add todo.sr.ht-lmtp
+rc-update add todo.sr.ht-webhooks
+
+rc-service todo.sr.ht-webhooks start
+rc-service todo.sr.ht-lmtp start
+rc-service todo.sr.ht-api start
+rc-service todo.sr.ht start
+```
+
+### lists.sr.ht
+
+- Web-UI: Port 5006
+- API: Port 5106
+
+lists.sr.ht ist der Mailingslisten-Dienst für Sourcehut. Mangels eines eigenen
+Mailservers konnte ich diesen Dienst in meiner Test-VM nicht einrichten.
+
+Eine Anleitung für die Mail-Konfiguration findet sich hier:
+
+
+```
+listssrht-initdb
+
+rc-update add lists.sr.ht
+rc-update add lists.sr.ht-api
+rc-update add lists.sr.ht-lmtp
+rc-update add lists.sr.ht-process
+rc-update add lists.sr.ht-webhooks
+
+rc-service lists.sr.ht-webhooks start
+rc-service lists.sr.ht-process start
+rc-service lists.sr.ht-lmtp start
+rc-service lists.sr.ht-api start
+rc-service lists.sr.ht start
+```