From 5b240159f513f587d6b2306099174a9dcf28399e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sat, 24 Oct 2026 10:01:56 +0200 Subject: [PATCH] feat: Initial setup for LaTeX Docker Builder --- .dockerignore | 6 ++ .gitignore | 22 ++++++ Dockerfile | 22 ++++++ Makefile | 34 +++++++++ README.md | 181 +++++++++++++++++++++++++++++++++++++++++++++ build-seminar.ps1 | 113 ++++++++++++++++++++++++++++ build-seminar.sh | 38 ++++++++++ docker-compose.yml | 27 +++++++ entrypoint.sh | 60 +++++++++++++++ 9 files changed, 503 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 build-seminar.ps1 create mode 100644 build-seminar.sh create mode 100644 docker-compose.yml create mode 100644 entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..96fcf39 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +.gitignore +*.md +docker-compose.yml +build-seminar.ps1 +build-seminar.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6111ade --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# OS-Dateien +.DS_Store +Thumbs.db +desktop.ini + +# LaTeX temporäre Dateien (falls hier getestet) +*.aux +*.bbl +*.bcf +*.blg +*.fdb_latexmk +*.fls +*.log +*.out +*.run.xml +*.synctex.gz +*.toc +out/ + +# Lokale Overrides / ENV +.env +.env.local diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..349215a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# ============================================================================== +# LaTeX Docker Builder +# Repository: https://git.djnizz.at/Nizz05/latex-docker.git +# Basis: TeX Live Full (inkl. latexmk, biber, biblatex, koma-script, tikz etc.) +# ============================================================================== + +FROM texlive/texlive:latest + +LABEL org.opencontainers.image.title="LaTeX Docker Builder" \ + org.opencontainers.image.description="Docker Image zum Kompilieren von LaTeX-Dokumenten (Seminararbeit, Thesis) mit latexmk & biber" \ + org.opencontainers.image.source="https://git.djnizz.at/Nizz05/latex-docker" \ + maintainer="Nizz05" + +# Arbeitsverzeichnis für das gemountete Projekt +WORKDIR /workspace + +# Entrypoint-Skript kopieren und ausführbar machen +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] +CMD ["build"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..80f3c42 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +IMAGE ?= latex-docker:latest +REMOTE_IMAGE ?= git.djnizz.at/nizz05/latex-docker:latest +PROJECT_DIR ?= $(CURDIR)/../Seminar_Arbeit_BITI_Gruber + +.PHONY: all build clean watch image push shell help + +all: build + +help: + @echo "Verfügbare Befehle:" + @echo " make image - Baut das Docker-Image ($(IMAGE))" + @echo " make build - Kompiliert die Seminararbeit im Container" + @echo " make clean - Bereinigt temporäre LaTeX-Dateien (latexmk -C)" + @echo " make watch - Startet kontinuierlichen Watch-Modus" + @echo " make shell - Öffnet eine interaktive Bash-Shell im Container" + @echo " make push - Pusht das Image zum Remote Registry ($(REMOTE_IMAGE))" + +image: + docker build -t $(IMAGE) -t $(REMOTE_IMAGE) . + +build: + docker run --rm -v "$(PROJECT_DIR):/workspace" -w /workspace $(IMAGE) build + +clean: + docker run --rm -v "$(PROJECT_DIR):/workspace" -w /workspace $(IMAGE) clean + +watch: + docker run --rm -it -v "$(PROJECT_DIR):/workspace" -w /workspace $(IMAGE) watch + +shell: + docker run --rm -it -v "$(PROJECT_DIR):/workspace" -w /workspace $(IMAGE) bash + +push: + docker push $(REMOTE_IMAGE) diff --git a/README.md b/README.md new file mode 100644 index 0000000..b122fc4 --- /dev/null +++ b/README.md @@ -0,0 +1,181 @@ +# LaTeX Docker Builder + +Docker-Image und Build-Umgebung zum plattformunabhängigen, reproduzierbaren Kompilieren von LaTeX-Dokumenten (wie der Seminararbeit oder Masterthesis an der Fachhochschule Burgenland). + +- **Git-Repository:** [https://git.djnizz.at/Nizz05/latex-docker.git](https://git.djnizz.at/Nizz05/latex-docker.git) +- **Basis-Image:** `texlive/texlive:latest` (Vollständige TeX Live Distribution inkl. `latexmk`, `biber`, `biblatex`, `koma-script`, `mathpazo`, `tikz` etc.) + +--- + +## Inhaltsverzeichnis + +- [Voraussetzungen](#voraussetzungen) +- [Schnellstart](#schnellstart) + - [1. PowerShell (Windows)](#1-powershell-windows) + - [2. Bash / Linux / macOS / WSL](#2-bash--linux--macos--wsl) + - [3. Docker Compose](#3-docker-compose) + - [4. Reines Docker CLI](#4-reines-docker-cli) + - [5. Makefile](#5-makefile) +- [Image selbst bauen](#image-selbst-bauen) +- [Git Remote & Image Registry](#git-remote--image-registry) + - [Code ins Git-Repo pushen](#code-ins-git-repo-pushen) + - [Image zur Registry pushen (optional)](#image-zur-registry-pushen-optional) +- [Beispiel für CI/CD (Gitea Actions / Woodpecker)](#beispiel-f%C3%BCr-cicd-gitea-actions--woodpecker) + +--- + +## Voraussetzungen + +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) (oder Docker Engine unter Linux) läuft auf Ihrem System. +- Keine lokale TeX-Live- oder MiKTeX-Installation auf dem Host erforderlich! + +--- + +## Schnellstart + +### 1. PowerShell (Windows) + +Das mitgelieferte Skript `build-seminar.ps1` vereinfacht alle Abläufe: + +```powershell +# Seminararbeit kompilieren +.\build-seminar.ps1 + +# Bereinigen (löscht temporäre Dateien im out/-Ordner) +.\build-seminar.ps1 -Clean + +# Kontinuierlicher Watch-Modus (baut bei jeder Dateiänderung neu) +.\build-seminar.ps1 -Watch + +# Anderes Dokument kompilieren +.\build-seminar.ps1 -File "meinDokument.tex" +``` + +### 2. Bash / Linux / macOS / WSL + +```bash +# Ausführbar machen +chmod +x build-seminar.sh + +# Kompilieren +./build-seminar.sh + +# Bereinigen +./build-seminar.sh ../Seminar_Arbeit_BITI_Gruber clean + +# Watch-Modus +./build-seminar.sh ../Seminar_Arbeit_BITI_Gruber watch +``` + +### 3. Docker Compose + +```bash +# Bauen +docker compose run --rm builder + +# Bereinigen +docker compose run --rm clean + +# Watch-Modus +docker compose run --rm watch +``` + +### 4. Reines Docker CLI + +Direkt ohne Hilfsskripte: + +```bash +# Windows PowerShell: +docker run --rm -v "${PWD}/../Seminar_Arbeit_BITI_Gruber:/workspace" -w /workspace latex-docker:latest + +# Linux / macOS / WSL: +docker run --rm -v "$(pwd)/../Seminar_Arbeit_BITI_Gruber:/workspace" -w /workspace latex-docker:latest +``` + +Beliebige LaTeX-Befehle übergeben: + +```bash +# Bereinigen +docker run --rm -v "${PWD}/../Seminar_Arbeit_BITI_Gruber:/workspace" latex-docker:latest clean + +# Spezifischen latexmk-Befehl ausführen +docker run --rm -v "${PWD}/../Seminar_Arbeit_BITI_Gruber:/workspace" latex-docker:latest latexmk -pdf seminararbeit.tex +``` + +### 5. Makefile + +```bash +make image # Image bauen +make build # Seminararbeit kompilieren +make clean # Temporäre Dateien löschen +make watch # Kontinuierlich bauen +make shell # Interaktive Bash-Shell im Container öffnen +``` + +--- + +## Image selbst bauen + +Das Image lokal bauen und mit Tags versehen: + +```bash +docker build -t latex-docker:latest -t git.djnizz.at/nizz05/latex-docker:latest . +``` + +--- + +## Git Remote & Image Registry + +### Code ins Git-Repo pushen + +Der Ordner ist bereits mit der Remote-URL `https://git.djnizz.at/Nizz05/latex-docker.git` verknüpft: + +```bash +git add . +git commit -m "feat: Initial commit of latex docker builder environment" +git push -u origin main +``` + +### Image zur Registry pushen (optional) + +Falls Ihre Gitea/Forgejo-Instanz die Docker-Container-Registry aktiviert hat: + +```bash +docker login git.djnizz.at +docker push git.djnizz.at/nizz05/latex-docker:latest +``` + +--- + +## Beispiel für CI/CD (Gitea Actions / Woodpecker) + +### Gitea / GitHub Actions (`.gitea/workflows/build.yml`) + +```yaml +name: Build Seminararbeit + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + container: + image: texlive/texlive:latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Compile LaTeX + run: | + latexmk -pdf seminararbeit.tex + + - name: Upload PDF Artifact + uses: actions/upload-artifact@v4 + with: + name: seminararbeit-pdf + path: out/seminararbeit.pdf +``` diff --git a/build-seminar.ps1 b/build-seminar.ps1 new file mode 100644 index 0000000..1bd0304 --- /dev/null +++ b/build-seminar.ps1 @@ -0,0 +1,113 @@ +<# +.SYNOPSIS + Kompiliert die Seminararbeit mithilfe des LaTeX Docker Containers. +.DESCRIPTION + Skript zum bequemen Bauen, Säubern oder Überwachen der LaTeX-Seminararbeit via Docker. +.PARAMETER Clean + Führt ein 'clean' durch (latexmk -C), um temporäre Build-Dateien zu löschen. +.PARAMETER Watch + Startet den kontinuierlichen Überwachungsmodus (latexmk -pvc). +.PARAMETER RebuildImage + Erzwingt das erneute Erstellen des Docker-Images vor dem LaTeX-Build. +.PARAMETER ProjectPath + Pfad zum LaTeX-Projektverzeichnis (Standard: '../Seminar_Arbeit_BITI_Gruber'). +.PARAMETER File + Spezifische LaTeX-Datei zum Kompilieren (Standard: 'seminararbeit.tex'). +.EXAMPLE + .\build-seminar.ps1 +.EXAMPLE + .\build-seminar.ps1 -Clean +.EXAMPLE + .\build-seminar.ps1 -Watch +#> +[CmdletBinding()] +param( + [switch]$Clean, + [switch]$Watch, + [switch]$RebuildImage, + [string]$ProjectPath = "", + [string]$File = "seminararbeit.tex" +) + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +if ([string]::IsNullOrWhiteSpace($ProjectPath)) { + $ProjectPath = Join-Path $ScriptDir "..\Seminar_Arbeit_BITI_Gruber" +} elseif (-not [System.IO.Path]::IsPathRooted($ProjectPath)) { + $ProjectPath = Join-Path $ScriptDir $ProjectPath +} + +$ErrorActionPreference = "Stop" + +# Prüfen, ob Docker erreichbar ist +$dockerCmd = Get-Command docker -ErrorAction SilentlyContinue +if (-not $dockerCmd) { + Write-Error "Docker ist nicht in PATH verfügbar." + exit 1 +} + +$oldEAP = $ErrorActionPreference +$ErrorActionPreference = "Continue" +& docker info 2>&1 | Out-Null +$dockerOk = ($LASTEXITCODE -eq 0) +$ErrorActionPreference = $oldEAP + +if (-not $dockerOk) { + Write-Error "Docker Desktop ist anscheinend nicht gestartet oder nicht erreichbar." + exit 1 +} + +$ImageName = "latex-docker:latest" +$FullProjectPath = [System.IO.Path]::GetFullPath($ProjectPath) + +if (-not (Test-Path $FullProjectPath)) { + Write-Error "Projektverzeichnis existiert nicht: $FullProjectPath" + exit 1 +} + +# Image prüfen bzw. bauen +$imageExists = docker images -q $ImageName +if (-not $imageExists -or $RebuildImage) { + Write-Host "==> Erstelle Docker Image '$ImageName'..." -ForegroundColor Cyan + docker build -t $ImageName -t "git.djnizz.at/nizz05/latex-docker:latest" "$PSScriptRoot" + if ($LASTEXITCODE -ne 0) { + Write-Error "Docker Build fehlgeschlagen." + exit 1 + } +} + +# Befehl bestimmen +$cmdArgs = @() +if ($Clean) { + Write-Host "==> Bereinige Projektverzeichnis: $FullProjectPath" -ForegroundColor Yellow + $cmdArgs += "clean" +} elseif ($Watch) { + Write-Host "==> Starte Watch-Modus für: $File" -ForegroundColor Cyan + $cmdArgs += "watch" + $cmdArgs += $File +} else { + Write-Host "==> Kompiliere Seminararbeit: $File" -ForegroundColor Green + $cmdArgs += "build" + $cmdArgs += $File +} + +$stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + +docker run --rm -v "${FullProjectPath}:/workspace" -w /workspace $ImageName @cmdArgs + +$stopwatch.Stop() + +if ($LASTEXITCODE -eq 0) { + if (-not $Clean -and -not $Watch) { + $pdfPath = Join-Path $FullProjectPath "out\seminararbeit.pdf" + Write-Host "`n[OK] Build erfolgreich abgeschlossen in $([math]::Round($stopwatch.Elapsed.TotalSeconds, 1))s!" -ForegroundColor Green + if (Test-Path $pdfPath) { + $pdfItem = Get-Item $pdfPath + Write-Host "PDF generiert: $pdfPath ($([math]::Round($pdfItem.Length / 1KB, 1)) KB)" -ForegroundColor Cyan + } + } else { + Write-Host "`n[OK] Aktion erfolgreich beendet!" -ForegroundColor Green + } +} else { + Write-Error "`n[FEHLER] LaTeX Build ist fehlgeschlagen mit Exit Code $LASTEXITCODE." + exit $LASTEXITCODE +} diff --git a/build-seminar.sh b/build-seminar.sh new file mode 100644 index 0000000..ebcc9bf --- /dev/null +++ b/build-seminar.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# ============================================================================== +# LaTeX Docker Build Skript (Bash / Linux / macOS / WSL) +# ============================================================================== +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="${1:-"${SCRIPT_DIR}/../Seminar_Arbeit_BITI_Gruber"}" +IMAGE_NAME="latex-docker:latest" + +ACTION="${2:-build}" + +if ! command -v docker &> /dev/null; then + echo "Fehler: Docker ist nicht installiert oder nicht in PATH." >&2 + exit 1 +fi + +PROJECT_FULL_PATH="$(cd "$PROJECT_DIR" && pwd)" + +echo "==> Projektpfad: $PROJECT_FULL_PATH" +echo "==> Aktion: $ACTION" + +case "$ACTION" in + clean) + docker run --rm -v "${PROJECT_FULL_PATH}:/workspace" -w /workspace "$IMAGE_NAME" clean + ;; + watch) + docker run --rm -it -v "${PROJECT_FULL_PATH}:/workspace" -w /workspace "$IMAGE_NAME" watch + ;; + build) + docker run --rm -v "${PROJECT_FULL_PATH}:/workspace" -w /workspace "$IMAGE_NAME" build + ;; + *) + docker run --rm -v "${PROJECT_FULL_PATH}:/workspace" -w /workspace "$IMAGE_NAME" "$ACTION" "${@:3}" + ;; +esac + +echo "==> Fertig!" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7699510 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +services: + builder: + build: + context: . + dockerfile: Dockerfile + image: latex-docker:latest + container_name: latex-docker-builder + volumes: + - ${PROJECT_DIR:-../Seminar_Arbeit_BITI_Gruber}:/workspace + working_dir: /workspace + command: ["build"] + + # Dienst zum Aufräumen (docker compose run --rm clean) + clean: + image: latex-docker:latest + volumes: + - ${PROJECT_DIR:-../Seminar_Arbeit_BITI_Gruber}:/workspace + working_dir: /workspace + command: ["clean"] + + # Dienst für kontinuierlichen Watch-Modus (docker compose run --rm watch) + watch: + image: latex-docker:latest + volumes: + - ${PROJECT_DIR:-../Seminar_Arbeit_BITI_Gruber}:/workspace + working_dir: /workspace + command: ["watch"] diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..3249d09 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -e + +# LaTeX Docker Builder Entrypoint +# Unterstützt Befehle: +# build [datei.tex] (Standard: baut seminararbeit.tex oder main.tex) +# clean (Führt latexmk -C aus) +# watch [datei.tex] (Kontinuierlicher Rebuild mit latexmk -pvc) +# Beliebiger Befehl (z.B. latexmk -pdf ...) + +resolve_target() { + if [ -n "$1" ]; then + echo "$1" + elif [ -f "seminararbeit.tex" ]; then + echo "seminararbeit.tex" + elif [ -f "main.tex" ]; then + echo "main.tex" + else + # Fallback auf alle tex-Dateien oder Standard-latexmk-Verhalten + echo "" + fi +} + +COMMAND="${1:-build}" + +case "$COMMAND" in + build) + shift || true + TARGET=$(resolve_target "$1") + echo "==> Starte LaTeX Build..." + if [ -n "$TARGET" ]; then + echo "==> Kompiliere: $TARGET" + exec latexmk -pdf "$TARGET" + else + echo "==> Keine Hauptdatei angegeben, führe latexmk -pdf aus..." + exec latexmk -pdf + fi + ;; + + clean) + shift || true + echo "==> Bereinige Ausgabedateien (latexmk -C)..." + exec latexmk -C "$@" + ;; + + watch) + shift || true + TARGET=$(resolve_target "$1") + echo "==> Starte kontinuierlichen Watch-Modus (latexmk -pvc)..." + if [ -n "$TARGET" ]; then + exec latexmk -pvc -pdf "$TARGET" + else + exec latexmk -pvc -pdf + fi + ;; + + *) + exec "$@" + ;; +esac