Phase 5: production FrankenPHP image (Dockerfile, Caddyfile, entrypoint), post-commit deploy hook, README
This commit is contained in:
parent
8863585fd6
commit
39661d3ca7
6 changed files with 200 additions and 0 deletions
16
.dockerignore
Normal file
16
.dockerignore
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.git
|
||||
.github
|
||||
auth.json
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vendor
|
||||
node_modules
|
||||
storage/framework/cache/data/*
|
||||
storage/framework/sessions/*
|
||||
storage/framework/views/*
|
||||
storage/logs/*
|
||||
docker/develop.compose.yaml
|
||||
Caddyfile.dev
|
||||
dev.sh
|
||||
*.db
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -22,3 +22,5 @@ yarn-error.log
|
|||
/.nova
|
||||
/.vscode
|
||||
/.zed
|
||||
|
||||
.env.deploy
|
||||
|
|
|
|||
45
Dockerfile
Normal file
45
Dockerfile
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: PHP dependencies (Flux Pro auth injected as a BuildKit secret)
|
||||
FROM docker.io/library/composer:2 AS composer-builder
|
||||
WORKDIR /app
|
||||
COPY composer.json composer.lock ./
|
||||
RUN --mount=type=secret,id=composer_auth,target=/tmp/auth.json \
|
||||
composer install \
|
||||
--no-dev \
|
||||
--no-interaction \
|
||||
--no-plugins \
|
||||
--no-scripts \
|
||||
--prefer-dist \
|
||||
--optimize-autoloader
|
||||
COPY . .
|
||||
RUN composer dump-autoload --optimize --no-dev
|
||||
|
||||
# Stage 2: frontend assets — needs vendor/livewire/flux CSS present
|
||||
FROM docker.io/library/node:20-alpine AS node-builder
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
COPY --from=composer-builder /app/vendor ./vendor
|
||||
RUN npm run build
|
||||
|
||||
# Stage 3: production image — FrankenPHP serves the app (no nginx, no supervisor)
|
||||
FROM docker.io/dunglas/frankenphp:1.12-php8.5
|
||||
|
||||
RUN install-php-extensions pdo_mysql intl zip opcache
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=composer-builder /app /app
|
||||
COPY --from=node-builder /app/public/build /app/public/build
|
||||
|
||||
COPY docker/Caddyfile /etc/caddy/Caddyfile
|
||||
COPY docker/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh \
|
||||
&& mkdir -p storage/framework/{sessions,views,cache/data} storage/logs bootstrap/cache \
|
||||
&& chown -R www-data:www-data /app/storage /app/bootstrap/cache
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
90
README.md
Normal file
90
README.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# festbench-web
|
||||
|
||||
Web viewer for [FestBench](../llm_benchmark) results — LLM benchmark datapoints
|
||||
submitted by the `festbench` CLI, displayed in a fast, sortable table.
|
||||
|
||||
Stack: Laravel 12 · Livewire 4 + Volt · Flux UI (Pro) · FrankenPHP · MySQL/MariaDB
|
||||
|
||||
## Features
|
||||
|
||||
- **Home** — every datapoint festbench captures (per-task rows across all runs),
|
||||
server-side sortable and filterable (suite, level, model, pass/fail, search).
|
||||
- **Run detail** (`/runs/{run_id}`) — full provenance: model identity, engine
|
||||
build, GPU, image digest, validators, suite versions, plus per-task metrics.
|
||||
- **Ownership overlay** — festbench-submitted data is immutable; owners can add
|
||||
notes and toggle public/private visibility. Immutability is enforced in the
|
||||
model (`BenchmarkRun::OVERLAY_FIELDS`) and covered by tests.
|
||||
- **My Runs** — list your own runs, flip visibility, see pass counts.
|
||||
- `festbench:import` — import a local festbench `benchmarks.db` (SQLite):
|
||||
`php artisan festbench:import results/benchmarks.db --user=you@example.com`
|
||||
|
||||
## Visibility rules
|
||||
|
||||
Runs are public by default (matching the CLI's redaction philosophy). Guests see
|
||||
public runs only; authenticated users additionally see their own private runs.
|
||||
|
||||
## Local development
|
||||
|
||||
Requirements: `frankenphp` binary on PATH, Node 20+, Docker/Podman.
|
||||
|
||||
```bash
|
||||
docker compose -f docker/develop.compose.yaml up -d # MariaDB on 127.0.0.1:3306
|
||||
./dev.sh # frankenphp :8080 + vite HMR
|
||||
```
|
||||
|
||||
Composer runs through frankenphp's embedded PHP (the host php.ini disables
|
||||
most extensions):
|
||||
|
||||
```bash
|
||||
frankenphp php-cli $(pwd)/vendor/bin/composer install
|
||||
frankenphp php-cli $(pwd)/vendor/bin/pest
|
||||
frankenphp php-cli $(pwd)/vendor/bin/pint
|
||||
frankenphp php-cli artisan <command>
|
||||
```
|
||||
|
||||
The dev DB deliberately lives in a **container, reached over TCP** — the same
|
||||
topology as production, where MariaDB runs on a nearby host. Point `.env`
|
||||
`DB_HOST`/`DB_PORT` at wherever the DB actually lives.
|
||||
|
||||
## Production (Coolify)
|
||||
|
||||
Build pack: **Dockerfile**. The image is a single FrankenPHP process — no
|
||||
nginx, no php-fpm, no supervisor.
|
||||
|
||||
- `Dockerfile` — 3 stages: composer (Flux Pro auth as a BuildKit **secret**),
|
||||
node assets, final `dunglas/frankenphp:1.12-php8.5` + `install-php-extensions
|
||||
pdo_mysql intl zip opcache`.
|
||||
- `docker/entrypoint.sh` — retries `artisan migrate --force`, then `artisan
|
||||
optimize`, then `exec frankenphp run`.
|
||||
- `docker/Caddyfile` — listens on `{$SERVER_NAME::8080}`; Coolify maps its
|
||||
port to it. Static files served directly by Caddy, Laravel via `php_server`.
|
||||
|
||||
Secrets/env to set in Coolify:
|
||||
|
||||
| Variable | Notes |
|
||||
|---|---|
|
||||
| `APP_KEY` | generate once, keep stable |
|
||||
| `DB_HOST` / `DB_PORT` / `DB_DATABASE` / `DB_USERNAME` / `DB_PASSWORD` | the nearby MariaDB |
|
||||
| `SESSION_DRIVER=database`, `CACHE_STORE=database` | no redis needed |
|
||||
|
||||
Flux Pro authentication: add a secret named `composer_auth`
|
||||
(file contents = the project's `auth.json`) and configure Coolify to build with
|
||||
`--secret id=composer_auth,src=…` (or `docker build --secret` in CI). The
|
||||
license key is never committed.
|
||||
|
||||
Health check: `GET /up`.
|
||||
|
||||
## Submitting results (CLI integration — separate session)
|
||||
|
||||
The schema (`benchmark_runs`, `run_results`) mirrors festbench's SQLite exactly,
|
||||
promotes the sortable columns, and stores the **complete source row as JSON**
|
||||
(`run_results.payload`) so new festbench fields require no migration. The
|
||||
authed ingestion endpoint for `festbench --submit` is future work; the
|
||||
`festbench:import` command is the reference implementation of that mapping.
|
||||
|
||||
## Deferred
|
||||
|
||||
- `scenario_runs` (scenariobench's agentic metrics: turns, submissions, oracle
|
||||
verdicts) — intentionally not imported yet.
|
||||
- Worker mode (`FRANKENPHP_CONFIG="worker ..."`) — available as a one-env-var
|
||||
toggle when throughput demands it.
|
||||
21
docker/Caddyfile
Normal file
21
docker/Caddyfile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Production config. Coolify sets PORT (and optionally SERVER_NAME); the app
|
||||
# listens on :8080 which Coolify maps to the exposed port.
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
|
||||
{$SERVER_NAME::8080} {
|
||||
root * /app/public
|
||||
encode zstd br gzip
|
||||
|
||||
# php_server serves existing static files directly and falls back to
|
||||
# index.php for everything else (Laravel routing).
|
||||
php_server {
|
||||
try_files {path} index.php
|
||||
}
|
||||
|
||||
log {
|
||||
output stdout
|
||||
format console
|
||||
}
|
||||
}
|
||||
26
docker/entrypoint.sh
Normal file
26
docker/entrypoint.sh
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
cd /app
|
||||
|
||||
# Storage must be writable at runtime (Coolify may mount a fresh volume).
|
||||
mkdir -p storage/framework/sessions storage/framework/views storage/framework/cache/data storage/logs bootstrap/cache
|
||||
chown -R www-data:www-data storage bootstrap/cache
|
||||
|
||||
# Migrate before serving, but never leave the container crash-looping on a
|
||||
# temporarily unavailable DB: retry a few times, then start anyway and let the
|
||||
# health endpoint report the state.
|
||||
i=0
|
||||
until php artisan migrate --force --no-ansi; do
|
||||
i=$((i+1))
|
||||
if [ "$i" -ge 5 ]; then
|
||||
echo "festbench-web: migrations failed after 5 attempts, starting anyway" >&2
|
||||
break
|
||||
fi
|
||||
echo "festbench-web: migrate failed (attempt $i), retrying in 3s…" >&2
|
||||
sleep 3
|
||||
done
|
||||
|
||||
php artisan optimize --no-ansi || true
|
||||
|
||||
exec frankenphp run --config /etc/caddy/Caddyfile
|
||||
Loading…
Reference in a new issue