Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.git
.github
docs
tests
vendor
node_modules
.phpunit.cache
.phpunit.result.cache
composer.lock
docker/host-app/vendor
docker/host-app/node_modules
docker/host-app/public/build
docker/host-app/bootstrap/cache
docker/host-app/storage/framework/cache/data
docker/host-app/storage/framework/sessions
docker/host-app/storage/framework/views
docker/host-app/storage/logs
docker/host-app/database/database.sqlite
17 changes: 16 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
# Force LF line endings for all text files
* text=auto eol=lf

*.sh text eol=lf
Dockerfile* text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.php text eol=lf
*.js text eol=lf
*.json text eol=lf
*.blade.php text eol=lf
*.css text eol=lf

*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
13 changes: 13 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Escalated WordPress — Docker demo (scaffold, not end-to-end)

Draft. Mounts the plugin into a stock `wordpress:6.6-php8.3-apache` image alongside MariaDB and Mailpit.

**Differs from the other escalated-* demos: uses MariaDB, not Postgres.** WordPress is a MySQL-family application and swapping its DB layer is out of scope for a demo.

**Not end-to-end.** Missing:

- WP-CLI based setup script: `wp core install`, `wp user create`, plugin activation, seed demo tickets via an `escalated` CLI subcommand if one exists.
- `/demo` picker — could be a WP page template that lists seeded users with WP auth cookie click-login via `wp_set_auth_cookie()`.
- Environment to reset the whole WP install on each `docker compose up` so the demo is reproducible.

See the PR body for the punch list.
63 changes: 63 additions & 0 deletions docker/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: escalated-wordpress-demo

services:
wordpress:
image: wordpress:6.6-php8.3-apache
ports:
- "${APP_PORT:-8090}:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: escalated
WORDPRESS_DB_PASSWORD: escalated
WORDPRESS_DB_NAME: escalated
WORDPRESS_DEBUG: "1"
WORDPRESS_CONFIG_EXTRA: |
define('WP_ENVIRONMENT_TYPE', 'development');
volumes:
- ../:/var/www/html/wp-content/plugins/escalated-wordpress:ro
- wp-data:/var/www/html
depends_on:
db:
condition: service_healthy

db:
# WordPress plays best with MySQL. Note this is the one escalated-* demo
# that doesn't use Postgres — the WP ecosystem targets MySQL/MariaDB.
image: mariadb:11-noble
environment:
MARIADB_DATABASE: escalated
MARIADB_USER: escalated
MARIADB_PASSWORD: escalated
MARIADB_ROOT_PASSWORD: escalated
healthcheck:
test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"]
interval: 3s
retries: 20

wpcli:
image: wordpress:cli-php8.3
user: "33:33"
depends_on:
wordpress:
condition: service_started
db:
condition: service_healthy
volumes:
- wp-data:/var/www/html
- ../:/var/www/html/wp-content/plugins/escalated-wordpress:ro
- ./setup.sh:/setup.sh:ro
- ./demo-picker.php:/demo-picker.php:ro
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: escalated
WORDPRESS_DB_PASSWORD: escalated
WORDPRESS_DB_NAME: escalated
command: ["sh", "/setup.sh"]

mailpit:
image: axllent/mailpit:latest
ports:
- "${MAILPIT_PORT:-8025}:8025"

volumes:
wp-data:
67 changes: 67 additions & 0 deletions docker/demo-picker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

/**
* Plugin Name: Escalated Demo Picker
* Description: Adds /demo click-to-login picker for the Docker dev environment.
*/
if (! defined('ABSPATH')) {
exit;
}

add_action('init', function () {
add_rewrite_rule('^demo/?$', 'index.php?escalated_demo=picker', 'top');
add_rewrite_rule('^demo/login/(\d+)/?$', 'index.php?escalated_demo=login&user_id=$matches[1]', 'top');
});

add_filter('query_vars', function ($vars) {
$vars[] = 'escalated_demo';
$vars[] = 'user_id';

return $vars;
});

add_action('template_redirect', function () {
$action = get_query_var('escalated_demo');
if (! $action) {
return;
}
if (getenv('APP_ENV') !== 'demo' && (defined('WP_ENVIRONMENT_TYPE') && WP_ENVIRONMENT_TYPE !== 'development')) {
status_header(404);
exit;
}

if ($action === 'picker') {
$users = get_users(['orderby' => 'ID', 'order' => 'ASC']);
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Escalated · WP Demo</title>';
echo '<style>*{box-sizing:border-box}body{font-family:-apple-system,sans-serif;background:#0f172a;color:#e2e8f0;margin:0;padding:2rem}.wrap{max-width:720px;margin:0 auto}h1{font-size:1.5rem;margin:0 0 .25rem}p.lede{color:#94a3b8;margin:0 0 2rem}form{display:block;margin:0}button.user{display:flex;width:100%;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:#1e293b;border:1px solid #334155;border-radius:8px;color:#f1f5f9;font-size:.95rem;cursor:pointer;margin-bottom:.5rem;text-align:left}button.user:hover{background:#273549;border-color:#475569}.meta{color:#94a3b8;font-size:.8rem}.badge{font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:#334155;color:#cbd5e1;margin-left:.5rem}.badge.admin{background:#7c3aed;color:#fff}.badge.agent{background:#0ea5e9;color:#fff}</style></head><body><div class="wrap"><h1>Escalated WordPress Demo</h1><p class="lede">Click a user to log in. Every restart resets the WordPress install.</p>';
foreach ($users as $u) {
$is_admin = user_can($u, 'manage_options');
$is_agent = user_can($u, 'edit_posts') && ! $is_admin;
$badge = $is_admin ? '<span class="badge admin">Admin</span>' : ($is_agent ? '<span class="badge agent">Agent</span>' : '');
echo '<form method="POST" action="/demo/login/'.esc_attr($u->ID).'">';
echo wp_nonce_field('demo_login_'.$u->ID, '_demo_nonce', true, false);
echo '<button type="submit" class="user"><span>'.esc_html($u->display_name ?: $u->user_login).' '.$badge.'</span><span class="meta">'.esc_html($u->user_email).'</span></button></form>';
}
echo '</div></body></html>';
exit;
}

if ($action === 'login' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$user_id = (int) get_query_var('user_id');
if (! wp_verify_nonce($_POST['_demo_nonce'] ?? '', 'demo_login_'.$user_id)) {
status_header(419);
exit('CSRF check failed');
}
wp_set_auth_cookie($user_id, true);
wp_set_current_user($user_id);
wp_safe_redirect(user_can($user_id, 'edit_posts') ? admin_url() : home_url());
exit;
}
});

register_activation_hook(__FILE__, function () {
flush_rewrite_rules();
});
register_deactivation_hook(__FILE__, function () {
flush_rewrite_rules();
});
44 changes: 44 additions & 0 deletions docker/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/sh
set -eu

# WP-CLI bootstrap script run by docker compose `wpcli` service.
# Idempotent: skips steps that already succeeded.

cd /var/www/html

if ! wp core is-installed --allow-root 2>/dev/null; then
echo "[demo] installing WordPress"
wp core install --allow-root \
--url=http://localhost:${APP_PORT:-8090} \
--title="Escalated Demo" \
--admin_user=alice \
--admin_email=alice@demo.test \
--admin_password=password \
--skip-email
fi

if ! wp plugin is-active escalated-wordpress --allow-root 2>/dev/null; then
echo "[demo] activating escalated-wordpress plugin"
wp plugin activate escalated-wordpress --allow-root
fi

echo "[demo] seeding additional users"
for u in "bob:bob@demo.test:editor" "carol:carol@demo.test:editor" "frank:frank@acme.example:subscriber" "grace:grace@acme.example:subscriber"; do
user=$(echo "$u" | cut -d: -f1)
email=$(echo "$u" | cut -d: -f2)
role=$(echo "$u" | cut -d: -f3)
wp user create "$user" "$email" --role="$role" --user_pass=password --allow-root 2>/dev/null \
|| echo " $user exists"
done

echo "[demo] installing /demo picker mu-plugin"
mkdir -p /var/www/html/wp-content/mu-plugins
cp /demo-picker.php /var/www/html/wp-content/mu-plugins/demo-picker.php

echo "[demo] enabling pretty permalinks (needed for /demo/ rewrite)"
wp option update permalink_structure "/%postname%/" --allow-root

echo "[demo] flushing rewrite rules"
wp rewrite flush --hard --allow-root || true

echo "[demo] WP setup complete"
Loading