Skip to content
Open
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
19 changes: 19 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copy to .env to override defaults. All values are optional.

# Host port for the dev site (http://localhost:8080)
WP_PORT=8080
# Host port for phpMyAdmin (`make tools`)
PMA_PORT=8081

# PHP version for the dev site image (any wordpress:php<ver>-apache tag)
WP_PHP_VERSION=8.2

# Cablecast server the dev site syncs from
CABLECAST_SERVER=https://rays-house.cablecast.net

# Xdebug mode for the dev site: off, debug, profile, develop, ...
XDEBUG_MODE=debug

# Default PHP / WordPress versions for `make test`
PHP_VERSION=8.2
WP_VERSION=latest
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ composer.lock

# IDE
.idea/
.vscode/
.vscode/*
!.vscode/launch.json
*.swp
*.swo

Expand All @@ -21,3 +22,6 @@ Thumbs.db

# Logs
*.log

# Docker dev environment
.env
14 changes: 14 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug (docker)",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html/wp-content/plugins/wp-cablecast": "${workspaceFolder}"
}
}
]
}
59 changes: 59 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
-include .env

# Local development helpers. See "Local Development" in README.md.

PHP ?= $(or $(PHP_VERSION),8.2)
WP ?= $(or $(WP_VERSION),latest)
WP_PORT ?= 8080
TS_PORT ?= 8443

COMPOSE = docker compose
WPCLI = $(COMPOSE) run --rm cli

.PHONY: up down setup wp sync logs debug-log tools tailscale tailscale-off test test-matrix test-clean reset

up: ## Start the dev site and install WordPress on first run
$(COMPOSE) up -d --build
$(WPCLI) sh wp-content/plugins/wp-cablecast/bin/dev-setup.sh

down: ## Stop containers (keeps data)
$(COMPOSE) --profile tools down

setup: ## Re-run WordPress install / plugin activation
$(WPCLI) sh wp-content/plugins/wp-cablecast/bin/dev-setup.sh

wp: ## Run WP-CLI: make wp ARGS="plugin list"
$(WPCLI) wp $(ARGS)

sync: ## Run the Cablecast sync now instead of waiting for cron
$(WPCLI) wp cron event run cablecast_cron_hook

logs: ## Tail container logs
$(COMPOSE) logs -f

debug-log: ## Tail wp-content/debug.log
$(COMPOSE) exec wordpress tail -f wp-content/debug.log

tools: ## Start phpMyAdmin on http://localhost:8081
$(COMPOSE) --profile tools up -d phpmyadmin

tailscale: ## Share the dev site on your tailnet over HTTPS (port TS_PORT, default 8443)
tailscale serve --bg --https=$(TS_PORT) http://localhost:$(WP_PORT)

tailscale-off: ## Stop sharing the dev site on your tailnet
tailscale serve --https=$(TS_PORT) off

test: ## Run PHPUnit: make test [PHP=7.4] [WP=6.2] [ARGS="--filter Foo"]
PHP_VERSION=$(PHP) WP_VERSION=$(WP) $(COMPOSE) run --rm --build tests bash bin/docker-test.sh $(ARGS)

test-matrix: ## Run the same PHP x WP matrix as CI
@set -e; for php in 7.4 8.0 8.1 8.2; do for wp in 6.2 6.4 latest; do \
$(MAKE) --no-print-directory test PHP=$$php WP=$$wp; \
done; done

test-clean: ## Drop cached WP test suites (e.g. to pick up a new "latest")
$(COMPOSE) down
docker volume rm $$(basename $(CURDIR))_test-cache

reset: ## Destroy everything, including the database
$(COMPOSE) --profile tools --profile cli --profile tests down -v
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,47 @@ I've set up a simple demo site that I'll be expanding with examples of how to do
## Installation
Upload the plugin and activate it. Then in the admin dashboard navigate to `Cablecast Settings`. There is a single input for the `Cablecast Server Address`. This is the url you use to login to Cablecast from the public internet. Example `http://tighty.tv`.

## Local Development
A Docker based dev environment is included. It runs WordPress with this repo mounted as the plugin, a MariaDB database, a container that runs WP-Cron every minute (instead of during page loads), and a PHPUnit runner.

Requirements: [Docker Desktop](https://www.docker.com/products/docker-desktop/) (or another Docker runtime with `docker compose`) and `make`. PHP and Composer are not needed on your machine.

```sh
make up # build, start, install WordPress, activate the plugin
```

- Site: http://localhost:8080
- Admin: http://localhost:8080/wp-admin (`admin` / `admin`)
- The plugin is pre-configured to sync from `https://rays-house.cablecast.net`. Copy `.env.example` to `.env` to change the server, ports, or PHP version, then run `make up` again.

Common commands:

|Command|Description|
|---|---|
|`make sync`|Run the Cablecast sync now instead of waiting for cron|
|`make wp ARGS="post list --post_type=show"`|Run any WP-CLI command|
|`make debug-log`|Tail `wp-content/debug.log`|
|`make logs`|Tail container logs|
|`make tools`|Start phpMyAdmin on http://localhost:8081|
|`make tailscale`|Share the site on your tailnet at `https://<machine>.<tailnet>.ts.net:8443` (`make tailscale-off` to stop)|
|`make down`|Stop containers (keeps data)|
|`make reset`|Delete containers and all data|

WordPress uses whatever host it's reached by, so the site also works behind a proxy such as Tailscale Serve or ngrok without changing its URL. `make tailscale` uses [Tailscale Serve](https://tailscale.com/kb/1312/serve), which also gets around the macOS firewall blocking direct tailnet connections to Docker ports.

### Tests
```sh
make test # PHP 8.2, latest WordPress
make test PHP=7.4 WP=6.2 # any version CI tests against
make test ARGS="--filter SyncLock" # pass args to PHPUnit
make test-matrix # the full CI matrix
```

The WordPress test suite for each version is cached in a Docker volume. Run `make test-clean` to re-download it (for example, to pick up a new `latest`).

### Debugging
Xdebug is installed in the WordPress container and starts when a request carries the `XDEBUG_TRIGGER` cookie (set it with a browser extension such as Xdebug Helper). It connects to port 9003. `.vscode/launch.json` contains the path mappings for VS Code's PHP Debug extension.

## Important Notes About cron_schedules
TL;DR - Follow these instructions for setting up wordpress cron: https://developer.wordpress.org/plugins/cron/hooking-into-the-system-task-scheduler/

Expand Down
41 changes: 41 additions & 0 deletions bin/dev-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env sh
# Installs WordPress and activates the plugin in the docker dev environment.
# Runs inside the `cli` container (`make setup`); safe to re-run.
set -e

cd /var/www/html

echo "Waiting for wp-config.php..."
i=0
until [ -f wp-config.php ]; do
i=$((i + 1))
if [ $i -gt 60 ]; then
echo "wp-config.php never appeared; is the wordpress container running?" >&2
exit 1
fi
sleep 1
done

if ! wp core is-installed 2>/dev/null; then
wp core install \
--url="http://localhost:${WP_PORT:-8080}" \
--title="Cablecast Dev" \
--admin_user=admin \
--admin_password=admin \
--admin_email=admin@example.com \
--skip-email
fi

# The wordpress image ships a rewrite-ready .htaccess, so no --hard needed.
wp rewrite structure '/%postname%/'
wp plugin activate wp-cablecast

if [ -n "$CABLECAST_SERVER" ]; then
wp option patch update cablecast_options server "$CABLECAST_SERVER" 2>/dev/null \
|| wp option patch insert cablecast_options server "$CABLECAST_SERVER"
fi

echo
echo "Site: http://localhost:${WP_PORT:-8080}"
echo "Admin: http://localhost:${WP_PORT:-8080}/wp-admin (admin / admin)"
echo "Cablecast server: $(wp option pluck cablecast_options server)"
24 changes: 24 additions & 0 deletions bin/docker-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Runs the PHPUnit suite inside the `tests` container (`make test`).
# WordPress core + test library are cached per WP version in the test-cache volume.
set -e

WP_VERSION=${WP_VERSION:-latest}
PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')

export WP_CORE_DIR=/cache/wp/$WP_VERSION/core
export WP_TESTS_DIR=/cache/wp/$WP_VERSION/tests-lib

echo "PHP $PHP_VERSION / WordPress $WP_VERSION"

# composer.lock isn't committed, so re-resolve when the PHP version changes.
if [ "$(cat vendor/.php-version 2>/dev/null)" != "$PHP_VERSION" ]; then
composer update --prefer-dist --no-progress --no-interaction
echo "$PHP_VERSION" > vendor/.php-version
fi

if [ ! -f "$WP_TESTS_DIR/wp-tests-config.php" ]; then
bash bin/install-wp-tests.sh wordpress_test root root db "$WP_VERSION" true
fi

composer test -- "$@"
128 changes: 128 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Local development environment. See "Local Development" in README.md.

x-wp-env: &wp-env
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DEBUG: 1
WORDPRESS_CONFIG_EXTRA: |
define('WP_ENVIRONMENT_TYPE', 'local');
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('DISABLE_WP_CRON', true);
// Use whatever host the site is reached by (localhost, a Tailscale/ngrok proxy, ...).
if (!empty($$_SERVER['HTTP_HOST'])) {
$$https = (!empty($$_SERVER['HTTPS']) && $$_SERVER['HTTPS'] !== 'off')
|| ($$_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https';
if ($$https) { $$_SERVER['HTTPS'] = 'on'; }
define('WP_HOME', ($$https ? 'https' : 'http') . '://' . $$_SERVER['HTTP_HOST']);
define('WP_SITEURL', WP_HOME);
}

x-wp-volumes: &wp-volumes
- wordpress:/var/www/html
- .:/var/www/html/wp-content/plugins/wp-cablecast

x-wp-cli: &wp-cli
image: wordpress:cli
# Match the apache image's www-data uid so files in the shared volume stay writable.
user: "33:33"
environment:
<<: *wp-env
WP_CLI_CACHE_DIR: /tmp/wp-cli-cache
WP_PORT: ${WP_PORT:-8080}
CABLECAST_SERVER: ${CABLECAST_SERVER:-https://rays-house.cablecast.net}
volumes: *wp-volumes
depends_on:
db:
condition: service_healthy
wordpress:
condition: service_started

services:
db:
# 11.4+ enables TLS out of the box, which WP-CLI's MariaDB client requires.
image: mariadb:11.8
environment:
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: wordpress
volumes:
- db:/var/lib/mysql
- ./docker/db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 5s
timeout: 5s
retries: 20

wordpress:
build:
context: docker/wordpress
args:
PHP_VERSION: ${WP_PHP_VERSION:-8.2}
ports:
- "${WP_PORT:-8080}:80"
environment:
<<: *wp-env
XDEBUG_MODE: ${XDEBUG_MODE:-debug}
volumes: *wp-volumes
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
db:
condition: service_healthy

# One-off WP-CLI commands: `make wp ARGS="plugin list"`
cli:
<<: *wp-cli
profiles: ["cli"]

# Runs WP-Cron from outside web requests, as the README recommends for production.
cron:
<<: *wp-cli
command: >
sh -c 'while true; do
wp core is-installed 2>/dev/null && wp cron event run --due-now --quiet;
sleep 60;
done'
restart: unless-stopped

# PHPUnit runner: `make test` / `make test PHP=7.4 WP=6.2`
tests:
build:
context: docker/tests
args:
PHP_VERSION: ${PHP_VERSION:-8.2}
image: wp-cablecast-tests:php${PHP_VERSION:-8.2}
profiles: ["tests"]
environment:
WP_VERSION: ${WP_VERSION:-latest}
COMPOSER_CACHE_DIR: /cache/composer
volumes:
- .:/app
- test-cache:/cache
command: bash bin/docker-test.sh
depends_on:
db:
condition: service_healthy

phpmyadmin:
image: phpmyadmin:5
profiles: ["tools"]
ports:
- "${PMA_PORT:-8081}:80"
environment:
PMA_HOST: db
PMA_USER: root
PMA_PASSWORD: root
depends_on:
db:
condition: service_healthy

volumes:
db:
wordpress:
test-cache:
4 changes: 4 additions & 0 deletions docker/db/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- The dev site database (`wordpress`) is created by the image; this adds the
-- database used by the PHPUnit suite.
CREATE DATABASE IF NOT EXISTS wordpress_test;
GRANT ALL PRIVILEGES ON wordpress_test.* TO 'wordpress'@'%';
17 changes: 17 additions & 0 deletions docker/tests/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
ARG PHP_VERSION=8.2
FROM php:${PHP_VERSION}-cli

# Older PHP images (7.4, 8.0) are built on Debian bullseye, which is EOL and
# only available from the archive.
RUN if grep -q bullseye /etc/os-release; then \
sed -i -e 's|deb.debian.org|archive.debian.org|g' -e '/bullseye-updates/d' /etc/apt/sources.list; \
fi

RUN apt-get update \
&& apt-get install -y --no-install-recommends git unzip subversion \
&& rm -rf /var/lib/apt/lists/* \
&& docker-php-ext-install mysqli

COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer

WORKDIR /app
7 changes: 7 additions & 0 deletions docker/wordpress/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ARG PHP_VERSION=8.2
FROM wordpress:php${PHP_VERSION}-apache

RUN pecl install xdebug \
&& docker-php-ext-enable xdebug

COPY xdebug.ini /usr/local/etc/php/conf.d/zz-xdebug.ini
6 changes: 6 additions & 0 deletions docker/wordpress/xdebug.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
; Mode comes from the XDEBUG_MODE env var (see docker-compose.yml).
; Start a session with the browser extension / XDEBUG_TRIGGER cookie.
xdebug.start_with_request=trigger
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.log_level=0
Loading