This page covers the developer tooling. For running the stack itself, see the
README. All recipes below are listed by just (run just with
no arguments to see them).
PHPStan checks the PHP code for type errors and other
bugs without running it. It is a require-dev dependency and runs inside the
php container (which has the mongodb extension the analysis needs):
just php-analyzeThe configuration lives in app/phpstan.neon. The code
is kept passing at level 8 (PHPStan's strictest level).
PHP CS Fixer checks the PHP code
for consistent formatting and style. Like PHPStan it is a require-dev
dependency and runs inside the php container:
just php-format-checkTo apply the fixes automatically:
just php-formatThe ruleset lives in
app/.php-cs-fixer.dist.php (tab indentation
plus a curated set of whitespace, casing, import and trailing-comma rules — not
a full reformat preset).
Twig CS Fixer checks the Twig
templates for consistent formatting and style. Like PHPStan it is a require-dev
dependency and runs inside the php container:
just twig-format-checkTo apply the fixes automatically:
just twig-formatThe ruleset lives in
app/.twig-cs-fixer.dist.php (tab indentation
plus delimiter/operator/punctuation spacing and trailing-whitespace rules).
The live-status UI is built from the TypeScript/React sources in
app/frontend/, bundled by esbuild
into app/public/assets/frontend/ (generated; not committed). Everything runs
in an ephemeral Node.js container (bin/node), so there are no host dependencies.
just js-buildjust run builds the assets automatically on first use and when files under
app/frontend/ or the Node Dockerfile are newer than the last successful build.
The app/public/assets/frontend/completed marker prevents unnecessary rebuilds.
Use just js-build to rebuild immediately without starting the stack.
While developing, rebuild on every change:
just js-build-continuouslyLinting (ESLint) and type-checking (strict TypeScript) together:
just js-analyzeType checking uses TypeScript 7's native tsc, installed under the
@typescript/native npm alias. ESLint still needs the TypeScript 6 JavaScript API,
so typescript aliases @typescript/typescript6, which re-exports that API and
provides tsc6. Keep both aliases when updating dependencies; see Microsoft's
side-by-side setup.
just js-autofix applies the auto-fixable ESLint fixes, and
just js-pull-dependencies populates app/frontend/node_modules on the host
so IDE autocompletion works.
Renovate tracks Composer, npm, Dockerfiles, Compose images and mise. Custom
extractors also track the Composer helper image, the Alpine image in justfile,
and the MongoDB PHP extension built in the PHP Dockerfile. The PHP image name in
Compose is a local build tag, so it is excluded from registry lookups.
The mail relay uses the maintained ghcr.io/devture/exim-relay registry;
explicit version rules track its release/build suffixes and Nagios's build-N tags.
The main php85 APK package is pinned so Renovate detects PHP patch and package
revision updates even when the Alpine image tag stays unchanged. Keep its APK
registry branch in renovate.json aligned with the PHP Dockerfile's Alpine base.
The PHP_VERSION argument selects the extension package names and must match the
pinned PHP package's major/minor series. Node's version rule also tracks its
Alpine suffix, which Docker's default compatibility rules would otherwise retain.
Runtime image and PHP package updates take priority in Renovate's PR queue.
PR branches are refreshed after base-branch changes so new CI workflows and
compatibility fixes become available for ArtiDezi assessments.
ArtiDezi owns automatic merging after review and successful GitHub checks;
Renovate itself does not merge updates. Weekly lockfile maintenance refreshes
transitive dependencies within the declared ranges.
Commit both app/composer.lock and app/frontend/package-lock.json after
updates. Symfony components are grouped in Renovate; their composer.json
constraints define the release line without a second Flex extra.symfony.require
constraint blocking upgrades. Composer's platform.ext-mongodb declares the
minimum driver version for resolution in the Composer container. It does not
install the driver: the PHP image builds the pinned official extension, and
platform-check verifies required extensions when the application loads.
React/React DOM, Bootstrap, jQuery, Select2 and Font Awesome are npm dependencies.
just js-build copies their browser assets to the ignored
app/public/assets/frontend/vendor/ directory and bundles the official React
packages into one shared runtime for comploader. No manually downloaded copies
of these libraries need updating. just run rebuilds changed assets; use
just js-build to rebuild them explicitly. Recreate the PHP container after rebuilding its image with
docker compose -f compose.yml build php when its driver or base image changes.
The GitHub Check workflow installs and audits both lockfiles, builds both
development images, and runs PHPStan, PHP/Twig style checks, ESLint, TypeScript
and the asset build. It does not start the monitoring stack or send notifications.
For equivalent local checks, run:
just composer-install
./bin/composer validate --strict
./bin/composer audit --locked
docker compose -f compose.yml build php
just php-analyze php-format-check twig-format-check js-analyze js-buildprek runs a set of quick checks before each
commit. The hooks are defined in
.pre-commit-config.yaml: built-in checks
(trailing whitespace, final newlines, YAML/JSON validity, merge-conflict
markers, accidentally-committed large files or private keys) plus the PHPStan
analysis, PHP CS Fixer, Twig CS Fixer and JS/TS analysis checks above.
prek itself is pinned and provided through mise, so the
only prerequisite is having mise installed; the recipes install prek into a
project-local data directory (var/mise) on first use.
Install the git hook so the checks run automatically on git commit:
just prek-install-git-pre-commit-hookSome hooks fix issues in place (e.g. trailing whitespace). When that happens the commit is aborted so you can review and stage the fixes, then commit again.
Run the hooks manually anytime:
just prek-run-on-staged # only the staged files
just prek-run-on-all # the whole repositoryLogging goes through Monolog and is written
to the container's stderr — never to log files — so it flows to
systemd-journald (via the devture-nagadmin service) like every other container's
output, and the journal handles rotation/retention.
In dev the application logs live at notice level, so a docker compose logs -f php tail shows warnings, errors and uncaught exceptions as they happen without
the per-request router/security debug chatter. In prod the logs are buffered
and only flushed when an error occurs (so a clean request logs nothing), then the
full debug trail for that request is emitted. Tune both in
app/config/packages/monolog.yaml.
The php-fpm pool sets catch_workers_output so the application's stderr is
emitted on the php container (not bounced back through nginx as [error]).