mesh-catalog: its consumer and tools run in the node's runtime, and its preparation is a run-once process (hq ADR 0198)
The mesh-catalog container goes with its Dockerfile, build bases, bus credential and mesh-state directory. Its words are the database URL file where the mesh writes it. `pg` is a dependency in its package.json, which the builder now installs and inlines into the bundle (mesh-controller: a TypeScript bundle installs its module's own packages). `prepares: true` needs a container running the module's own artifact, so it becomes what ADR 0198 §3 says it is: prepare/index.js run by node as a run-once process, with the same words and no bus, before the runtime is started with the version that needs it, and again when the database URL changes.
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
# mesh-catalog's runtime: the tool runtime, carrying the catalogue's compiled graph, its consumer
|
||||
# of what the builder announces, and its tools.
|
||||
#
|
||||
# **Built from this module's own directory and nothing else.** The sdk is in the base image, so
|
||||
# nothing is copied out of a neighbouring checkout — which is what lets the mesh build this from a
|
||||
# repository and a path (novox/hq ADR 0069) rather than only on a workstation with the siblings.
|
||||
#
|
||||
# Two bases, named rather than pinned: the image this is COMPILED in, and the image it RUNS in.
|
||||
# They are different images on purpose — the first carries a compiler and the second must not, or
|
||||
# every running container would carry one it never invokes. The mesh answers both with the copies it
|
||||
# holds, because a fingerprint written here would name one particular copy and no other mesh has it
|
||||
# (novox/hq issue 044). Declared in module.json's `build.on`; deliberately no defaults, so a build
|
||||
# nobody told stops here and says which module to build first.
|
||||
ARG BUILD_BASE
|
||||
ARG RUNTIME_BASE
|
||||
|
||||
FROM ${BUILD_BASE} AS build
|
||||
# Compiled under /app/modules so `@novox/mesh-sdk` resolves upward into the base's own
|
||||
# node_modules — the module is compiled against exactly the sdk it will run against.
|
||||
WORKDIR /app/modules/mesh-catalog
|
||||
COPY . .
|
||||
# The compiler is invoked by its real path rather than through node_modules/.bin, whose entries are
|
||||
# symlinks to a launcher that requires its library relatively — resolved away when the base image
|
||||
# was assembled.
|
||||
RUN node /app/node_modules/typescript/bin/tsc pg.d.ts store.ts index.ts tools/index.ts prepare/index.ts \
|
||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
||||
|
||||
# **A module may need something the base image does not carry.** The base holds what every module
|
||||
# needs — the sdk, the broker client — and a postgres driver is not that: the one other module that
|
||||
# reaches a database shells out to psql instead. So the catalogue brings its own.
|
||||
#
|
||||
# Installed into an empty directory rather than into the module's, because the module's package.json
|
||||
# also names `@novox/mesh-sdk`, which is not on any registry — it is in the base image. Asking npm to
|
||||
# resolve this module's dependencies would therefore fail on the one it already has.
|
||||
RUN mkdir -p /deps && cd /deps && \
|
||||
npm install --omit=dev --no-audit --no-fund --no-package-lock pg@8
|
||||
|
||||
FROM ${RUNTIME_BASE}
|
||||
COPY --from=build /app/modules/mesh-catalog/dist /app/modules/mesh-catalog/dist
|
||||
# Beside the compiled code, so `pg` resolves from it while `@novox/mesh-sdk` keeps walking up to the
|
||||
# base image's own node_modules — the module gets its extra dependency without shadowing the sdk it
|
||||
# was compiled against.
|
||||
COPY --from=build /deps/node_modules /app/modules/mesh-catalog/node_modules
|
||||
# Both entrypoints, loaded in serve mode.
|
||||
#
|
||||
# **A consumer cannot be started with `run`.** That mode imports an entrypoint without binding a
|
||||
# broker — it is for a step that does its work offline and exits — and the catalogue's whole job is
|
||||
# to listen for what the builder announces. Serve binds the broker first, then imports these, so
|
||||
# `on()` has something to subscribe to.
|
||||
ENV MESH_TOOL_MODULES=/app/modules/mesh-catalog/dist/index.js,/app/modules/mesh-catalog/dist/tools/index.js
|
||||
|
||||
# And what prepares this module's state, for the runtime's `prepare` mode (novox/hq ADR 0135). Named
|
||||
# here, beside the entrypoints above, because the module knows which of its files prepares its state
|
||||
# and nothing else could: the mesh asks one word and this says what answers it.
|
||||
ENV MESH_PREPARE=/app/modules/mesh-catalog/dist/prepare/index.js
|
||||
@@ -25,9 +25,6 @@
|
||||
"secrets": {
|
||||
"postgres-database": "${dir:state}/database.secret"
|
||||
},
|
||||
"own-secrets": {
|
||||
"broker": "${dir:mesh-state}/broker"
|
||||
},
|
||||
"consumes": [
|
||||
"mesh-build-machine.built",
|
||||
"mesh-controller.built-before"
|
||||
@@ -38,14 +35,7 @@
|
||||
"rebuild-needed",
|
||||
"catching-up"
|
||||
],
|
||||
"prepares": true,
|
||||
"resources": [
|
||||
{
|
||||
"id": "mesh-state",
|
||||
"type": "directory",
|
||||
"mode": "0700",
|
||||
"place": "mesh"
|
||||
},
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
@@ -60,43 +50,41 @@
|
||||
"content": "postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}\n"
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
"type": "container",
|
||||
"name": "mesh-catalog",
|
||||
"network": "host",
|
||||
"volumes": [
|
||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
||||
"${dir:state}:/run/state",
|
||||
"${dir:state}/database.url:/run/secrets/database-url:ro"
|
||||
"id": "prepare",
|
||||
"type": "process",
|
||||
"name": "mesh-catalog-prepare",
|
||||
"artifact": "code",
|
||||
"run": [
|
||||
"node",
|
||||
"prepare/index.js"
|
||||
],
|
||||
"run-once": true,
|
||||
"env": {
|
||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
||||
"DATABASE_URL_FILE": "/run/secrets/database-url"
|
||||
"DATABASE_URL_FILE": "${dir:state}/database.url"
|
||||
},
|
||||
"artifact": "runtime",
|
||||
"restart-on": [
|
||||
"database-url"
|
||||
]
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"on": [
|
||||
{
|
||||
"arg": "BUILD_BASE",
|
||||
"module": "mesh-tools",
|
||||
"artifact": "build"
|
||||
},
|
||||
{
|
||||
"arg": "RUNTIME_BASE",
|
||||
"module": "mesh-tools",
|
||||
"artifact": "runtime"
|
||||
}
|
||||
],
|
||||
"artifacts": [
|
||||
{
|
||||
"name": "runtime",
|
||||
"kind": "image",
|
||||
"from": "Dockerfile"
|
||||
"name": "code",
|
||||
"kind": "bundle",
|
||||
"language": "typescript",
|
||||
"entrypoints": [
|
||||
"index.js",
|
||||
"tools/index.js",
|
||||
"prepare/index.js"
|
||||
],
|
||||
"loads": [
|
||||
"index.js",
|
||||
"tools/index.js"
|
||||
],
|
||||
"env": {
|
||||
"DATABASE_URL_FILE": "${dir:state}/database.url"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -1,9 +1,9 @@
|
||||
// Ambient types for `pg` (node-postgres), which ships its types only via the separate `@types/pg`
|
||||
// package. Rather than pull that in at tsc time, this declares the exact slice model-usage uses —
|
||||
// the same precedent anthropic-manager sets for `tweetnacl-sealedbox-js` (a local ambient .d.ts,
|
||||
// listed in tsconfig `include`, default-imported). The real `pg` is installed into the module's
|
||||
// runtime image (package.json `dependencies`; novox/hq ADR 0052), so this types the code without
|
||||
// deciding what runs.
|
||||
// listed in tsconfig `include`, default-imported). The real `pg` is the package.json dependency the
|
||||
// builder installs and inlines into the module's bundle (novox/hq ADR 0198 §4), so this types the
|
||||
// code without deciding what runs.
|
||||
declare module "pg" {
|
||||
/** One checked-out connection. Needed because registering a module-version and its edges is one
|
||||
* act: a half-written registration is a graph that lies about what something was built against. */
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
// nothing anywhere said so.
|
||||
//
|
||||
// Nothing here connects to the broker. Preparation runs before the version that would use it, so
|
||||
// there is nothing yet to talk to; the runtime's `prepare` mode imports this and awaits it, and this
|
||||
// process exiting non-zero is how the host knows not to start the runtime.
|
||||
// there is nothing yet to talk to: the host runs this file as a run-once process, with the module's
|
||||
// words and no bus (novox/hq ADR 0198 §3), before the node's runtime is started with the version
|
||||
// that needs it, and this process exiting non-zero is how the host knows the step did not complete.
|
||||
import { Graph } from "../store.js";
|
||||
|
||||
const graph = Graph.fromEnv();
|
||||
|
||||
Reference in New Issue
Block a user