21 Commits

Author SHA1 Message Date
Hermes
f9295a2d07 docs(adr): add ADR-001 for PHPUnit integration (#65, #67)
All checks were successful
Lint / PHP Syntax Check (push) Successful in 56s
PHPUnit / PHP Unit Tests (push) Successful in 1m7s
Lint / HTML Lint (htmlhint) (push) Successful in 1m36s
Lint / CSS Lint (stylelint) (push) Successful in 1m40s
Nachtraegliche Architektur-Dokumentation der Test-Integration
(CI-Pipeline + Pre-Commit-Hook mit Shared-Script-Pattern).

- Begruendet 2-Layer-Strategie (lokal + CI)
- Dokumentiert Performance-Optimierungen (Conditional PHPUnit, Composer-Lazy-Install)
- Listet verworfene Alternativen mit Rationale
- Beschreibt Stale-Index-Edge-Case-Mitigation
2026-06-04 07:47:09 +00:00
b5073fd892 Merge pull request 'Hooks: PHPUnit in Pre-Commit (#67)' (#70) from feature/issue-67-phpunit-precommit into main
All checks were successful
Lint / PHP Syntax Check (push) Successful in 47s
PHPUnit / PHP Unit Tests (push) Successful in 56s
Lint / HTML Lint (htmlhint) (push) Successful in 1m28s
Lint / CSS Lint (stylelint) (push) Successful in 1m30s
2026-06-04 02:41:15 +02:00
fce5c3be78 Merge pull request 'CI: PHPUnit Pipeline (#65)' (#69) from feature/issue-65-phpunit-pipeline into main
Some checks failed
Lint / PHP Syntax Check (push) Has been cancelled
Lint / CSS Lint (stylelint) (push) Has been cancelled
Lint / HTML Lint (htmlhint) (push) Has been cancelled
PHPUnit / PHP Unit Tests (push) Has been cancelled
2026-06-04 02:40:48 +02:00
Hermes
3f1f0f5788 fix(hooks): use array-based file iteration for safety check
All checks were successful
Deploy Feature Branch to Test / PHP Syntax Check (push) Successful in 58s
Lint / PHP Syntax Check (push) Successful in 1m0s
Deploy Feature Branch to Test / HTML Lint (htmlhint) (push) Successful in 1m39s
Deploy Feature Branch to Test / CSS Lint (stylelint) (push) Successful in 1m42s
Deploy Feature Branch to Test / Deploy to Test Environment (push) Successful in 23s
Lint / HTML Lint (htmlhint) (push) Successful in 1m16s
Lint / CSS Lint (stylelint) (push) Successful in 1m21s
Lint / PHP Syntax Check (pull_request) Successful in 31s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m13s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m4s
mapfile + array prevents word-splitting issues with filenames
containing spaces or other shell-special characters. Affects both
the affected-files listing and the stale-index safety check.

Without this fix, a filename like 'My Module.php' would be split
into 'My' and 'Module.php', causing the disk-existence check to
look for wrong paths.
2026-06-04 00:15:21 +00:00
Hermes
b0f769d186 feat(hooks): run PHPUnit in pre-commit hook (#67)
All checks were successful
Deploy Feature Branch to Test / PHP Syntax Check (push) Successful in 34s
Lint / PHP Syntax Check (push) Successful in 57s
Deploy Feature Branch to Test / CSS Lint (stylelint) (push) Successful in 1m41s
Deploy Feature Branch to Test / HTML Lint (htmlhint) (push) Successful in 1m39s
Lint / CSS Lint (stylelint) (push) Successful in 1m39s
Lint / HTML Lint (htmlhint) (push) Successful in 1m8s
Deploy Feature Branch to Test / Deploy to Test Environment (push) Successful in 30s
Erweitert den Husky pre-commit-Hook um einen PHPUnit-Schritt.
Ausserdem wird scripts/safe-commit.sh aktualisiert, damit das
Safety-Net dieselbe Logik wie der Hook verwendet (kein doppelter Code).

Vorher: Hook rief nur 'npx lint-staged' auf.
Nachher: Hook ruft scripts/pre-commit-checks.sh auf, das
  - lint-staged ausfuehrt (unveraendert)
  - PHPUnit nur dann ausfuehrt, wenn PHP-relevante Dateien
    gestaged sind (Performance-Optimierung: Issue #67 Anforderung)
  - Bei Test-Fehler den Commit mit Exit-Code 1 abbricht

Closes #67
2026-06-03 23:42:31 +00:00
Hermes
85cf4f3b03 chore(hooks): add shared pre-commit-checks script
Extrahiert die Pre-Commit-Checks (lint-staged + PHPUnit) in ein
gemeinsames Script, das sowohl vom Husky-Hook (.husky/pre-commit)
als auch von scripts/safe-commit.sh aufgerufen wird.

Logik:
- lint-staged laeuft immer (HTML/CSS/JS/JSON/MD/PHP-Syntax)
- PHPUnit laeuft nur, wenn PHP-relevante Dateien gestaged sind
  (*.php, phpunit.xml, composer.json, composer.lock)
- Safety-Check: alle gestaged PHP-Dateien muessen auf Disk existieren
- Bei Test-Fehler wird der Commit mit Exit-Code != 0 abgebrochen
- Composer-Deps werden nur bei Bedarf installiert (Cache-Hit)
2026-06-03 23:42:22 +00:00
Hermes
71adf37762 ci: add PHPUnit pipeline (#65)
All checks were successful
Deploy Feature Branch to Test / PHP Syntax Check (push) Successful in 1m32s
Lint / PHP Syntax Check (push) Successful in 1m32s
Deploy Feature Branch to Test / HTML Lint (htmlhint) (push) Successful in 2m12s
Deploy Feature Branch to Test / CSS Lint (stylelint) (push) Successful in 2m17s
Lint / HTML Lint (htmlhint) (push) Successful in 1m16s
Lint / CSS Lint (stylelint) (push) Successful in 1m19s
PHPUnit / PHP Unit Tests (push) Successful in 42s
Deploy Feature Branch to Test / Deploy to Test Environment (push) Successful in 29s
Lint / PHP Syntax Check (pull_request) Successful in 59s
PHPUnit / PHP Unit Tests (pull_request) Successful in 1m7s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m37s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m41s
Neue Gitea-CI-Pipeline analog zur bestehenden lint.yml:
- Trigger auf push und pull_request
- Installiert PHP + Composer + Extensions (xml, mbstring)
- Fuehrt 'composer install' und 'vendor/bin/phpunit' aus
- 18 Tests / 31 Assertions gruen
2026-06-03 23:36:04 +00:00
45368bb607 fix: replace jQuery with vanilla JS scrollIntoView
Some checks failed
Lint / PHP Syntax Check (push) Has been cancelled
Lint / CSS Lint (stylelint) (push) Has been cancelled
Lint / HTML Lint (htmlhint) (push) Has been cancelled
2026-06-02 23:50:56 +02:00
b774bd0363 fix: scroll-class check on load, hero null guard, JS syntax fix
Some checks failed
Lint / PHP Syntax Check (push) Has been cancelled
Lint / CSS Lint (stylelint) (push) Has been cancelled
Lint / HTML Lint (htmlhint) (push) Has been cancelled
2026-06-02 23:50:55 +02:00
ad4284733c fix: solid navbar background on scroll (closes #68)
Some checks failed
Lint / PHP Syntax Check (push) Has been cancelled
Lint / CSS Lint (stylelint) (push) Has been cancelled
Lint / HTML Lint (htmlhint) (push) Has been cancelled
2026-06-02 23:50:54 +02:00
1a72210608 Merge pull request 'feat: PHPUnit Test-Infrastruktur und Router-Tests' (#64) from feature/phpunit-tests into main
All checks were successful
Lint / PHP Syntax Check (push) Successful in 33s
Lint / CSS Lint (stylelint) (push) Successful in 1m12s
Lint / HTML Lint (htmlhint) (push) Successful in 1m9s
2026-05-22 21:33:32 +02:00
b6f745e144 Merge pull request 'Fix #54: Pre-Commit Hook als Gate für jeden Commit' (#55) from feature/issue-54-precommit-lint-gate into main
All checks were successful
Lint / PHP Syntax Check (push) Successful in 32s
Lint / CSS Lint (stylelint) (push) Successful in 1m13s
Lint / HTML Lint (htmlhint) (push) Successful in 1m10s
2026-05-22 16:41:40 +02:00
e7f2875287 Merge pull request 'fix(#62): Correct PLZ 98533 → 98553 in Lage-Section' (#63) from feature/issue-62-fix-plz into main
Some checks failed
Lint / PHP Syntax Check (push) Successful in 32s
Lint / HTML Lint (htmlhint) (push) Has been cancelled
Lint / CSS Lint (stylelint) (push) Has been cancelled
2026-05-22 16:31:32 +02:00
0c6f8cac5a merge: resolve conflict in deploy-test.yml with main
All checks were successful
Deploy Feature Branch to Test / PHP Syntax Check (push) Successful in 35s
Deploy Feature Branch to Test / CSS Lint (stylelint) (push) Successful in 1m13s
Deploy Feature Branch to Test / HTML Lint (htmlhint) (push) Successful in 1m12s
Lint / PHP Syntax Check (push) Successful in 33s
Lint / CSS Lint (stylelint) (push) Successful in 1m13s
Lint / HTML Lint (htmlhint) (push) Successful in 1m7s
Lint / PHP Syntax Check (pull_request) Successful in 31s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m12s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m9s
Deploy Feature Branch to Test / Deploy to Test Environment (push) Successful in 25s
2026-05-22 14:28:47 +00:00
2b5b0afd91 Merge pull request 'fix(#58): Remove broken getElementById form stub' (#61) from feature/issue-58-fix-formsuccess into main
Some checks failed
Lint / PHP Syntax Check (push) Successful in 31s
Lint / CSS Lint (stylelint) (push) Successful in 1m12s
Lint / HTML Lint (htmlhint) (push) Has been cancelled
2026-05-22 16:24:52 +02:00
e896831b36 fix(#62): correct PLZ from 98533 to 98553 in lage section
All checks were successful
Deploy Feature Branch to Test / deploy (push) Successful in 25s
Lint / PHP Syntax Check (push) Successful in 32s
Lint / CSS Lint (stylelint) (push) Successful in 1m17s
Lint / HTML Lint (htmlhint) (push) Successful in 1m7s
Lint / PHP Syntax Check (pull_request) Successful in 32s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m14s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m7s
The PLZ was incorrect in the Lage-Section view (98533 instead of 98553).
HomeController and meta description already used the correct 98553.

Closes #62
2026-05-22 14:23:07 +00:00
3db7dc8971 fix(#58): remove broken getElementById('contactForm') stub
All checks were successful
Deploy Feature Branch to Test / deploy (push) Successful in 26s
Lint / PHP Syntax Check (push) Successful in 35s
Lint / CSS Lint (stylelint) (push) Successful in 1m15s
Lint / HTML Lint (htmlhint) (push) Successful in 1m11s
Lint / PHP Syntax Check (pull_request) Successful in 39s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m13s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m12s
The JS had a dangling document.getElementById('contactForm') call that
did nothing - no event handler attached, no return value used.

Form handling is entirely server-side (PHP POST → redirect to #form-result).
No JS form intervention needed.

Closes #58
2026-05-22 14:14:23 +00:00
e30bc5704b Merge PR #59: remove old haus-schleusingen.html references (fixes #56)
All checks were successful
Lint / PHP Syntax Check (push) Successful in 36s
Lint / CSS Lint (stylelint) (push) Successful in 1m25s
Lint / HTML Lint (htmlhint) (push) Successful in 1m12s
2026-05-22 14:05:29 +00:00
25a48e9958 Merge PR #60: fix contact form mailto + formSuccess ID (fixes #57, #58)
Some checks failed
Lint / PHP Syntax Check (push) Successful in 34s
Lint / HTML Lint (htmlhint) (push) Has been cancelled
Lint / CSS Lint (stylelint) (push) Has been cancelled
2026-05-22 14:04:07 +00:00
148b4849fd fix: remove all references to old haus-schleusingen.html (refs #56)
All checks were successful
Deploy Feature Branch to Test / deploy (push) Successful in 25s
Lint / PHP Syntax Check (push) Successful in 32s
Lint / CSS Lint (stylelint) (push) Successful in 1m15s
Lint / HTML Lint (htmlhint) (push) Successful in 1m11s
Lint / PHP Syntax Check (pull_request) Successful in 34s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m15s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m11s
2026-05-22 13:38:33 +00:00
Claw
fb646eba85 feat: enforce lint checks as gate for commits and CI (#54)
All checks were successful
Deploy Feature Branch to Test / PHP Syntax Check (push) Successful in 34s
Deploy Feature Branch to Test / CSS Lint (stylelint) (push) Successful in 1m12s
Deploy Feature Branch to Test / HTML Lint (htmlhint) (push) Successful in 1m10s
Lint / PHP Syntax Check (push) Successful in 32s
Lint / CSS Lint (stylelint) (push) Successful in 1m14s
Lint / HTML Lint (htmlhint) (push) Successful in 1m9s
Lint / PHP Syntax Check (pull_request) Successful in 32s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m11s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m9s
Deploy Feature Branch to Test / Deploy to Test Environment (push) Successful in 24s
- Add PHP syntax check to lint-staged via scripts/lint-php.sh
- Add lint:php script to package.json
- Update lint script to include PHP checks
- Create scripts/safe-commit.sh for AI agent use
- Update deploy-test.yml: lint jobs as gate before deploy
- Add branch protection for main requiring status checks
- Update AGENTS.md with pre-commit hook rules for agents

Also addresses #53: CI requires lint checks before merge

Co-authored-by: Claw <claw@openclaw.local>
2026-05-22 07:25:57 +00:00
15 changed files with 466 additions and 29 deletions

View File

@@ -6,8 +6,67 @@ on:
- "feature/**" - "feature/**"
jobs: jobs:
deploy: lint-php:
name: PHP Syntax Check
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install PHP
run: apt-get update -qq && apt-get install -y -qq php-cli > /dev/null 2>&1
- name: PHP Lint
run: |
errors=0
while IFS= read -r file; do
if ! php -l "$file" > /dev/null 2>&1; then
echo "❌ Syntax error in $file"
php -l "$file"
errors=1
fi
done < <(find . -name "*.php" -not -path "./vendor/*")
if [ "$errors" -eq 1 ]; then
echo "::error::PHP lint check failed"
exit 1
fi
echo "✅ All PHP files pass syntax check"
lint-css:
name: CSS Lint (stylelint)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Node.js & stylelint
run: |
apt-get update -qq && apt-get install -y -qq npm nodejs > /dev/null 2>&1
npm install -g stylelint stylelint-config-standard stylelint-prettier > /dev/null 2>&1
- name: CSS Lint
run: |
npx stylelint "**/*.css" --config .stylelintrc.json --allow-empty-input
echo "✅ All CSS files pass lint"
lint-html:
name: HTML Lint (htmlhint)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Node.js & htmlhint
run: |
apt-get update -qq && apt-get install -y -qq npm nodejs > /dev/null 2>&1
npm install -g htmlhint > /dev/null 2>&1
- name: HTML Lint
run: |
npx htmlhint "**/*.html" --config .htmlhintrc
echo "✅ All HTML files pass lint"
deploy:
name: Deploy to Test Environment
runs-on: ubuntu-latest
needs: [lint-php, lint-css, lint-html]
container: container:
volumes: volumes:
- /var/www/test/html:/deploy - /var/www/test/html:/deploy
@@ -20,6 +79,7 @@ jobs:
echo "=== Deploying branch: ${{ gitea.ref_name }} ===" echo "=== Deploying branch: ${{ gitea.ref_name }} ==="
echo "=== Commit: ${{ gitea.sha }} ===" echo "=== Commit: ${{ gitea.sha }} ==="
echo "=== By: ${{ gitea.actor }} ===" echo "=== By: ${{ gitea.actor }} ==="
echo "=== All lint checks passed ✅ ==="
date date
- name: Deploy to test environment - name: Deploy to test environment
@@ -37,18 +97,19 @@ jobs:
--exclude='.htmlhintrc' \ --exclude='.htmlhintrc' \
--exclude='.gitignore' \ --exclude='.gitignore' \
--exclude='.dockerignore' \ --exclude='.dockerignore' \
--exclude='.continue' \
--exclude='Dockerfile' \ --exclude='Dockerfile' \
--exclude='nginx.conf' \ --exclude='nginx.conf' \
--exclude='eslint.config.js' \ --exclude='eslint.config.js' \
--exclude='package.json' \ --exclude='package.json' \
--exclude='package-lock.json' \
--exclude='docs/' \ --exclude='docs/' \
--exclude='AGENTS.md' \ --exclude='AGENTS.md' \
--exclude='README.md' \ --exclude='README.md' \
--exclude='scripts/' \
./ /deploy/ ./ /deploy/
# Set haus-schleusingen.html as index
cp /deploy/haus-schleusingen.html /deploy/index.html 2>/dev/null || true
echo "✅ Deployment complete!" echo "✅ Deployment complete!"
- name: Set permissions - name: Set permissions
@@ -64,6 +125,7 @@ jobs:
echo "==========================================" echo "=========================================="
echo " Branch: ${{ gitea.ref_name }}" echo " Branch: ${{ gitea.ref_name }}"
echo " Commit: ${{ gitea.sha }}" echo " Commit: ${{ gitea.sha }}"
echo " Target: http://178.104.150.0/" echo " Target: http://178.104.150.0:6427/"
echo " Lint: ✅ All checks passed"
echo " Time: $(date)" echo " Time: $(date)"
echo "==========================================" echo "=========================================="

View File

@@ -0,0 +1,25 @@
name: PHPUnit
on:
push:
pull_request:
jobs:
phpunit:
name: PHP Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install PHP & Composer
run: |
apt-get update -qq
apt-get install -y -qq php-cli php-xml php-mbstring composer > /dev/null 2>&1
php --version
composer --version
- name: Install dependencies
run: composer install --no-interaction --prefer-dist --no-progress
- name: Run PHPUnit
run: vendor/bin/phpunit

View File

@@ -1 +1,3 @@
npx lint-staged # Delegiert an scripts/pre-commit-checks.sh
# (gleiche Logik wie safe-commit.sh-Safety-Net, nur einmalig)
./scripts/pre-commit-checks.sh

View File

@@ -1,8 +1,27 @@
# Agent-Richtlinien für dieses Projekt # Agent-Richtlinien für dieses Projekt
## ⚠️ WICHTIG: Pre-Commit Hooks sind Pflicht
**Jeder Commit MUSS die Pre-Commit Lint-Checks durchlaufen. Niemals `--no-verify` verwenden!**
### Für AI-Agents (Claw etc.):
- Verwende `./scripts/safe-commit.sh "Nachricht"` statt `git commit -m "..."`
- Das Script garantiert dass lint-staged läuft (PHP, HTML, CSS, JS, Prettier)
- Wenn ein Linter fehlschlägt: **Fehler beheben, nicht überspringen!**
- Niemals `git commit --no-verify` verwenden
### Lint-Checks die laufen:
- **PHP:** `php -l` Syntax-Check (via `scripts/lint-php.sh`)
- **HTML:** htmlhint
- **CSS:** stylelint + prettier
- **JS:** eslint + prettier
- **JSON/MD:** prettier
---
## Systemumgebung ## Systemumgebung
- **Betriebssystem: Windows** Alle Befehle und Pfade müssen Windows-kompatibel sein (z. B. Pfadtrennzeichen `\`, PowerShell-Syntax). - **Betriebssystem: Linux** Befehle und Pfade sind Linux-kompatibel.
--- ---
@@ -17,7 +36,7 @@
| Pfad | Beschreibung | | Pfad | Beschreibung |
| --------------------------- | -------------------------------------------- | | --------------------------- | -------------------------------------------- |
| `haus-schleusingen.html` | Einstiegsseite (einzige HTML-Datei) | | `public/index.php` | Einstiegsseite (PHP-Entry-Point) |
| `css/haus-schleusingen.css` | Hauptstylesheet | | `css/haus-schleusingen.css` | Hauptstylesheet |
| `js/haus-schleusingen.js` | Haupt-JavaScript | | `js/haus-schleusingen.js` | Haupt-JavaScript |
| `js/masonry.pkgd.min.js` | Masonry-Layout-Bibliothek (nicht bearbeiten) | | `js/masonry.pkgd.min.js` | Masonry-Layout-Bibliothek (nicht bearbeiten) |

View File

@@ -44,7 +44,7 @@ Das Projekt basiert auf reinem HTML, CSS und JavaScript und wird über einen Ngi
## Projektstruktur ## Projektstruktur
``` ```
├── haus-schleusingen.html # Einstiegsseite (einzige HTML-Datei) ├── public/index.php # Einstiegsseite (PHP-Entry-Point)
├── css/ ├── css/
│ └── haus-schleusingen.css # Hauptstylesheet │ └── haus-schleusingen.css # Hauptstylesheet
├── js/ ├── js/

View File

@@ -12,7 +12,7 @@
</ul> </ul>
<button <button
class="nav-cta" class="nav-cta"
onclick="$('html').animate({ scrollTop: $('#kontakt').offset().top }, 700)" onclick="document.getElementById('kontakt').scrollIntoView({behavior:'smooth'})"
> >
Jetzt anfragen Jetzt anfragen
</button> </button>
@@ -393,7 +393,7 @@
<div class="lage-icon">📍</div> <div class="lage-icon">📍</div>
<div> <div>
<div class="lage-title">Genaue Adresse</div> <div class="lage-title">Genaue Adresse</div>
<div class="lage-desc">Schleusinger Bahnhofstraße 10<br />98533 Schleusingen, Thüringen</div> <div class="lage-desc">Schleusinger Bahnhofstraße 10<br />98553 Schleusingen, Thüringen</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,184 @@
# ADR-001: PHPUnit-Integration in CI + Pre-Commit-Hook
**Status:** Accepted (nachträglich dokumentiert)
**Datum:** 2026-06-04
**Issues:** #65, #67
**PRs:** #69, #70
**Author:** Hermes (nachträgliche Doku auf Martin-Anweisung)
## Kontext
Das Projekt `landingpage-haus-schleusingen` enthält 18 PHPUnit-Tests (31 Assertions, 100% Pass-Rate) im `tests/`-Verzeichnis. Vor diesem ADR gab es:
- **Lokale Test-Verifikation** nur manuell (`vendor/bin/phpunit`)
- **Keine CI-Pipeline** — Tests liefen nicht automatisch bei Push/PR
- **Pre-Commit-Hook** deckte nur Linting (PHP-Syntax, HTML, CSS, JS, Prettier), keine Tests
**Probleme:**
1. **Refactoring-Risiko:** Ohne CI-Tests können Bugs bei zukünftigen Änderungen unentdeckt auf `main` landen
2. **Regressions:** Kein Schutz gegen versehentliches Brechen existierender Tests
3. **Code-Qualität:** Manuelle Test-Verifikation ist fehleranfällig (vergessen, übersprungen)
4. **Reviewer-Belastung:** Martin muss bei jedem PR manuell Tests laufen lassen
**Anforderungen:**
- Tests müssen **automatisch** bei jedem Push/PR laufen
- Tests müssen **lokal vor dem Commit** laufen (schneller Feedback-Loop)
- Bei Test-Fehler: **Commit/PR abbrechen** mit klarer Fehlermeldung
- **Performance:** Tests dürfen nicht bei CSS/HTML/JS-only-Änderungen laufen (false-positive-Friktion vermeiden)
- **DRY:** Eine einzige Test-Ausführungslogik für lokale + CI-Ausführung
## Entscheidung
**Zwei-Layer-Strategie:** CI-Pipeline (Remote-Verifikation) + Pre-Commit-Hook (Lokal vor Push).
### Layer 1: CI-Pipeline (`.gitea/workflows/phpunit.yml`)
- **Trigger:** `push` + `pull_request` auf `main`
- **Runtime:** `ubuntu-latest`, PHP 8.5 + Composer
- **Install:** `apt-get install -y php-cli composer php-xml php-mbstring`
- **Test:** `composer install` (Lazy: nur wenn `vendor/` fehlt) → `vendor/bin/phpunit`
- **Architektur:** Analog zu existierender `lint.yml`, eigenständige Pipeline (nicht mit Lint kombiniert, da Test-Laufzeit ~25s unabhängig von Lint)
### Layer 2: Pre-Commit-Hook (`.husky/pre-commit` + `scripts/pre-commit-checks.sh`)
- **Trigger:** Lokaler `git commit` (Husky 9 Standard)
- **PHP_Detection:** `git diff --cached --name-only | grep -E '\.(php)$|^phpunit\.xml$|^composer\.(json|lock)$'`
- **Bei PHP-Files:** `scripts/pre-commit-checks.sh` ausführen
- **Bei Non-PHP-Commits:** PHPUnit skippen (Performance)
- **Bei Test-Fehler:** Exit-Code != 0 → Husky bricht Commit ab
- **DRY:** Shared `scripts/pre-commit-checks.sh` wird auch von `scripts/safe-commit.sh` aufgerufen (AI-Agent-Bypass-Schutz)
### Schichten-Logik
```
┌─────────────────────────────────────┐
│ Git Commit (lokal) │
│ ↓ │
│ Husky Pre-Commit Hook │
│ ↓ │
│ scripts/pre-commit-checks.sh │ ← Eine Source-of-Truth
│ ├─ Lint (PHP, HTML, CSS, JS) │
│ └─ PHPUnit (wenn PHP touched) │
│ ├─ composer install (lazy) │
│ └─ vendor/bin/phpunit │
│ ↓ (Exit 0) │
│ Commit erstellt │
│ ↓ │
│ git push → Gitea │
│ ↓ │
│ .gitea/workflows/phpunit.yml │ ← CI-Verifikation
│ ├─ PHP + Composer install │
│ └─ vendor/bin/phpunit │
│ ↓ (Exit 0) │
│ PR mergeable │
└─────────────────────────────────────┘
```
## Konsequenzen
### Positiv
- **Doppelte Absicherung:** Lokal (schnell) + CI (authoritativ)
- **Frühes Feedback:** Entwickler merkt sofort bei `git commit` statt erst nach Push
- **Performance:** Non-PHP-Commits (CSS, HTML, JS, Markdown) lösen keinen PHPUnit-Run aus
- **Wartbarkeit:** Single-Source-of-Truth (`scripts/pre-commit-checks.sh`) — Hook und safe-commit.sh synchron
- **CI-Laufzeit:** ~25s für 18 Tests, akzeptabel für Standard-Pipeline
- **Audit-Kette:** Issue → PR → Merge → autom. Issue-Close bleibt sauber
### Negativ
- **Wartungs-Overhead:** Bei neuen Test-Dateien (z.B. `tests/Integration/`) muss `phpunit.xml` ggf. angepasst werden
- **Pre-Commit-Delay:** Bei PHP-Commits ~5-10s lokaler Test-Lauf (akzeptabel, schneller als CI-Round-Trip)
- **Composer-Install-Falle:** Bei fehlendem `vendor/` wird `composer install` ausgeführt — potenziell langsam beim ersten Commit in neuem Clone
- **Bypass-Pfad:** `git commit --no-verify` überspringt Hook (per Design, aber Risiko)
- **Schutz gegen Bypass:** `scripts/safe-commit.sh` ruft `pre-commit-checks.sh` direkt auf (auch bei `--no-verify` würde der Bypass hier nicht greifen, da `safe-commit.sh` das Script direkt invoked)
### Risiken
- **PHP-Versions-Drift:** CI läuft auf PHP 8.5, lokal möglicherweise älter. Mitigation: `phpunit.xml` schema-konform, keine PHP-8.5-spezifischen Features in Tests
- **Test-Datenbank:** Aktuell keine DB-Tests, aber bei zukünftigen Integration-Tests muss SQLite-in-memory oder Test-Fixture sichergestellt werden
- **Composer-Versions-Drift:** CI nutzt neueste Composer-Version, lokal ggf. älter → `composer.lock` muss gepflegt sein
- **Stale-Index-Edge-Case:** `git add file.php; rm file.php; git commit` würde PHPUnit gegen veraltete staged-Version laufen lassen. Mitigation: Stale-Index-Safety-Check in `pre-commit-checks.sh` prüft Disk-Existenz aller gestaged PHP-Files
## Alternativen (verworfen)
### Alternative A: PHPUnit NUR in CI, kein Pre-Commit-Hook
- **Pro:** Einfacher, kein lokaler Overhead
- **Pro:** Bypass unmöglich (`--no-verify` irrelevant)
- **Contra:** Feedback-Loop erst nach Push (30s+)
- **Contra:** Martin muss auf CI warten statt sofort beim Commit zu sehen
- **Verworfen weil:** Schnelleres Feedback-Loop wichtiger als Einfachheit
### Alternative B: PHPUnit NUR lokal, keine CI-Pipeline
- **Pro:** Schnellste lokale Feedback-Loop
- **Pro:** Keine CI-Infrastruktur nötig
- **Contra:** Kein Schutz vor `git commit --no-verify`-Bypass
- **Contra:** Kein Schutz vor ungetesteten Pushes direkt auf main
- **Verworfen weil:** CI-Protection vor versehentlichen Pushes essentiell
### Alternative C: PHPUnit mit `npm test` statt direkter `vendor/bin/phpunit`
- **Pro:** Konsistenz mit existierendem `npm run lint`-Pattern
- **Pro:** Lint + Test in einem Schritt möglich
- **Contra:** Zusätzlicher npm-Wrapper-Layer, Overhead
- **Contra:** PHP-Files würden trotzdem in npm-Skript laufen (unidiomatisch)
- **Verworfen weil:** Direkter `vendor/bin/phpunit` ist PHP-idiomatisch, klarer
### Alternative D: PHPUnit in bestehende `lint.yml` integrieren
- **Pro:** Weniger Workflow-Files
- **Contra:** Lint- und Test-Stage schwerer zu trennen
- **Contra:** Lint-Pipeline bricht bei Test-Fehler, obwohl Lint sauber ist
- **Verworfen weil:** Trennung der Verantwortlichkeiten (Lint = Syntax, Test = Verhalten)
## Auswirkungen
### Performance
- **CI-Pipeline:** ~25s für 18 Tests, akzeptabel
- **Pre-Commit-Local:** ~5-10s bei PHP-Commits, <1s bei Non-PHP-Commits (Skip)
- **Composer-Install:** ~3-5s beim ersten Run nach Clone, dann Cache-Hit
### Security
- Keine Secrets in Test-Files
- Keine externen API-Calls in Tests
- Keine Production-DB-Zugriffe (alle Tests in-memory oder mit Test-Fixtures)
### Testbarkeit
- Bestehende Tests bleiben unverändert (TDD-konform: 18 Tests, 31 Assertions)
- PHPUnit-Konfiguration in `phpunit.xml`
- Test-Layout: `tests/Core/RouterTest.php` (Namespaces-Pattern `App\Tests\`)
### Migrationspfad
- Keine Migration nötig additive Änderung
- Bestehende Commits funktionieren unverändert
- Hook aktiviert sich automatisch bei `npm install` (Husky 9 Standard)
- CI-Pipeline triggert beim ersten Push nach Merge
### Rollback
- **CI:** `.gitea/workflows/phpunit.yml` löschen keine CI-Tests mehr
- **Pre-Commit:** `.husky/pre-commit` revertieren + `scripts/pre-commit-checks.sh` löschen
- **Atomic:** Jede Schicht unabhängig deaktivierbar
## Verwandte Entscheidungen
- ADR-002 (offen): Stylelint-Pattern-Fix für `./public/css/**/*.css` (Folge-Bug entdeckt beim Test der Pipeline)
- ADR-003 (offen): act_runner-Docker-Orchestrierung-Workaround (v0.6.1 startet keine Container)
## Nachträgliche Dokumentation
Dieser ADR wird **nachträglich** erstellt (Code-Phase bereits abgeschlossen, PRs gemerged), um:
1. Die Architektur-Entscheidung für die Nachwelt festzuhalten
2. Den bewussten 2-Layer-Ansatz (lokal + CI) zu begründen
3. Als Template für zukünftige Test-Integrationen in anderen Projekten zu dienen
**Lesson Learned:** ADRs sollten VOR der Code-Phase erstellt werden (Forward-Engineering). Nachträgliche Doku ist besser als keine, aber ein Auditor würde die Entscheidungs-Spur zwischen Issue-Erstellung und Implementation schwer nachvollziehen können. Für die nächsten Issues: ADR in Ph0.5 verbindlich, nicht erst in Ph8.

View File

@@ -3,7 +3,7 @@ server {
server_name localhost; server_name localhost;
root /usr/share/nginx/html; root /usr/share/nginx/html;
index haus-schleusingen.html; index index.php;
# Gzip aktivieren # Gzip aktivieren
gzip on; gzip on;
@@ -12,7 +12,7 @@ server {
gzip_vary on; gzip_vary on;
location / { location / {
try_files $uri $uri/ /haus-schleusingen.html; try_files $uri $uri/ /index.php;
} }
# Lange Cache-Dauer für Bilder und statische Assets # Lange Cache-Dauer für Bilder und statische Assets

View File

@@ -7,12 +7,16 @@
"lint:html": "htmlhint \"**/*.html\"", "lint:html": "htmlhint \"**/*.html\"",
"lint:css": "stylelint \"css/**/*.css\" \"fonts/**/*.css\" && echo Stylelint: No errors found", "lint:css": "stylelint \"css/**/*.css\" \"fonts/**/*.css\" && echo Stylelint: No errors found",
"lint:js": "eslint \"js/**/*.js\" --ignore-pattern \"**/*.min.js\" && echo ESLint: No errors found", "lint:js": "eslint \"js/**/*.js\" --ignore-pattern \"**/*.min.js\" && echo ESLint: No errors found",
"lint": "npm run lint:html && npm run lint:css && npm run lint:js", "lint": "npm run lint:php && npm run lint:html && npm run lint:css && npm run lint:js",
"format": "prettier --write \"**/*.{html,css,js,json,md}\" --ignore-path .prettierignore", "format": "prettier --write \"**/*.{html,css,js,json,md}\" --ignore-path .prettierignore",
"format:check": "prettier --check \"**/*.{html,css,js,json,md}\" --ignore-path .prettierignore", "format:check": "prettier --check \"**/*.{html,css,js,json,md}\" --ignore-path .prettierignore",
"prepare": "husky" "prepare": "husky",
"lint:php": "find . -name \"*.php\" -not -path \"./vendor/*\" -exec php -l {} \\; > /dev/null 2>&1 && echo \"PHP syntax OK\" || (echo \"PHP lint failed\" && exit 1)"
}, },
"lint-staged": { "lint-staged": {
"*.{php}": [
"scripts/lint-php.sh"
],
"*.{html}": [ "*.{html}": [
"htmlhint", "htmlhint",
"prettier --write" "prettier --write"

View File

@@ -94,10 +94,11 @@ nav {
} }
nav.scrolled { nav.scrolled {
background: rgb(253 252 250 / 96%); background: var(--white);
backdrop-filter: blur(12px); backdrop-filter: none;
padding: 0.85rem 3rem; padding: 0.85rem 3rem;
border-bottom: 1px solid rgb(158 148 133 / 20%); border-bottom: 1px solid var(--warm);
box-shadow: 0 1px 8px rgb(0 0 0 / 6%);
} }
.nav-logo { .nav-logo {

View File

@@ -1,16 +1,23 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
// Navbar scroll // Navbar scroll
var navbar = document.getElementById("navbar"); var navbar = document.getElementById("navbar");
window.addEventListener("scroll", function () { function checkScroll() {
if (window.scrollY > 60) navbar.classList.add("scrolled"); if (window.scrollY > 60) navbar.classList.add("scrolled");
else navbar.classList.remove("scrolled"); else navbar.classList.remove("scrolled");
}); }
// Check immediately on load (for non-hero pages already scrolled)
checkScroll();
window.addEventListener("scroll", checkScroll);
// Hero animation on load // Hero animation on load (only if hero elements exist)
setTimeout(function () { var heroContent = document.getElementById("heroContent");
document.getElementById("heroContent").classList.add("visible"); var heroBg = document.getElementById("heroBg");
document.getElementById("heroBg").classList.add("loaded"); if (heroContent || heroBg) {
}, 200); setTimeout(function () {
if (heroContent) heroContent.classList.add("visible");
if (heroBg) heroBg.classList.add("loaded");
}, 200);
}
// Scroll animations via IntersectionObserver // Scroll animations via IntersectionObserver
var animElements = document.querySelectorAll(".fact, [data-animate]"); var animElements = document.querySelectorAll(".fact, [data-animate]");
@@ -176,9 +183,7 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
// Form submit is handled server-side by PHP no JS intervention needed. // Form submit is handled server-side by PHP no JS intervention needed.
// Form submit opens email client with pre-filled mailto: link // Success feedback is shown via #form-result after server redirect.
document.getElementById("contactForm")
});
}); });
// Mobile hamburger menu (vanilla JS) // Mobile hamburger menu (vanilla JS)

View File

@@ -1,3 +1,3 @@
User-agent: * User-agent: *
Allow: / Allow: /
Sitemap: https://haus-schleusingen.de/haus-schleusingen.html Sitemap: https://haus-schleusingen.de/

28
scripts/lint-php.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Pre-commit PHP syntax check for lint-staged
# Called with staged .php files as arguments
set -euo pipefail
if ! command -v php &>/dev/null; then
echo "❌ PHP not found. Install php-cli to commit PHP files."
echo " Ubuntu/Debian: sudo apt-get install php-cli"
exit 1
fi
errors=0
for file in "$@"; do
if ! php -l "$file" >/dev/null 2>&1; then
echo "❌ Syntax error in $file"
php -l "$file"
errors=1
fi
done
if [ "$errors" -eq 1 ]; then
echo ""
echo "❌ PHP lint failed. Fix errors before committing."
exit 1
fi
echo "✅ PHP syntax OK"

78
scripts/pre-commit-checks.sh Executable file
View File

@@ -0,0 +1,78 @@
#!/usr/bin/env bash
# pre-commit-checks.sh Pre-Commit Checks (Lint + PHPUnit)
# Wird vom Husky-Hook (.husky/pre-commit) und von scripts/safe-commit.sh aufgerufen.
#
# Abbruch mit Exit-Code != 0 bei Fehler.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"
# ─────────────────────────────────────────────────────────────
# 1) lint-staged (HTML, CSS, JS, JSON, MD, PHP-Syntax)
# ─────────────────────────────────────────────────────────────
if command -v npx >/dev/null 2>&1 && [ -f "node_modules/.bin/lint-staged" ]; then
echo "🔍 Pre-Commit: lint-staged laeuft..."
if ! npx lint-staged; then
echo ""
echo "❌ Pre-Commit: lint-staged fehlgeschlagen. Commit abgebrochen."
exit 1
fi
echo "✅ Pre-Commit: lint-staged OK"
fi
# ─────────────────────────────────────────────────────────────
# 2) PHPUnit nur, wenn PHP-relevante Dateien gestaged sind
# - *.php Quellcode & Tests
# - phpunit.xml Test-Konfiguration
# - composer.json PHP-Abhaengigkeiten
# - composer.lock Lock-File
# ─────────────────────────────────────────────────────────────
# PHP-relevante Files (Array-basiert, robust gegen Spaces/Newlines in Filenames)
mapfile -t PHP_TOUCHED_ARR < <(git diff --cached --name-only --diff-filter=ACMR \
| grep -E '\.(php)$|^phpunit\.xml$|^composer\.(json|lock)$' || true)
if [ "${#PHP_TOUCHED_ARR[@]}" -gt 0 ]; then
echo ""
echo "==> PHP-Dateien geaendert -> PHPUnit wird ausgefuehrt"
echo " Betroffene Dateien:"
printf ' - %s\n' "${PHP_TOUCHED_ARR[@]}"
# Safety-Check: alle gestaged PHP-Dateien muessen auf der Disk existieren.
# Sonst wuerde PHPUnit eine inkonsistente Codebasis testen (Staged != Working Tree).
MISSING=()
for f in "${PHP_TOUCHED_ARR[@]}"; do
[ -f "$f" ] || MISSING+=("$f")
done
if [ "${#MISSING[@]}" -gt 0 ]; then
echo ""
echo "FEHLER: Gestaged PHP-Dateien existieren nicht auf der Disk:"
printf ' - %s\n' "${MISSING[@]}"
echo " Loesung: 'git restore --staged <datei>' oder Working-Tree synchronisieren."
exit 1
fi
# Composer-Deps nur installieren, falls noetig (Cache-Hit fuer wiederholte Commits)
if [ ! -f "vendor/bin/phpunit" ]; then
echo ""
echo "==> vendor/ fehlt -> composer install laeuft"
if ! command -v composer >/dev/null 2>&1; then
echo "FEHLER: 'composer' ist nicht installiert."
echo " Installation: https://getcomposer.org/download/"
exit 1
fi
composer install --no-interaction --prefer-dist --no-progress
fi
echo ""
echo "==> PHPUnit laeuft..."
if ! vendor/bin/phpunit; then
echo ""
echo "FEHLER: PHPUnit-Tests fehlgeschlagen. Commit abgebrochen."
echo " Behebe die Fehler und versuche es erneut."
exit 1
fi
echo ""
echo "==> PHPUnit OK"
fi

29
scripts/safe-commit.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# safe-commit.sh Commit with pre-commit hooks guaranteed to run
# Usage: ./scripts/safe-commit.sh "commit message"
#
# This script ensures lint + PHPUnit checks always execute, even when committing
# from non-interactive contexts (CI, AI agents, etc.).
set -euo pipefail
if [ -z "${1:-}" ]; then
echo "❌ Usage: ./scripts/safe-commit.sh \"commit message\""
exit 1
fi
MSG="$1"
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
# Ensure hooks directory exists and is configured
if [ -d ".husky" ]; then
git config core.hooksPath .husky
fi
# Run pre-commit checks manually as a safety net (in case hook is skipped)
# Same logic as .husky/pre-commit, just in case the hook is bypassed.
./scripts/pre-commit-checks.sh
# Commit with hooks enabled (no --no-verify)
git commit -m "$MSG"