From 29b54a340f2ec106167a3f0f3147c23127be2bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Wed, 8 Apr 2026 11:01:16 +0200 Subject: [PATCH] docs(phase-1): research dev environment setup Co-Authored-By: Claude Opus 4.6 --- .planning/phase-1/RESEARCH.md | 361 ++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 .planning/phase-1/RESEARCH.md diff --git a/.planning/phase-1/RESEARCH.md b/.planning/phase-1/RESEARCH.md new file mode 100644 index 0000000..758d75a --- /dev/null +++ b/.planning/phase-1/RESEARCH.md @@ -0,0 +1,361 @@ +# Phase 1: Dev Environment - Research + +**Researched:** 2026-04-08 +**Domain:** Nix flake devshell, Kimai 2.x local instance, MariaDB, PHP 8.2+, data seeding +**Confidence:** HIGH + +## Summary + +Phase 1 is about getting a reproducible dev environment where `nix develop` gives you a working Kimai instance with test data ready for plugin development. The key findings are: + +1. **Kimai requires MySQL/MariaDB** -- SQLite support was removed years ago. The Nix flake must provision a local MariaDB instance (11.4.x available in nixpkgs). This is the single biggest complexity driver for this phase. +2. **Kimai 2.52.0** is the current latest stable release (March 2025), requiring PHP 8.1-8.5 and Symfony 6.4. PHP 8.2.29 is already available in the system nixpkgs as `php82`. +3. **Kimai ships built-in fixtures** (`bin/console kimai:reset:dev` loads Doctrine fixtures that create ~100-1000 timesheet entries per user across 3 years of data). No need to hand-roll a seed script. +4. **Plugins live in `var/plugins/`** with a symlink from the project directory. The namespace must be `KimaiPlugin\` and the bundle class must implement `App\Plugin\PluginInterface`. + +**Primary recommendation:** Use a shell script (`dev/start.sh`) invoked from the Nix devshell that starts MariaDB in a local data directory, runs Kimai install + fixture loading, symlinks the plugin, and starts the Symfony dev server. Use `process-compose` to manage MariaDB + web server lifecycle. + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|------------------| +| DEV-01 | Nix flake/devshell provides a running local Kimai instance | Nix flake with php82, mariadb, composer, nodejs, symfony-cli, process-compose; shell hook bootstraps Kimai | +| DEV-02 | Local Kimai has a seeded database with realistic time entry data | `bin/console kimai:reset:dev` loads built-in Doctrine fixtures with thousands of timesheet entries | +| DEV-03 | Plugin is loadable in the local Kimai instance for manual testing | Symlink project dir into Kimai's `var/plugins/KimaiHeatmapBundle`, clear cache | + + +## Standard Stack + +### Core (Nix Flake Packages) + +| Package | Nixpkgs Attr | Version | Purpose | +|---------|-------------|---------|---------| +| PHP | `php82` | 8.2.29 | Kimai runtime (with extensions: gd, intl, mbstring, pdo_mysql, xml, xsl, zip, tokenizer) | [VERIFIED: nix search nixpkgs php82] | +| MariaDB | `mariadb` | 11.4.8 | Database server (Kimai dropped SQLite support) | [VERIFIED: nix search nixpkgs mariadb] | +| Composer | `php82Packages.composer` | 2.8.x | PHP dependency management | [VERIFIED: system composer --version] | +| Node.js | `nodejs` | 22.x | JS tooling for d3/esbuild (later phases) | [VERIFIED: system node --version] | +| npm | (bundled with nodejs) | 10.x | JS package manager | [VERIFIED: system npm --version] | +| symfony-cli | `symfony-cli` | 5.16.0 | Dev web server with PHP integration | [VERIFIED: nix search nixpkgs symfony-cli] | +| process-compose | `process-compose` | 1.78.0 | Process manager for MariaDB + web server | [VERIFIED: nix search nixpkgs process-compose] | + +### Kimai + +| Property | Value | Source | +|----------|-------|--------| +| Latest stable | 2.52.0 | [VERIFIED: github.com/kimai/kimai/releases] | +| PHP support | 8.1 - 8.5 | [VERIFIED: composer.json require] | +| Symfony version | 6.4 LTS | [VERIFIED: composer.json extra] | +| Database | MySQL 8.3+ or MariaDB 11.1+ | [CITED: kimai.org/documentation/developers.html] | +| SQLite | NOT supported (removed 2021) | [VERIFIED: kimai.org/en/blog/2021/sqlite-and-ftp-support-removed] | + +### Required PHP Extensions + +From Kimai's `composer.json` [VERIFIED]: + +`ext-gd`, `ext-intl`, `ext-json`, `ext-mbstring`, `ext-pdo`, `ext-tokenizer`, `ext-xml`, `ext-xsl`, `ext-zip` + +Plus `pdo_mysql` for MariaDB connectivity. + +## Architecture Patterns + +### Nix Flake Structure + +``` +flake.nix # PHP, MariaDB, Composer, Node, symfony-cli, process-compose +flake.lock +dev/ + process-compose.yaml # MariaDB + symfony web server process definitions + setup.sh # One-time: clone Kimai, composer install, DB setup, fixtures, symlink plugin + seed.sh # Re-run fixtures without full setup +``` + +### Pattern 1: Local MariaDB with Data Directory in Project + +**What:** MariaDB runs from a local data directory (`./dev/.mariadb-data/`) using `mysqld --datadir` without needing root or system-level MySQL installation. [ASSUMED] +**When to use:** Always for this project -- avoids polluting the system. +**Example:** + +```bash +# Initialize MariaDB data directory +mysql_install_db --datadir=./dev/.mariadb-data --auth-root-authentication-method=normal + +# Start MariaDB on a non-standard port (to avoid conflicts) +mysqld --datadir=./dev/.mariadb-data --socket=./dev/.mariadb.sock --port=3307 --skip-grant-tables +``` + +### Pattern 2: Kimai Clone as Git-Ignored Dependency + +**What:** Clone Kimai into `./dev/kimai/` (git-ignored), run `composer install` there, then symlink the plugin directory into `./dev/kimai/var/plugins/KimaiHeatmapBundle`. +**Why:** Keeps the host application separate from plugin source. Kimai is a dependency, not part of the plugin repo. + +``` +dev/ + kimai/ # .gitignore'd -- cloned Kimai instance + var/ + plugins/ + KimaiHeatmapBundle -> ../../../../ # symlink to project root +``` + +### Pattern 3: Plugin Symlink for Live Development + +**What:** Symlink the project root (which IS the plugin bundle) into Kimai's `var/plugins/` directory. [CITED: kimai.org/documentation/plugins.html] +**Why:** Code changes in the plugin are immediately reflected. No copy/install step needed. + +```bash +ln -sf "$(pwd)" ./dev/kimai/var/plugins/KimaiHeatmapBundle +``` + +**Critical:** The symlink target directory name MUST match the bundle class name (`KimaiHeatmapBundle`). + +### Pattern 4: process-compose for Multi-Service Dev + +**What:** `process-compose` manages MariaDB and Symfony dev server as a single dev stack. +**Why:** Both services need to run simultaneously. process-compose handles startup order, log aggregation, and clean shutdown. Better than manual terminal management. + +```yaml +# dev/process-compose.yaml +version: "0.5" +processes: + mariadb: + command: mysqld --datadir=./dev/.mariadb-data --socket=./dev/.mariadb.sock --port=3307 + readiness_probe: + exec: + command: mysqladmin --socket=./dev/.mariadb.sock ping + initial_delay_seconds: 2 + period_seconds: 1 + kimai: + command: symfony server:start --no-tls --dir=./dev/kimai + depends_on: + mariadb: + condition: process_healthy +``` + +### Anti-Patterns to Avoid + +- **Using SQLite for dev:** Kimai dropped SQLite support. Migrations will fail. No workaround. [VERIFIED] +- **Installing Kimai globally or via Composer require:** Kimai is the host application, not a library. Clone it. +- **Trying to Nixify Composer dependencies:** Let Composer manage `vendor/` normally inside the Kimai clone. Nix provides the PHP binary and extensions only. +- **Running MariaDB as a system service:** Use a local data directory. No root, no systemd, no conflicts with existing databases. + +## Don't Hand-Roll + +| Problem | Don't Build | Use Instead | Why | +|---------|-------------|-------------|-----| +| Test data seeding | Custom SQL insert scripts | `bin/console kimai:reset:dev` | Kimai ships Doctrine fixtures that create users, customers, projects, activities, and 100-1000+ timesheet entries per user spanning 3 years [VERIFIED: TimesheetFixtures.php] | +| PHP dev web server | nginx/Apache config | `symfony server:start` | Handles PHP-FPM, routing, and `.env` loading automatically [VERIFIED: nixpkgs symfony-cli 5.16.0] | +| Process management | Shell scripts with `&` and `trap` | `process-compose` | Handles startup ordering, health checks, log aggregation, clean shutdown [VERIFIED: nixpkgs process-compose 1.78.0] | +| MariaDB initialization | Manual SQL DDL | `mysql_install_db` + `kimai:install` | Standard tooling that handles system tables, migrations, and schema creation | + +## Database Schema (for Seeding Context) + +Key tables for timesheet data [VERIFIED: Kimai source Entity classes]: + +| Table | Key Columns | Notes | +|-------|-------------|-------| +| `kimai2_timesheet` | `id`, `start_time`, `end_time`, `duration`, `timezone`, `date_tz`, `user` (FK), `project_id` (FK), `activity_id` (FK), `rate`, `billable`, `exported` | Duration in seconds | +| `kimai2_customers` | `id`, `name`, `country`, `currency`, `timezone`, `visible` | Required parent for projects | +| `kimai2_projects` | `id`, `name`, `customer_id` (FK), `visible`, `billable` | Required parent for activities | +| `kimai2_activities` | `id`, `name`, `project_id` (FK, nullable), `visible`, `billable` | Can be global (null project) or project-specific | +| `kimai2_users` | `id`, `username`, `email`, `roles`, `password`, `timezone` | Created via `kimai:user:create` | + +**The `kimai:reset:dev` command** loads Doctrine fixtures that populate ALL these tables with realistic data, including: +- 5 users (clara_customer, john_user, tony_teamlead, anna_admin, susan_super) -- all with password "password" [CITED: kimai.org/documentation/developers.html] +- Multiple customers, projects, and activities +- 100-1000 timesheet entries per user spanning ~3 years back [VERIFIED: TimesheetFixtures.php source] + +## Kimai Setup Commands (Exact Sequence) + +```bash +# 1. Clone Kimai at specific version +git clone -b 2.52.0 --depth 1 https://github.com/kimai/kimai.git dev/kimai + +# 2. Configure environment +cp dev/kimai/.env.dist dev/kimai/.env +# Edit .env: set DATABASE_URL, APP_SECRET + +# 3. Install PHP dependencies (--no-dev is fine for plugin dev; add --dev if you need Kimai's own test tools) +cd dev/kimai && composer install --no-dev --optimize-autoloader + +# 4. Create database and run migrations +bin/console kimai:install -n + +# 5. Load demo fixtures (drops and recreates schema with test data!) +bin/console kimai:reset:dev + +# 6. Symlink plugin +ln -sf "$(pwd)/../../" var/plugins/KimaiHeatmapBundle + +# 7. Clear cache (required after adding plugin) +bin/console cache:clear +``` + +**DATABASE_URL format for local MariaDB:** +``` +DATABASE_URL=mysql://root@127.0.0.1:3307/kimai?charset=utf8mb4&serverVersion=11.4.8-MariaDB +``` + +## Common Pitfalls + +### Pitfall 1: MariaDB Socket vs TCP Connection + +**What goes wrong:** Kimai tries to connect via Unix socket (default MySQL behavior) but the local MariaDB is listening on a custom socket path or TCP port. +**Why it happens:** Default MySQL client config looks for `/var/run/mysqld/mysqld.sock` which doesn't exist in a Nix devshell. +**How to avoid:** Use TCP connection (`127.0.0.1:3307`) in DATABASE_URL, not `localhost` (which triggers socket mode on Linux). Or set `--socket` path explicitly. +**Warning signs:** "Can't connect to local MySQL server through socket" errors. + +### Pitfall 2: Kimai Cache Issues After Plugin Symlink + +**What goes wrong:** After symlinking the plugin, Kimai doesn't see it or throws container compilation errors. +**Why it happens:** Symfony caches the container, routes, and bundle list. A new plugin requires cache clear. +**How to avoid:** Always run `bin/console cache:clear` after symlinking. In dev, set `APP_ENV=dev` to enable auto-recompilation. +**Warning signs:** Plugin not listed in admin, or DI errors referencing old cached container. + +### Pitfall 3: PHP Extension Mismatch in Nix + +**What goes wrong:** Kimai fails at runtime with "extension not found" errors even though you listed extensions in `flake.nix`. +**Why it happens:** Nix PHP needs explicit extension configuration via `php.withExtensions` or `php.buildEnv`. Simply adding `php82` doesn't include all extensions. +**How to avoid:** Build a custom PHP with required extensions: +```nix +php = pkgs.php82.buildEnv { + extensions = { enabled, all }: enabled ++ (with all; [ xsl ]); +}; +``` +The default `enabled` set includes most common extensions (gd, intl, mbstring, pdo, xml, zip, etc.) but `xsl` is often missing. [ASSUMED] +**Warning signs:** `composer install` fails, or Kimai throws "class not found" for Intl/GD operations. + +### Pitfall 4: kimai:reset:dev Drops Everything + +**What goes wrong:** Running `kimai:reset:dev` drops the entire database schema and reloads fixtures. Developers run it thinking it adds data on top of existing data. +**Why it happens:** The command runs `doctrine:fixtures:load` which purges all tables first. +**How to avoid:** Only run during initial setup or when you explicitly want a clean slate. For adding entries, write a separate seed script or use Kimai's API. +**Warning signs:** Custom data disappears after running the command. + +### Pitfall 5: Plugin Directory Name Must Match Bundle Class + +**What goes wrong:** Symlink named `heatmap-plugin` or `KimaiHeatmap` instead of `KimaiHeatmapBundle`. Kimai's plugin loader silently ignores it. +**Why it happens:** Kimai expects the directory name in `var/plugins/` to match the bundle class name exactly. +**How to avoid:** Name the symlink `KimaiHeatmapBundle` to match the class `KimaiHeatmapBundle.php`. +**Warning signs:** Plugin not appearing in Kimai admin, no errors in logs. + +## Validation Architecture + +### Test Framework + +| Property | Value | +|----------|-------| +| Framework | PHPUnit (match Kimai's version) | +| Config file | None yet -- Wave 0 task | +| Quick run command | `php vendor/bin/phpunit --filter TestName` | +| Full suite command | `php vendor/bin/phpunit` | + +### Phase Requirements to Test Map + +| Req ID | Behavior | Test Type | Automated Command | File Exists? | +|--------|----------|-----------|-------------------|-------------| +| DEV-01 | Nix devshell provides PHP, Composer, Node | smoke | `nix develop --command bash -c "php --version && composer --version && node --version"` | N/A (manual) | +| DEV-02 | Seeded database has timesheet entries | smoke | `cd dev/kimai && bin/console doctrine:query:sql "SELECT COUNT(*) FROM kimai2_timesheet"` | N/A (manual) | +| DEV-03 | Plugin is loadable | smoke | `cd dev/kimai && bin/console kimai:plugins` (should list the plugin) | N/A (manual) | + +### Wave 0 Gaps + +- [ ] None for this phase -- DEV-01/02/03 are infrastructure tasks verified by smoke commands, not unit tests. + +## Environment Availability + +| Dependency | Required By | Available | Version | Fallback | +|------------|------------|-----------|---------|----------| +| Nix | Flake/devshell | Yes | 2.93.3 (Lix) | -- | +| PHP 8.2 | Kimai runtime | Yes (system) | 8.2.29 | Provided by Nix flake | +| Composer | PHP deps | Yes (system) | 2.8.12 | Provided by Nix flake | +| Node.js | JS tooling | Yes (system) | 22.21.1 | Provided by Nix flake | +| MariaDB | Database | No (not installed) | -- | Provided by Nix flake (11.4.8 available) | +| symfony-cli | Dev server | No (not installed) | -- | Provided by Nix flake (5.16.0 available) | +| process-compose | Process mgmt | No (not installed) | -- | Provided by Nix flake (1.78.0 available) | + +**Missing dependencies with no fallback:** None -- all provided by the Nix flake. + +## Plugin Loading Mechanism + +Kimai discovers plugins automatically from `var/plugins/`. Key details [CITED: kimai.org/documentation/plugins.html]: + +1. Plugin directory must contain a bundle class that `implements PluginInterface` +2. Namespace must be `KimaiPlugin\` (the `KimaiPlugin` vendor prefix is mandatory) +3. Routes auto-load from `Resources/config/routes.{php,yaml}` inside the plugin directory +4. Plugins load in ALL environments without modifying `config/bundles.php` +5. The `composer.json` must have `"type": "kimai-plugin"` and an `extra.kimai` section with `require` (minimum Kimai version as integer: major*10000 + minor*100 + patch) and `name` + +### Minimum Plugin composer.json + +```json +{ + "name": "kimai-plugin/heatmap-bundle", + "type": "kimai-plugin", + "description": "GitHub-style activity heatmap dashboard widget", + "license": "MIT", + "autoload": { + "psr-4": { + "KimaiPlugin\\KimaiHeatmapBundle\\": "" + } + }, + "extra": { + "kimai": { + "require": 25200, + "name": "Activity Heatmap" + } + } +} +``` + +## Assumptions Log + +| # | Claim | Section | Risk if Wrong | +|---|-------|---------|---------------| +| A1 | MariaDB can run with `--datadir` pointing to a local project directory without root | Architecture Patterns | HIGH -- would need alternative DB setup approach (Docker, system service) | +| A2 | Nix php82 default `enabled` extensions cover most of Kimai's requirements except `xsl` | Common Pitfalls | MEDIUM -- may need to explicitly list more extensions | +| A3 | `symfony server:start` works with the Nix-provided PHP binary | Architecture Patterns | LOW -- symfony-cli auto-detects PHP; fallback is `php -S` | + +## Open Questions + +1. **Exact `php.buildEnv` extension list needed** + - What we know: Kimai requires gd, intl, mbstring, pdo_mysql, xml, xsl, zip, tokenizer, json + - What's unclear: Which of these are in Nix's default `enabled` set vs. needing explicit addition + - Recommendation: Start with `enabled ++ [xsl pdo_mysql]` and iterate if `composer install` fails + +2. **MariaDB version compatibility with Kimai migrations** + - What we know: Kimai docs say MariaDB 11.1+; nixpkgs has 11.4.8 + - What's unclear: Whether any migration uses MySQL-specific syntax that breaks on MariaDB 11.4 + - Recommendation: Use 11.4.8 (latest in nixpkgs); if issues arise, try 10.11.14 (`mariadb_1011`) + +## Sources + +### Primary (HIGH confidence) +- [GitHub kimai/kimai releases](https://github.com/kimai/kimai/releases) -- version 2.52.0 confirmed +- [Kimai composer.json](https://raw.githubusercontent.com/kimai/kimai/refs/heads/main/composer.json) -- PHP/Symfony versions, extensions +- [Kimai Entity/Timesheet.php](https://github.com/kimai/kimai/blob/main/src/Entity/Timesheet.php) -- database schema +- [Kimai Entity/Customer.php](https://github.com/kimai/kimai/blob/main/src/Entity/Customer.php) -- customer table schema +- [Kimai Entity/Project.php](https://github.com/kimai/kimai/blob/main/src/Entity/Project.php) -- project table schema +- [Kimai Entity/Activity.php](https://github.com/kimai/kimai/blob/main/src/Entity/Activity.php) -- activity table schema +- [Kimai WidgetInterface.php](https://github.com/kimai/kimai/blob/main/src/Widget/WidgetInterface.php) -- widget API +- [Kimai TimesheetFixtures.php](https://github.com/kimai/kimai/blob/main/src/DataFixtures/TimesheetFixtures.php) -- fixture data details +- [Kimai DemoBundle](https://github.com/kimai/DemoBundle) -- reference plugin implementation + +### Secondary (MEDIUM confidence) +- [Kimai plugin documentation](https://www.kimai.org/documentation/plugins.html) -- plugin loading mechanism +- [Kimai installation docs](https://www.kimai.org/documentation/installation.html) -- setup commands +- [Kimai developer docs](https://www.kimai.org/documentation/developers.html) -- dev environment, fixture commands +- [Kimai SQLite removal announcement](https://www.kimai.org/en/blog/2021/sqlite-and-ftp-support-removed) -- database requirements +- [loophp/nix-shell](https://github.com/loophp/nix-shell) -- Nix PHP packaging reference + +### Tertiary (LOW confidence) +- None + +## Metadata + +**Confidence breakdown:** +- Standard stack: HIGH -- versions verified against nixpkgs and Kimai source +- Architecture: HIGH for Kimai plugin mechanics (verified against source), MEDIUM for Nix MariaDB setup (assumed pattern) +- Pitfalls: HIGH -- based on verified Kimai constraints (no SQLite, cache clearing, fixture behavior) + +**Research date:** 2026-04-08 +**Valid until:** 2026-05-08 (Kimai releases roughly monthly; pin to 2.52.0)