WP test container created

This commit is contained in:
jasa
2026-09-24 18:06:11 +02:00
parent 3ee471bf39
commit e2442d1d15
44 changed files with 2244 additions and 47 deletions
+87
View File
@@ -0,0 +1,87 @@
# The demo WordPress
A clean install with **no Avada and no plugins**, so the theme has to stand on
its own. The `wp-migrate` rig is the opposite — the production tree, kept as the
reference to compare against — and is never touched from here.
cd local
docker compose up -d # http://localhost:8090/
Docroot is the project's own `../site`, so a `npm run build` lands in the running
theme with no copy step and no restart.
## Logging in
http://localhost:8090/wp-admin/ admin / tlig
Throwaway credentials for a throwaway local install. Nothing here is ever
deployed, and the database lives in a named volume, not in the repo.
## wp-cli
The `cli` service is behind a Compose profile so it never starts with the site.
Run it one command at a time:
docker compose --profile cli run --rm cli wp user list
docker compose --profile cli run --rm cli wp plugin list
docker compose --profile cli run --rm cli wp post list --post_type=page
Adding a user, for instance:
docker compose --profile cli run --rm cli \
wp user create editor editor@example.org --role=editor --user_pass=tlig
It talks to MySQL directly — it does not go through Apache — so it works even
when the site itself is down. It shares the same `../site` mount and the same
`wp-config.php` as the web container, so the two can never disagree about which
database they are looking at.
## Resetting
docker compose down -v # -v also drops the database volume
`site/` survives, because it is the repo's own tree. Re-running `wp core install`
gives a fresh site against the same files.
## Offline by design
`wp-config.php` sets `WP_HTTP_BLOCK_EXTERNAL`: the rig boots with no network and
never reports this install to WordPress.org. Blocking the request does not stop
WordPress making it, though — the update checks still ran, each got a `WP_Error`
back, and under `WP_DEBUG` each printed a warning *before* the headers, so every
redirect and cookie after it failed too. The message it printed ("could not
establish a secure connection") was misleading: nothing was wrong with TLS, the
request was refused locally.
`site/wp-content/mu-plugins/tlig-offline.php` stops the checks at the source
instead of letting them fail. Keep it; without it the admin fills with warnings.
## The menus
`site/wp-content/import/` holds the fifteen desktop menu trees lifted from the
live site — 748 items, structure and labels only, with UberMenu's 149-key
settings blob reduced to the eight fields the audit shows are ever used. Re-run
it after changing `menus.json`; it replaces each tree rather than doubling it:
docker compose --profile cli run --rm cli \
wp eval-file wp-content/import/import-menus.php
It takes a couple of minutes — `wp_update_nav_menu_item` is not fast — and
finishes with a count of menus, items and images.
`?lang=el` picks the language (WPML is not installed; `inc/demo-languages.php`
stands in for it). `?open=3` holds the third panel open and `?open=all` drops
every panel into the flow — the second is the argument in one picture: seven
panels of seven heights, all from one 328px file.
## Regenerating the parchment
python3 tools/cut-parchment.py && npm run build
It reads `papyrus_submenu_450_cnd.png` from the production tree, so that path
has to exist. The tile is the master whole — 1200x450, WebP at quality 90 —
because every narrower cut has to manufacture junctions the artwork does not
have, and the corners showed it badly. The script's header records what was
tried; its output prints the four corner junctions (the master's own, so a
regression check rather than a correction) and how many vertical repeats each
panel height needs.
+22
View File
@@ -0,0 +1,22 @@
# Logs and the PID file must go to writable paths: /var/log/apache2 and
# /var/run/apache2 are root-owned in the image, and this container runs as an
# unprivileged uid so that it matches the owner of the bind-mounted tree.
ErrorLog /dev/stderr
PidFile /tmp/apache2.pid
ServerName localhost
<Directory /var/www/html>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Fonts are the only subresource the browser always fetches in CORS mode, and
# WordPress emits asset URLs from WP_HOME — so opening the site at any other
# host turns every font into a blocked cross-origin request. Fine on a local
# rig; never in production.
<IfModule mod_headers.c>
<FilesMatch "\.(woff2?|ttf|otf|eot)$">
Header always set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
+4
View File
@@ -0,0 +1,4 @@
# Apache runs as an unprivileged uid here, so it cannot bind port 80. It listens
# on 8080 inside the container; compose publishes that as 8090 on the host, one
# port along from the wp-migrate rig so both can run side by side.
Listen 8080
+12
View File
@@ -0,0 +1,12 @@
# Replaces the image's 000-default.conf, which is bound to port 80.
<VirtualHost *:8080>
DocumentRoot /var/www/html
ErrorLog /dev/stderr
CustomLog /dev/stdout combined
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
+88
View File
@@ -0,0 +1,88 @@
# A clean WordPress for the TLIG theme.
#
# Deliberately NOT the wp-migrate rig. That one bind-mounts the production tree
# — Avada, 55 plugins, a 1.5 GB database — and exists to mirror what ships. This
# one boots in seconds with nothing but core, so the theme has to stand on its
# own. Keep both: the other is the reference to compare against.
name: tlig-theme
services:
db:
image: mysql:8.4
command: ["--mysql-native-password=ON"]
environment:
MYSQL_DATABASE: tlig
MYSQL_USER: tlig
MYSQL_PASSWORD: tlig
MYSQL_ROOT_PASSWORD: tlig
volumes:
- db:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-ptlig"]
interval: 5s
timeout: 5s
retries: 20
wp:
image: wordpress:php8.3-apache
# Skip the image's core-unpacking entrypoint: core is already on disk in
# site/, extracted from this same image so the versions cannot drift. The
# entrypoint would otherwise try to chmod a bind mount it does not own.
entrypoint: ["apache2-foreground"]
# Fedora runs SELinux enforcing, which blocks container access to bind
# mounts — including, silently, the config files below, which is why Apache
# kept binding port 80 despite a mounted ports.conf.
security_opt:
- label=disable
# Rootless podman maps the host user to container root by default, but
# Apache refuses to run workers as root. keep-id maps host uid 1000 to
# container uid 1000, so the container process is literally the owner of the
# bind-mounted tree. Under rootful docker, drop this and set RUN_UID=1000.
userns_mode: "keep-id"
depends_on:
db:
condition: service_healthy
ports:
- "8090:8080"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: tlig
WORDPRESS_DB_USER: tlig
WORDPRESS_DB_PASSWORD: tlig
WORDPRESS_DEBUG: "1"
WORDPRESS_CONFIG_EXTRA: |
define( 'WP_DEBUG_DISPLAY', true );
define( 'SCRIPT_DEBUG', true );
define( 'DISALLOW_FILE_EDIT', true );
# The project's own site/ is the docroot, so a Vite build lands in the
# running theme immediately — no copy step, no container restart. Config is
# mounted over the top, so site/ never holds environment-specific settings.
volumes:
- ../site:/var/www/html
- ./wp-config.php:/var/www/html/wp-config.php:ro
- ./apache-ports.conf:/etc/apache2/ports.conf:ro
- ./apache-vhost.conf:/etc/apache2/sites-enabled/000-default.conf:ro
- ./apache-local.conf:/etc/apache2/conf-enabled/zz-local.conf:ro
user: "${RUN_UID:-1000}:${RUN_GID:-1000}"
cli:
image: wordpress:cli-php8.3
security_opt:
- label=disable
userns_mode: "keep-id"
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: tlig
WORDPRESS_DB_USER: tlig
WORDPRESS_DB_PASSWORD: tlig
volumes:
- ../site:/var/www/html
- ./wp-config.php:/var/www/html/wp-config.php:ro
user: "${RUN_UID:-1000}:${RUN_GID:-1000}"
profiles: ["cli"]
volumes:
db:
+47
View File
@@ -0,0 +1,47 @@
<?php
/**
* Local config for the clean TLIG rig.
*
* Mounted over /var/www/html/wp-config.php, so site/ stays free of config and
* nothing environment-specific can be committed by accident.
*/
define( 'DB_NAME', 'tlig' );
define( 'DB_USER', 'tlig' );
define( 'DB_PASSWORD', 'tlig' );
define( 'DB_HOST', 'db' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );
// Local only — these are not secrets and are not used anywhere else.
define( 'AUTH_KEY', 'tlig-local-auth' );
define( 'SECURE_AUTH_KEY', 'tlig-local-secure-auth' );
define( 'LOGGED_IN_KEY', 'tlig-local-logged-in' );
define( 'NONCE_KEY', 'tlig-local-nonce' );
define( 'AUTH_SALT', 'tlig-local-auth-salt' );
define( 'SECURE_AUTH_SALT', 'tlig-local-secure-auth-salt' );
define( 'LOGGED_IN_SALT', 'tlig-local-logged-in-salt' );
define( 'NONCE_SALT', 'tlig-local-nonce-salt' );
$table_prefix = 'wp_';
define( 'WP_HOME', 'http://localhost:8090' );
define( 'WP_SITEURL', 'http://localhost:8090' );
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', true );
define( 'SCRIPT_DEBUG', true );
define( 'DISALLOW_FILE_EDIT', true );
// Outbound HTTP is allowed. It was blocked here once, to stop the rig
// reporting itself to WordPress.org on every admin page — but blocking the
// domain also broke Add Plugins, which has to reach the directory to list
// anything. The automatic calls are stopped where they start instead, in
// site/wp-content/mu-plugins/tlig-offline.php; deliberate ones go out.
define( 'AUTOMATIC_UPDATER_DISABLED', true );
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
require_once ABSPATH . 'wp-settings.php';