ci: bump test service containers to latest majors (postgres 18, mysql 9, redis 8) - #33097
ci: bump test service containers to latest majors (postgres 18, mysql 9, redis 8)#33097robobun wants to merge 6 commits into
Conversation
WalkthroughUpdates Docker and test harness container configurations, adjusts SQL test assertions, removes Valkey TLS Docker assets, and refreshes documentation formatting. ChangesTest infrastructure and docs
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
Updated 11:06 PM PT - Jul 9th, 2026
❌ @robobun, your commit 2b00a21 has 2 failures in
🧪 To try this PR locally: bunx bun-pr 33097That installs a local version of the PR into your bun-33097 --bun |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
27b49fe to
9fcc439
Compare
There was a problem hiding this comment.
LGTM — test-infra-only container bumps with both prior review points addressed and two green CI runs on the final diff.
Extended reasoning...
Overview
This PR bumps the Docker service containers that CI database tests run against to their latest stable majors: PostgreSQL 15→18, MySQL 8.4→9 (and the mysql_native_password service 8.0→8.4), and Redis 7→8. All 19 changed files are under test/ — no runtime or production code is touched. The bumps forced a handful of well-scoped follow-ups: adjusting the postgres tmpfs mount for the new PGDATA layout, replacing MySQL 8.4's removed --default-authentication-plugin with --mysql-native-password=ON + --authentication-policy, fixing two sql.test.ts assertions that encoded PG15-specific facts, hardening two MySQL error assertions to use the stable errno instead of server prose, removing the now-dead env/args/archs options from describeWithContainer and their no-op call sites, deleting the unreferenced test/js/valkey/docker*/ directories, and fixing a README env-var name.
Security risks
None. This touches only test infrastructure — Docker image tags, compose config, test harness plumbing, and test assertions. No auth, crypto, permissions, or user-facing code paths are involved. The deleted TLS cert/key were unused test fixtures.
Level of scrutiny
Low-to-moderate. The blast radius is confined to CI test reliability for the SQL suites, which is highly visible and easily reverted. No src/ code changes. The PR description documents two full CI builds (#66936, #66976) with 283/286 passing and the 3 failures traced to known unrelated flakes. Each non-obvious change (tmpfs path, MySQL 8.4 auth flags, PG18 \v escape, pg_database catalog shape, arch-guard removal) is explicitly justified.
Other factors
I raised two issues on earlier revisions of this PR — the newly-dead env parameter and the PG15-shaped pg_database record literal breaking under PG18 — and both were addressed (3ccb5b2 and b7183fd respectively). The bug-hunting pass on the current diff found nothing. I verified that the deleted valkey directories have no remaining references, that the README's BUN_DOCKER_COMPOSE_FILE fix matches what test/docker/index.ts actually reads, and that no CODEOWNERS entry covers these paths. The MySQL error-assertion changes are strictly stronger than before (code + errno + message substring vs. exact prose).
… 9, redis 8) PostgreSQL 18 moved PGDATA under /var/lib/postgresql/<major>/docker, so the tmpfs mounts now target /var/lib/postgresql. MySQL 8.4 removed --default-authentication-plugin and MySQL 9 removed the mysql_native_password plugin. mysql_plain and mysql_tls move to mysql:9; mysql_native_password stays on the 8.4 LTS with --mysql-native-password=ON and --authentication-policy=mysql_native_password,, so the handshake keeps advertising native_password (sql-mysql.auth.test.ts depends on that for its AuthSwitchRequest coverage). mysql:8.0 is no longer maintained upstream. Remove the mysql:8/mysql:9 aliases in test/harness.ts (they routed on the caller's env field and were already mislabeled); tests now name the compose service directly. Delete the unreferenced redis:7 Dockerfiles under test/js/valkey/docker and test/js/valkey/docker-tls.
…reads The README said BUN_DOCKER_COMPOSE_PATH, but index.ts reads BUN_DOCKER_COMPOSE_FILE.
…ontainer
Removing the mysql:8/mysql:9 alias block deleted the only reader of env.
args and archs were never read. Remove all three from the signature and
the five callers that passed no-op env: {} / args: [] objects.
The two transaction error tests pinned the server's exact error message, the one field that is not stable: it varies across MySQL versions and differs on MariaDB, which Bun's MySQL adapter also targets (mariadb:// URLs and the BUN_TEST_SERVICE_* override let the suite run against any MySQL-compatible server). They ignored errno, the MySQL wire-protocol error number, which is identical across MySQL 8/9 and MariaDB (1366 = ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, 1054 = ER_BAD_FIELD_ERROR). Assert code + errno plus the identifying message substring instead. Strictly stronger than the prose-only assertion, and matches the existing expect(error.code) idiom in the same describe block.
Two PostgreSQL tests encoded PostgreSQL-15-specific facts and fail on postgres:18 on every Linux x64 lane: "pg_database[] - null values" hardcoded a 16-field pg_database record literal. pg_database gained daticurules in PostgreSQL 16 and dathasloginevt in 17 (which also renamed daticulocale to datlocale), so on 18 the eighth field of the literal lands on a boolean column and the cast fails with 'invalid input syntax for type boolean: "-1"'. Source the record from the live server instead of pinning the catalog shape. "text[] - special character escaping" produced its vertical tab via E'\v', which PostgreSQL did not recognize as an escape until version 18, so the test actually decoded a literal letter v. On 18 E'\v' is a real U+000B and the assertion broke. Use the version-independent hex escape E'\x0b' and expect a real vertical tab, which is what the test name claims to cover.
9fcc439 to
2b00a21
Compare
There was a problem hiding this comment.
Both earlier review comments are addressed and I found no further issues, but this is a coordinated multi-service version bump plus harness API cleanup and test-assertion rewrites across 21 files — worth a quick human glance (cirospaciari?) to sign off on the target server versions before merge.
Extended reasoning...
Overview
This PR bumps the CI test service containers to their latest stable majors (postgres 15→18, mysql 8.4→9, mysql_native_password 8.0→8.4, redis 7→8) and carries the fallout: PGDATA relocated outside the new VOLUME /var/lib/postgresql, MySQL 8.4's --mysql-native-password=ON / --authentication-policy replacing the removed --default-authentication-plugin, two PostgreSQL-18-incompatible assertions in sql.test.ts rewritten, two MySQL transaction assertions hardened from server prose to errno, the dead mysql:8/mysql:9 alias layer and its env/args/archs options removed from describeWithContainer, and the unreferenced test/js/valkey/docker*/ directories deleted. Zero lines of src/ change.
Security risks
None. All changes are to test infrastructure. The deleted server.key/server.crt were unreferenced self-signed test fixtures.
Level of scrutiny
Medium. Each individual change is mechanical and well-justified, and the PR description documents multiple CI builds where every database suite passes on the bumped containers. But this changes which server majors the entire SQL test surface runs against going forward, removes an option from a shared harness helper, and rewrites four test assertions — that's a policy/coverage decision a maintainer familiar with the SQL adapters should sign off on rather than a pure config tweak.
Other factors
I previously left two inline comments (dead env param; PG15-shaped pg_database literal breaking under PG18) — both were fully addressed and the threads are resolved. The bug-hunting system found nothing on the current diff. No CODEOWNERS cover the touched paths. Given the 21-file scope and the "which versions do we test against" decision embedded here, deferring to a human is the safer call.
For 1.4.0, update the Docker service containers CI tests run against to the latest stable major of each.
postgres_plain/postgres_authpostgres:15postgres:18postgres_tlspostgres:15.13postgres:18mysql_plain/mysql_tlsmysql:8.4mysql:9(9.7, the current LTS)mysql_native_passwordmysql:8.0mysql:8.4redis_plainredis:7-alpineredis:8-alpineredis_unified, the container theBun.redissuite actually uses, was already onredis:8-alpine(the latest Redis major), andminiowas already:latest.Compatibility fixes the bumps required
PostgreSQL 18 declares
VOLUME /var/lib/postgresql(not/var/lib/postgresql/data). Since #33822 the postgres Dockerfiles bake a pre-initialized cluster into a build-timePGDATA, which was/var/lib/postgresql/pgdata-init. That path is now insidepostgres:18'sVOLUME, so build-time writes there would be discarded and the baked-data optimization would silently disappear.PGDATAmoves to/var/lib/pgdata-init(outside the volume) in all three postgres Dockerfiles.MySQL 8.4 removed
--default-authentication-plugin, and MySQL 9.0 removed themysql_native_passwordplugin entirely. So:mysql_plainandmysql_tlsgo tomysql:9; nothing in their tests is auth-plugin sensitive.mysql_native_passwordcannot go to 9.x without losing the only real-server coverage of Bun'smysql_native_passwordscramble and the AuthSwitchRequest path (test/js/sql/sql-mysql.auth.test.ts, which relies on the handshake advertising a different plugin than thecaching_sha2_passworduser it creates). It moves to the 8.4 LTS, the last line that ships the plugin, and starts withwhich enables the plugin (off by default in 8.4) and keeps it as the default first-factor plugin so the handshake still advertises it. Since test/docker: bake mysql+postgres data dirs so cold start is ~2-3s, stop healthcheck wedge #33822 moved init-time config into
Dockerfile.mysql-native-password, the two flags are applied both in its build-timeRUNinit and itsCMD. Staying onmysql:8.0was not an option: it has been dropped from the official-images build list, so it no longer receives updates.Incompatibilities CI found
CI on the bumped containers surfaced two real PostgreSQL 18 incompatibilities in
test/js/sql/sql.test.ts, failing on every Linux x64 lane. Both were tests that encoded PostgreSQL-15-specific facts. Fixed inb7183fd:pg_database[] - null valueshardcoded a 16-fieldpg_databaserecord literal. Thepg_databasecatalog gaineddaticurulesin PostgreSQL 16 anddathasloginevtin 17 (which also renameddaticulocaletodatlocale), so on 18 the literal's eighth field (PG15'sdatconnlimit = -1) lands on a boolean column and the cast fails:The record is now sourced from the live server (
(SELECT d FROM pg_database d WHERE datname = 'postgres')), so the test no longer pins a catalog shape and exercises what it is named for: NULL positions preserved around a record element in apg_database[]array.text[] - special character escapingdepended onE'\v'NOT being an escape. PostgreSQL did not recognize\vin escape strings until version 18, so the test was actually decoding a literal letterv(and its comment said so). PostgreSQL 18 added\v, so the element became a real U+000B and thetoEqualbroke:The element now uses the version-independent hex escape
E'\x0b'and the expectation is a real"\v", which is what the test name claims to cover and is a stronger assertion than a plain letterv.Both fixes were verified through Bun's decoder against a live PostgreSQL.
Cleanup this forced
test/harness.tshad a"mysql:8"/"mysql:9"alias layer that picked a compose service based on the caller'senvfield. It was already misleading ("mysql:9"resolved to themysql:8.0container), and after the bumps the labels would have inverted outright (the test labeledmysql:8would run MySQL 9.7). The three call sites now name the compose service directly and the alias block is removed:test/js/sql/sql-mysql.test.ts: the"MySQL 9"block becomes"MySQL with mysql_native_password"withimage: "mysql_native_password". Itsprocess.arch === "x64"guard is dropped; it dated from when that service was backed bymysql:8.0, andmysql:8.4publishes amd64 and arm64 (sql-mysql.auth.test.tsalready ran the same service ungated).test/integration/mysql2/mysql2.test.ts: names the two services directly.That deletion left
describeWithContainer'senvoption with no reader, so it is removed from the signature along withargsandarchs, which were already unused. The five callers that passed no-openv: {}/args: []objects are cleaned up too (sql-mysql.auth.test.ts,sql-mysql.helpers.test.ts,sql-mysql.transactions.test.ts,test/regression/issue/21311.test.ts,test/regression/issue/26030.test.ts).Also deleted
test/js/valkey/docker/andtest/js/valkey/docker-tls/: bothFROM redis:7-alpine, referenced by nothing (notdocker-compose.yml, nottest/docker/index.ts, not any test), superseded bytest/js/valkey/docker-unified/.Two assertions in
test/js/sql/sql-mysql.transactions.test.ts("Transaction throws"and"Uncaught transaction request errors bubbles to transaction") were hardened. They pinned the server's exact error prose, which is the one field that is not stable: it changes across MySQL versions and differs on MariaDB, which the adapter also targets (mariadb://URLs, and theBUN_TEST_SERVICE_*override lets the suite run against any MySQL-compatible server). They ignorederrno, the MySQL wire-protocol error number, which is identical across MySQL 8/9 and MariaDB. They now assertcode+errno(1366ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, 1054ER_BAD_FIELD_ERROR) plus the identifying substring of the message, matching the existingexpect(error.code)idiom in the same describe block. Strictly stronger than the prose-only assertion and robust to exactly the kind of change a container bump can cause.Not done here
redis_plainis defined in compose and mapped by the harness but has zero test consumers today; bumped rather than deleted to keep this a version-bump PR.Bun.redistests run against actual Valkey; they all use the officialredisimage. Valkey 9.1 is out. Covering it means either doubling thetest/js/valkey/suite or swapping the base image, so it is left for a separate decision.Verification
Two full CI builds of the complete diff confirm the bumped containers work: #66936 and the
ci: retriggerre-roll #66976. In both,postgres:18,mysql:9,mysql:8.4, andredis:8-alpineall came up and every database test suite passed on every Linux lane. The twosql.test.tsPostgreSQL 18 incompatibilities documented above went from red on all 7 Linux x64 lanes to green with the fixes.#66976 has 283 of 286 jobs passing. The 3 failures are unrelated to this diff:
darwin 26 aarch64 - test-bun:buildkite-agent artifact download timed out after 120s for step 'darwin-aarch64-build-bun'. This infrastructure timeout has hit the same lane on every build of this PR.alpine 3.23 x64 - test-bunandalpine 3.23 x64-baseline - test-bun:test/js/node/test/parallel/test-net-connect-memleak.js, a GC-observability assertion (assert.strictEqual(collected, true)afteronGC) with no relationship to database containers. This is the known CI flake tracked in CI: test-net-connect-memleak.js fails on half of PR builds on linux-x64-musl since June 28 ~23:00 UTC #33044 and it is currently hitting most PR builds in the repo: the same annotation appears on 20 of the last 22 failed builds here, across unrelated branches.I have stopped pushing so as not to keep re-rolling CI; the diff is ready.
Rebases
9fcc439e8bonto #33622, which reorderedsql-mysql.test.ts'simagesarray somysql_tlsruns last for startup ordering. Kept that order, dropped the now-dead.filter(Boolean), and updated the comment'smysql:9tomysql_native_passwordto reflect the alias removal. Build #70079 afterwards (275/286 jobs passed) had no SQL, Redis, or docker-compose test in its failures; the red was repo-wide flakes that also hit other unrelated PR builds at the time (proxy-stress-concurrent.test.tsDNS-resolver LeakSanitizer on 7 builds,test-net-localport.json 4,terminal.test.tson 2) plus thedarwin 26 aarch64artifact-download timeout.2b00a211ebonto #33822, a substantialtest/docker/rewrite that bakes pre-initialized data directories into the mysql/postgres images, moved the version pins fromdocker-compose.ymlinto newDockerfile.mysql-plainandDockerfile.mysql-native-password, removed the composetmpfs/environment/commandblocks those services used, and changed healthcheck intervals. Resolution:docker-compose.yml: took main'sbuild:structure everywhere; the per-service version bumps and the MySQL 8.4 flag rewrite move into the Dockerfiles instead.PGDATA=/var/lib/postgresql/pgdata-initis insidepostgres:18's newVOLUME /var/lib/postgresql, so build-time writes there would be discarded and test/docker: bake mysql+postgres data dirs so cold start is ~2-3s, stop healthcheck wedge #33822's ~1-2s cold start would silently revert to a fullinitdb.PGDATAmoves to/var/lib/pgdata-initin all three.Dockerfile.mysql-plain:FROM mysql:8.4→FROM mysql:9(no other change; the baked-init approach is version-agnostic).Dockerfile.mysql-native-password:FROM mysql:8.0→FROM mysql:8.4, and--default-authentication-plugin=mysql_native_password→--mysql-native-password=ON --authentication-policy=mysql_native_password,,in both the build-timeRUNinit andCMD.test/js/sql/mysql-tls/Dockerfile: took main's restructured layout withARG MYSQL_VERSION=9.The full set of touched test files still passes locally after the rebase (50 pass, 0 fail). Build #71312 afterwards has 281/286 jobs passing with no SQL, Redis, or docker-compose test in its failures, so the PGDATA move and the Dockerfile bumps on top of #33822's baked-data-dir structure all work. Its 3 failures are
test/js/node/test/sequential/test-net-localport.json the twoalpine 3.23 x64lanes (same annotation on builds #71300, #71297, #71294, #71287, #71286, #71284, #71259; does not importharness) andtest/js/sql/postgres-invalid-message-length.test.tsonwindows 2019 x64(a mock-server fault-injection test from #32769 that does not use docker; same annotation on builds #71313, #71300, #71297, #71294, #71291, #71290, #71287, #71286, #71284, #71259). This PR changes zero lines ofsrc/.[stamp-90s] gate passed · iteration 11 · 21 files touched
passes on PR (with fix)
diff hotspot
gate history · 1 passed · 0 rejected · iteration 11
evidence per changed file