Merge pull request 'Twelve media modules' code moves into bundles the node's runtime serves (hq ADR 0198, to-be 38 WP4c)' (#13) from feat/0198-waves-2-3-module-code-moves into main

This commit was merged in pull request #13.
This commit is contained in:
2026-10-03 23:01:18 +00:00
25 changed files with 287 additions and 950 deletions
-27
View File
@@ -1,27 +0,0 @@
# bazarr's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/bazarr
COPY . .
RUN node /app/node_modules/typescript/bin/tsc apikey.ts client.ts index.ts tools/index.ts servarr/settings.ts servarr/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/bazarr/dist /app/modules/bazarr/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/bazarr/dist/index.js,/app/modules/bazarr/dist/tools/index.js
# NOT dist/servarr/index.js: that is a step the host runs to completion, named by the `servarr`
# container's args as `mesh-tools run …` (novox/hq ADR 0052). Listed here it would run inside the
# serving sidecar too, and exit it.
+27 -63
View File
@@ -7,9 +7,6 @@
"emits": [
"subtitle.downloaded"
],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"listens": [
{
"name": "web",
@@ -42,12 +39,6 @@
}
],
"resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{
"id": "state",
"type": "directory",
@@ -100,56 +91,27 @@
"content": "{}\n",
"merge": "json"
},
{
"id": "runtime",
"type": "container",
"name": "mesh-bazarr",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:state}/config.json:/run/config/config.json:ro",
"${dir:config}:/var/lib/bazarr/config:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_BAZARR_URL": "http://127.0.0.1:${port:6767}",
"MESH_BAZARR_CONFIG_FILE": "/run/config/config.json",
"MESH_BAZARR_CONFIG_DIR": "/var/lib/bazarr/config"
},
"restart-on": [
"runtime-config"
],
"artifact": "runtime"
},
{
"id": "servarr",
"type": "container",
"name": "mesh-bazarr-servarr",
"network": "host",
"run-once": true,
"volumes": [
"${dir:config}:/var/lib/bazarr/config:ro",
"${dir:state}/sonarr-api.json:/run/servarr/sonarr-api.json:ro",
"${dir:state}/sonarr-api.secret:/run/servarr/sonarr-api.secret:ro",
"${dir:state}/radarr-api.json:/run/servarr/radarr-api.json:ro",
"${dir:state}/radarr-api.secret:/run/servarr/radarr-api.secret:ro"
"type": "process",
"name": "bazarr-servarr",
"artifact": "code",
"run": [
"node",
"servarr/index.js"
],
"run-once": true,
"env": {
"MESH_BAZARR_URL": "http://127.0.0.1:${port:6767}",
"MESH_BAZARR_CONFIG_DIR": "/var/lib/bazarr/config",
"MESH_SERVARR_DIR": "/run/servarr"
"MESH_BAZARR_CONFIG_DIR": "${dir:config}",
"MESH_SERVARR_DIR": "${dir:state}"
},
"args": [
"run",
"/app/modules/bazarr/dist/servarr/index.js"
],
"restart-on": [
"bound-sonarr-api",
"secret-sonarr-api",
"bound-radarr-api",
"secret-radarr-api"
],
"artifact": "runtime"
]
}
],
"requires": [
@@ -173,23 +135,25 @@
"radarr-api": "${dir:state}/radarr-api.secret"
},
"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",
"servarr/index.js"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_BAZARR_URL": "http://127.0.0.1:${port:6767}",
"MESH_BAZARR_CONFIG_FILE": "${dir:state}/config.json",
"MESH_BAZARR_CONFIG_DIR": "${dir:config}"
}
}
]
}
-27
View File
@@ -1,27 +0,0 @@
# bookshelf's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/bookshelf
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts tools/index.ts downloads/settings.ts downloads/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/bookshelf/dist /app/modules/bookshelf/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/bookshelf/dist/index.js,/app/modules/bookshelf/dist/tools/index.js
# NOT dist/downloads/index.js: that is a step the host runs to completion, named by the `downloads`
# container's args as `mesh-tools run …` (novox/hq ADR 0052). Listed here it would run inside the
# serving sidecar too, and exit it.
+28 -64
View File
@@ -10,9 +10,6 @@
"download.completed"
],
"consumes": [],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"listens": [
{
"name": "web",
@@ -35,12 +32,6 @@
}
],
"resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{
"id": "state",
"type": "directory",
@@ -83,22 +74,6 @@
"identity"
]
},
{
"id": "runtime",
"type": "container",
"name": "mesh-bookshelf",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:config}:/var/lib/bookshelf/config:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_BOOKSHELF_URL": "http://127.0.0.1:${port:8787}",
"MESH_BOOKSHELF_CONFIG_DIR": "/var/lib/bookshelf/config"
},
"artifact": "runtime"
},
{
"id": "downloads-config",
"type": "file",
@@ -114,34 +89,23 @@
},
{
"id": "downloads",
"type": "container",
"name": "mesh-bookshelf-downloads",
"network": "host",
"run-once": true,
"volumes": [
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro",
"${dir:state}/nzbget-api.json:/run/downloads/nzbget-api.json:ro",
"${dir:state}/nzbget-api.secret:/run/downloads/nzbget-api.secret:ro",
"${dir:state}/qbittorrent-api.json:/run/downloads/qbittorrent-api.json:ro",
"${dir:state}/qbittorrent-api.secret:/run/downloads/qbittorrent-api.secret:ro",
"${dir:state}/jackett-api.json:/run/downloads/jackett-api.json:ro",
"${dir:state}/jackett-api.secret:/run/downloads/jackett-api.secret:ro",
"${dir:config}:/var/lib/bookshelf/config:ro",
"${dir:downloads-memory}:/var/lib/downloads"
"type": "process",
"name": "bookshelf-downloads",
"artifact": "code",
"run": [
"node",
"downloads/index.js"
],
"run-once": true,
"env": {
"MESH_DOWNLOADS_APP": "bookshelf",
"MESH_DOWNLOADS_API": "v1",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8787}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/bookshelf/config/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json"
"MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
},
"args": [
"run",
"/app/modules/bookshelf/dist/downloads/index.js"
],
"restart-on": [
"downloads-config",
"bound-nzbget-api",
@@ -150,8 +114,7 @@
"secret-qbittorrent-api",
"bound-jackett-api",
"secret-jackett-api"
],
"artifact": "runtime"
]
}
],
"requires": [
@@ -178,23 +141,24 @@
"jackett-api": "${dir:state}/jackett-api.secret"
},
"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",
"downloads/index.js"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_BOOKSHELF_URL": "http://127.0.0.1:${port:8787}",
"MESH_BOOKSHELF_CONFIG_DIR": "${dir:config}"
}
}
]
}
-24
View File
@@ -1,24 +0,0 @@
# jackett's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/jackett
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts tools/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/jackett/dist /app/modules/jackett/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/jackett/dist/tools/index.js
+15 -40
View File
@@ -99,34 +99,9 @@
"mode": "0600",
"content": "{}\n",
"merge": "json"
},
{
"id": "runtime",
"type": "container",
"name": "mesh-jackett",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:state}/config.json:/run/config/config.json:ro",
"${dir:config}:/var/lib/jackett/config:ro",
"${dir:mesh-state}/api-key:/run/secrets/api-key:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_JACKETT_URL": "http://127.0.0.1:${port:9117}",
"MESH_JACKETT_CONFIG_FILE": "/run/config/config.json",
"MESH_JACKETT_CONFIG_DIR": "/var/lib/jackett/config",
"MESH_JACKETT_API_KEY_FILE": "/run/secrets/api-key"
},
"restart-on": [
"runtime-config",
"needs-api-key"
],
"artifact": "runtime"
}
],
"own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": {
"path": "${dir:mesh-state}/api-key",
"taken": "at-start"
@@ -145,23 +120,23 @@
"route": "${dir:state}/route.json"
},
"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": [
"tools/index.js"
],
"loads": [
"tools/index.js"
],
"env": {
"MESH_JACKETT_URL": "http://127.0.0.1:${port:9117}",
"MESH_JACKETT_CONFIG_FILE": "${dir:state}/config.json",
"MESH_JACKETT_CONFIG_DIR": "${dir:config}",
"MESH_JACKETT_API_KEY_FILE": "${dir:mesh-state}/api-key"
}
}
]
}
-14
View File
@@ -1,14 +0,0 @@
# kometa's runtime: the tool runtime, carrying this module's compiled code.
#
# Built from this module's own directory and nothing else; the sdk and the tool runtime are in the
# base images, declared in module.json's `build.on` (novox/hq ADR 0069, issue 044).
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/kometa
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts tools/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/kometa/dist /app/modules/kometa/dist
ENV MESH_TOOL_MODULES=/app/modules/kometa/dist/tools/index.js
+14 -44
View File
@@ -8,27 +8,24 @@
"plex-api"
],
"own-secrets": {
"tmdb": "${dir:state}/tmdb.secret",
"broker": "${dir:mesh-state}/broker"
"tmdb": "${dir:state}/tmdb.secret"
},
"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": [
"tools/index.js"
],
"loads": [
"tools/index.js"
],
"env": {
"MESH_KOMETA_CONFIG_FILE": "${dir:state}/config.yml",
"MESH_KOMETA_CONFIG_DIR": "${dir:config}"
}
}
]
},
@@ -39,12 +36,6 @@
"plex-api": "${dir:state}/plex-api.secret"
},
"resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{
"id": "state",
"type": "directory",
@@ -96,27 +87,6 @@
"mode": "0600",
"content": "{}\n",
"merge": "json"
},
{
"id": "runtime",
"type": "container",
"name": "mesh-kometa",
"artifact": "runtime",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:state}/config.json:/run/config/config.json:ro",
"${dir:state}/config.yml:/var/lib/kometa/config.yml:ro",
"${dir:config}:/var/lib/kometa/config:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_KOMETA_CONFIG_FILE": "/var/lib/kometa/config.yml",
"MESH_KOMETA_CONFIG_DIR": "/var/lib/kometa/config"
},
"restart-on": [
"runtime-config",
"settings"
]
}
]
}
-27
View File
@@ -1,27 +0,0 @@
# lidarr's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/lidarr
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts plex.ts index.ts tools/index.ts downloads/settings.ts downloads/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/lidarr/dist /app/modules/lidarr/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/lidarr/dist/index.js,/app/modules/lidarr/dist/tools/index.js
# NOT dist/downloads/index.js: that is a step the host runs to completion, named by the `downloads`
# container's args as `mesh-tools run …` (novox/hq ADR 0052). Listed here it would run inside the
# serving sidecar too, and exit it.
+31 -67
View File
@@ -26,7 +26,6 @@
],
"consumes": [],
"own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": {
"path": "${dir:mesh-state}/api-key",
"taken": "at-start"
@@ -124,33 +123,6 @@
"api-key-init"
]
},
{
"id": "runtime",
"type": "container",
"name": "mesh-lidarr",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:config}:/var/lib/lidarr/config:ro",
"${dir:mesh-state}/api-key:/run/secrets/api-key:ro",
"${dir:state}/plex-api.json:/run/plex/plex-api.json:ro",
"${dir:state}/plex-api.secret:/run/plex/plex-api.secret:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_LIDARR_URL": "http://127.0.0.1:${port:8686}",
"MESH_LIDARR_CONFIG_DIR": "/var/lib/lidarr/config",
"MESH_LIDARR_API_KEY_FILE": "/run/secrets/api-key",
"MESH_PLEX_BIND_FILE": "/run/plex/plex-api.json",
"MESH_PLEX_TOKEN_FILE": "/run/plex/plex-api.secret"
},
"artifact": "runtime",
"restart-on": [
"needs-api-key",
"bound-plex-api",
"secret-plex-api"
]
},
{
"id": "downloads-config",
"type": "file",
@@ -166,34 +138,23 @@
},
{
"id": "downloads",
"type": "container",
"name": "mesh-lidarr-downloads",
"network": "host",
"run-once": true,
"volumes": [
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro",
"${dir:state}/nzbget-api.json:/run/downloads/nzbget-api.json:ro",
"${dir:state}/nzbget-api.secret:/run/downloads/nzbget-api.secret:ro",
"${dir:state}/qbittorrent-api.json:/run/downloads/qbittorrent-api.json:ro",
"${dir:state}/qbittorrent-api.secret:/run/downloads/qbittorrent-api.secret:ro",
"${dir:state}/jackett-api.json:/run/downloads/jackett-api.json:ro",
"${dir:state}/jackett-api.secret:/run/downloads/jackett-api.secret:ro",
"${dir:config}:/var/lib/lidarr/config:ro",
"${dir:downloads-memory}:/var/lib/downloads"
"type": "process",
"name": "lidarr-downloads",
"artifact": "code",
"run": [
"node",
"downloads/index.js"
],
"run-once": true,
"env": {
"MESH_DOWNLOADS_APP": "lidarr",
"MESH_DOWNLOADS_API": "v1",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8686}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/lidarr/config/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json"
"MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
},
"args": [
"run",
"/app/modules/lidarr/dist/downloads/index.js"
],
"restart-on": [
"downloads-config",
"bound-nzbget-api",
@@ -202,8 +163,7 @@
"secret-qbittorrent-api",
"bound-jackett-api",
"secret-jackett-api"
],
"artifact": "runtime"
]
}
],
"requires": [
@@ -233,23 +193,27 @@
"plex-api": "${dir:state}/plex-api.secret"
},
"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",
"downloads/index.js"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_LIDARR_URL": "http://127.0.0.1:${port:8686}",
"MESH_LIDARR_CONFIG_DIR": "${dir:config}",
"MESH_LIDARR_API_KEY_FILE": "${dir:mesh-state}/api-key",
"MESH_PLEX_BIND_FILE": "${dir:state}/plex-api.json",
"MESH_PLEX_TOKEN_FILE": "${dir:state}/plex-api.secret"
}
}
]
}
-24
View File
@@ -1,24 +0,0 @@
# nzbget's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/nzbget
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts tools/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/nzbget/dist /app/modules/nzbget/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/nzbget/dist/index.js,/app/modules/nzbget/dist/tools/index.js
+17 -40
View File
@@ -10,7 +10,6 @@
],
"consumes": [],
"own-secrets": {
"broker": "${dir:mesh-state}/broker",
"password": {
"path": "${dir:mesh-state}/password",
"taken": "at-start"
@@ -99,30 +98,6 @@
"mode": "0600",
"content": "{}\n",
"merge": "json"
},
{
"id": "runtime",
"type": "container",
"name": "mesh-nzbget",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:mesh-state}/password:/run/secrets/password:ro",
"${dir:state}/config.json:/run/config/config.json:ro",
"${dir:config}:/var/lib/nzbget/config:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_NZBGET_URL": "http://127.0.0.1:${port:6789}",
"MESH_NZBGET_PASSWORD_FILE": "/run/secrets/password",
"MESH_NZBGET_CONFIG_FILE": "/run/config/config.json",
"MESH_NZBGET_CONFIG_DIR": "/var/lib/nzbget/config"
},
"restart-on": [
"runtime-config",
"needs-password"
],
"artifact": "runtime"
}
],
"provides": [
@@ -154,23 +129,25 @@
"route": "${dir:state}/route.json"
},
"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"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_NZBGET_URL": "http://127.0.0.1:${port:6789}",
"MESH_NZBGET_PASSWORD_FILE": "${dir:mesh-state}/password",
"MESH_NZBGET_CONFIG_FILE": "${dir:state}/config.json",
"MESH_NZBGET_CONFIG_DIR": "${dir:config}"
}
}
]
}
-27
View File
@@ -1,27 +0,0 @@
# ombi's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/ombi
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts tools/index.ts servarr/settings.ts plex/settings.ts connections/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/ombi/dist /app/modules/ombi/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/ombi/dist/index.js,/app/modules/ombi/dist/tools/index.js
# NOT dist/connections/index.js: that is a step the host runs to completion, named by the `connections`
# container's args as `mesh-tools run …` (novox/hq ADR 0052). Listed here it would run inside the
# serving sidecar too, and exit it.
+27 -59
View File
@@ -9,7 +9,6 @@
"request.approved"
],
"own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": "${dir:mesh-state}/api-key"
},
"listens": [
@@ -76,53 +75,21 @@
"content": "{}\n",
"merge": "json"
},
{
"id": "runtime",
"type": "container",
"name": "mesh-ombi",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:mesh-state}/api-key:/run/secrets/api-key:ro",
"${dir:mesh-state}/config.json:/run/config/config.json:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_OMBI_URL": "http://127.0.0.1:${port:3579}",
"MESH_OMBI_API_KEY_FILE": "/run/secrets/api-key",
"MESH_OMBI_CONFIG_FILE": "/run/config/config.json"
},
"restart-on": [
"runtime-config"
],
"artifact": "runtime"
},
{
"id": "connections",
"type": "container",
"name": "mesh-ombi-connections",
"network": "host",
"run-once": true,
"volumes": [
"${dir:mesh-state}/api-key:/run/secrets/api-key:ro",
"${dir:state}/sonarr-api.json:/run/connections/sonarr-api.json:ro",
"${dir:state}/sonarr-api.secret:/run/connections/sonarr-api.secret:ro",
"${dir:state}/radarr-api.json:/run/connections/radarr-api.json:ro",
"${dir:state}/radarr-api.secret:/run/connections/radarr-api.secret:ro",
"${dir:state}/lidarr-api.json:/run/connections/lidarr-api.json:ro",
"${dir:state}/lidarr-api.secret:/run/connections/lidarr-api.secret:ro",
"${dir:state}/plex-api.json:/run/connections/plex-api.json:ro",
"${dir:state}/plex-api.secret:/run/connections/plex-api.secret:ro"
"type": "process",
"name": "ombi-connections",
"artifact": "code",
"run": [
"node",
"connections/index.js"
],
"run-once": true,
"env": {
"MESH_OMBI_URL": "http://127.0.0.1:${port:3579}",
"MESH_OMBI_API_KEY_FILE": "/run/secrets/api-key",
"MESH_CONNECTIONS_DIR": "/run/connections"
"MESH_OMBI_API_KEY_FILE": "${dir:mesh-state}/api-key",
"MESH_CONNECTIONS_DIR": "${dir:state}"
},
"args": [
"run",
"/app/modules/ombi/dist/connections/index.js"
],
"restart-on": [
"bound-sonarr-api",
"secret-sonarr-api",
@@ -132,8 +99,7 @@
"secret-lidarr-api",
"bound-plex-api",
"secret-plex-api"
],
"artifact": "runtime"
]
}
],
"requires": [
@@ -163,23 +129,25 @@
"plex-api": "${dir:state}/plex-api.secret"
},
"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",
"connections/index.js"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_OMBI_URL": "http://127.0.0.1:${port:3579}",
"MESH_OMBI_API_KEY_FILE": "${dir:mesh-state}/api-key",
"MESH_OMBI_CONFIG_FILE": "${dir:mesh-state}/config.json"
}
}
]
}
-24
View File
@@ -1,24 +0,0 @@
# plex's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/plex
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts tools/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/plex/dist /app/modules/plex/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/plex/dist/index.js,/app/modules/plex/dist/tools/index.js
+7 -5
View File
@@ -57,19 +57,21 @@ export class PlexClient {
*/
static fromEnv(env: NodeJS.ProcessEnv = process.env): PlexClient {
const url = env.MESH_PLEX_URL ?? `http://127.0.0.1:${env.PLEX_PORT ?? "32400"}`;
const dataDir = env.MESH_PLEX_DATA_DIR ?? "/var/lib/plex";
// The server's config directory itself, where the mesh places it on the machine (novox/hq ADR
// 0198); the data directory above it is the container's layout, kept for a hand run.
const configDir = env.MESH_PLEX_CONFIG_DIR ?? join(env.MESH_PLEX_DATA_DIR ?? "/var/lib/plex", "config");
// The manifest sets neither MESH_PLEX_TOKEN_FILE nor MESH_PLEX_TOKEN: the server already keeps its
// token in Preferences.xml, read here through the manifest's read-only mount of the config dir.
// A token the mesh minted would be one plex.tv never issued, and preferring it would break every
// call, so the module declares no token secret. The file and env overrides stay for hand runs.
const token = readSecret(env.MESH_PLEX_TOKEN_FILE) ?? env.MESH_PLEX_TOKEN ?? PlexClient.detectToken(dataDir);
const token = readSecret(env.MESH_PLEX_TOKEN_FILE) ?? env.MESH_PLEX_TOKEN ?? PlexClient.detectToken(configDir);
if (!token) throw new Error("no Plex token — set MESH_PLEX_TOKEN or make the data dir readable");
return new PlexClient(url, token);
}
/** Discover the token from the server's Preferences.xml, falling back to null. */
static detectToken(dataDir: string): string | null {
const prefs = join(dataDir, "config", "Library", "Application Support", "Plex Media Server", "Preferences.xml");
/** Discover the token from the server's Preferences.xml under its config directory, falling back to null. */
static detectToken(configDir: string): string | null {
const prefs = join(configDir, "Library", "Application Support", "Plex Media Server", "Preferences.xml");
if (existsSync(prefs)) {
const match = readFileSync(prefs, "utf8").match(/PlexOnlineToken="([^"]+)"/);
if (match) return match[1];
+15 -40
View File
@@ -24,9 +24,6 @@
"consumes": [
"*.download.completed"
],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"listens": [
{
"name": "stream",
@@ -107,12 +104,6 @@
}
],
"resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{
"id": "state",
"type": "directory",
@@ -180,22 +171,6 @@
"restart-on": [
"identity"
]
},
{
"id": "runtime",
"type": "container",
"name": "mesh-plex",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:config}:/var/lib/plex/config:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_PLEX_URL": "http://127.0.0.1:${port:32400}",
"MESH_PLEX_DATA_DIR": "/var/lib/plex"
},
"artifact": "runtime"
}
],
"requires": [
@@ -211,23 +186,23 @@
"route": "${dir:state}/route.json"
},
"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"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_PLEX_URL": "http://127.0.0.1:${port:32400}",
"MESH_PLEX_CONFIG_DIR": "${dir:config}"
}
}
]
}
-24
View File
@@ -1,24 +0,0 @@
# qbittorrent's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/qbittorrent
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts tools/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/qbittorrent/dist /app/modules/qbittorrent/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/qbittorrent/dist/index.js,/app/modules/qbittorrent/dist/tools/index.js
+17 -40
View File
@@ -11,7 +11,6 @@
],
"consumes": [],
"own-secrets": {
"broker": "${dir:mesh-state}/broker",
"password": {
"path": "${dir:mesh-state}/password",
"taken": "at-start"
@@ -118,30 +117,6 @@
"mode": "0600",
"content": "{}\n",
"merge": "json"
},
{
"id": "runtime",
"type": "container",
"name": "mesh-qbittorrent",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:mesh-state}/password:/run/secrets/password:ro",
"${dir:state}/config.json:/run/config/config.json:ro",
"${dir:config}:/var/lib/qbittorrent/config:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_QBITTORRENT_URL": "http://127.0.0.1:${port:8112}",
"MESH_QBITTORRENT_PASSWORD_FILE": "/run/secrets/password",
"MESH_QBITTORRENT_CONFIG_FILE": "/run/config/config.json",
"MESH_QBITTORRENT_CONFIG_DIR": "/var/lib/qbittorrent/config"
},
"restart-on": [
"runtime-config",
"needs-password"
],
"artifact": "runtime"
}
],
"provides": [
@@ -173,23 +148,25 @@
"route": "${dir:state}/route.json"
},
"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"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_QBITTORRENT_URL": "http://127.0.0.1:${port:8112}",
"MESH_QBITTORRENT_PASSWORD_FILE": "${dir:mesh-state}/password",
"MESH_QBITTORRENT_CONFIG_FILE": "${dir:state}/config.json",
"MESH_QBITTORRENT_CONFIG_DIR": "${dir:config}"
}
}
]
}
-27
View File
@@ -1,27 +0,0 @@
# radarr's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/radarr
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts plex.ts index.ts tools/index.ts downloads/settings.ts downloads/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/radarr/dist /app/modules/radarr/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/radarr/dist/index.js,/app/modules/radarr/dist/tools/index.js
# NOT dist/downloads/index.js: that is a step the host runs to completion, named by the `downloads`
# container's args as `mesh-tools run …` (novox/hq ADR 0052). Listed here it would run inside the
# serving sidecar too, and exit it.
+31 -67
View File
@@ -26,7 +26,6 @@
],
"consumes": [],
"own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": {
"path": "${dir:mesh-state}/api-key",
"taken": "at-start"
@@ -118,33 +117,6 @@
"api-key-init"
]
},
{
"id": "runtime",
"type": "container",
"name": "mesh-radarr",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:config}:/var/lib/radarr/config:ro",
"${dir:mesh-state}/api-key:/run/secrets/api-key:ro",
"${dir:state}/plex-api.json:/run/plex/plex-api.json:ro",
"${dir:state}/plex-api.secret:/run/plex/plex-api.secret:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_RADARR_URL": "http://127.0.0.1:${port:7878}",
"MESH_RADARR_CONFIG_DIR": "/var/lib/radarr/config",
"MESH_RADARR_API_KEY_FILE": "/run/secrets/api-key",
"MESH_PLEX_BIND_FILE": "/run/plex/plex-api.json",
"MESH_PLEX_TOKEN_FILE": "/run/plex/plex-api.secret"
},
"artifact": "runtime",
"restart-on": [
"needs-api-key",
"bound-plex-api",
"secret-plex-api"
]
},
{
"id": "downloads-config",
"type": "file",
@@ -160,34 +132,23 @@
},
{
"id": "downloads",
"type": "container",
"name": "mesh-radarr-downloads",
"network": "host",
"run-once": true,
"volumes": [
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro",
"${dir:state}/nzbget-api.json:/run/downloads/nzbget-api.json:ro",
"${dir:state}/nzbget-api.secret:/run/downloads/nzbget-api.secret:ro",
"${dir:state}/qbittorrent-api.json:/run/downloads/qbittorrent-api.json:ro",
"${dir:state}/qbittorrent-api.secret:/run/downloads/qbittorrent-api.secret:ro",
"${dir:state}/jackett-api.json:/run/downloads/jackett-api.json:ro",
"${dir:state}/jackett-api.secret:/run/downloads/jackett-api.secret:ro",
"${dir:config}:/var/lib/radarr/config:ro",
"${dir:downloads-memory}:/var/lib/downloads"
"type": "process",
"name": "radarr-downloads",
"artifact": "code",
"run": [
"node",
"downloads/index.js"
],
"run-once": true,
"env": {
"MESH_DOWNLOADS_APP": "radarr",
"MESH_DOWNLOADS_API": "v3",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:7878}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/radarr/config/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json"
"MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
},
"args": [
"run",
"/app/modules/radarr/dist/downloads/index.js"
],
"restart-on": [
"downloads-config",
"bound-nzbget-api",
@@ -196,8 +157,7 @@
"secret-qbittorrent-api",
"bound-jackett-api",
"secret-jackett-api"
],
"artifact": "runtime"
]
}
],
"requires": [
@@ -227,23 +187,27 @@
"plex-api": "${dir:state}/plex-api.secret"
},
"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",
"downloads/index.js"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_RADARR_URL": "http://127.0.0.1:${port:7878}",
"MESH_RADARR_CONFIG_DIR": "${dir:config}",
"MESH_RADARR_API_KEY_FILE": "${dir:mesh-state}/api-key",
"MESH_PLEX_BIND_FILE": "${dir:state}/plex-api.json",
"MESH_PLEX_TOKEN_FILE": "${dir:state}/plex-api.secret"
}
}
]
}
-27
View File
@@ -1,27 +0,0 @@
# sonarr's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/sonarr
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts plex.ts index.ts tools/index.ts downloads/settings.ts downloads/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/sonarr/dist /app/modules/sonarr/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/sonarr/dist/index.js,/app/modules/sonarr/dist/tools/index.js
# NOT dist/downloads/index.js: that is a step the host runs to completion, named by the `downloads`
# container's args as `mesh-tools run …` (novox/hq ADR 0052). Listed here it would run inside the
# serving sidecar too, and exit it.
+31 -67
View File
@@ -26,7 +26,6 @@
],
"consumes": [],
"own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": {
"path": "${dir:mesh-state}/api-key",
"taken": "at-start"
@@ -124,33 +123,6 @@
"api-key-init"
]
},
{
"id": "runtime",
"type": "container",
"name": "mesh-sonarr",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:config}:/var/lib/sonarr/config:ro",
"${dir:mesh-state}/api-key:/run/secrets/api-key:ro",
"${dir:state}/plex-api.json:/run/plex/plex-api.json:ro",
"${dir:state}/plex-api.secret:/run/plex/plex-api.secret:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_SONARR_URL": "http://127.0.0.1:${port:8989}",
"MESH_SONARR_CONFIG_DIR": "/var/lib/sonarr/config",
"MESH_SONARR_API_KEY_FILE": "/run/secrets/api-key",
"MESH_PLEX_BIND_FILE": "/run/plex/plex-api.json",
"MESH_PLEX_TOKEN_FILE": "/run/plex/plex-api.secret"
},
"artifact": "runtime",
"restart-on": [
"needs-api-key",
"bound-plex-api",
"secret-plex-api"
]
},
{
"id": "downloads-config",
"type": "file",
@@ -166,34 +138,23 @@
},
{
"id": "downloads",
"type": "container",
"name": "mesh-sonarr-downloads",
"network": "host",
"run-once": true,
"volumes": [
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro",
"${dir:state}/nzbget-api.json:/run/downloads/nzbget-api.json:ro",
"${dir:state}/nzbget-api.secret:/run/downloads/nzbget-api.secret:ro",
"${dir:state}/qbittorrent-api.json:/run/downloads/qbittorrent-api.json:ro",
"${dir:state}/qbittorrent-api.secret:/run/downloads/qbittorrent-api.secret:ro",
"${dir:state}/jackett-api.json:/run/downloads/jackett-api.json:ro",
"${dir:state}/jackett-api.secret:/run/downloads/jackett-api.secret:ro",
"${dir:config}:/var/lib/sonarr/config:ro",
"${dir:downloads-memory}:/var/lib/downloads"
"type": "process",
"name": "sonarr-downloads",
"artifact": "code",
"run": [
"node",
"downloads/index.js"
],
"run-once": true,
"env": {
"MESH_DOWNLOADS_APP": "sonarr",
"MESH_DOWNLOADS_API": "v3",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8989}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/sonarr/config/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json"
"MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
},
"args": [
"run",
"/app/modules/sonarr/dist/downloads/index.js"
],
"restart-on": [
"downloads-config",
"bound-nzbget-api",
@@ -202,8 +163,7 @@
"secret-qbittorrent-api",
"bound-jackett-api",
"secret-jackett-api"
],
"artifact": "runtime"
]
}
],
"requires": [
@@ -233,23 +193,27 @@
"plex-api": "${dir:state}/plex-api.secret"
},
"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",
"downloads/index.js"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_SONARR_URL": "http://127.0.0.1:${port:8989}",
"MESH_SONARR_CONFIG_DIR": "${dir:config}",
"MESH_SONARR_API_KEY_FILE": "${dir:mesh-state}/api-key",
"MESH_PLEX_BIND_FILE": "${dir:state}/plex-api.json",
"MESH_PLEX_TOKEN_FILE": "${dir:state}/plex-api.secret"
}
}
]
}
-27
View File
@@ -1,27 +0,0 @@
# tautulli's runtime: the tool runtime, carrying this module's compiled code.
#
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
# the base images, published like any other artifact — which is what makes this buildable by the
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
# happens to have the siblings.
#
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
ARG BUILD_BASE
ARG RUNTIME_BASE
FROM ${BUILD_BASE} AS build
WORKDIR /app/modules/tautulli
COPY . .
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts tools/index.ts plex/check.ts plex/index.ts \
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
FROM ${RUNTIME_BASE}
COPY --from=build /app/modules/tautulli/dist /app/modules/tautulli/dist
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
# the convention novox/hq issues 060/061 settled.
ENV MESH_TOOL_MODULES=/app/modules/tautulli/dist/index.js,/app/modules/tautulli/dist/tools/index.js
# NOT dist/plex/index.js: that is a step the host runs to completion, named by the `plex`
# container's args as `mesh-tools run …` (novox/hq ADR 0052). Listed here it would run inside the
# serving sidecar too, and exit it.
+27 -55
View File
@@ -4,9 +4,6 @@
"emits": [
"watch.recorded"
],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"capabilities": [
"container-runtime"
],
@@ -96,53 +93,26 @@
"content": "{}\n",
"merge": "json"
},
{
"id": "runtime",
"type": "container",
"name": "mesh-tautulli",
"network": "host",
"volumes": [
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
"${dir:mesh-state}/config.json:/run/config/config.json:ro",
"${dir:config}:/var/lib/tautulli/config:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_TAUTULLI_URL": "http://127.0.0.1:${port:8181}",
"MESH_TAUTULLI_CONFIG_FILE": "/run/config/config.json",
"MESH_TAUTULLI_CONFIG_DIR": "/var/lib/tautulli/config"
},
"restart-on": [
"runtime-config"
],
"artifact": "runtime"
},
{
"id": "plex",
"type": "container",
"name": "mesh-tautulli-plex",
"network": "host",
"run-once": true,
"volumes": [
"${dir:state}/plex-api.json:/run/plex/plex-api.json:ro",
"${dir:state}/plex-api.secret:/run/plex/plex-api.secret:ro",
"${dir:config}:/var/lib/tautulli/config:ro"
"type": "process",
"name": "tautulli-plex",
"artifact": "code",
"run": [
"node",
"plex/index.js"
],
"run-once": true,
"env": {
"MESH_TAUTULLI_URL": "http://127.0.0.1:${port:8181}",
"MESH_TAUTULLI_CONFIG_DIR": "/var/lib/tautulli/config",
"MESH_PLEX_DIR": "/run/plex"
"MESH_TAUTULLI_CONFIG_DIR": "${dir:config}",
"MESH_PLEX_DIR": "${dir:state}"
},
"args": [
"run",
"/app/modules/tautulli/dist/plex/index.js"
],
"restart-on": [
"server",
"bound-plex-api",
"secret-plex-api"
],
"artifact": "runtime"
]
}
],
"requires": [
@@ -163,23 +133,25 @@
"plex-api": "${dir:state}/plex-api.secret"
},
"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",
"plex/index.js"
],
"loads": [
"index.js",
"tools/index.js"
],
"env": {
"MESH_TAUTULLI_URL": "http://127.0.0.1:${port:8181}",
"MESH_TAUTULLI_CONFIG_FILE": "${dir:mesh-state}/config.json",
"MESH_TAUTULLI_CONFIG_DIR": "${dir:config}"
}
}
]
}