From 1f27a80f56045c897832c211d2ab3b9d87e65694 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 5 Oct 2026 17:23:59 -0400 Subject: [PATCH 1/7] fix: start the stack from a fresh clone Signed-off-by: YvesCesar --- Makefile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 21273ce..8c17105 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,9 @@ SELECTED_COMPONENTS := $(filter $(COMPONENT_TARGETS),$(MAKECMDGOALS)) UP_COMPONENTS := $(if $(SELECTED_COMPONENTS),$(SELECTED_COMPONENTS),$(COMPONENT_TARGETS)) # Docker Compose commands -NEXTCLOUD_COMPOSE := HTTP_PORT=$(NEXTCLOUD_HTTP_PORT) docker compose -f $(NEXTCLOUD_DIR)/docker-compose.yml +NEXTCLOUD_COMPOSE := IP_BIND=0.0.0.0 HTTP_PORT=$(NEXTCLOUD_HTTP_PORT) docker compose -f $(NEXTCLOUD_DIR)/docker-compose.yml WORDPRESS_COMPOSE := docker compose -f $(WORDPRESS_DIR)/docker-compose.yml -f $(ROOT_DIR)/docker-compose.override.yml -SITE_COMPOSE := UID=$(LOCAL_UID) GID=$(LOCAL_GID) HTTP_PORT=$(SITE_HTTP_PORT) HTTP_PORT_BROWSERSYNC=$(SITE_BROWSERSYNC_PORT) SERVER_MODE=$(SITE_SERVER_MODE) URL_SITE=$(SITE_BASE_URL) LIBRESIGN_PUBLISH_HEADER_FRAGMENTS=$(LIBRESIGN_PUBLISH_HEADER_FRAGMENTS) LIBRESIGN_HEADER_WEBHOOK_URL=$(LIBRESIGN_HEADER_WEBHOOK_URL) LIBRESIGN_HEADER_WEBHOOK_SECRET=$(LIBRESIGN_HEADER_WEBHOOK_SECRET) LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS=$(LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS) LIBRESIGN_FOOTER_WEBHOOK_URL=$(LIBRESIGN_FOOTER_WEBHOOK_URL) LIBRESIGN_FOOTER_WEBHOOK_SECRET=$(LIBRESIGN_FOOTER_WEBHOOK_SECRET) docker compose -f $(SITE_DIR)/docker-compose.yml +SITE_COMPOSE := env UID=$(LOCAL_UID) GID=$(LOCAL_GID) HTTP_PORT=$(SITE_HTTP_PORT) HTTP_PORT_BROWSERSYNC=$(SITE_BROWSERSYNC_PORT) SERVER_MODE=$(SITE_SERVER_MODE) URL_SITE=$(SITE_BASE_URL) LIBRESIGN_PUBLISH_HEADER_FRAGMENTS=$(LIBRESIGN_PUBLISH_HEADER_FRAGMENTS) LIBRESIGN_HEADER_WEBHOOK_URL=$(LIBRESIGN_HEADER_WEBHOOK_URL) LIBRESIGN_HEADER_WEBHOOK_SECRET=$(LIBRESIGN_HEADER_WEBHOOK_SECRET) LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS=$(LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS) LIBRESIGN_FOOTER_WEBHOOK_URL=$(LIBRESIGN_FOOTER_WEBHOOK_URL) LIBRESIGN_FOOTER_WEBHOOK_SECRET=$(LIBRESIGN_FOOTER_WEBHOOK_SECRET) docker compose -f $(SITE_DIR)/docker-compose.yml NEXTCLOUD_OCC := $(NEXTCLOUD_COMPOSE) exec -u www-data nextcloud php occ WORDPRESS_CLI := $(WORDPRESS_COMPOSE) exec wordpress wp --allow-root @@ -147,7 +147,7 @@ _refresh-wordpress-images: _refresh-nextcloud-images: @echo "Refreshing Nextcloud images..." - @$(NEXTCLOUD_COMPOSE) pull mysql redis nextcloud nginx + @$(NEXTCLOUD_COMPOSE) pull database redis nextcloud nginx _start-site: @echo "Starting site services..." @@ -169,7 +169,7 @@ _start-wordpress: _start-nextcloud: @echo "Starting Nextcloud services..." - @$(NEXTCLOUD_COMPOSE) up -d mysql redis nextcloud nginx + @$(NEXTCLOUD_COMPOSE) up -d database redis nextcloud nginx _install-wordpress: @echo "Ensuring WordPress core is installed..." @@ -238,7 +238,7 @@ _enable-wordpress-plugin: @echo "Enabling WordPress plugin..." @$(WORDPRESS_CLI) plugin activate woocommerce-nextcloud-admin-group-manager >/dev/null || true -_setup-apps: _ensure-wordpress-app _ensure-nextcloud-app _enable-apps _set-wordpress-dsn +_setup-apps: _ensure-wordpress-app _ensure-nextcloud-app _enable-apps _set-wordpress-dsn _set-trusted-domains _ensure-wordpress-app: @echo "Setting up wordpress_login_backend app..." @@ -267,6 +267,9 @@ _enable-apps: _set-wordpress-dsn: @$(NEXTCLOUD_OCC) config:system:set wordpress_dsn --value "mysql:host=mariadb;port=3306;dbname=wordpress;user=root;password=root" >/dev/null +_set-trusted-domains: + @$(NEXTCLOUD_OCC) config:system:set trusted_domains 1 --value host.docker.internal >/dev/null + _connect-networks: @echo "Connecting Docker networks..." @NEXTCLOUD_CONTAINER=$$($(NEXTCLOUD_COMPOSE) ps -q nextcloud); \ From 15be611d00bb896ed9645cf196fc5f733765a065 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 5 Oct 2026 18:01:02 -0400 Subject: [PATCH 2/7] fix: keep nextcloud off the network and append its trusted domain Signed-off-by: YvesCesar --- Makefile | 10 ++++++++-- docker-compose.nextcloud.override.yml | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 docker-compose.nextcloud.override.yml diff --git a/Makefile b/Makefile index 8c17105..60e7403 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,8 @@ SITE_SERVER_MODE ?= build NEXTCLOUD_HTTP_PORT ?= 8082 SITE_BASE_URL := http://localhost:$(SITE_HTTP_PORT) NEXTCLOUD_BASE_URL := http://localhost:$(NEXTCLOUD_HTTP_PORT) +NEXTCLOUD_LOCAL_URL ?= http://host.docker.internal:$(NEXTCLOUD_HTTP_PORT) +DOCKER_HOST_GATEWAY_IP ?= $(shell docker network inspect bridge -f '{{(index .IPAM.Config 0).Gateway}}' 2>/dev/null) NEXTCLOUD_APPS_DIR := $(NEXTCLOUD_DIR)/volumes/nextcloud/apps-extra LOCAL_UID := $(shell id -u) LOCAL_GID := $(shell id -g) @@ -38,7 +40,7 @@ SELECTED_COMPONENTS := $(filter $(COMPONENT_TARGETS),$(MAKECMDGOALS)) UP_COMPONENTS := $(if $(SELECTED_COMPONENTS),$(SELECTED_COMPONENTS),$(COMPONENT_TARGETS)) # Docker Compose commands -NEXTCLOUD_COMPOSE := IP_BIND=0.0.0.0 HTTP_PORT=$(NEXTCLOUD_HTTP_PORT) docker compose -f $(NEXTCLOUD_DIR)/docker-compose.yml +NEXTCLOUD_COMPOSE := HTTP_PORT=$(NEXTCLOUD_HTTP_PORT) DOCKER_HOST_GATEWAY_IP=$(DOCKER_HOST_GATEWAY_IP) docker compose -f $(NEXTCLOUD_DIR)/docker-compose.yml -f $(ROOT_DIR)/docker-compose.nextcloud.override.yml WORDPRESS_COMPOSE := docker compose -f $(WORDPRESS_DIR)/docker-compose.yml -f $(ROOT_DIR)/docker-compose.override.yml SITE_COMPOSE := env UID=$(LOCAL_UID) GID=$(LOCAL_GID) HTTP_PORT=$(SITE_HTTP_PORT) HTTP_PORT_BROWSERSYNC=$(SITE_BROWSERSYNC_PORT) SERVER_MODE=$(SITE_SERVER_MODE) URL_SITE=$(SITE_BASE_URL) LIBRESIGN_PUBLISH_HEADER_FRAGMENTS=$(LIBRESIGN_PUBLISH_HEADER_FRAGMENTS) LIBRESIGN_HEADER_WEBHOOK_URL=$(LIBRESIGN_HEADER_WEBHOOK_URL) LIBRESIGN_HEADER_WEBHOOK_SECRET=$(LIBRESIGN_HEADER_WEBHOOK_SECRET) LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS=$(LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS) LIBRESIGN_FOOTER_WEBHOOK_URL=$(LIBRESIGN_FOOTER_WEBHOOK_URL) LIBRESIGN_FOOTER_WEBHOOK_SECRET=$(LIBRESIGN_FOOTER_WEBHOOK_SECRET) docker compose -f $(SITE_DIR)/docker-compose.yml NEXTCLOUD_OCC := $(NEXTCLOUD_COMPOSE) exec -u www-data nextcloud php occ @@ -268,7 +270,11 @@ _set-wordpress-dsn: @$(NEXTCLOUD_OCC) config:system:set wordpress_dsn --value "mysql:host=mariadb;port=3306;dbname=wordpress;user=root;password=root" >/dev/null _set-trusted-domains: - @$(NEXTCLOUD_OCC) config:system:set trusted_domains 1 --value host.docker.internal >/dev/null + @host=$$(echo "$(NEXTCLOUD_LOCAL_URL)" | sed -E 's#^[a-z]+://##; s#[:/].*##'); \ + index=$$($(NEXTCLOUD_COMPOSE) exec -T -u www-data -e HOST="$$host" nextcloud php -r 'require "config/config.php"; $$domains = $$CONFIG["trusted_domains"] ?? []; echo in_array(getenv("HOST"), $$domains, true) ? "" : ($$domains ? max(array_keys($$domains)) + 1 : 0);'); \ + if [ -n "$$index" ]; then \ + $(NEXTCLOUD_OCC) config:system:set trusted_domains $$index --value "$$host" >/dev/null; \ + fi _connect-networks: @echo "Connecting Docker networks..." diff --git a/docker-compose.nextcloud.override.yml b/docker-compose.nextcloud.override.yml new file mode 100644 index 0000000..1d77110 --- /dev/null +++ b/docker-compose.nextcloud.override.yml @@ -0,0 +1,6 @@ +services: + nginx: + ports: + - target: 80 + published: "${HTTP_PORT:-80-99}" + host_ip: ${DOCKER_HOST_GATEWAY_IP:?DOCKER_HOST_GATEWAY_IP is required} From 23f500fb5e9a73701a537539085933f5d9717265 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 9 Oct 2026 10:48:31 -0400 Subject: [PATCH 3/7] chore: bump nextcloud-development to the shared proxy lifecycle Signed-off-by: YvesCesar --- nextcloud-development | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextcloud-development b/nextcloud-development index 56660f4..812fe2d 160000 --- a/nextcloud-development +++ b/nextcloud-development @@ -1 +1 @@ -Subproject commit 56660f41f5786a925ab85335b91ec599e02e8429 +Subproject commit 812fe2d9b1bbf3c42fe45fc26df5b3f74fa15bcc From 9e734ba3c1a60718a2cec81cc0215e7303d17cf6 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 9 Oct 2026 10:48:31 -0400 Subject: [PATCH 4/7] fix: reach nextcloud through the ncdd shared proxy Signed-off-by: YvesCesar --- .../mu-plugins/nextcloud-dev-proxy.php | 46 +++++++++ .docker/wordpress/provision-admin-group.php | 20 ++++ Makefile | 99 ++++++++++--------- README.md | 13 ++- docker-compose.nextcloud.override.yml | 6 -- docker-compose.override.yml | 13 ++- 6 files changed, 140 insertions(+), 57 deletions(-) create mode 100644 .docker/wordpress/mu-plugins/nextcloud-dev-proxy.php create mode 100644 .docker/wordpress/provision-admin-group.php delete mode 100644 docker-compose.nextcloud.override.yml diff --git a/.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php b/.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php new file mode 100644 index 0000000..da12ef8 --- /dev/null +++ b/.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php @@ -0,0 +1,46 @@ + 30, + 'body' => array( + 'groupid' => 'admlibrecode', + 'email' => 'adm@librecode.coop', + 'displayname' => 'Adm Librecode', + ), + ) +); + +if ( ! $response->succeeded() ) { + WP_CLI::error( 'Could not provision the admin group in Nextcloud: ' . $response->failure_message() ); +} + +WP_CLI::success( 'Admin group provisioned in Nextcloud.' ); diff --git a/Makefile b/Makefile index 60e7403..903a0be 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: up down help _help site wordpress nextcloud _up-site _up-wordpress _up-nextcloud _down-site _down-wordpress _down-nextcloud _up-integration _ensure-site-repo +.PHONY: up down test test-config test-integration help _help site wordpress nextcloud _up-site _up-wordpress _up-nextcloud _down-site _down-wordpress _down-nextcloud _up-integration _ensure-site-repo -include .env export @@ -13,20 +13,17 @@ SITE_REPO_BRANCH ?= main SITE_HTTP_PORT ?= 8081 SITE_BROWSERSYNC_PORT ?= 3000 SITE_SERVER_MODE ?= build -NEXTCLOUD_HTTP_PORT ?= 8082 SITE_BASE_URL := http://localhost:$(SITE_HTTP_PORT) -NEXTCLOUD_BASE_URL := http://localhost:$(NEXTCLOUD_HTTP_PORT) -NEXTCLOUD_LOCAL_URL ?= http://host.docker.internal:$(NEXTCLOUD_HTTP_PORT) -DOCKER_HOST_GATEWAY_IP ?= $(shell docker network inspect bridge -f '{{(index .IPAM.Config 0).Gateway}}' 2>/dev/null) NEXTCLOUD_APPS_DIR := $(NEXTCLOUD_DIR)/volumes/nextcloud/apps-extra LOCAL_UID := $(shell id -u) LOCAL_GID := $(shell id -g) -WORDPRESS_SITE_URL ?= http://localhost +WORDPRESS_HTTP_PORT ?= 8080 +WORDPRESS_SITE_URL ?= http://localhost:$(WORDPRESS_HTTP_PORT) WORDPRESS_SITE_TITLE ?= LibreSign SaaS WORDPRESS_ADMIN_USER ?= admin WORDPRESS_ADMIN_PASSWORD ?= admin WORDPRESS_ADMIN_EMAIL ?= admin@example.com -WORDPRESS_WEBHOOK_BASE_URL ?= http://host.docker.internal +WORDPRESS_WEBHOOK_BASE_URL ?= http://host.docker.internal:$(WORDPRESS_HTTP_PORT) LIBRESIGN_PUBLISH_HEADER_FRAGMENTS ?= true LIBRESIGN_HEADER_WEBHOOK_URL ?= $(WORDPRESS_WEBHOOK_BASE_URL)/wp-json/libresign/v1/header-fragment LIBRESIGN_HEADER_WEBHOOK_SECRET ?= change-me-header @@ -40,11 +37,12 @@ SELECTED_COMPONENTS := $(filter $(COMPONENT_TARGETS),$(MAKECMDGOALS)) UP_COMPONENTS := $(if $(SELECTED_COMPONENTS),$(SELECTED_COMPONENTS),$(COMPONENT_TARGETS)) # Docker Compose commands -NEXTCLOUD_COMPOSE := HTTP_PORT=$(NEXTCLOUD_HTTP_PORT) DOCKER_HOST_GATEWAY_IP=$(DOCKER_HOST_GATEWAY_IP) docker compose -f $(NEXTCLOUD_DIR)/docker-compose.yml -f $(ROOT_DIR)/docker-compose.nextcloud.override.yml +NEXTCLOUD_COMPOSE := docker compose -f $(NEXTCLOUD_DIR)/docker-compose.yml WORDPRESS_COMPOSE := docker compose -f $(WORDPRESS_DIR)/docker-compose.yml -f $(ROOT_DIR)/docker-compose.override.yml SITE_COMPOSE := env UID=$(LOCAL_UID) GID=$(LOCAL_GID) HTTP_PORT=$(SITE_HTTP_PORT) HTTP_PORT_BROWSERSYNC=$(SITE_BROWSERSYNC_PORT) SERVER_MODE=$(SITE_SERVER_MODE) URL_SITE=$(SITE_BASE_URL) LIBRESIGN_PUBLISH_HEADER_FRAGMENTS=$(LIBRESIGN_PUBLISH_HEADER_FRAGMENTS) LIBRESIGN_HEADER_WEBHOOK_URL=$(LIBRESIGN_HEADER_WEBHOOK_URL) LIBRESIGN_HEADER_WEBHOOK_SECRET=$(LIBRESIGN_HEADER_WEBHOOK_SECRET) LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS=$(LIBRESIGN_PUBLISH_FOOTER_FRAGMENTS) LIBRESIGN_FOOTER_WEBHOOK_URL=$(LIBRESIGN_FOOTER_WEBHOOK_URL) LIBRESIGN_FOOTER_WEBHOOK_SECRET=$(LIBRESIGN_FOOTER_WEBHOOK_SECRET) docker compose -f $(SITE_DIR)/docker-compose.yml NEXTCLOUD_OCC := $(NEXTCLOUD_COMPOSE) exec -u www-data nextcloud php occ WORDPRESS_CLI := $(WORDPRESS_COMPOSE) exec wordpress wp --allow-root +WORDPRESS_CLI_STDIN := $(WORDPRESS_COMPOSE) exec -T wordpress wp --allow-root _help: @echo "LibreSign SaaS - Available commands:" @@ -59,19 +57,23 @@ _help: @echo " make down site - Stop only the static site stack" @echo " make down nextcloud - Stop only the Nextcloud stack" @echo " make down wordpress nextcloud - Stop only the selected stacks" + @echo " make test - Validate the Compose files and the running integration" + @echo " make test-config - Validate the Compose files only" @echo "" @echo "Environment variables:" @echo " SITE_HTTP_PORT - Static site port (default: 8081)" @echo " SITE_BROWSERSYNC_PORT - Static site HMR port (default: 3000)" @echo " SITE_SERVER_MODE - Static site container mode for make up (default: build)" - @echo " NEXTCLOUD_HTTP_PORT - Nextcloud port (default: 8082)" + @echo " WORDPRESS_HTTP_PORT - WordPress port (default: 8080)" + @echo " NEXTCLOUD_HOST - Nextcloud hostname behind the shared proxy (default: nextcloud-development.localhost)" + @echo " NEXTCLOUD_PROTOCOL - Nextcloud protocol behind the shared proxy (default: https)" @echo " NEXTCLOUD_ADMIN_USER - Nextcloud admin username (default: admin)" @echo " NEXTCLOUD_ADMIN_PASSWORD - Nextcloud admin password (default: admin)" - @echo " WORDPRESS_SITE_URL - WordPress site URL for first install (default: http://localhost)" + @echo " WORDPRESS_SITE_URL - WordPress site URL for first install (default: http://localhost:8080)" @echo " WORDPRESS_ADMIN_USER - WordPress admin username for first install (default: admin)" @echo " WORDPRESS_ADMIN_PASSWORD - WordPress admin password for first install (default: admin)" @echo " WORDPRESS_ADMIN_EMAIL - WordPress admin email for first install (default: admin@example.com)" - @echo " WORDPRESS_WEBHOOK_BASE_URL - Hostname used by the static site container to reach WordPress webhooks (default: http://host.docker.internal)" + @echo " WORDPRESS_WEBHOOK_BASE_URL - Hostname used by the static site container to reach WordPress webhooks (default: http://host.docker.internal:8080)" @echo " LIBRESIGN_PUBLISH_HEADER_FRAGMENTS - Publish shared header artifacts after site build (default: true)" @echo " LIBRESIGN_HEADER_WEBHOOK_URL - Target WordPress header webhook URL" @echo " LIBRESIGN_HEADER_WEBHOOK_SECRET - Shared secret for header artifact publishing" @@ -86,19 +88,29 @@ help: _help up: @for component in $(UP_COMPONENTS); do \ - $(MAKE) --no-print-directory _up-$$component; \ + $(MAKE) --no-print-directory _up-$$component || exit 1; \ done @if [ -n "$(filter wordpress,$(UP_COMPONENTS))" ] && [ -n "$(filter nextcloud,$(UP_COMPONENTS))" ]; then \ - $(MAKE) --no-print-directory _up-integration; \ + $(MAKE) --no-print-directory _up-integration || exit 1; \ fi @echo "Environment up ($(UP_COMPONENTS))." site wordpress nextcloud: @: +test: test-config test-integration + +test-config: + @echo "Validating Compose configuration..." + @$(NEXTCLOUD_COMPOSE) config --quiet + @$(WORDPRESS_COMPOSE) config --quiet + +test-integration: + @sh $(ROOT_DIR)/tests/integration.sh + down: @for component in $(UP_COMPONENTS); do \ - $(MAKE) --no-print-directory _down-$$component; \ + $(MAKE) --no-print-directory _down-$$component || exit 1; \ done @echo "Environment down ($(UP_COMPONENTS))." @@ -149,7 +161,7 @@ _refresh-wordpress-images: _refresh-nextcloud-images: @echo "Refreshing Nextcloud images..." - @$(NEXTCLOUD_COMPOSE) pull database redis nextcloud nginx + @$(NEXTCLOUD_COMPOSE) pull --ignore-buildable _start-site: @echo "Starting site services..." @@ -171,7 +183,7 @@ _start-wordpress: _start-nextcloud: @echo "Starting Nextcloud services..." - @$(NEXTCLOUD_COMPOSE) up -d database redis nextcloud nginx + @$(NEXTCLOUD_COMPOSE) up -d _install-wordpress: @echo "Ensuring WordPress core is installed..." @@ -210,11 +222,12 @@ _install-wordpress: _wait-wordpress: @echo "Waiting for WordPress to be ready..." @attempt=0; \ - until output=$$($(WORDPRESS_CLI) option get siteurl 2>&1); do \ + until output=$$($(WORDPRESS_CLI) option get siteurl 2>&1) && $(WORDPRESS_COMPOSE) exec -T wordpress grep -qx php-fpm /proc/1/comm; do \ attempt=$$((attempt + 1)); \ - if [ $$attempt -ge 60 ]; then \ - echo "WordPress is not ready after 120s"; \ + if [ $$attempt -ge 300 ]; then \ + echo "WordPress did not finish installing plugins and themes after 600s"; \ echo "$$output"; \ + $(WORDPRESS_COMPOSE) logs --tail 50 wordpress; \ exit 1; \ fi; \ sleep 2; \ @@ -223,10 +236,11 @@ _wait-wordpress: _wait-nextcloud: @echo "Waiting for Nextcloud to be ready..." @attempt=0; \ - until $(NEXTCLOUD_COMPOSE) exec -T nextcloud pgrep php-fpm >/dev/null 2>&1; do \ + until $(NEXTCLOUD_OCC) status 2>/dev/null | grep -q 'installed: true' && $(NEXTCLOUD_COMPOSE) exec -T nextcloud pgrep php-fpm >/dev/null 2>&1; do \ attempt=$$((attempt + 1)); \ - if [ $$attempt -ge 120 ]; then \ - echo "Nextcloud is not ready for app operations after 240s"; \ + if [ $$attempt -ge 120 ] || ! $(NEXTCLOUD_COMPOSE) ps --status running --services | grep -qx nextcloud; then \ + echo "Nextcloud is not ready for app operations"; \ + $(NEXTCLOUD_COMPOSE) logs --tail 50 nextcloud; \ exit 1; \ fi; \ sleep 2; \ @@ -238,9 +252,9 @@ _fix-nextcloud-apps-permissions: _enable-wordpress-plugin: @echo "Enabling WordPress plugin..." - @$(WORDPRESS_CLI) plugin activate woocommerce-nextcloud-admin-group-manager >/dev/null || true + @$(WORDPRESS_CLI) plugin activate woocommerce-nextcloud-admin-group-manager -_setup-apps: _ensure-wordpress-app _ensure-nextcloud-app _enable-apps _set-wordpress-dsn _set-trusted-domains +_setup-apps: _ensure-wordpress-app _ensure-nextcloud-app _enable-apps _set-wordpress-dsn _set-nextcloud-api-host _ensure-wordpress-app: @echo "Setting up wordpress_login_backend app..." @@ -262,32 +276,27 @@ _ensure-nextcloud-app: _enable-apps: @echo "Enabling apps..." - @$(NEXTCLOUD_OCC) app:enable wordpress_login_backend >/dev/null || true - @$(NEXTCLOUD_OCC) app:enable admin_group_manager --force >/dev/null || true - @$(NEXTCLOUD_OCC) app:enable groupquota --force >/dev/null || true + @$(NEXTCLOUD_OCC) app:enable wordpress_login_backend --force + @$(NEXTCLOUD_OCC) app:enable admin_group_manager --force + @$(NEXTCLOUD_OCC) app:enable groupquota --force _set-wordpress-dsn: - @$(NEXTCLOUD_OCC) config:system:set wordpress_dsn --value "mysql:host=mariadb;port=3306;dbname=wordpress;user=root;password=root" >/dev/null + @$(NEXTCLOUD_OCC) config:system:set wordpress_dsn --value "mysql:host=wordpress-mariadb;port=3306;dbname=wordpress;user=root;password=root" >/dev/null -_set-trusted-domains: - @host=$$(echo "$(NEXTCLOUD_LOCAL_URL)" | sed -E 's#^[a-z]+://##; s#[:/].*##'); \ - index=$$($(NEXTCLOUD_COMPOSE) exec -T -u www-data -e HOST="$$host" nextcloud php -r 'require "config/config.php"; $$domains = $$CONFIG["trusted_domains"] ?? []; echo in_array(getenv("HOST"), $$domains, true) ? "" : ($$domains ? max(array_keys($$domains)) + 1 : 0);'); \ - if [ -n "$$index" ]; then \ - $(NEXTCLOUD_OCC) config:system:set trusted_domains $$index --value "$$host" >/dev/null; \ - fi +_set-nextcloud-api-host: + @nextcloud_url=$$($(NEXTCLOUD_COMPOSE) exec -T nextcloud sh -c 'printf "%s://%s" "$$NEXTCLOUD_PROTOCOL" "$$NEXTCLOUD_HOST"'); \ + $(WORDPRESS_CLI) option update nextcloud_api_host "$$nextcloud_url" >/dev/null _connect-networks: - @echo "Connecting Docker networks..." - @NEXTCLOUD_CONTAINER=$$($(NEXTCLOUD_COMPOSE) ps -q nextcloud); \ - WORDPRESS_CONTAINER=$$($(WORDPRESS_COMPOSE) ps -q mariadb); \ - WORDPRESS_NETWORK=$$(docker inspect -f '{{range $$k,$$v := .NetworkSettings.Networks}}{{println $$k}}{{end}}' $$WORDPRESS_CONTAINER | head -n1); \ - docker network connect $$WORDPRESS_NETWORK $$NEXTCLOUD_CONTAINER 2>/dev/null || true + @echo "Connecting WordPress to the Nextcloud network..." + @NEXTCLOUD_NETWORK=$$(docker inspect -f '{{range $$k,$$v := .NetworkSettings.Networks}}{{println $$k}}{{end}}' $$($(NEXTCLOUD_COMPOSE) ps -q nextcloud) | head -n1); \ + for service in wordpress mariadb; do \ + container=$$($(WORDPRESS_COMPOSE) ps -q $$service); \ + if ! docker inspect -f '{{json .NetworkSettings.Networks}}' $$container | grep -q "\"$$NEXTCLOUD_NETWORK\""; then \ + docker network connect --alias wordpress-$$service $$NEXTCLOUD_NETWORK $$container || exit 1; \ + fi; \ + done _provision-user: @echo "Provisioning admin user..." - @curl -sS -u admin:admin \ - -X POST "$(NEXTCLOUD_BASE_URL)/ocs/v2.php/apps/admin_group_manager/api/v1/admin-group" \ - -H "OCS-APIREQUEST: true" \ - -d "groupid=admlibrecode" \ - -d "email=adm@librecode.coop" \ - -d "displayname=Adm Librecode" >/dev/null || true + @$(WORDPRESS_CLI_STDIN) eval-file - < $(ROOT_DIR)/.docker/wordpress/provision-admin-group.php diff --git a/README.md b/README.md index 7ecae1e..6891d9a 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,13 @@ To keep local development simple and avoid `/etc/hosts` changes, the static site is exposed on its own localhost port instead of using the production subdomain layout: -- WordPress: +- WordPress: - Static site: -- Nextcloud: +- Nextcloud: , served by the + `nextcloud-development` shared proxy, which owns host ports `80` and `443` + +WordPress reaches Nextcloud through the same HTTPS URL and validates its +certificate against the shared proxy's local certificate authority. `make up` refreshes the remote Docker images used by the WordPress, static site, and Nextcloud services and rebuilds local buildable services before starting the @@ -89,6 +93,11 @@ On a fresh WordPress database, `make up` also performs the initial WordPress installation via WP-CLI and restarts the WordPress container once so the `wordpress-docker` entrypoint can install the configured plugins and themes. +`make test` validates the Compose configuration and checks the running +integration: required services, the WordPress → Nextcloud HTTPS connection, an +authenticated Nextcloud API request, the required apps and plugins, and that +running `make up` again keeps the existing configuration. + ## Integration Flow WordPress is the customer-facing commerce and account portal (plans, subscriptions, invoices, payments, and account reports). diff --git a/docker-compose.nextcloud.override.yml b/docker-compose.nextcloud.override.yml deleted file mode 100644 index 1d77110..0000000 --- a/docker-compose.nextcloud.override.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - nginx: - ports: - - target: 80 - published: "${HTTP_PORT:-80-99}" - host_ip: ${DOCKER_HOST_GATEWAY_IP:?DOCKER_HOST_GATEWAY_IP is required} diff --git a/docker-compose.override.yml b/docker-compose.override.yml index de40397..87d14a9 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -2,8 +2,7 @@ services: wordpress: environment: PROD_SITE_URL: https://account.libresign.coop - LOCAL_SITE_URL: http://localhost - NEXTCLOUD_LOCAL_URL: ${NEXTCLOUD_LOCAL_URL:-http://host.docker.internal:8082} + LOCAL_SITE_URL: http://localhost:${WORDPRESS_HTTP_PORT:-8080} WORDPRESS_LOCAL_USERS_PASSWORD: ${WORDPRESS_LOCAL_USERS_PASSWORD:-admin} WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS: ${WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS:-1} WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER: ${WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER:-} @@ -48,7 +47,6 @@ services: - slug: woocommerce-nextcloud-admin-group-manager source: https://github.com/LibreSign/woocommerce-nextcloud-admin-group-manager.git post_install_commands: - - wp option update nextcloud_api_host $$NEXTCLOUD_LOCAL_URL - wp option update nextcloud_api_login ${NEXTCLOUD_ADMIN_USER:-admin} - wp option update nextcloud_api_password ${NEXTCLOUD_ADMIN_PASSWORD:-admin} - slug: woocommerce-restrict-switch @@ -58,10 +56,13 @@ services: wordpress_custom_themes: - slug: libresign source: https://github.com/LibreSign/wordpress-theme.git + volumes: + - ../.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php:/var/www/html/wp-content/mu-plugins/nextcloud-dev-proxy.php + - librecode-dev-proxy-certs:/etc/librecode-dev-proxy-certs:ro nginx: ports: - - "80:80" + - "${WORDPRESS_HTTP_PORT:-8080}:80" volumes: - ../.docker/wordpress-nginx/default.conf:/etc/nginx/conf.d/default.conf:ro @@ -74,3 +75,7 @@ services: image: axllent/mailpit:latest ports: - "127.0.0.1:8025:8025" + +volumes: + librecode-dev-proxy-certs: + name: librecode-dev-proxy-certs From b5199fd687090f7a39a168d52b0154de6030fc50 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 9 Oct 2026 10:48:31 -0400 Subject: [PATCH 5/7] test: cover the fresh clone startup and integration Signed-off-by: YvesCesar --- .github/workflows/compose-config.yml | 18 +++++ tests/integration.sh | 108 +++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 .github/workflows/compose-config.yml create mode 100644 tests/integration.sh diff --git a/.github/workflows/compose-config.yml b/.github/workflows/compose-config.yml new file mode 100644 index 0000000..d27364d --- /dev/null +++ b/.github/workflows/compose-config.yml @@ -0,0 +1,18 @@ +name: Compose configuration + +on: + pull_request: + push: + branches: + - main + +jobs: + compose-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: true + + - name: Validate Compose configuration + run: make test-config diff --git a/tests/integration.sh b/tests/integration.sh new file mode 100644 index 0000000..17dc71e --- /dev/null +++ b/tests/integration.sh @@ -0,0 +1,108 @@ +#!/bin/sh + +set -eu + +root_dir="$(cd -- "$(dirname -- "$0")/.." && pwd)" + +nextcloud_compose() { + docker compose -f "$root_dir/nextcloud-development/docker-compose.yml" "$@" +} + +wordpress_compose() { + docker compose -f "$root_dir/wordpress-docker/docker-compose.yml" -f "$root_dir/docker-compose.override.yml" "$@" +} + +occ() { + nextcloud_compose exec -T -u www-data nextcloud php occ "$@" +} + +wp() { + wordpress_compose exec -T wordpress wp --allow-root "$@" +} + +fail() { + echo "not ok - $1" >&2 + exit 1 +} + +pass() { + echo "ok - $1" +} + +assert_services_running() { + stack="$1" + shift + running="$("${stack}_compose" ps --status running --services)" + for service in "$@"; do + echo "$running" | grep -qx "$service" || fail "$stack service $service is running" + done + pass "$stack services are running" +} + +nextcloud_url() { + nextcloud_compose exec -T nextcloud sh -c 'printf "%s://%s" "$NEXTCLOUD_PROTOCOL" "$NEXTCLOUD_HOST"' +} + +assert_wordpress_uses_nextcloud_url() { + expected="$(nextcloud_url)" + actual="$(wp option get nextcloud_api_host)" + [ "$actual" = "$expected" ] || fail "WordPress uses $expected for Nextcloud (got $actual)" + pass "WordPress uses the Nextcloud URL configured by NCDD" +} + +assert_wordpress_reaches_nextcloud() { + wp eval ' + $response = wp_remote_get( rtrim( get_option( "nextcloud_api_host" ), "/" ) . "/status.php", array( "sslverify" => true ) ); + if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { + WP_CLI::error( is_wp_error( $response ) ? $response->get_error_message() : wp_remote_retrieve_body( $response ) ); + } + ' >/dev/null || fail "WordPress reaches Nextcloud with a verified TLS certificate" + pass "WordPress reaches Nextcloud with a verified TLS certificate" +} + +assert_authenticated_request() { + wp eval ' + $response = agm_nextcloud_request( "GET", "/ocs/v2.php/cloud/user?format=json" ); + if ( ! $response->succeeded() ) { + WP_CLI::error( $response->failure_message() ); + } + ' >/dev/null || fail "WordPress performs an authenticated Nextcloud API request" + pass "WordPress performs an authenticated Nextcloud API request" +} + +assert_integration_enabled() { + enabled_apps="$(occ app:list --enabled)" + for app in wordpress_login_backend admin_group_manager groupquota; do + echo "$enabled_apps" | grep -q "^ - $app:" || fail "Nextcloud app $app is enabled" + done + wp plugin is-active woocommerce-nextcloud-admin-group-manager || fail "WordPress plugin woocommerce-nextcloud-admin-group-manager is active" + occ group:info admlibrecode >/dev/null || fail "Nextcloud group admlibrecode is provisioned" + pass "Integration apps, plugin and admin group are in place" +} + +configuration_snapshot() { + occ config:system:get trusted_domains + occ config:system:get overwritehost + occ config:system:get overwriteprotocol + occ config:system:get wordpress_dsn + wp option get nextcloud_api_host +} + +assert_environment() { + assert_services_running nextcloud database redis nextcloud nginx proxy-coordinator + assert_services_running wordpress mariadb wordpress nginx mailpit + assert_wordpress_uses_nextcloud_url + assert_wordpress_reaches_nextcloud + assert_authenticated_request + assert_integration_enabled +} + +assert_environment + +before="$(configuration_snapshot)" +make -C "$root_dir" --no-print-directory up wordpress nextcloud >/dev/null +after="$(configuration_snapshot)" +[ "$before" = "$after" ] || fail "Running make up again keeps the existing configuration" +pass "Running make up again keeps the existing configuration" + +assert_environment From 9d28e2d0aa3247ee22065726934c6b3356821265 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 9 Oct 2026 11:24:19 -0400 Subject: [PATCH 6/7] fix: harden the wordpress and nextcloud integration Signed-off-by: YvesCesar --- .../mu-plugins/nextcloud-dev-proxy.php | 11 +++- Makefile | 55 +++++++++++++++---- README.md | 8 ++- docker-compose.override.yml | 3 +- 4 files changed, 60 insertions(+), 17 deletions(-) diff --git a/.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php b/.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php index da12ef8..dd693e9 100644 --- a/.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php +++ b/.docker/wordpress/mu-plugins/nextcloud-dev-proxy.php @@ -4,7 +4,7 @@ function librecode_dev_proxy_nextcloud_host( string $url ): ?string { $nextcloud_host = wp_parse_url( (string) get_option( 'nextcloud_api_host' ), PHP_URL_HOST ); - if ( ! $nextcloud_host || wp_parse_url( $url, PHP_URL_HOST ) !== $nextcloud_host || ! is_readable( LIBRECODE_DEV_PROXY_CA ) ) { + if ( ! $nextcloud_host || wp_parse_url( $url, PHP_URL_HOST ) !== $nextcloud_host ) { return null; } @@ -14,8 +14,14 @@ function librecode_dev_proxy_nextcloud_host( string $url ): ?string { add_filter( 'http_request_args', static function ( array $args, string $url ): array { - if ( librecode_dev_proxy_nextcloud_host( $url ) ) { + if ( ! librecode_dev_proxy_nextcloud_host( $url ) ) { + return $args; + } + + if ( is_readable( LIBRECODE_DEV_PROXY_CA ) ) { $args['sslcertificates'] = LIBRECODE_DEV_PROXY_CA; + } else { + error_log( 'The NCDD shared proxy CA is not available at ' . LIBRECODE_DEV_PROXY_CA . '.' ); } return $args; @@ -35,6 +41,7 @@ static function ( $handle, array $parsed_args, string $url ): void { // libcurl resolves *.localhost to the loopback (RFC 6761) and skips Docker DNS, where the shared proxy alias lives. $proxy_ip = gethostbyname( $nextcloud_host ); if ( $proxy_ip === $nextcloud_host ) { + error_log( "The NCDD shared proxy alias {$nextcloud_host} does not resolve on the WordPress network." ); return; } diff --git a/Makefile b/Makefile index 903a0be..8db5e40 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ NEXTCLOUD_APPS_DIR := $(NEXTCLOUD_DIR)/volumes/nextcloud/apps-extra LOCAL_UID := $(shell id -u) LOCAL_GID := $(shell id -g) WORDPRESS_HTTP_PORT ?= 8080 -WORDPRESS_SITE_URL ?= http://localhost:$(WORDPRESS_HTTP_PORT) +WORDPRESS_LEGACY_SITE_URL := http://localhost +WORDPRESS_SITE_URL ?= http://127.0.0.1:$(WORDPRESS_HTTP_PORT) WORDPRESS_SITE_TITLE ?= LibreSign SaaS WORDPRESS_ADMIN_USER ?= admin WORDPRESS_ADMIN_PASSWORD ?= admin @@ -43,6 +44,9 @@ SITE_COMPOSE := env UID=$(LOCAL_UID) GID=$(LOCAL_GID) HTTP_PORT=$(SITE_HTTP_PORT NEXTCLOUD_OCC := $(NEXTCLOUD_COMPOSE) exec -u www-data nextcloud php occ WORDPRESS_CLI := $(WORDPRESS_COMPOSE) exec wordpress wp --allow-root WORDPRESS_CLI_STDIN := $(WORDPRESS_COMPOSE) exec -T wordpress wp --allow-root +NEXTCLOUD_NETWORK = $$($(NEXTCLOUD_COMPOSE) config | sed -n 's/^name: //p')_default +WORDPRESS_NETWORK = $$($(WORDPRESS_COMPOSE) config | sed -n 's/^name: //p')_default +NCDD_PROXY_FILTER := label=coop.librecode.dev-proxy=true _help: @echo "LibreSign SaaS - Available commands:" @@ -69,7 +73,7 @@ _help: @echo " NEXTCLOUD_PROTOCOL - Nextcloud protocol behind the shared proxy (default: https)" @echo " NEXTCLOUD_ADMIN_USER - Nextcloud admin username (default: admin)" @echo " NEXTCLOUD_ADMIN_PASSWORD - Nextcloud admin password (default: admin)" - @echo " WORDPRESS_SITE_URL - WordPress site URL for first install (default: http://localhost:8080)" + @echo " WORDPRESS_SITE_URL - WordPress site URL for first install (default: http://127.0.0.1:8080)" @echo " WORDPRESS_ADMIN_USER - WordPress admin username for first install (default: admin)" @echo " WORDPRESS_ADMIN_PASSWORD - WordPress admin password for first install (default: admin)" @echo " WORDPRESS_ADMIN_EMAIL - WordPress admin email for first install (default: admin@example.com)" @@ -117,15 +121,15 @@ down: _down-site: $(SITE_COMPOSE) down -_down-wordpress: +_down-wordpress: _disconnect-networks $(WORDPRESS_COMPOSE) down -_down-nextcloud: +_down-nextcloud: _disconnect-networks $(NEXTCLOUD_COMPOSE) down _up-site: _ensure-site-repo _prepare-site-output-dir _refresh-site-images _start-site -_up-wordpress: _refresh-wordpress-images _start-wordpress _install-wordpress _wait-wordpress _enable-wordpress-plugin +_up-wordpress: _refresh-wordpress-images _start-wordpress _install-wordpress _wait-wordpress _migrate-wordpress-url _enable-wordpress-plugin _up-nextcloud: _refresh-nextcloud-images _start-nextcloud _wait-nextcloud _fix-nextcloud-apps-permissions @@ -179,6 +183,7 @@ _start-site: _start-wordpress: @echo "Starting WordPress services..." + @docker volume create librecode-dev-proxy-certs >/dev/null @$(WORDPRESS_COMPOSE) up -d mariadb wordpress nginx mailpit _start-nextcloud: @@ -250,6 +255,14 @@ _fix-nextcloud-apps-permissions: @echo "Fixing Nextcloud apps-extra permissions..." @$(NEXTCLOUD_COMPOSE) exec -u root nextcloud sh -lc 'mkdir -p /var/www/html/apps-extra && chown -R $(LOCAL_UID):$(LOCAL_GID) /var/www/html/apps-extra' >/dev/null +_migrate-wordpress-url: + @for option in home siteurl; do \ + if [ "$$($(WORDPRESS_CLI_STDIN) option get $$option)" = "$(WORDPRESS_LEGACY_SITE_URL)" ]; then \ + echo "Moving WordPress $$option from $(WORDPRESS_LEGACY_SITE_URL) to $(WORDPRESS_SITE_URL)..."; \ + $(WORDPRESS_CLI_STDIN) option update $$option "$(WORDPRESS_SITE_URL)" >/dev/null || exit 1; \ + fi; \ + done + _enable-wordpress-plugin: @echo "Enabling WordPress plugin..." @$(WORDPRESS_CLI) plugin activate woocommerce-nextcloud-admin-group-manager @@ -284,17 +297,35 @@ _set-wordpress-dsn: @$(NEXTCLOUD_OCC) config:system:set wordpress_dsn --value "mysql:host=wordpress-mariadb;port=3306;dbname=wordpress;user=root;password=root" >/dev/null _set-nextcloud-api-host: - @nextcloud_url=$$($(NEXTCLOUD_COMPOSE) exec -T nextcloud sh -c 'printf "%s://%s" "$$NEXTCLOUD_PROTOCOL" "$$NEXTCLOUD_HOST"'); \ + @nextcloud_url=$$($(NEXTCLOUD_COMPOSE) exec -T nextcloud sh -c 'printf "%s://%s" "$${NEXTCLOUD_PROTOCOL:?}" "$${NEXTCLOUD_HOST:?}"') && \ $(WORDPRESS_CLI) option update nextcloud_api_host "$$nextcloud_url" >/dev/null _connect-networks: - @echo "Connecting WordPress to the Nextcloud network..." - @NEXTCLOUD_NETWORK=$$(docker inspect -f '{{range $$k,$$v := .NetworkSettings.Networks}}{{println $$k}}{{end}}' $$($(NEXTCLOUD_COMPOSE) ps -q nextcloud) | head -n1); \ - for service in wordpress mariadb; do \ - container=$$($(WORDPRESS_COMPOSE) ps -q $$service); \ - if ! docker inspect -f '{{json .NetworkSettings.Networks}}' $$container | grep -q "\"$$NEXTCLOUD_NETWORK\""; then \ - docker network connect --alias wordpress-$$service $$NEXTCLOUD_NETWORK $$container || exit 1; \ + @echo "Connecting the Nextcloud proxy and the WordPress database..." + @nextcloud_host=$$($(NEXTCLOUD_COMPOSE) exec -T nextcloud sh -c 'printf "%s" "$${NEXTCLOUD_HOST:?}"') || exit 1; \ + proxy=$$(docker ps -q --filter $(NCDD_PROXY_FILTER)); \ + mariadb=$$($(WORDPRESS_COMPOSE) ps -q mariadb); \ + if [ -z "$$proxy" ] || [ -z "$$mariadb" ]; then \ + echo "The NCDD shared proxy and the WordPress database must be running."; \ + exit 1; \ + fi; \ + connect() { \ + docker inspect -f '{{json .NetworkSettings.Networks}}' "$$3" | grep -q "\"$$1\"" || docker network connect --alias "$$2" "$$1" "$$3"; \ + }; \ + connect "$(WORDPRESS_NETWORK)" "$$nextcloud_host" "$$proxy" && \ + connect "$(NEXTCLOUD_NETWORK)" wordpress-mariadb "$$mariadb" + +_disconnect-networks: + @disconnect() { \ + if docker inspect -f '{{json .NetworkSettings.Networks}}' "$$2" | grep -q "\"$$1\""; then \ + docker network disconnect "$$1" "$$2"; \ fi; \ + }; \ + for proxy in $$(docker ps -q --filter $(NCDD_PROXY_FILTER)); do \ + disconnect "$(WORDPRESS_NETWORK)" "$$proxy" || exit 1; \ + done; \ + for mariadb in $$($(WORDPRESS_COMPOSE) ps -q mariadb); do \ + disconnect "$(NEXTCLOUD_NETWORK)" "$$mariadb" || exit 1; \ done _provision-user: diff --git a/README.md b/README.md index 6891d9a..00ea8e1 100644 --- a/README.md +++ b/README.md @@ -75,13 +75,17 @@ To keep local development simple and avoid `/etc/hosts` changes, the static site is exposed on its own localhost port instead of using the production subdomain layout: -- WordPress: +- WordPress: - Static site: - Nextcloud: , served by the `nextcloud-development` shared proxy, which owns host ports `80` and `443` WordPress reaches Nextcloud through the same HTTPS URL and validates its -certificate against the shared proxy's local certificate authority. +certificate against the shared proxy's local certificate authority. WordPress +uses `127.0.0.1` instead of `localhost` because the shared proxy sends HSTS for +`https://localhost`, which makes browsers upgrade `http://localhost:8080` to +HTTPS. Existing installs that still use `http://localhost` are moved to the new +URL by `make up`. `make up` refreshes the remote Docker images used by the WordPress, static site, and Nextcloud services and rebuilds local buildable services before starting the diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 87d14a9..ddee26e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -2,7 +2,7 @@ services: wordpress: environment: PROD_SITE_URL: https://account.libresign.coop - LOCAL_SITE_URL: http://localhost:${WORDPRESS_HTTP_PORT:-8080} + LOCAL_SITE_URL: http://127.0.0.1:${WORDPRESS_HTTP_PORT:-8080} WORDPRESS_LOCAL_USERS_PASSWORD: ${WORDPRESS_LOCAL_USERS_PASSWORD:-admin} WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS: ${WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS:-1} WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER: ${WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER:-} @@ -79,3 +79,4 @@ services: volumes: librecode-dev-proxy-certs: name: librecode-dev-proxy-certs + external: true From 85c0538dc2ad1edb0a481dcdb7bb9e2121800efd Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 9 Oct 2026 11:24:19 -0400 Subject: [PATCH 7/7] test: report every integration failure and cover wordpress name resolution Signed-off-by: YvesCesar --- tests/integration.sh | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/tests/integration.sh b/tests/integration.sh index 17dc71e..dc846e7 100644 --- a/tests/integration.sh +++ b/tests/integration.sh @@ -2,14 +2,17 @@ set -eu +: "${NEXTCLOUD_COMPOSE:?Run this test with make test-integration}" +: "${WORDPRESS_COMPOSE:?Run this test with make test-integration}" + root_dir="$(cd -- "$(dirname -- "$0")/.." && pwd)" nextcloud_compose() { - docker compose -f "$root_dir/nextcloud-development/docker-compose.yml" "$@" + $NEXTCLOUD_COMPOSE "$@" } wordpress_compose() { - docker compose -f "$root_dir/wordpress-docker/docker-compose.yml" -f "$root_dir/docker-compose.override.yml" "$@" + $WORDPRESS_COMPOSE "$@" } occ() { @@ -32,7 +35,7 @@ pass() { assert_services_running() { stack="$1" shift - running="$("${stack}_compose" ps --status running --services)" + running="$("${stack}_compose" ps --status running --services)" || fail "list the running $stack services" for service in "$@"; do echo "$running" | grep -qx "$service" || fail "$stack service $service is running" done @@ -40,16 +43,25 @@ assert_services_running() { } nextcloud_url() { - nextcloud_compose exec -T nextcloud sh -c 'printf "%s://%s" "$NEXTCLOUD_PROTOCOL" "$NEXTCLOUD_HOST"' + nextcloud_compose exec -T nextcloud sh -c 'printf "%s://%s" "${NEXTCLOUD_PROTOCOL:?}" "${NEXTCLOUD_HOST:?}"' } assert_wordpress_uses_nextcloud_url() { - expected="$(nextcloud_url)" - actual="$(wp option get nextcloud_api_host)" + expected="$(nextcloud_url)" || fail "read the Nextcloud URL configured by NCDD" + actual="$(wp option get nextcloud_api_host)" || fail "read the Nextcloud URL configured in WordPress" [ "$actual" = "$expected" ] || fail "WordPress uses $expected for Nextcloud (got $actual)" pass "WordPress uses the Nextcloud URL configured by NCDD" } +assert_wordpress_uses_its_own_services() { + mailpit_ips="$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' "$(wordpress_compose ps -q mailpit)")" || fail "inspect the WordPress mailpit" + resolved="$(wordpress_compose exec -T wordpress getent hosts mailpit | awk '{print $1}')" || fail "resolve mailpit from WordPress" + case " $mailpit_ips " in + *" $resolved "*) pass "WordPress resolves mailpit to its own service" ;; + *) fail "WordPress resolves mailpit to its own service (got $resolved, expected one of $mailpit_ips)" ;; + esac +} + assert_wordpress_reaches_nextcloud() { wp eval ' $response = wp_remote_get( rtrim( get_option( "nextcloud_api_host" ), "/" ) . "/status.php", array( "sslverify" => true ) ); @@ -71,7 +83,7 @@ assert_authenticated_request() { } assert_integration_enabled() { - enabled_apps="$(occ app:list --enabled)" + enabled_apps="$(occ app:list --enabled)" || fail "list the enabled Nextcloud apps" for app in wordpress_login_backend admin_group_manager groupquota; do echo "$enabled_apps" | grep -q "^ - $app:" || fail "Nextcloud app $app is enabled" done @@ -81,17 +93,19 @@ assert_integration_enabled() { } configuration_snapshot() { - occ config:system:get trusted_domains - occ config:system:get overwritehost - occ config:system:get overwriteprotocol - occ config:system:get wordpress_dsn - wp option get nextcloud_api_host + occ config:system:get trusted_domains && + occ config:system:get overwritehost && + occ config:system:get overwriteprotocol && + occ config:system:get wordpress_dsn && + wp option get nextcloud_api_host && + wp option get siteurl } assert_environment() { assert_services_running nextcloud database redis nextcloud nginx proxy-coordinator assert_services_running wordpress mariadb wordpress nginx mailpit assert_wordpress_uses_nextcloud_url + assert_wordpress_uses_its_own_services assert_wordpress_reaches_nextcloud assert_authenticated_request assert_integration_enabled @@ -99,9 +113,9 @@ assert_environment() { assert_environment -before="$(configuration_snapshot)" -make -C "$root_dir" --no-print-directory up wordpress nextcloud >/dev/null -after="$(configuration_snapshot)" +before="$(configuration_snapshot)" || fail "read the configuration before running make up again" +make -C "$root_dir" --no-print-directory up wordpress nextcloud >/dev/null || fail "run make up again" +after="$(configuration_snapshot)" || fail "read the configuration after running make up again" [ "$before" = "$after" ] || fail "Running make up again keeps the existing configuration" pass "Running make up again keeps the existing configuration"