Skip to content

Commit 0e01dde

Browse files
os-trumpclaude
andauthored
fix(docker): install pg and mysql2 in the official runtime image, and pin the published driver list to it (#14699)
* fix(docker): install pg and mysql2 in the official runtime image `@objectstack/driver-sql` declares `pg`, `mysql2` and `tedious` as OPTIONAL peer dependencies, and npm skips optional peers, so the official runtime image -- built by `npm install -g @objectstack/cli` and nothing else -- had no `pg` in it. Every documented Postgres path landed on that image: the `docker run ... -e OS_DATABASE_URL=postgres://...` invocation in docker/README.md, the same invocation in this Dockerfile's own header, and `npm create objectstack` followed by `docker compose up` against the generated `postgres:17` service. All of them failed fast at boot with `Cannot find module 'pg'`. Per the maintainer ruling on #14510 (direction B'): the image installs the drivers, and docker/README.md publishes which ones it carries as a maintained public promise. The scaffolder is unchanged. The version ranges are copied verbatim from driver-sql's `peerDependencies`, so the image satisfies the driver's own contract rather than a second one; both packages are pure JavaScript with no native build. A promise with nothing holding it to the artifact drifts silently, so `check-docs-image-tag` -- the gate that already owns exactly this file pair -- grows a third limb comparing the Dockerfile's install line against the README's published table: package set, version ranges, and an anti-vacuity rule on both sides. Every new finding kind is observed failing in `--self-test` (94 assertions), and the limb was proven red against the pre-change tree before the docker files were touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza * docs(docker): correct the image's 'and nothing else' contents claim The README opened by saying the image packages the CLI 'and nothing else', which the driver install one commit earlier makes false. Both the README and the Dockerfile header now name what is actually inside. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a8fac3a commit 0e01dde

3 files changed

Lines changed: 651 additions & 16 deletions

File tree

docker/Dockerfile

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# ObjectStack official runtime image — ghcr.io/objectstack-ai/objectstack
22
#
3-
# A generic, app-agnostic production runtime: Node + @objectstack/cli +
4-
# `os start`. It contains NO app — bring your compiled artifact
5-
# (dist/objectstack.json, built by `os build` in CI):
3+
# A generic, app-agnostic production runtime: Node + @objectstack/cli + the
4+
# `pg` / `mysql2` SQL drivers + `os start`. It contains NO app — bring your
5+
# compiled artifact (dist/objectstack.json, built by `os build` in CI):
66
#
77
# FROM ghcr.io/objectstack-ai/objectstack:<version>
88
# COPY --chown=node:node dist/objectstack.json /srv/app/objectstack.json
@@ -40,7 +40,36 @@ FROM node:22-slim
4040
# Pinned by CI to the @objectstack/cli release that triggered the publish.
4141
# `latest` is only the fallback for ad-hoc local builds of this file.
4242
ARG OS_CLI_VERSION=latest
43-
RUN npm install -g @objectstack/cli@${OS_CLI_VERSION} \
43+
44+
# The SQL drivers ship WITH the image, not with the app (#14510).
45+
#
46+
# `@objectstack/driver-sql` declares `pg`, `mysql2` and `tedious` as OPTIONAL
47+
# peer dependencies. npm 7+ installs peer dependencies automatically but SKIPS
48+
# the optional ones, so `npm install -g @objectstack/cli` on its own produced a
49+
# tree with no `pg` in it -- while the header of this very file, README.md, and
50+
# the `docker-compose.yml` that `npm create objectstack` generates (whose `db`
51+
# service is `postgres:17`) all hand this image a `postgres://` URL. Every one
52+
# of those paths died at boot on `Cannot find module 'pg'`, with the loud
53+
# fail-fast ADR-0062 D5 requires but nothing documented to act on. Installing
54+
# the drivers here is the "whoever makes the promise installs it" half of the
55+
# fix; the other half is that README.md publishes the list.
56+
#
57+
# The ranges are copied VERBATIM from driver-sql's `peerDependencies`, so the
58+
# image satisfies the driver's own contract rather than a second one invented
59+
# here. Both packages are pure JavaScript with no native build step, which is
60+
# why they are affordable for every user of the image. `tedious` (SQL Server)
61+
# and `@objectstack/driver-turso` are deliberately NOT installed -- extend the
62+
# image when you need one, as README.md shows.
63+
#
64+
# This install line is a PUBLIC PROMISE, published as the driver table in
65+
# ./README.md. Adding or removing a package here changes which databases a
66+
# deployment can reach, so the two files must move in the same commit:
67+
# `pnpm check:docs-image-tag` fails when the install line and that table
68+
# disagree on the package set or on a version range.
69+
RUN npm install -g \
70+
@objectstack/cli@${OS_CLI_VERSION} \
71+
"pg@^8.0.0" \
72+
"mysql2@^3.0.0" \
4473
&& npm cache clean --force
4574

4675
LABEL org.opencontainers.image.source="https://github.com/objectstack-ai/objectstack" \

docker/README.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# ObjectStack Official Runtime Image
22

33
`ghcr.io/objectstack-ai/objectstack` — the official production runtime for
4-
standalone ObjectStack apps. It packages Node 22 and `@objectstack/cli`
5-
(`os start`) and nothing else: **your compiled artifact is the app**, the
6-
image is the runtime.
4+
standalone ObjectStack apps. It packages Node 22, `@objectstack/cli`
5+
(`os start`) and the SQL drivers listed below — and no application code:
6+
**your compiled artifact is the app**, the image is the runtime.
77

88
```
99
objectstack.config.ts ──(os build, CI)──▶ dist/objectstack.json ──(this image)──▶ running app
@@ -46,6 +46,44 @@ docker run -p 8080:8080 \
4646
`OS_ARTIFACT_PATH` also accepts an `https://` URL, so the artifact can come
4747
straight from your release storage.
4848

49+
## Database drivers in the image
50+
51+
**This list is a public promise.** The dialects below need nothing installed —
52+
their driver is already in the image, which is why the `postgres://` invocation
53+
above works exactly as written.
54+
55+
| `OS_DATABASE_URL` scheme | Driver package installed in the image |
56+
|:---|:---|
57+
| `postgres://`, `postgresql://` | `pg@^8.0.0` |
58+
| `mysql://`, `mysql2://` | `mysql2@^3.0.0` |
59+
60+
The ranges are `@objectstack/driver-sql`'s own optional-peer ranges, so the
61+
image satisfies the driver's contract rather than a second one. Both packages
62+
are pure JavaScript — they add no native build step and no compiler to the
63+
image.
64+
65+
Two more dialects work without appearing above, because they arrive with
66+
`@objectstack/cli` rather than from that install line: SQLite (`better-sqlite3`,
67+
for a `file:…` path — one box only, wrong for multi-node) and MongoDB
68+
(`mongodb://…`, **single-tenant only**; see
69+
[Drivers](https://objectstack.ai/docs/data-modeling/drivers)).
70+
71+
**Not in the image:** `tedious` (SQL Server) and `@objectstack/driver-turso`
72+
(`libsql://…` / Turso). Add one by extending the image:
73+
74+
```dockerfile
75+
FROM ghcr.io/objectstack-ai/objectstack:17.2.0
76+
USER root
77+
RUN npm install -g tedious
78+
USER node
79+
COPY --chown=node:node dist/objectstack.json /srv/app/objectstack.json
80+
```
81+
82+
Changing this table is a change to what deployments can connect to, so it does
83+
not move on its own: `pnpm check:docs-image-tag` compares it against
84+
[`Dockerfile`](./Dockerfile)'s install line and fails if the two disagree on the
85+
package set or on a version range.
86+
4987
## What the image presets
5088

5189
- `OS_ARTIFACT_PATH=/srv/app/objectstack.json`, `OS_PORT=8080`,

0 commit comments

Comments
 (0)