Repository navigation
CI: add a non-UTC TZ leg for the db adapter conformance suites #55
Description
Activity
- addedpriority: nextKnown and queued for coming PRsKnown and queued for coming PRsarea: db-adaptersDatabase adapters (db-postgres, db-mysql, conformance suite)Database adapters (db-postgres, db-mysql, conformance suite)
on Jul 25, 2026 - changed the title
[-]CI: add a non-UTC TZ leg and a MySQL 8.0 + latest version matrix[/-][+]CI: add a non-UTC TZ leg for the db adapter conformance suites[/+]on Jul 27, 2026 Narrowed and closing. The two gaps got different answers.
Gap 1 — non-UTC timezone leg: closed
This was the gap worth acting on, because it could hide a real regression rather than merely a hypothetical one. CI now re-runs both adapter conformance suites under
TZ=Asia/Kathmandu(UTC+5:45 — a non-whole-hour offset, so it also catches arithmetic that assumes offsets land on the hour).The step was verified non-vacuous before landing, rather than assumed to work. Replacing
normalize-row.ts's explicitT00:00:00.000Zanchor with a host-localT00:00:00.000:- under
TZ=UTC(what CI ran before): 177/177 Postgres tests pass — the regression is completely invisible - under
TZ=Asia/Kathmandu(the new leg): fails on02 Field Types > round-trips a calendar date at UTC midnight on both adapters
The catching test lives in the shared
@byline/db-conformancefield-types suite, so one step covers both adapters. Unmutated, both suites pass under the new offset (Postgres 177/177, MySQL 201/201).Cost was the deciding constraint, so the leg runs as an extra step inside the existing
test-suitejob rather than as a separate job — the service containers and the package build are already warm, so the added time is the two adapter suites themselves (~1 min) rather than another install/build/service cycle.Gap 2 — MySQL version matrix: descoped, deliberately
Not doing this on every run. A matrix leg would roughly double the DB-integration surface of every pull request to guard against a class of drift that has not bitten us, and keeping CI times short is the higher priority right now.
The pin stays at
mysql:8.0because that is the documented engine floor (8.0.14+, forLATERALjoins and the lifted no-subquery-in-a-view's-FROMrestriction). Pinning the floor is what actually exercises the floor — a matrix that also ranlatestwould not change what the required check guarantees.Coverage of newer releases instead comes from:
- local development, which already runs
mysql:latest(9.x) viamysql/docker-compose.yml— so the current release is exercised continuously by anyone developing the adapter - a manual run when a new MySQL GA lands, or if adapter-visible behaviour is suspected
If that proves too loose in practice — a MySQL release breaks us and local development did not catch it first — reopen for a scheduled (not per-PR) job.
Reflected in the GA criteria at #58, where CI is now checked off and CLI adapter selection is the sole remaining blocker.
- under
Follow-up from implementing
@byline/db-mysql: two gaps in the current MySQL CI coverage that came out of finishing the temporal (date/datetime) convergence work, not from the original design spec.Spec:
specs/2026-07-24-db-mysql-adapter-design.md— background on the adapter this CI coverage protects.Gap 1 — no non-UTC timezone leg
CI's MySQL service container runs under
TZ=UTC(the container and runner default). Both adapters anchordatefield values to UTC midnight specifically because host-local midnight would make the same storeddatevalue materialise as a different calendar day depending on which host served it (seepackages/db-postgres/src/modules/storage/normalize-row.ts'stoDateOnlydocblock and its MySQL counterpart inpackages/db-mysql/src/modules/storage/normalize-row.ts).Under
TZ=UTC, local midnight and UTC midnight are the same instant — so a regression that silently reintroduced local-midnightdatehandling (e.g. dropping the explicitT00:00:00.000Zanchor and lettingnew Date('YYYY-MM-DD')parse against the runtime's local zone) would pass every existing CI run and only misbehave on a host running under a non-UTC offset.Ask: add a CI leg — or parameterise the existing integration job — that runs with
TZset to a non-UTC, non-whole-hour-friendly offset (e.g.Asia/Kathmandu, UTC+5:45, or at minimumAsia/Bangkok, UTC+7, per the live-server evidence already gathered innormalize-row.ts's docblocks) so this class of regression fails CI instead of shipping.Gap 2 — no MySQL version matrix
CI currently pins a single
mysql:8.0service container — the adapter's documented floor (packages/db-mysql/src/lib/boot-check.ts— MySQL 8.0.14+ forLATERALjoins and the lifted no-subquery-in-a-view's-FROMrestriction). Nothing exercises a newer MySQL release (9.x, ormysql:latest) automatically, so a behavioural change in a newer MySQL release affecting this adapter's SQL would only surface when someone happens to test against it manually.Ask: add a matrix leg (or a second, non-blocking job) running the same integration suite against
mysql:latest(or the current newest GA) alongside the pinnedmysql:8.0floor leg, so drift between the floor and the current release is visible in CI rather than discovered live.Where CI lives today
.github/workflows/ci.yml— the MySQL service container anddb-mysqltest env were added in this program (see the mergedci: added a mysql service container and db-mysql test envcommit).