Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 5 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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}"

Expand Down
38 changes: 25 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
WordPress with SQLite, ready to use out of the box.

<!-- release-availability: pending -->
> **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
Expand All @@ -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:

Expand Down Expand Up @@ -61,7 +61,7 @@ docker exec -it <container> 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 .
Expand All @@ -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`:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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**.

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions plugins/sqlite-diagnostics.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -253,13 +253,13 @@ function sqlite_diagnostics_pragmas() {
* @return array<string,string> 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',
Expand Down Expand Up @@ -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 ) ) {
Expand All @@ -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;
Expand Down
40 changes: 40 additions & 0 deletions tests/image-smoke-storage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/** Verify the packaged 3.1 storage manager preserves existing database data. */
require '/usr/src/wordpress/wp-content/mu-plugins/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-storage.php';

function storage_assert($condition, $message) {
if (!$condition) {
throw new RuntimeException($message);
}
}

$root = sys_get_temp_dir() . '/sqlite-storage-smoke-' . bin2hex(random_bytes(8));
mkdir($root);
$root = realpath($root);
try {
foreach (['.ht.sqlite', '.ht.sqlite.php'] as $index => $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);
}
12 changes: 12 additions & 0 deletions tests/image-smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track the new storage smoke fixture in image-change detection

Add tests/image-smoke-storage.php to the Detect image changes path list in .github/workflows/ci.yaml. The image-smoke job now mounts and executes this file, but a future PR that changes only this fixture will leave image_changed=false, so none of the three packaged-image test runs will exercise the updated test.

Useful? React with 👍 / 👎.

--entrypoint php "${image}" /tmp/image-smoke-storage.php
Loading