Compare commits

..

57 Commits

Author SHA1 Message Date
50f4638c71 Disable swarm-test deployment (server is down) 2026-06-02 16:50:59 +01:00
0394491779 Run CI tests on pull requests (not just push to main) 2026-06-02 16:39:34 +01:00
8dd3125a52 Added basic tests 2026-06-02 16:30:39 +01:00
cf54575187 restricts ownership changes to files still owned by root (e.g., from the image build). On subsequent restarts, files already owned by www-data are skipped entirely, avoiding a full recursive write cycle. 2026-06-02 16:24:48 +01:00
b4db12f09c Added ignores for .env, *.log, .DS_Store, Thumbs.db, and common editor/IDE files 2026-06-02 16:23:11 +01:00
e4b87c8ab9 chown entire wp-content to ensure correct permissions 2026-06-02 16:11:39 +01:00
7e170adbb4 Merge pull request 'Added xtra chown to ensure correct perms on every container start' (#57) from kawaiipunk/wordpress:main into main
Reviewed-on: coop-cloud/wordpress#57
2026-05-26 17:10:01 +00:00
66e0687456 Removed redundant chown 2026-05-26 17:05:24 +01:00
9209f007cb revert 69cf451b98
revert Merge pull request 'chore(deps): update wordpress docker tag to v7' (#55) from renovate/wordpress-7.x into main

Reviewed-on: coop-cloud/wordpress#55

Sorry this was done by mistake!
2026-05-26 14:08:49 +00:00
69cf451b98 Merge pull request 'chore(deps): update wordpress docker tag to v7' (#55) from renovate/wordpress-7.x into main
Reviewed-on: coop-cloud/wordpress#55
2026-05-26 13:59:35 +00:00
73a2e98d2e Added xtra chown to ensure correct perms on every container start 2026-05-26 14:10:22 +01:00
0e229168fc chore(deps): update wordpress docker tag to v7 2026-05-22 00:34:30 +00:00
332ab0b97d chore: publish 2.19.2+6.9.4 release 2026-04-28 02:25:26 +02:00
3b598e82dd harden htaccess 2026-04-28 01:57:52 +02:00
8e81f3f81c selfmanaged wordpress 2026-04-28 01:54:50 +02:00
a09bd166ad chore: publish 2.19.1+6.9.4 release 2026-03-17 17:11:56 +01:00
b4c5d04382 chore: replace depricated traefik.docker.* with traefik.swarm.* 2026-03-17 17:11:07 +01:00
3c013d39fd chore: publish 2.19.0+6.9.4 release 2026-03-17 16:11:37 +01:00
285bc01bef Merge pull request 'chore(config): migrate Renovate config' (#52) from renovate/migrate-config into main
Reviewed-on: coop-cloud/wordpress#52
2026-03-10 19:34:45 +00:00
2ca12bff9c chore(config): migrate config renovate.json 2026-03-10 19:33:29 +00:00
da2e62b618 chore: publish 2.18.0+6.9.1 release 2026-03-07 09:39:38 +01:00
364b5c8b8a chore: update image tags 2026-03-07 09:39:16 +01:00
306b348961 chore: publish 2.17.1+6.9.0 release 2026-02-16 10:58:29 +01:00
3c1121b7ce remove default TIMEOUT (abra #596) 2025-12-30 14:02:39 +01:00
3wc
b0eb1756a3 chore: publish 2.17.0+6.9.0 release 2025-12-07 17:27:49 -05:00
97b9b99e93 chore: publish 2.16.2+6.8.3 release 2025-10-15 11:30:23 -04:00
cf2a5077c9 chore: publish 2.16.1+6.8.1 release 2025-07-01 19:16:41 +02:00
74282d5658 chore: publish 2.16.0+6.8.1 release 2025-05-27 17:17:23 +02:00
ab7716bf1e chore: publish 2.15.0+6.8.0 release 2025-04-23 09:39:59 -07:00
3wc
b0cc5a49a1 chore: publish 2.14.0+6.7.2 release 2025-02-19 12:38:06 -05:00
3cca5d2cf3 Update .drone.yml 2025-01-08 10:09:13 -08:00
f403648cf6 chore: publish 2.13.3+6.7.1 release 2024-12-17 16:42:36 +01:00
83f724a316 add all possible ftp port compose files to .env 2024-12-17 16:40:40 +01:00
e6b9f8117f chore: publish 2.13.2+6.7.1 release 2024-12-17 15:29:21 +01:00
e7aef38d3b remove 2222 default port binding to avoid port conflicts 2024-12-17 15:28:47 +01:00
e08838561d chore: publish 2.13.1+6.7.1 release 2024-12-17 15:21:10 +01:00
04d26a59a9 add possiblitliy to use other sftp ports per compose overwrite 2024-12-17 15:16:42 +01:00
591019112e chore: publish 2.13.0+6.7.1 release 2024-12-10 19:23:34 +01:00
76e9b80fbd fix uploads.ini templating 2024-12-10 19:23:34 +01:00
f49fa05ded add some more wordpress options for file uploads (#44)
Co-authored-by: Steven Sting
Reviewed-on: coop-cloud/wordpress#44
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
2024-12-09 14:59:57 +00:00
716e6df3cd move USERS_CONF_VERSION env to abra.sh 2024-11-11 22:51:58 +01:00
457c2defaf update backupbot label 2024-10-24 15:26:10 +02:00
37ff3e9b1b chore: publish 2.12.2+6.6.2 release 2024-10-09 16:58:22 +02:00
c5443cc14b chore: publish 2.12.1+6.6.1 release 2024-08-26 18:21:25 +01:00
34f70889e3 chore: publish 2.12.0+6.6.1 release 2024-08-14 12:02:42 +02:00
af04e30e36 add show_plugins command 2024-08-14 12:02:33 +02:00
17574fd3fe chore: publish 2.11.0+6.6.0 release 2024-07-19 18:45:32 +02:00
8b6983d240 alaconnect: replace 'execute' with 'initial-hooks' 2024-07-15 13:57:23 +02:00
6fb30c5bc1 chore: publish 2.10.0+6.5.5 release 2024-07-06 20:25:45 +02:00
929bfb4239 add redirects 2024-07-06 20:21:05 +02:00
7e4ff5c075 add alakazam integration file alaconnect.yml 2024-05-13 17:34:43 +02:00
3wc
564db5b63f chore: publish 2.9.1+6.5.3 release 2024-05-11 12:18:46 -03:00
cf9b5f529e chore: publish 2.9.0+6.5.2 release 2024-04-15 16:50:18 +02:00
3wc
240b70a967 chore: publish 2.8.0+6.5.0 release 2024-04-07 12:43:44 -03:00
3wc
7f6b6a5ff2 Update metadata 2024-03-30 16:19:49 -03:00
4d99aae234 Updated .drone.yml to use main branch 2024-03-28 14:17:57 +00:00
959484f5e5 Added HTACCESS_CONF_VERSION to drone config 2024-03-28 14:15:37 +00:00
38 changed files with 707 additions and 52 deletions

View File

@ -1,29 +1,51 @@
---
kind: pipeline
name: deploy to swarm-test.autonomic.zone
name: test and deploy to swarm-test.autonomic.zone
steps:
- name: deployment
image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest
settings:
host: swarm-test.autonomic.zone
stack: wordpress
generate_secrets: true
purge: true
deploy_key:
from_secret: drone_ssh_swarm_test
networks:
- proxy
- name: test
image: alpine:3.21
environment:
DOMAIN: wordpress.swarm-test.autonomic.zone
STACK_NAME: wordpress
LETS_ENCRYPT_ENV: production
SECRET_DB_PASSWORD_VERSION: v1
SECRET_DB_ROOT_PASSWORD_VERSION: v1
PHP_UPLOADS_CONF_VERSION: v1
ENTRYPOINT_CONF_VERSION: v1
SHELLCHECK_OPTS: -s bash
commands:
- apk add --no-cache bash shellcheck py3-pip py3-yaml curl
- pip3 install --break-system-packages yamllint 2>/dev/null || true
- curl -sSLo /usr/local/bin/gomplate https://github.com/hairyhenderson/gomplate/releases/latest/download/gomplate_linux-amd64
- chmod +x /usr/local/bin/gomplate
- tests/run.sh
# deployment step disabled: swarm-test.autonomic.zone is down
# - name: deployment
# image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest
# when:
# event:
# - push
# branch:
# - main
# settings:
# host: swarm-test.autonomic.zone
# stack: wordpress
# generate_secrets: true
# purge: true
# deploy_key:
# from_secret: drone_ssh_swarm_test
# networks:
# - proxy
# environment:
# DOMAIN: wordpress.swarm-test.autonomic.zone
# STACK_NAME: wordpress
# LETS_ENCRYPT_ENV: production
# SECRET_DB_PASSWORD_VERSION: v1
# SECRET_DB_ROOT_PASSWORD_VERSION: v1
# PHP_UPLOADS_CONF_VERSION: v1
# ENTRYPOINT_CONF_VERSION: v1
# HTACCESS_CONF_VERSION: v1
trigger:
event:
- push
- pull_request
branch:
- master
- main
---
kind: pipeline
name: generate recipe catalogue
@ -36,7 +58,7 @@ steps:
from_secret: drone_abra-bot_token
fork: true
repositories:
- coop-cloud/auto-recipes-catalogue-json
- toolshed/auto-recipes-catalogue-json
trigger:
event: tag

View File

@ -1,11 +1,16 @@
TYPE=wordpress
TIMEOUT=300
#TIMEOUT=300
ENABLE_AUTO_UPDATE=true
COMPOSE_FILE="compose.yml"
ENABLE_BACKUPS=true
DOMAIN=wordpress.example.com
## Domain aliases
#EXTRA_DOMAINS=', `www.wordpress.example.com`'
# Redirects
# All redirect domains have to be added to EXTRA_DOMAINS as well)
# multiple redirects can be added by seperating them with a | character
#REDIRECTS=www.wordpress.example.com
LETS_ENCRYPT_ENV=production
# Setup Wordpress settings on each deploy:
@ -23,6 +28,9 @@ LETS_ENCRYPT_ENV=production
# PHP composer for plugin installation
#COMPOSE_FILE="$COMPOSE_FILE:compose.composer.yml"
# Self managed Wordpress for automatic updates
#COMPOSE_FILE="$COMPOSE_FILE:compose.selfmanaged.yml"
#WORDPRESS_DEBUG=true
## Additional extensions
@ -37,6 +45,10 @@ SECRET_DB_PASSWORD_VERSION=v1
# Multisite (see README)
#MULTISITE=enable # either 'enable', 'subdomain' or 'subfolder'
# File upload settings
#UPLOAD_MAX_SIZE=256M
#UPLOAD_MAX_TIME=30
# Local SMTP relay
#COMPOSE_FILE="$COMPOSE_FILE:compose.mailrelay.yml"
#SMTP_HOST="postfix_relay_app"
@ -60,6 +72,10 @@ SECRET_DB_PASSWORD_VERSION=v1
#SECRET_AUTHENTIK_ID_VERSION=v1
#LOGIN_TYPE='auto'
# Matrix .well-known redirect
#COMPOSE_FILE="$COMPOSE_FILE:compose.matrix.yml"
#MATRIX_DOMAIN=matrix.example.com
# Allow remote connections to db
# 🚩🚩 dangerous, use only for development sites!
#COMPOSE_FILE="$COMPOSE_FILE:compose.public-db.yml
@ -68,8 +84,13 @@ SECRET_DB_PASSWORD_VERSION=v1
# 🚩🚩 dangerous, use only for development sites!
#CORS_ALLOW_ALL=1
# FTP
#COMPOSE_FILE="$COMPOSE_FILE:compose.ftp.yml"
#SECRET_FTP_PASS_VERSION=v1
#USERS_CONF_VERSION=v1
# You can use a Port between 2220-2225
#COMPOSE_FILE="$COMPOSE_FILE:compose.ftp-2220.yml"
#COMPOSE_FILE="$COMPOSE_FILE:compose.ftp-2221.yml"
#COMPOSE_FILE="$COMPOSE_FILE:compose.ftp-2222.yml"
#COMPOSE_FILE="$COMPOSE_FILE:compose.ftp-2223.yml"
#COMPOSE_FILE="$COMPOSE_FILE:compose.ftp-2224.yml"
#COMPOSE_FILE="$COMPOSE_FILE:compose.ftp-2225.yml"

21
.gitignore vendored
View File

@ -1 +1,22 @@
# direnv
/.envrc
# Environment files (may contain secrets)
.env
# Logs
*.log
# OS metadata
.DS_Store
Thumbs.db
# Editor/IDE
*.swp
*.swo
*~
*.bak
.idea/
.vscode/
.project
.classpath

View File

@ -7,7 +7,7 @@ Coöp Cloud + [Wordpress](https://wordpress.org) = 🥳
<!-- metadata -->
* **Category**: Apps
* **Status**: 3, stable
* **Status**: 4
* **Image**: [`wordpress`](https://hub.docker.com/_/wordpress), 4, upstream
* **Healthcheck**: Yes
* **Backups**: Yes

43
abra.sh
View File

@ -1,13 +1,25 @@
export PHP_UPLOADS_CONF_VERSION=v3
export ENTRYPOINT_CONF_VERSION=v7
export PHP_UPLOADS_CONF_VERSION=v4
export ENTRYPOINT_CONF_VERSION=v8
export ENTRYPOINT_MAILRELAY_CONF_VERSION=v2
export MSMTP_CONF_VERSION=v4
export HTACCESS_CONF_VERSION=v2
export HTACCESS_CONF_VERSION=v3
export USERS_CONF_VERSION=v1
wp() {
su -p www-data -s /bin/bash -c "/usr/local/bin/wp $@"
}
update() {
wp "core update-db"
wp "plugin update --all"
wp "plugin auto-updates enable --all"
wp "theme update --all"
wp "theme auto-updates enable --all"
wp "language core update"
wp "language plugin update --all"
wp "language theme update --all"
}
core_install(){
ADMIN=admin
if [ -n "$AUTHENTIK_DOMAIN" ]
@ -19,15 +31,27 @@ core_install(){
wp "language core install $LOCALE"
wp "site switch-language $LOCALE"
wp "rewrite structure '/%year%/%monthnum%/%day%/%postname%/'"
wp "plugin install --activate disable-update-notifications"
wp "option update disable_notification_setting --format=json '{\"dpun_setting\":false,\"dwtu_setting\":false,\"dwcun_setting\":true}'"
if [ -n "$DEFAULT_USER_ROLE" ]
then
wp "option set default_role $DEFAULT_USER_ROLE"
else
wp "option set default_role subscriber"
fi
wp 'plugin auto-updates enable --all' || exit 0
wp "theme auto-updates enable --all"
wp 'plugin auto-updates enable --all' || true
}
enable_auto_updates(){
wp "plugin deactivate disable-update-notifications --allow-root"
wp "plugin uninstall disable-update-notifications --allow-root"
wp "option delete disable_notification_setting --allow-root"
wp "plugin auto-updates enable --all --allow-root"
wp "theme auto-updates enable --all --allow-root"
}
disable_auto_updates(){
wp "plugin install --activate disable-update-notifications"
wp "option update disable_notification_setting --format=json '{\"dpun_setting\":false,\"dwtu_setting\":false,\"dwcun_setting\":true}'"
}
set_authentik(){
@ -39,6 +63,7 @@ set_authentik(){
fi
wp "user create akadmin admin@example.com --role=administrator"
wp "plugin install --activate daggerhart-openid-connect-generic"
wp 'plugin auto-updates enable daggerhart-openid-connect-generic'
wp "option update --format=json openid_connect_generic_settings '
{
\"login_type\":\"$LOGIN_TYPE\",
@ -49,6 +74,8 @@ set_authentik(){
\"endpoint_userinfo\":\"https://$AUTHENTIK_DOMAIN/application/o/userinfo/\",
\"endpoint_token\":\"https://$AUTHENTIK_DOMAIN/application/o/token/\",
\"endpoint_end_session\":\"https://$AUTHENTIK_DOMAIN/application/o/wordpress/end-session/\",
\"endpoint_jwks\":\"https://$AUTHENTIK_DOMAIN/application/o/wordpress/jwks/\",
\"issuer\":\"https://$AUTHENTIK_DOMAIN/application/o/wordpress/\",
\"acr_values\":\"\",
\"identity_key\":\"preferred_username\",
\"no_sslverify\":\"0\",
@ -76,3 +103,7 @@ set_authentik(){
fix_mysql() {
echo "ALTER TABLE mysql.column_stats MODIFY histogram longblob; ALTER TABLE mysql.column_stats MODIFY hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB');" | mysql -u root -p$(cat /run/secrets/db_root_password)
}
show_plugins() {
wp "plugin list --fields=name,status,wporg_status,version,update_version,auto_update,tested_up_to,wporg_last_updated"
}

16
alaconnect.yml Normal file
View File

@ -0,0 +1,16 @@
authentik:
uncomment:
- compose.authentik.yml
- AUTHENTIK_DOMAIN
- SECRET_AUTHENTIK_SECRET_VERSION
- SECRET_AUTHENTIK_ID_VERSION
- LOGIN_TYPE
inital-hooks:
- app set_authentik
shared_secrets:
wordpress_secret: authentik_secret
wordpress_id: authentik_id
matrix:
uncomment:
- compose.matrix.yml
- MATRIX_DOMAIN

7
compose.ftp-2220.yml Normal file
View File

@ -0,0 +1,7 @@
---
version: "3.8"
services:
ftp:
ports:
- 2220:22

7
compose.ftp-2221.yml Normal file
View File

@ -0,0 +1,7 @@
---
version: "3.8"
services:
ftp:
ports:
- 2221:22

7
compose.ftp-2222.yml Normal file
View File

@ -0,0 +1,7 @@
---
version: "3.8"
services:
ftp:
ports:
- 2222:22

7
compose.ftp-2223.yml Normal file
View File

@ -0,0 +1,7 @@
---
version: "3.8"
services:
ftp:
ports:
- 2223:22

7
compose.ftp-2224.yml Normal file
View File

@ -0,0 +1,7 @@
---
version: "3.8"
services:
ftp:
ports:
- 2224:22

7
compose.ftp-2225.yml Normal file
View File

@ -0,0 +1,7 @@
---
version: "3.8"
services:
ftp:
ports:
- 2220:22

View File

@ -3,11 +3,9 @@ version: "3.8"
services:
ftp:
image: atmoz/sftp
image: atmoz/sftp:alpine
secrets:
- ftp_pass
ports:
- 2222:22
volumes:
- "wordpress_content:/home/ftp_user/wp-content"
configs:

10
compose.matrix.yml Normal file
View File

@ -0,0 +1,10 @@
---
version: "3.8"
services:
app:
deploy:
labels:
- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect-matrix-well-known"
- "traefik.http.middlewares.${STACK_NAME}-redirect-matrix-well-known.redirectregex.regex=^https://(.*)/.well-known/matrix/(.*)"
- "traefik.http.middlewares.${STACK_NAME}-redirect-matrix-well-known.redirectregex.replacement=https://${MATRIX_DOMAIN}/.well-known/matrix/$$2"

21
compose.selfmanaged.yml Normal file
View File

@ -0,0 +1,21 @@
---
version: "3.8"
services:
app:
image: "wordpress:latest"
volumes:
- "wordpress:/var/www/html/"
environment:
WORDPRESS_CONFIG_EXTRA: |
define( 'AUTOMATIC_UPDATER_DISABLED', false );
define( 'WP_AUTO_UPDATE_CORE', true );
define( 'FS_METHOD', 'direct' );
${WORDPRESS_CONFIG_EXTRA}
ftp:
volumes:
- "wordpress:/home/ftp_user/"
volumes:
wordpress:

View File

@ -3,7 +3,7 @@ version: "3.8"
services:
app:
image: "wordpress:6.4.3"
image: "wordpress:6.9.4"
volumes:
- "wordpress_content:/var/www/html/wp-content/"
networks:
@ -48,7 +48,7 @@ services:
order: start-first
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.swarm.network=proxy"
- "traefik.http.routers.${STACK_NAME}.tls=true"
- "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=80"
- "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})"
@ -57,13 +57,15 @@ services:
#- "traefik.http.routers.${STACK_NAME}.rule=HostRegexp(`{subdomain:.+}.${DOMAIN}`, `${DOMAIN}`)"
- "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}"
- "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure"
- "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT:-120}"
- "backupbot.backup=true"
- "backupbot.backup.path=/var/www/html"
- "coop-cloud.${STACK_NAME}.version=2.7.3+6.4.3"
- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect"
- "traefik.http.middlewares.${STACK_NAME}-redirect.redirectregex.regex=^https://(${REDIRECTS})/(.*)"
- "traefik.http.middlewares.${STACK_NAME}-redirect.redirectregex.replacement=https://${DOMAIN}/$${2}"
- "traefik.http.middlewares.${STACK_NAME}-redirect.redirectregex.permanent=true"
- "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT}"
- "coop-cloud.${STACK_NAME}.version=2.19.2+6.9.4"
db:
image: "mariadb:11.3"
image: "mariadb:12.2"
volumes:
- "mariadb:/var/lib/mysql"
networks:
@ -78,12 +80,10 @@ services:
- db_root_password
deploy:
labels:
backupbot.backup: "true"
backupbot.backup.pre-hook: "sh -c 'mariadb-dump --single-transaction -u root -p\"$$(cat /run/secrets/db_root_password)\" wordpress | gzip > /var/lib/mysql/dump.sql.gz'"
backupbot.backup.path: "/var/lib/mysql/dump.sql.gz"
backupbot.backup.post-hook: "rm -f /var/lib/mysql/dump.sql.gz"
backupbot.restore: "true"
backupbot.restore.post-hook: "sh -c 'gzip -d /var/lib/mysql/dump.sql.gz && mariadb -u root -p\"$$(cat /run/secrets/db_root_password)\" wordpress < /var/lib/mysql/dump.sql && rm -f /var/lib/mysql/dump.sql'"
backupbot.backup: "${ENABLE_BACKUPS:-true}"
backupbot.backup.pre-hook: "mariadb-dump --single-transaction -u root -p\"$$(cat /run/secrets/db_root_password)\" wordpress | gzip > /var/lib/mysql/dump.sql.gz"
backupbot.backup.volumes.mariadb.path: "dump.sql.gz"
backupbot.restore.post-hook: "gzip -d /var/lib/mysql/dump.sql.gz && mariadb -u root -p\"$$(cat /run/secrets/db_root_password)\" wordpress < /var/lib/mysql/dump.sql && rm -f /var/lib/mysql/dump.sql"
networks:
backend:
@ -109,7 +109,8 @@ configs:
template_driver: golang
php_uploads_conf:
name: ${STACK_NAME}_php_uploads_conf_${PHP_UPLOADS_CONF_VERSION}
file: uploads.ini
file: uploads.ini.tmpl
template_driver: golang
htaccess_conf:
name: ${STACK_NAME}_htaccess_conf_${HTACCESS_CONF_VERSION}
file: htaccess.tmpl

View File

@ -42,6 +42,20 @@ define('FORCE_SSL_ADMIN', true );
define('COOKIE_DOMAIN', \$_SERVER['HTTP_HOST']);"
{{ end }}
UPLOADS_HTACCESS=/var/www/html/wp-content/uploads/.htaccess
if [ ! -f "$UPLOADS_HTACCESS" ]; then
mkdir -p /var/www/html/wp-content/uploads
cat > "$UPLOADS_HTACCESS" <<'EOF'
# Prevent PHP execution in uploads directory
<FilesMatch "\.(?i:php|phtml|phar)$">
Require all denied
</FilesMatch>
EOF
fi
chown -R --from=root:root www-data:www-data /var/www/html/wp-content/
if [ -n "$@" ]; then
"$@"
fi

View File

@ -1,3 +1,8 @@
# Protect sensitive files from direct access
<FilesMatch "^(wp-config\.php|\.htaccess|\.htpasswd|readme\.html|license\.txt)$">
Require all denied
</FilesMatch>
{{ if eq (env "MULTISITE") "" -}}
# BEGIN WordPress

1
release/2.10.0+6.5.5 Normal file
View File

@ -0,0 +1 @@
Adds redirects and alakazam integration

1
release/2.13.2+6.7.1 Normal file
View File

@ -0,0 +1 @@
Breaking change for ftp container: you need to uncomment COMPOSE_FILE="$COMPOSE_FILE:compose.ftp-2222.yml" to open port 2222 again. You can also select between port 2220-2225.

1
release/2.17.1+6.9.0 Normal file
View File

@ -0,0 +1 @@
Breaking change for openid plugin: The issuer must be provided, thus the set_authentik function now includes issuer and endpoint_jwks.

View File

@ -1,6 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
"config:recommended"
]
}

2
tests/fixtures/composer.env vendored Normal file
View File

@ -0,0 +1,2 @@
DOMAIN=wordpress.example.com
ENABLE_COMPOSER=1

2
tests/fixtures/cors.env vendored Normal file
View File

@ -0,0 +1,2 @@
DOMAIN=wordpress.example.com
CORS_ALLOW_ALL=1

1
tests/fixtures/default.env vendored Normal file
View File

@ -0,0 +1 @@
DOMAIN=wordpress.example.com

2
tests/fixtures/multisite-enable.env vendored Normal file
View File

@ -0,0 +1,2 @@
DOMAIN=wordpress.example.com
MULTISITE=enable

View File

@ -0,0 +1,2 @@
DOMAIN=wordpress.example.com
MULTISITE=subdomain

View File

@ -0,0 +1,2 @@
DOMAIN=wordpress.example.com
MULTISITE=subfolder

2
tests/fixtures/php-extensions.env vendored Normal file
View File

@ -0,0 +1,2 @@
DOMAIN=wordpress.example.com
PHP_EXTENSIONS=calendar

8
tests/fixtures/smtp-full.env vendored Normal file
View File

@ -0,0 +1,8 @@
DOMAIN=wordpress.example.com
SMTP_HOST=mail.example.com
SMTP_PORT=587
MAIL_FROM=wordpress@example.com
SMTP_USER=relay@example.com
SMTP_AUTH=on
SMTP_TLS=on
SMTP_OVERRIDE_FROM=on

3
tests/fixtures/upload-sizes.env vendored Normal file
View File

@ -0,0 +1,3 @@
DOMAIN=wordpress.example.com
UPLOAD_MAX_SIZE=512M
UPLOAD_MAX_TIME=60

49
tests/run.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/bash
set -euo pipefail
ROOT="$(dirname "$(realpath "$0")")"
echo "==================================="
echo " WordPress Recipe Test Suite"
echo "==================================="
echo ""
# Collect all compose files
COMPOSE_FILES=()
while IFS= read -r f; do
COMPOSE_FILES+=("$f")
done < <(find "$ROOT/.." -maxdepth 1 -name 'compose*.yml' | sort)
# Collect all tmpl files with bash content
SHELL_TMPL_FILES=()
while IFS= read -r f; do
SHELL_TMPL_FILES+=("$f")
done < <(find "$ROOT/.." -maxdepth 1 -name '*.tmpl' | sort)
TMPL_FILES=()
while IFS= read -r f; do
TMPL_FILES+=("$f")
done < <(find "$ROOT/.." -maxdepth 1 -name '*.tmpl' | sort)
failures=0
echo "========================================================================"
"$ROOT/test_yaml.sh" "${COMPOSE_FILES[@]}" || failures=$((failures + 1))
echo ""
echo "========================================================================"
"$ROOT/test_shell.sh" "${SHELL_TMPL_FILES[@]}" || failures=$((failures + 1))
echo ""
echo "========================================================================"
"$ROOT/test_templates.sh" || failures=$((failures + 1))
echo ""
echo "==================================="
if [ "$failures" -eq 0 ]; then
echo " All tests passed!"
else
echo " $failures test suite(s) failed"
fi
echo "==================================="
exit "$failures"

37
tests/test_shell.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash
set -euo pipefail
pass=0
fail=0
EXTRA_SHELLCHECK_OPTS="${SHELLCHECK_OPTS:-}"
shellcheck_tmpl() {
local tmpl=$1
# Strip Go template tags ({{ ... }} and {{- ... -}}) into whitespace
# so shellcheck can parse the remaining bash.
local cleaned
cleaned=$(sed 's/{{[-]*[^}]*[-]*}}/true/g' "$tmpl")
local tmpfile
tmpfile=$(mktemp)
printf '%s\n' "$cleaned" > "$tmpfile"
if shellcheck $EXTRA_SHELLCHECK_OPTS "$tmpfile"; then
echo " PASS $tmpl"
pass=$((pass + 1))
else
echo " FAIL $tmpl"
fail=$((fail + 1))
fi
rm -f "$tmpfile"
}
echo "=== ShellCheck ==="
for f in "$@"; do
[ -f "$f" ] && shellcheck_tmpl "$f"
done
echo "---"
echo "Passed: $pass Failed: $fail"
[ "$fail" -eq 0 ]

278
tests/test_templates.sh Executable file
View File

@ -0,0 +1,278 @@
#!/bin/bash
set -euo pipefail
ROOT="$(dirname "$(realpath "$0")")/.."
pass=0
fail=0
gomplate="${GOMPLATE_BIN:-gomplate}"
require_gomplate() {
if ! command -v "$gomplate" &>/dev/null; then
echo "gomplate not found. Install it from https://github.com/hairyhenderson/gomplate"
echo "or set GOMPLATE_BIN env var."
exit 1
fi
}
render() {
local tmpl=$1 envfile=$2
"$gomplate" \
--template t="$tmpl" \
--context "_=fmt:%s" \
--datasource "env=env://?$envfile" \
-f "$tmpl" 2>/dev/null
}
# Render by exporting env vars directly (avoids gomplate datasource quirks)
render_via_env() {
local tmpl=$1 envfile=$2
# shellcheck disable=2046
env $(xargs < "$envfile") "$gomplate" -f "$tmpl" 2>/dev/null
}
# --- entrypoint.sh.tmpl tests ---
test_entrypoint_default() {
local envfile=$1
local output
output=$(render_via_env "entrypoint.sh.tmpl" "$envfile")
# Should NOT have multisite config
if echo "$output" | grep -q "WP_ALLOW_MULTISITE"; then
echo " FAIL entrypoint default: unexpected WP_ALLOW_MULTISITE"
return 1
fi
# Should have uploads .htaccess guard
if ! echo "$output" | grep -q "Prevent PHP execution in uploads"; then
echo " FAIL entrypoint default: missing uploads htaccess"
return 1
fi
# Should use --from=root:root on chown
if ! echo "$output" | grep -q "chown -R --from=root:root"; then
echo " FAIL entrypoint default: missing --from=root:root on chown"
return 1
fi
# Should have wp-cli download
if ! echo "$output" | grep -q "wp-cli.phar"; then
echo " FAIL entrypoint default: missing wp-cli download"
return 1
fi
echo " PASS entrypoint default"
}
test_entrypoint_multisite_enable() {
local envfile=$1
local output
output=$(render_via_env "entrypoint.sh.tmpl" "$envfile")
if ! echo "$output" | grep -q "WP_ALLOW_MULTISITE"; then
echo " FAIL entrypoint multisite enable: missing WP_ALLOW_MULTISITE"
return 1
fi
echo " PASS entrypoint multisite enable"
}
test_entrypoint_multisite_subdomain() {
local envfile=$1
local output
output=$(render_via_env "entrypoint.sh.tmpl" "$envfile")
if ! echo "$output" | grep -q "MULTISITE"; then
echo " FAIL entrypoint multisite subdomain: missing MULTISITE"
return 1
fi
if ! echo "$output" | grep -q "SUBDOMAIN_INSTALL"; then
echo " FAIL entrypoint multisite subdomain: missing SUBDOMAIN_INSTALL"
return 1
fi
if ! echo "$output" | grep -q "DOMAIN_CURRENT_SITE"; then
echo " FAIL entrypoint multisite subdomain: missing DOMAIN_CURRENT_SITE"
return 1
fi
echo " PASS entrypoint multisite subdomain"
}
test_entrypoint_multisite_subfolder() {
local envfile=$1
local output
output=$(render_via_env "entrypoint.sh.tmpl" "$envfile")
if ! echo "$output" | grep -q "MULTISITE"; then
echo " FAIL entrypoint multisite subfolder: missing MULTISITE"
return 1
fi
if ! echo "$output" | grep -q "SUBDOMAIN_INSTALL"; then
echo " FAIL entrypoint multisite subfolder: missing SUBDOMAIN_INSTALL"
return 1
fi
echo " PASS entrypoint multisite subfolder"
}
test_entrypoint_cors() {
local envfile=$1
local output
output=$(render_via_env "entrypoint.sh.tmpl" "$envfile")
if ! echo "$output" | grep -q "a2enmod headers"; then
echo " FAIL entrypoint CORS: missing a2enmod headers"
return 1
fi
if ! echo "$output" | grep -q "Access-Control-Allow-Origin"; then
echo " FAIL entrypoint CORS: missing Access-Control-Allow-Origin"
return 1
fi
echo " PASS entrypoint CORS"
}
test_entrypoint_php_extensions() {
local envfile=$1
local output
output=$(render_via_env "entrypoint.sh.tmpl" "$envfile")
if ! echo "$output" | grep -q "docker-php-ext-install calendar"; then
echo " FAIL entrypoint PHP extensions: missing docker-php-ext-install calendar"
return 1
fi
echo " PASS entrypoint PHP extensions"
}
test_entrypoint_composer() {
local envfile=$1
local output
output=$(render_via_env "entrypoint.sh.tmpl" "$envfile")
if ! echo "$output" | grep -q "getcomposer.org"; then
echo " FAIL entrypoint composer: missing composer download"
return 1
fi
echo " PASS entrypoint composer"
}
# --- htaccess.tmpl tests ---
test_htaccess_default() {
local envfile=$1
local output
output=$(render_via_env "htaccess.tmpl" "$envfile")
if ! echo "$output" | grep -q "RewriteRule . /index.php"; then
echo " FAIL htaccess default: missing standard rewrite rule"
return 1
fi
if echo "$output" | grep -q "WordPress Multisite"; then
echo " FAIL htaccess default: unexpected multisite section"
return 1
fi
echo " PASS htaccess default"
}
test_htaccess_multisite() {
local envfile=$1 mode=$2
local output
output=$(render_via_env "htaccess.tmpl" "$envfile")
if ! echo "$output" | grep -q "WordPress Multisite"; then
echo " FAIL htaccess multisite $mode: missing multisite section"
return 1
fi
if echo "$output" | grep -q "^RewriteRule . /index.php"; then
echo " FAIL htaccess multisite $mode: has non-multisite rewrite rule"
return 1
fi
echo " PASS htaccess multisite $mode"
}
# --- uploads.ini.tmpl tests ---
test_uploads_default() {
local output
output=$(render_via_env "uploads.ini.tmpl" "tests/fixtures/default.env")
if ! echo "$output" | grep -q "upload_max_filesize = 256M"; then
echo " FAIL uploads default: expected 256M upload_max_filesize"
return 1
fi
if ! echo "$output" | grep -q "max_execution_time = 30"; then
echo " FAIL uploads default: expected 30 max_execution_time"
return 1
fi
echo " PASS uploads default"
}
test_uploads_custom() {
local envfile=$1
local output
output=$(render_via_env "uploads.ini.tmpl" "$envfile")
if ! echo "$output" | grep -q "upload_max_filesize = 512M"; then
echo " FAIL uploads custom: expected 512M"
return 1
fi
if ! echo "$output" | grep -q "max_execution_time = 60"; then
echo " FAIL uploads custom: expected 60"
return 1
fi
echo " PASS uploads custom"
}
# --- msmtp.conf.tmpl tests ---
test_msmtp_default() {
local output
output=$(render_via_env "msmtp.conf.tmpl" "tests/fixtures/smtp-full.env")
if ! echo "$output" | grep -q "host mail.example.com"; then
echo " FAIL msmtp default: missing host"
return 1
fi
if ! echo "$output" | grep -q "from wordpress@example.com"; then
echo " FAIL msmtp default: missing from"
return 1
fi
if ! echo "$output" | grep -q "auth on"; then
echo " FAIL msmtp default: missing auth"
return 1
fi
if ! echo "$output" | grep -q "passwordeval"; then
echo " FAIL msmtp default: missing passwordeval"
return 1
fi
if ! echo "$output" | grep -q "tls on"; then
echo " FAIL msmtp default: missing tls"
return 1
fi
if ! echo "$output" | grep -q "set_from_header on"; then
echo " FAIL msmtp default: missing set_from_header"
return 1
fi
echo " PASS msmtp full config"
}
# --- Run all template tests ---
echo "=== Template Rendering Tests ==="
cd "$ROOT"
echo "--- entrypoint.sh.tmpl ---"
require_gomplate
test_entrypoint_default "tests/fixtures/default.env" && pass=$((pass+1)) || fail=$((fail+1))
test_entrypoint_multisite_enable "tests/fixtures/multisite-enable.env" && pass=$((pass+1)) || fail=$((fail+1))
test_entrypoint_multisite_subdomain "tests/fixtures/multisite-subdomain.env" && pass=$((pass+1)) || fail=$((fail+1))
test_entrypoint_multisite_subfolder "tests/fixtures/multisite-subfolder.env" && pass=$((pass+1)) || fail=$((fail+1))
test_entrypoint_cors "tests/fixtures/cors.env" && pass=$((pass+1)) || fail=$((fail+1))
test_entrypoint_php_extensions "tests/fixtures/php-extensions.env" && pass=$((pass+1)) || fail=$((fail+1))
test_entrypoint_composer "tests/fixtures/composer.env" && pass=$((pass+1)) || fail=$((fail+1))
echo "--- htaccess.tmpl ---"
test_htaccess_default "tests/fixtures/default.env" && pass=$((pass+1)) || fail=$((fail+1))
test_htaccess_multisite "tests/fixtures/multisite-subfolder.env" "subfolder" && pass=$((pass+1)) || fail=$((fail+1))
test_htaccess_multisite "tests/fixtures/multisite-subdomain.env" "subdomain" && pass=$((pass+1)) || fail=$((fail+1))
echo "--- uploads.ini.tmpl ---"
test_uploads_default && pass=$((pass+1)) || fail=$((fail+1))
test_uploads_custom "tests/fixtures/upload-sizes.env" && pass=$((pass+1)) || fail=$((fail+1))
echo "--- msmtp.conf.tmpl ---"
test_msmtp_default && pass=$((pass+1)) || fail=$((fail+1))
echo "---"
echo "Passed: $pass Failed: $fail"
[ "$fail" -eq 0 ]

54
tests/test_yaml.sh Executable file
View File

@ -0,0 +1,54 @@
#!/bin/bash
set -euo pipefail
pass=0
fail=0
checker=""
if command -v yamllint &>/dev/null; then
checker=yamllint
elif python3 -c "import yaml" 2>/dev/null; then
checker=python
fi
test_yaml() {
local file=$1
case "$checker" in
yamllint)
if yamllint -d "{extends: relaxed, rules: {line-length: disable}}" "$file"; then
echo " PASS $file"
pass=$((pass+1))
else
echo " FAIL $file"
fail=$((fail+1))
fi
;;
python)
if python3 -c "
import yaml, sys
with open('$file') as f:
yaml.safe_load(f)
" 2>/dev/null; then
echo " PASS $file"
pass=$((pass+1))
else
echo " FAIL $file"
fail=$((fail+1))
fi
;;
*)
echo " SKIP $file (no yamllint or PyYAML)"
pass=$((pass+1))
;;
esac
}
echo "=== YAML Validation ==="
for f in "$@"; do
[ -f "$f" ] && test_yaml "$f"
done
echo "---"
echo "Passed: $pass Failed: $fail"
[ "$fail" -eq 0 ]

View File

@ -1,3 +0,0 @@
file_uploads = On
upload_max_filesize = 256M
post_max_size = 256M

11
uploads.ini.tmpl Normal file
View File

@ -0,0 +1,11 @@
{{ $upload_max_size := "256M" }}
{{ if ne (env "UPLOAD_MAX_SIZE") "" }} {{ $upload_max_size = env "UPLOAD_MAX_SIZE" }} {{ end }}
{{ $upload_max_time := "30" }}
{{ if ne (env "UPLOAD_MAX_TIME") "" }} {{ $upload_max_time = env "UPLOAD_MAX_TIME" }} {{ end }}
file_uploads = On
upload_max_filesize = {{ $upload_max_size }}
post_max_size = {{ $upload_max_size }}
memory_limit = {{ $upload_max_size }}
max_execution_time = {{ $upload_max_time }}
max_input_time = {{ $upload_max_time }}