From d1d1eb3a24840537a2d31627d15408e5f7249eb8 Mon Sep 17 00:00:00 2001 From: Marijn Doeve Date: Fri, 3 Jul 2026 17:05:29 +0200 Subject: [PATCH] docs: update README and add pre-commit hook (#173) * docs: replace requirements with developer and deployment guide * docs: add CI badge, disclaimer, contributing guide, and license * docs: fix and expand disclaimer based on legal review * docs: clarify test vs dev fixture commands in testing section * feat: add pre-commit hook for staged-file quality checks Adds a versioned .githooks/pre-commit script that runs Rector, PHP-CS-Fixer, and PHPStan on staged PHP files, and Twig-CS-Fixer on staged Twig files. Auto-fixes are re-staged before PHPStan runs. Falls back to docker compose run --rm when the PHP service is not up. Install with: just install-hooks * chore: switch pre-commit hook shebang from bash to zsh --- .githooks/pre-commit | 48 ++++++++++++ Justfile | 5 ++ README.md | 177 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 203 insertions(+), 27 deletions(-) create mode 100755 .githooks/pre-commit diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..bc6ea31 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,48 @@ +#!/usr/bin/env zsh +setopt ERR_EXIT PIPE_FAIL NOUNSET + +# Collect staged PHP and Twig files +STAGED_PHP=() +while IFS= read -r file; do + [[ -n "$file" ]] && STAGED_PHP+=("$file") +done < <(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.php$' || true) + +STAGED_TWIG=() +while IFS= read -r file; do + [[ -n "$file" ]] && STAGED_TWIG+=("$file") +done < <(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.twig$' || true) + +if [[ ${#STAGED_PHP[@]} -eq 0 && ${#STAGED_TWIG[@]} -eq 0 ]]; then + exit 0 +fi + +# Use exec if the service is up, otherwise spin up a one-off container +if docker compose exec -T php true 2>/dev/null; then + DOCKER_CMD=(docker compose exec -T php) +else + echo "PHP service not running — using docker compose run..." + DOCKER_CMD=(docker compose run --rm php) +fi + +if [[ ${#STAGED_PHP[@]} -gt 0 ]]; then + echo "PHP (${#STAGED_PHP[@]} file(s)): Rector → CS-Fixer → PHPStan" + + echo " → Rector" + "${DOCKER_CMD[@]}" vendor/bin/rector process "${STAGED_PHP[@]}" + git add "${STAGED_PHP[@]}" + + echo " → PHP-CS-Fixer" + "${DOCKER_CMD[@]}" vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php "${STAGED_PHP[@]}" + git add "${STAGED_PHP[@]}" + + echo " → PHPStan" + "${DOCKER_CMD[@]}" vendor/bin/phpstan analyse "${STAGED_PHP[@]}" --no-progress +fi + +if [[ ${#STAGED_TWIG[@]} -gt 0 ]]; then + echo "Twig (${#STAGED_TWIG[@]} file(s)): Twig-CS-Fixer" + + echo " → Twig-CS-Fixer" + "${DOCKER_CMD[@]}" vendor/bin/twig-cs-fixer fix "${STAGED_TWIG[@]}" + git add "${STAGED_TWIG[@]}" +fi diff --git a/Justfile b/Justfile index 54f1712..3ea6bc6 100644 --- a/Justfile +++ b/Justfile @@ -51,6 +51,11 @@ reload-tests: @docker compose exec php bin/console --env=test doctrine:migrations:migrate -n @docker compose exec php bin/console --env=test doctrine:fixtures:load -n --group=test +install-hooks: + git config core.hooksPath .githooks + chmod +x .githooks/pre-commit + @echo "Pre-commit hook installed." + trust-cert: sudo security add-trusted-cer -d \ -r trustRoot \ diff --git a/README.md b/README.md index e846d3a..c6e28ec 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,162 @@ # Tijd voor de test +![CI](https://github.com/MarijnDoeve/TijdVoorDeTest/actions/workflows/ci.yml/badge.svg) + +PHP/Symfony application for WIDM-style quiz management. +Built with FrankenPHP, PostgreSQL, and Docker. + +> **Disclaimer:** This is an unofficial, non-commercial, open-source fan +> project. It is not affiliated with, endorsed by, or associated with +> *Wie is de Mol?* (produced by IDTV, broadcast by AVROTROS/NPO) or +> *De Mol* (produced by Woestijnvis, broadcast by Play/De Vijver Media). +> *Wie is de Mol?* and *De Mol* are trademarks of their respective rights +> holders. No copyright infringement is intended. + ## Requirements -### Maken van de test +- Docker +- [Just](https://just.systems) (`brew install just`) -- WIDM-tests met een variabel aantal vragen. -- Vragen in een vaste volgorde zijn samen één test (een vraag kan niet bij - meerdere tests horen). -- Vragen hebben 2 of meer antwoordmogelijkheden. Slechts één antwoord is correct. -- Meerdere test samen vormen een seizoen. -- Een seizoen heeft één of geen actieve tests, als er een test actief is kan - uitsluitend die test gemaakt worden. -- Kandidaten kunnen een test maximaal 1 keer invullen. -- Vanaf het moment dat de kandidaat op start klikt na het intypen van hun naam - gaat de tijd lopen. Deze stopt na het aanklikken van een antwoord op de laatste - vraag van de test. -- Achtergrondmuziek +## Local development -### Schermen kijken +```bash +just up # Start PHP + PostgreSQL containers +just migrate # Run pending database migrations +just fixtures # Load dev fixtures (truncates first) +``` -- Nadat een speler een test heeft gemaakt (of vooraf als de namen vooraf - ingevoerd zijn) kunnen jokers toegekend worden aan de test van kandidaat. Een - positief getal om antwoorden goed te rekenen, een negatief getal om - antwoorden fout te rekenen. -- Vooraf kan gekozen worden hoe veel afvallers er zijn. -- Bij het kijken naam rode en groene schermen wordt een naam ingevoerd. Er - wordt een rood of groen scherm getoond. -- Spelers kunnen geforceerd op groen of rood gezet worden, deze worden dan niet - meegenomen in de berekening van de slechtste speler. +The app is available at **https://localhost** (self-signed cert — run +`just trust-cert` on macOS to trust it). -### Statistieken +### Useful commands -TBD +```bash +just shell # Shell inside the running PHP container +just shell-run # Shell in a fresh one-off container +just stop # Stop containers (keep volumes) +just down # Stop and remove containers +just clean # Nuclear: remove containers + volumes + generated files +just exec # Run any command inside the PHP container +``` -## Nice to haves +### Environment -- Optie voor antwoord geven in twee klikken (selecteren en volgende). +Copy `.env` and override locally via `.env.local` (not committed): +| Variable | Description | +|----------------|-------------------------------------| +| `APP_SECRET` | Symfony app secret | +| `DATABASE_URL` | PostgreSQL DSN (auto-set in Docker) | +| `SENTRY_DSN` | Sentry error tracking | +| `DEFAULT_URI` | Base URL for CLI-generated links | + +## Testing + +```bash +just test # Full PHPUnit suite +just test tests/Path/To/TestFile.php # Single file +just test --coverage-html var/coverage # HTML coverage report +just reload-tests # Drop/recreate test DB + migrate + test fixtures +``` + +Tests use a separate database configured via `.env.test`. The DAMA +Doctrine bundle wraps each test in a transaction that is rolled back after. +`just reload-tests` loads the `--group=test` fixtures; `just fixtures` +loads the dev group and is unrelated to the test database. + +## Code quality + +All checks run in CI and must pass before merging. + +```bash +just fix-cs # Auto-fix PHP-CS-Fixer + Twig-CS-Fixer +just phpstan # PHPStan static analysis (level 8) +just rector # Apply Rector modernizations +just rector --dry-run # Preview Rector changes without applying +``` + +## Database + +```bash +just migrate # Run pending migrations +just fixtures # Load dev fixtures +bin/console make:migration # Generate a new migration (inside container) +``` + +Migrations live in `migrations/` (namespace `DoctrineMigrations`). Test +fixtures are in `src/DataFixtures/` loaded with `--group=test`. + +## Translations + +```bash +just translations # Extract/update nl translation strings into translations/ +``` + +## Contributing + +1. Create a branch from `main` — use a prefix like `feat/`, `fix/`, + or `docs/`. +2. Open a pull request; CI must pass before merging. +3. Install the pre-commit hook (see below) to catch issues before pushing. + +### Pre-commit hook + +A pre-commit hook lives in `.githooks/pre-commit`. Install it once after cloning: + +```bash +just install-hooks +``` + +On every commit it runs automatically, **only on staged files**: + +| Staged file type | Tools run | +|-------------------------|------------------------------------------------------------------------------| +| `.php` | Rector → PHP-CS-Fixer (auto-fix + re-stage), then PHPStan (blocks on errors) | +| `.twig` | Twig-CS-Fixer (auto-fix + re-stage) | +| Other (docs, config, …) | Nothing — commit proceeds immediately | + +If the PHP container is not running, the hook falls back to +`docker compose run --rm` so checks still execute. PHPUnit is not +run in the hook; CI covers that. + +## Deployment + +Docker images are published to `ghcr.io/marijndoeve/tijdvoordetest` +for each tagged release. + +### First-time setup + +1. Copy `compose.yaml` and `compose.prod.yaml` to your server. +2. Create a `.env.prod.local` file with the required variables (see below). +3. Start the stack — migrations run automatically on container start: + +```bash +IMAGE_TAG=latest docker compose -f compose.yaml -f compose.prod.yaml up -d +``` + +### Updating to a new version + +```bash +IMAGE_TAG= docker compose -f compose.yaml -f compose.prod.yaml pull +IMAGE_TAG= docker compose -f compose.yaml -f compose.prod.yaml up -d +``` + +### Required environment variables + +| Variable | Description | +|----------------------------|---------------------------------------------| +| `IMAGE_TAG` | Image tag to run (e.g. `1.2.3` or `latest`) | +| `APP_SECRET` | Random secret string for Symfony | +| `CADDY_MERCURE_JWT_SECRET` | JWT secret for the Mercure hub | +| `POSTGRES_PASSWORD` | PostgreSQL password | +| `MAILER_DSN` | Mailer transport DSN | +| `MAILER_SENDER` | From address for emails | +| `SENTRY_DSN` | Sentry project DSN (optional) | + +The `compose.prod.yaml` configures Traefik labels for TLS termination at +`tijdvoordetest.nl`. Adjust the `traefik` labels in that file if you're +hosting on a different domain or using a different reverse proxy. + +## License + +[MIT](LICENSE)