diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a8374..db72abe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ ## [Unreleased] +## [2026.10.12-r1] - 2026-10-12 + +### Changed + +- Upgraded SQLite Database Integration from `3.0.2` to `3.1.0`, pinned to + upstream commit `6546e4538b708a1d8a565a1d31688bd63b610fa3`. +- Refreshed the pinned WordPress 7.1.0/PHP 8.5 Apache base manifest digest + to match the current official image required by the upstream release gate. +- Included randomized default database storage, the `DB_PATH` configuration + constant, SQL `IF()` and `TRADITIONAL` mode fixes, and improved table + collation defaults for MariaDB exports. +- Updated SQLite Diagnostics to prefer the runtime `DB_PATH` while retaining + compatibility with the legacy `FQDB` constant. + +### Release status + +- Prepared for publication as `2026.10.12-r1`. `2026.09.10-r1` remains the + previous verified release until both registries and release evidence pass. + +### Upgrade notes + +- Back up the entire `wp-content/database/` directory before upgrading. + Default legacy databases migrate automatically to a randomized directory; + preserve `db-path.php` and the database's WAL/SHM sidecars on the same volume. +- Read `DB_PATH` after WordPress loads for backup and migration integrations. + Explicit paths remain unchanged. Configure an absolute persistent path + outside the web root; changing `DB_PATH` does not move existing data. +- `DB_DIR` and `DB_FILE` are deprecated, conflicting legacy constants warn, + and relative database paths are rejected. + ## [2026.09.10-r1] - 2026-09-10 ### Changed @@ -14,9 +44,8 @@ ### Release status -- Prepared as the first immutable release for 2026-09-10. Until its protected - tag is published and the two registry indexes, evidence, and signatures are - verified, `2026.09.03-r1` remains the current complete release. +- Published successfully to Docker Hub and GHCR with verified release evidence + and signatures; the `2026.09.10` and `latest` aliases were promoted. ### Upgrade notes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c101631..aae5663 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -103,7 +103,7 @@ php tests/test-sqlite-select-id-key-fix.php php tests/test-tool-update-site-url.php php tests/test-tool-reset-user-password.php go install github.com/soulteary/ci-recipes/cmd/ci-recipes@83ccd6f83d7e7ef40f5d6faf2e11960f1de74a78 -ci-recipes docker-sqlite-wordpress validate-release 2026.09.10-r1 +ci-recipes docker-sqlite-wordpress validate-release 2026.10.12-r1 ``` To reproduce the remaining lint and configuration checks: diff --git a/Dockerfile b/Dockerfile index 43c2bc0..b62c3b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ # plugin: https://github.com/WordPress/sqlite-database-integration # The optional native Rust extension `wp_mysql_parser` accelerates the MySQL # lexer/parser used by the SQLite driver. It requires the 3.0 monorepo layout. -ARG IMAGE_VERSION=2026.09.10-r1 +ARG IMAGE_VERSION=2026.10.12-r1 ARG IMAGE_REVISION=unknown ARG WORDPRESS_VERSION=7.1.0 -ARG WORDPRESS_IMAGE=wordpress:7.1.0-php8.5-apache@sha256:397daa8a8816347e724362c2122129601eb0811fbaff0ce9b2b22c7b057a745d -ARG SQLITE_DATABASE_INTEGRATION_VERSION=3.0.2 -ARG SQLITE_DATABASE_INTEGRATION_COMMIT=085ea60b16c648d40464654afa667f0812c040de +ARG WORDPRESS_IMAGE=wordpress:7.1.0-php8.5-apache@sha256:a8ee182a3806f9bb9ec2a34851ecb0d8fc6fbf9e421198d2ca7aeaae4bdfc807 +ARG SQLITE_DATABASE_INTEGRATION_VERSION=3.1.0 +ARG SQLITE_DATABASE_INTEGRATION_COMMIT=6546e4538b708a1d8a565a1d31688bd63b610fa3 ARG RUSTUP_VERSION=1.29.0 ARG RUST_TOOLCHAIN_VERSION=1.98.0 @@ -114,7 +114,7 @@ LABEL org.opencontainers.image.authors="soulteary@gmail.com" \ org.opencontainers.image.revision="${IMAGE_REVISION}" \ org.opencontainers.image.licenses="Apache-2.0 AND GPL-2.0-or-later" \ org.opencontainers.image.base.name="docker.io/library/wordpress:7.1.0-php8.5-apache" \ - org.opencontainers.image.base.digest="sha256:397daa8a8816347e724362c2122129601eb0811fbaff0ce9b2b22c7b057a745d" \ + org.opencontainers.image.base.digest="sha256:a8ee182a3806f9bb9ec2a34851ecb0d8fc6fbf9e421198d2ca7aeaae4bdfc807" \ io.soulteary.wordpress.version="${WORDPRESS_VERSION}" \ io.soulteary.sqlite-integration.version="${SQLITE_DATABASE_INTEGRATION_VERSION}" diff --git a/README.md b/README.md index 7437ff5..1ab5bb9 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ WordPress with SQLite, ready to use out of the box. -> **Container release availability:** `2026.09.10-r1` is prepared but is not +> **Container release availability:** `2026.10.12-r1` is prepared but is not > published yet as a verified container image. The previous verified release, -> [`2026.09.03-r1`](https://github.com/soulteary/docker-sqlite-wordpress/releases/tag/2026.09.03-r1), -> remains available from Docker Hub and GHCR with matching manifest digest -> `sha256:61c3ac9fa44c28a36dd77415e1563281711134dcb9a9a4979ed601d6039a3812`. -> Until the protected `2026.09.10-r1` tag succeeds in both registries, build -> current `main` as shown in Quick Start. The existing `latest` alias still -> represents the previous release and does not include this candidate's fixes. +> [`2026.09.10-r1`](https://github.com/soulteary/docker-sqlite-wordpress/releases/tag/2026.09.10-r1), +> remains available from Docker Hub and GHCR. Until the protected +> `2026.10.12-r1` tag succeeds in both registries, build current `main` as +> shown in Quick Start. The existing `latest` alias still represents the +> previous release. + - Based on [official image](https://hub.docker.com/_/wordpress), Easier and more sustainable solution. - DockerHub Page: https://hub.docker.com/r/soulteary/sqlite-wordpress @@ -21,7 +21,7 @@ WordPress with SQLite, ready to use out of the box. ## Native MySQL Parser Extension -The image bundles WordPress `7.1.0` on PHP 8.5/Apache and [`sqlite-database-integration`](https://github.com/WordPress/sqlite-database-integration) `v3.0.2` together with its optional native Rust extension `wp_mysql_parser`. The extension is compiled and enabled on `amd64` and `arm64`; published 32-bit ARM variants use the plugin's pure-PHP fallback. The SQLite driver detects the available implementation automatically (the upstream project reports roughly 4.8x faster lexing and 15.5x faster parsing for the native path). +The image bundles WordPress `7.1.0` on PHP 8.5/Apache and [`sqlite-database-integration`](https://github.com/WordPress/sqlite-database-integration) `v3.1.0` together with its optional native Rust extension `wp_mysql_parser`. The extension is compiled and enabled on `amd64` and `arm64`; published 32-bit ARM variants use the plugin's pure-PHP fallback. The SQLite driver detects the available implementation automatically (the upstream project reports roughly 4.8x faster lexing and 15.5x faster parsing for the native path). Verify it is loaded inside the container: @@ -61,7 +61,7 @@ docker exec -it ls -l /var/www/html/wp-content/mu-plugins/ ## Quick Start -Until `2026.09.10-r1` is published, build current `main` locally: +Until `2026.10.12-r1` is published, build current `main` locally: ```bash docker build -t sqlite-wordpress:main . @@ -74,11 +74,11 @@ convenience or the immutable CalVer release for reproducible deployments: # Docker Hub: use latest docker pull soulteary/sqlite-wordpress # Docker Hub: use an immutable release -docker pull soulteary/sqlite-wordpress:2026.09.10-r1 +docker pull soulteary/sqlite-wordpress:2026.10.12-r1 # GHCR: use latest docker pull ghcr.io/soulteary/sqlite-wordpress:latest # GHCR: use an immutable release -docker pull ghcr.io/soulteary/sqlite-wordpress:2026.09.10-r1 +docker pull ghcr.io/soulteary/sqlite-wordpress:2026.10.12-r1 ``` Launch the locally built image on port `8080`: @@ -263,7 +263,7 @@ fit for local development and many small or read-heavy sites, but MySQL or MariaDB may be more appropriate for workloads with sustained concurrent writes. Before production use, test the expected traffic, themes, plugins, scheduled jobs, and backup/restore procedure. See the upstream -[`sqlite-database-integration` production and migration guidance](https://github.com/WordPress/sqlite-database-integration/blob/v3.0.2/packages/plugin-sqlite-database-integration/readme.txt) +[`sqlite-database-integration` production and migration guidance](https://github.com/WordPress/sqlite-database-integration/blob/v3.1.0/packages/plugin-sqlite-database-integration/readme.txt) for the underlying compatibility boundary. ## Emergency Site URL Recovery Tool @@ -540,11 +540,23 @@ refuses Multisite installations. ## Volume and Upgrade Notes Back up `wp-content/database/` before upgrading an existing site to the -WordPress 7.1.0 / SQLite Database Integration 3.0.2 image. The v3 integration +WordPress 7.1.0 / SQLite Database Integration 3.1.0 image. The v3 integration requires a non-empty `DB_NAME` in custom `wp-config.php` files and uses WAL journaling by default, so the database's `-wal` and `-shm` sidecar files must remain on the same persistent volume as the main SQLite file. +SQLite Integration 3.1 migrates default `.ht.sqlite` and `.ht.sqlite.php` +databases into a randomized directory under `wp-content/database/`, recorded +in `wp-content/database/db-path.php`. Persist and back up the entire database +directory, including the path record and WAL/SHM sidecars. Backup tools should +read `DB_PATH` after WordPress loads instead of assuming a fixed filename. + +For an explicit database location, define `DB_PATH` in `wp-config.php` as an +absolute file path outside the web root, on a persistent volume writable by +PHP. Changing `DB_PATH` does not move an existing database. `DB_DIR` and +`DB_FILE` are deprecated; conflicting legacy constants produce warnings, +and relative paths are rejected. + ### Updating Core in a Persistent Document Root Mounting all of `/var/www/html` persists WordPress core as well as content. The diff --git a/RELEASING.md b/RELEASING.md index 46f8c0f..0ec34c6 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -41,7 +41,7 @@ tag protection remains the authoritative control. php tests/test-tool-update-site-url.php php tests/test-tool-reset-user-password.php go install github.com/soulteary/ci-recipes/cmd/ci-recipes@83ccd6f83d7e7ef40f5d6faf2e11960f1de74a78 - ci-recipes docker-sqlite-wordpress validate-release 2026.09.10-r1 + ci-recipes docker-sqlite-wordpress validate-release 2026.10.12-r1 ``` 4. Let pull-request CI test amd64, native arm64, and the 32-bit ARM pure-PHP @@ -57,7 +57,7 @@ Publish from the GitHub Releases page after the release pull request is merged and `main` CI succeeds: 1. Open **Releases → Draft a new release**. -2. Enter `2026.09.10-r1` in **Choose a tag**, choose **Create new tag**, and +2. Enter `2026.10.12-r1` in **Choose a tag**, choose **Create new tag**, and target the current `main` branch. 3. Generate and review the release notes, then select **Publish release**. @@ -74,8 +74,8 @@ Manual dispatch remains the explicit operator path described below. The `Release` workflow builds each runtime platform once and publishes the same manifest digest to Docker Hub and GHCR under the immutable exact tag: -- `soulteary/sqlite-wordpress:2026.09.10-r1` -- `ghcr.io/soulteary/sqlite-wordpress:2026.09.10-r1` +- `soulteary/sqlite-wordpress:2026.10.12-r1` +- `ghcr.io/soulteary/sqlite-wordpress:2026.10.12-r1` BuildKit emits an SPDX SBOM and maximum-mode SLSA provenance for each platform. The merged index receives OCI version, source, revision, and license @@ -136,7 +136,7 @@ manifest; an additional valid signature referrer does not change its digest. Set the exact tag once for all commands: ```bash -release=2026.09.10-r1 +release=2026.10.12-r1 ``` 1. Confirm both registries expose the same five runtime platforms, SBOM, and diff --git a/plugins/sqlite-diagnostics.php b/plugins/sqlite-diagnostics.php index 97a4fb9..9357b57 100644 --- a/plugins/sqlite-diagnostics.php +++ b/plugins/sqlite-diagnostics.php @@ -3,7 +3,7 @@ * Plugin Name: SQLite Diagnostics * Plugin URI: https://github.com/soulteary/docker-sqlite-wordpress * Description: Read-only diagnostics page under the Tools menu that surfaces the native wp_mysql_parser lexer/parser state, SQLite version/source id, live-connection PRAGMA values, main/WAL/SHM storage sizes, PHP/architecture environment details, and the sqlite-database-integration plugin version. Performs no writes and never modifies the live site database. - * Version: 1.0.0 + * Version: 1.1.0 * Author: soulteary * Author URI: https://soulteary.com * @@ -253,13 +253,13 @@ function sqlite_diagnostics_pragmas() { * @return array Label => value pairs (raw, unescaped). */ function sqlite_diagnostics_storage() { - if ( ! defined( 'FQDB' ) ) { + if ( ! defined( 'DB_PATH' ) && ! defined( 'FQDB' ) ) { return array( - __( 'Main file', 'sqlite-diagnostics' ) => __( 'FQDB not defined', 'sqlite-diagnostics' ), + __( 'Main file', 'sqlite-diagnostics' ) => __( 'DB_PATH not defined', 'sqlite-diagnostics' ), ); } - $fqdb = (string) FQDB; + $fqdb = defined( 'DB_PATH' ) ? (string) DB_PATH : (string) FQDB; $files = array( __( 'Main file', 'sqlite-diagnostics' ) => $fqdb, __( 'WAL sidecar', 'sqlite-diagnostics' ) => $fqdb . '-wal', @@ -303,8 +303,8 @@ function sqlite_diagnostics_environment() { ? (string) SQLITE_DB_DROPIN_VERSION : __( 'not defined', 'sqlite-diagnostics' ); - if ( defined( 'FQDB' ) ) { - $db_path = (string) FQDB; + if ( defined( 'DB_PATH' ) || defined( 'FQDB' ) ) { + $db_path = defined( 'DB_PATH' ) ? (string) DB_PATH : (string) FQDB; $rows[ __( 'Database file', 'sqlite-diagnostics' ) ] = $db_path; if ( file_exists( $db_path ) ) { @@ -316,7 +316,7 @@ function sqlite_diagnostics_environment() { $rows[ __( 'Database file size', 'sqlite-diagnostics' ) ] = __( 'file not found', 'sqlite-diagnostics' ); } } else { - $rows[ __( 'Database file', 'sqlite-diagnostics' ) ] = __( 'FQDB not defined', 'sqlite-diagnostics' ); + $rows[ __( 'Database file', 'sqlite-diagnostics' ) ] = __( 'DB_PATH not defined', 'sqlite-diagnostics' ); } return $rows; diff --git a/tests/image-smoke-storage.php b/tests/image-smoke-storage.php new file mode 100644 index 0000000..33418c4 --- /dev/null +++ b/tests/image-smoke-storage.php @@ -0,0 +1,40 @@ + $filename) { + $directory = $root . '/legacy-' . $index; + mkdir($directory); + $legacy = $directory . '/' . $filename; + $db = new PDO('sqlite:' . $legacy); + $db->exec('CREATE TABLE preserved (value TEXT)'); + $db->exec("INSERT INTO preserved VALUES ('existing site data')"); + $db = null; + $path = WP_SQLite_Storage::with_secret_path($directory)->initialize(); + storage_assert($path !== $legacy && is_file($path), 'Legacy database was not migrated'); + storage_assert(is_file($directory . '/db-path.php'), 'Database path record missing'); + $db = new PDO('sqlite:' . $path); + storage_assert($db->query('SELECT value FROM preserved')->fetchColumn() === 'existing site data', 'Migration lost existing data'); + $db = null; + storage_assert(WP_SQLite_Storage::with_secret_path($directory)->initialize() === $path, 'Restart changed the recorded database path'); + } + $explicit = $root . '/explicit.sqlite'; + storage_assert(WP_SQLite_Storage::with_explicit_path($explicit)->initialize() === $explicit, 'Explicit database path changed'); + echo "SQLite storage migration and restart tests passed\n"; +} finally { + $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST); + foreach ($files as $file) { + $file->isDir() ? rmdir($file->getPathname()) : unlink($file->getPathname()); + } + rmdir($root); +} diff --git a/tests/image-smoke.sh b/tests/image-smoke.sh index 4a1e31b..7d6ad7e 100755 --- a/tests/image-smoke.sh +++ b/tests/image-smoke.sh @@ -121,6 +121,13 @@ docker run --rm --platform "${platform}" \ # shellcheck disable=SC2016 docker run --rm --platform "${platform}" "${image}" php -r ' require "/var/www/html/wp-load.php"; + if (!defined("DB_PATH") || !is_file(DB_PATH)) { + throw new RuntimeException("Runtime DB_PATH is missing or invalid."); + } + $environment = sqlite_diagnostics_environment(); + if ($environment["Database file"] !== DB_PATH) { + throw new RuntimeException("Diagnostics did not report runtime DB_PATH."); + } $driver = $GLOBALS["wpdb"]->get_driver(); foreach (["beginTransaction", "commit", "rollBack", "inTransaction"] as $method) { if (!method_exists($driver, $method)) { @@ -143,3 +150,8 @@ docker run --rm --platform "${platform}" \ docker run --rm --platform "${platform}" \ --volume "${repo_root}/tests/image-smoke-user-password.php:/tmp/image-smoke-user-password.php:ro" \ "${image}" php /tmp/image-smoke-user-password.php + +# Verify both legacy default filenames survive migration and a storage restart. +docker run --rm --platform "${platform}" \ + --volume "${repo_root}/tests/image-smoke-storage.php:/tmp/image-smoke-storage.php:ro" \ + --entrypoint php "${image}" /tmp/image-smoke-storage.php