Skip to content

Commit 9a968a0

Browse files
authored
Merge pull request #43 from hokoo/codex/add-dockerfile-for-phpunit-tests
Make phpunit Docker image fully self-contained
2 parents d836241 + e958249 commit 9a968a0

File tree

7 files changed

+317
-4
lines changed

7 files changed

+317
-4
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Unit Tests (Dockerfile)
2+
3+
on:
4+
push:
5+
branches: [ "master", "main", "dev" ]
6+
pull_request:
7+
types: [synchronize, opened, reopened]
8+
9+
permissions: {}
10+
11+
jobs:
12+
phpunit:
13+
name: PHP Unit ${{ matrix.php-version }}
14+
runs-on: ubuntu-latest
15+
16+
strategy:
17+
matrix:
18+
php-version: [ 8.0, 8.1, 8.2, 8.3, 8.4 ]
19+
20+
steps:
21+
- name: Check out the source code
22+
uses: actions/checkout@v4
23+
24+
- name: Build PHPUnit image
25+
run: |
26+
docker build \
27+
--build-arg PHP_VERSION=${{ matrix.php-version }} \
28+
-t wpconnections-phpunit:${{ matrix.php-version }} \
29+
-f Dockerfile.phpunit .
30+
31+
- name: Run all tests
32+
run: |
33+
docker run --rm -v "$PWD:/srv/web" \
34+
wpconnections-phpunit:${{ matrix.php-version }} test:all

.github/workflows/wp-unit-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818

1919
strategy:
2020
matrix:
21-
php-version: [ 7.4, 8.0, 8.1, 8.2, 8.3 ]
21+
php-version: [ 8.0, 8.1, 8.2, 8.3, 8.4 ]
2222

2323
services:
2424
mysql:
@@ -56,7 +56,7 @@ jobs:
5656

5757
strategy:
5858
matrix:
59-
php-version: [ 7.4, 8.0, 8.1, 8.2, 8.3 ]
59+
php-version: [ 8.0, 8.1, 8.2, 8.3, 8.4 ]
6060

6161
services:
6262
mysql:

Dockerfile.phpunit

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# syntax=docker/dockerfile:1
2+
# Universal image for running wpConnections PHPUnit suites with embedded services
3+
ARG PHP_VERSION=8.2
4+
ARG WP_VERSION=latest
5+
FROM php:${PHP_VERSION}-cli
6+
7+
ARG WP_VERSION=latest
8+
9+
LABEL maintainer="wpConnections"
10+
11+
ENV COMPOSER_ALLOW_SUPERUSER=1 \
12+
PATH="/root/.composer/vendor/bin:${PATH}" \
13+
WP_VERSION=${WP_VERSION} \
14+
WP_CORE_DIR=/opt/wordpress \
15+
WP_DEVELOP_DIR=/opt/wordpress-develop \
16+
WP_TESTS_DIR=/opt/wordpress-develop/tests/phpunit \
17+
MYSQL_DATA_DIR=/tmp/mysql-data \
18+
MYSQL_SOCKET=/run/mysqld/mysqld.sock \
19+
DB_HOST=127.0.0.1 \
20+
DB_NAME=wordpress_test \
21+
DB_USER=wordpress \
22+
DB_PASSWORD=wordpress
23+
24+
RUN set -eux; \
25+
apt-get update; \
26+
apt-get install -y --no-install-recommends \
27+
ca-certificates \
28+
curl \
29+
git \
30+
gnupg \
31+
libonig-dev \
32+
libxml2-dev \
33+
libzip-dev \
34+
mariadb-client \
35+
mariadb-server \
36+
unzip \
37+
zip; \
38+
docker-php-ext-install -j"$(nproc)" mbstring mysqli pdo_mysql zip; \
39+
rm -rf /var/lib/apt/lists/*
40+
41+
# Provide composer inside the image
42+
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
43+
44+
# Pre-install WordPress core and the WordPress test library so the container
45+
# is fully self-contained when executing the WordPress PHPUnit suite.
46+
RUN set -eux; \
47+
rm -rf "${WP_DEVELOP_DIR}"; \
48+
mkdir -p "${WP_DEVELOP_DIR}"; \
49+
if [ "${WP_VERSION}" = "latest" ]; then \
50+
WP_TARBALL_URL="https://wordpress.org/latest.tar.gz"; \
51+
WP_TESTS_ARCHIVE="https://github.com/WordPress/wordpress-develop/archive/refs/heads/master.tar.gz"; \
52+
WP_TESTS_REF="master"; \
53+
else \
54+
WP_TARBALL_URL="https://wordpress.org/wordpress-${WP_VERSION}.tar.gz"; \
55+
WP_TESTS_ARCHIVE="https://github.com/WordPress/wordpress-develop/archive/refs/tags/${WP_VERSION}.tar.gz"; \
56+
WP_TESTS_REF="${WP_VERSION}"; \
57+
fi; \
58+
59+
curl -fsSL "$WP_TESTS_ARCHIVE" -o /tmp/wordpress-develop.tar.gz; \
60+
tar -xzf /tmp/wordpress-develop.tar.gz -C "${WP_DEVELOP_DIR}" --strip-components=1; \
61+
rm /tmp/wordpress-develop.tar.gz; \
62+
if [ ! -f "${WP_DEVELOP_DIR}/wp-tests-config-sample.php" ]; then \
63+
echo "Missing wp-tests-config-sample.php in extracted WordPress tests archive" >&2; \
64+
exit 1; \
65+
fi; \
66+
cp "${WP_DEVELOP_DIR}/wp-tests-config-sample.php" "${WP_DEVELOP_DIR}/wp-tests-config.php"
67+
68+
WORKDIR /srv/web
69+
70+
COPY docker/phpunit-entrypoint.sh /usr/local/bin/phpunit-entrypoint.sh
71+
RUN chmod +x /usr/local/bin/phpunit-entrypoint.sh
72+
73+
ENTRYPOINT ["phpunit-entrypoint.sh"]
74+
CMD ["test:all"]

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# WP Connections: post-to-post connections for WordPress
22
[![PHP CS](https://github.com/hokoo/wpConnections/actions/workflows/php-cs.yml/badge.svg)](https://github.com/hokoo/wpConnections/actions/workflows/phpunit.yml)
33
[![PHP WordPress Unit Tests](https://github.com/hokoo/wpConnections/actions/workflows/wp-unit-tests.yml/badge.svg)](https://github.com/hokoo/wpConnections/actions/workflows/wp-unit-tests.yml)
4+
[![Dockerfile Unit Tests](https://github.com/hokoo/wpConnections/actions/workflows/wp-unit-tests-docker.yml/badge.svg)](https://github.com/hokoo/wpConnections/actions/workflows/wp-unit-tests-docker.yml)
45

56
<!-- TOC -->
67
* [Why wpConnection?](#why-wpconnection)
@@ -95,4 +96,21 @@ Since you have initialized new client, its REST API endpoints are available.
9596
5. Run folowing command in the root directory to install the project:
9697
```bash
9798
bash ./local-dev/init.sh && make tests.init && make docker.up && make dev.install
98-
```
99+
```
100+
101+
### Running the test suites
102+
103+
The project ships with a dedicated `Dockerfile.phpunit` image that bundles Composer, the WordPress test library and an embedded MariaDB server so the entire PHPUnit stack runs inside a single container locally and in CI. After the installation step you can run all tests from the project root with:
104+
105+
```bash
106+
make tests.run
107+
```
108+
109+
Behind the scenes this calls `docker compose` with the `phpunit` service defined in `local-dev/docker-compose.yml`. The service no longer depends on any other containers—the entrypoint spins up MariaDB and configures the WordPress test library on demand—so these commands can be executed anywhere Docker is available. You can also run the individual commands manually, for example:
110+
111+
```bash
112+
docker compose -f local-dev/docker-compose.yml run --rm phpunit composer run phpunit
113+
docker compose -f local-dev/docker-compose.yml run --rm phpunit vendor/bin/phpunit -c php-wp-unit.xml
114+
```
115+
116+
The same Dockerfile is also used by the optional GitHub Actions workflow defined in `.github/workflows/wp-unit-tests-docker.yml`, allowing you to compare its output against the long-standing `wp-unit-tests.yml` pipeline before switching over entirely. You can pin WordPress to a specific release by passing `--build-arg WP_VERSION=6.5.2` (or any other version number) when building the image.

docker/phpunit-entrypoint.sh

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
DB_HOST="${DB_HOST:-127.0.0.1}"
5+
DB_NAME="${DB_NAME:-wordpress_test}"
6+
DB_USER="${DB_USER:-wordpress}"
7+
DB_PASSWORD="${DB_PASSWORD:-wordpress}"
8+
WP_CORE_DIR="${WP_CORE_DIR:-/opt/wordpress}"
9+
WP_DEVELOP_DIR="${WP_DEVELOP_DIR:-/opt/wordpress-develop}"
10+
WP_TESTS_DIR="${WP_TESTS_DIR:-/opt/wordpress-develop/tests/phpunit}"
11+
MYSQL_SOCKET="${MYSQL_SOCKET:-/run/mysqld/mysqld.sock}"
12+
MYSQL_DATA_DIR="${MYSQL_DATA_DIR:-/tmp/mysql-data}"
13+
CONFIG_SAMPLE="${WP_DEVELOP_DIR}/wp-tests-config-sample.php"
14+
CONFIG_FILE="${WP_DEVELOP_DIR}/wp-tests-config.php"
15+
16+
MYSQL_RUNTIME_USER="${MYSQL_RUNTIME_USER:-$(id -un 2>/dev/null || echo root)}"
17+
MYSQL_RUN_DIR="$(dirname "${MYSQL_SOCKET}")"
18+
19+
mkdir -p "${MYSQL_RUN_DIR}" "${MYSQL_DATA_DIR}"
20+
21+
if [ "$(id -u)" -eq 0 ]; then
22+
chown -R "${MYSQL_RUNTIME_USER}" "${MYSQL_RUN_DIR}" "${MYSQL_DATA_DIR}"
23+
fi
24+
25+
if [ ! -d "${MYSQL_DATA_DIR}/mysql" ]; then
26+
mariadb-install-db \
27+
--user="${MYSQL_RUNTIME_USER}" \
28+
--datadir="${MYSQL_DATA_DIR}" \
29+
--skip-test-db \
30+
--auth-root-authentication-method=normal >/dev/null
31+
fi
32+
33+
mariadbd \
34+
--user="${MYSQL_RUNTIME_USER}" \
35+
--datadir="${MYSQL_DATA_DIR}" \
36+
--socket="${MYSQL_SOCKET}" \
37+
--bind-address=127.0.0.1 \
38+
--skip-networking=0 &
39+
MYSQLD_PID=$!
40+
41+
cleanup() {
42+
if kill -0 "${MYSQLD_PID}" >/dev/null 2>&1; then
43+
mysqladmin --protocol=socket --socket="${MYSQL_SOCKET}" -uroot shutdown >/dev/null 2>&1 || true
44+
wait "${MYSQLD_PID}" >/dev/null 2>&1 || true
45+
fi
46+
}
47+
trap cleanup EXIT
48+
49+
MYSQL_READY=0
50+
for _ in $(seq 1 30); do
51+
if mysqladmin --protocol=socket --socket="${MYSQL_SOCKET}" -uroot ping >/dev/null 2>&1; then
52+
MYSQL_READY=1
53+
break
54+
fi
55+
sleep 1
56+
done
57+
58+
if [ "${MYSQL_READY}" -ne 1 ]; then
59+
echo "Timed out waiting for MariaDB to accept connections" >&2
60+
exit 1
61+
fi
62+
63+
mysql --protocol=socket --socket="${MYSQL_SOCKET}" -uroot <<SQL
64+
CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
65+
CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}';
66+
GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'%';
67+
FLUSH PRIVILEGES;
68+
SQL
69+
70+
if [ ! -f "${CONFIG_FILE}" ]; then
71+
echo "wp-tests-config.php missing; recreating from sample" >&2
72+
if [ -f "${CONFIG_SAMPLE}" ]; then
73+
cp "${CONFIG_SAMPLE}" "${CONFIG_FILE}"
74+
else
75+
echo "Sample config not found at ${CONFIG_SAMPLE}" >&2
76+
exit 1
77+
fi
78+
fi
79+
80+
sed -i "s/youremptytestdbnamehere/${DB_NAME}/" "${CONFIG_FILE}"
81+
sed -i "s/yourusernamehere/${DB_USER}/" "${CONFIG_FILE}"
82+
sed -i "s/yourpasswordhere/${DB_PASSWORD}/" "${CONFIG_FILE}"
83+
sed -i "s|localhost|${DB_HOST}|1" "${CONFIG_FILE}"
84+
sed -i "s|dirname( __FILE__ ) . '/../../'|'${WP_CORE_DIR}/'|" "${CONFIG_FILE}"
85+
86+
export WP_TESTS_DIR DB_HOST DB_NAME DB_USER DB_PASSWORD
87+
88+
# ==== Here and below is a universal "command dispatcher" ====
89+
90+
WORKDIR="/srv/web"
91+
cd "$WORKDIR"
92+
93+
log_section() {
94+
echo
95+
echo "========================================"
96+
echo ">>> $1"
97+
echo "========================================"
98+
}
99+
100+
run_composer_install() {
101+
log_section "Composer install"
102+
if [ -f composer.json ]; then
103+
# Skip if vendor/ already exists.
104+
if [ -d vendor ]; then
105+
echo "vendor/ already exists, skipping composer install"
106+
else
107+
composer install --no-interaction --prefer-dist
108+
fi
109+
else
110+
echo "composer.json not found in ${WORKDIR}, skipping composer install"
111+
fi
112+
}
113+
114+
run_phpunit() {
115+
log_section "PHP Unit tests (phpunit.xml)"
116+
vendor/bin/phpunit -c phpunit.xml "$@"
117+
}
118+
119+
run_wpunit() {
120+
log_section "WordPress Unit tests (php-wp-unit.xml)"
121+
vendor/bin/phpunit -c php-wp-unit.xml "$@"
122+
}
123+
124+
# Collect exit codes of both suites
125+
run_all_tests() {
126+
local phpunit_exit=0
127+
local wpunit_exit=0
128+
129+
run_composer_install
130+
131+
run_phpunit "$@" || phpunit_exit=$?
132+
run_wpunit "$@" || wpunit_exit=$?
133+
134+
if [ "$phpunit_exit" -ne 0 ] || [ "$wpunit_exit" -ne 0 ]; then
135+
echo
136+
echo "One or more test suites failed:"
137+
echo " PHP Unit exit code: $phpunit_exit"
138+
echo " WP Unit exit code: $wpunit_exit"
139+
# If needed to distinguish, we could return, for example, the first non-zero
140+
exit 1
141+
fi
142+
}
143+
144+
CMD="${1:-test:all}"
145+
146+
case "$CMD" in
147+
test:all)
148+
shift
149+
run_all_tests "$@"
150+
;;
151+
152+
test:phpunit)
153+
shift
154+
run_composer_install
155+
run_phpunit "$@"
156+
;;
157+
158+
test:wpunit)
159+
shift
160+
run_composer_install
161+
run_wpunit "$@"
162+
;;
163+
164+
composer-install)
165+
shift
166+
run_composer_install
167+
;;
168+
169+
*)
170+
# Fallback to default behavior: run the given command.
171+
# docker run image vendor/bin/phpunit -c phpunit.xml
172+
exec "$@"
173+
;;
174+
esac

local-dev/docker-compose.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,19 @@ services:
6969
networks:
7070
- wpconnections-local
7171

72+
phpunit:
73+
build:
74+
context: ..
75+
dockerfile: Dockerfile.phpunit
76+
args:
77+
PHP_VERSION: $PHP_VERSION
78+
container_name: "${PROJECT_NAME}_phpunit"
79+
working_dir: /srv/web/
80+
volumes:
81+
- ../:/srv/web/
82+
networks:
83+
- wpconnections-local
84+
7285
networks:
7386
wpconnections-local:
7487
driver: bridge

makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ tests.init:
33

44
tests.run:
55
cd ./local-dev/ && \
6-
docker-compose -p wpconnections exec php sh -c 'vendor/bin/phpunit -c phpunit.xml && vendor/bin/phpunit -c php-wp-unit.xml'
6+
docker-compose -p wpconnections run --rm phpunit sh -c 'vendor/bin/phpunit -c phpunit.xml && vendor/bin/phpunit -c php-wp-unit.xml'
77

88
dev.install:
99
cd ./local-dev/ && \

0 commit comments

Comments
 (0)