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": [ "emits": [
"subtitle.downloaded" "subtitle.downloaded"
], ],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"listens": [ "listens": [
{ {
"name": "web", "name": "web",
@@ -42,12 +39,6 @@
} }
], ],
"resources": [ "resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{ {
"id": "state", "id": "state",
"type": "directory", "type": "directory",
@@ -100,56 +91,27 @@
"content": "{}\n", "content": "{}\n",
"merge": "json" "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", "id": "servarr",
"type": "container", "type": "process",
"name": "mesh-bazarr-servarr", "name": "bazarr-servarr",
"network": "host", "artifact": "code",
"run-once": true, "run": [
"volumes": [ "node",
"${dir:config}:/var/lib/bazarr/config:ro", "servarr/index.js"
"${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"
], ],
"run-once": true,
"env": { "env": {
"MESH_BAZARR_URL": "http://127.0.0.1:${port:6767}", "MESH_BAZARR_URL": "http://127.0.0.1:${port:6767}",
"MESH_BAZARR_CONFIG_DIR": "/var/lib/bazarr/config", "MESH_BAZARR_CONFIG_DIR": "${dir:config}",
"MESH_SERVARR_DIR": "/run/servarr" "MESH_SERVARR_DIR": "${dir:state}"
}, },
"args": [
"run",
"/app/modules/bazarr/dist/servarr/index.js"
],
"restart-on": [ "restart-on": [
"bound-sonarr-api", "bound-sonarr-api",
"secret-sonarr-api", "secret-sonarr-api",
"bound-radarr-api", "bound-radarr-api",
"secret-radarr-api" "secret-radarr-api"
], ]
"artifact": "runtime"
} }
], ],
"requires": [ "requires": [
@@ -173,23 +135,25 @@
"radarr-api": "${dir:state}/radarr-api.secret" "radarr-api": "${dir:state}/radarr-api.secret"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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" "download.completed"
], ],
"consumes": [], "consumes": [],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"listens": [ "listens": [
{ {
"name": "web", "name": "web",
@@ -35,12 +32,6 @@
} }
], ],
"resources": [ "resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{ {
"id": "state", "id": "state",
"type": "directory", "type": "directory",
@@ -83,22 +74,6 @@
"identity" "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", "id": "downloads-config",
"type": "file", "type": "file",
@@ -114,34 +89,23 @@
}, },
{ {
"id": "downloads", "id": "downloads",
"type": "container", "type": "process",
"name": "mesh-bookshelf-downloads", "name": "bookshelf-downloads",
"network": "host", "artifact": "code",
"run-once": true, "run": [
"volumes": [ "node",
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro", "downloads/index.js"
"${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"
], ],
"run-once": true,
"env": { "env": {
"MESH_DOWNLOADS_APP": "bookshelf", "MESH_DOWNLOADS_APP": "bookshelf",
"MESH_DOWNLOADS_API": "v1", "MESH_DOWNLOADS_API": "v1",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8787}", "MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8787}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/bookshelf/config/config.xml", "MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads", "MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json", "MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json" "MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
}, },
"args": [
"run",
"/app/modules/bookshelf/dist/downloads/index.js"
],
"restart-on": [ "restart-on": [
"downloads-config", "downloads-config",
"bound-nzbget-api", "bound-nzbget-api",
@@ -150,8 +114,7 @@
"secret-qbittorrent-api", "secret-qbittorrent-api",
"bound-jackett-api", "bound-jackett-api",
"secret-jackett-api" "secret-jackett-api"
], ]
"artifact": "runtime"
} }
], ],
"requires": [ "requires": [
@@ -178,23 +141,24 @@
"jackett-api": "${dir:state}/jackett-api.secret" "jackett-api": "${dir:state}/jackett-api.secret"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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", "mode": "0600",
"content": "{}\n", "content": "{}\n",
"merge": "json" "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": { "own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": { "api-key": {
"path": "${dir:mesh-state}/api-key", "path": "${dir:mesh-state}/api-key",
"taken": "at-start" "taken": "at-start"
@@ -145,23 +120,23 @@
"route": "${dir:state}/route.json" "route": "${dir:state}/route.json"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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" "plex-api"
], ],
"own-secrets": { "own-secrets": {
"tmdb": "${dir:state}/tmdb.secret", "tmdb": "${dir:state}/tmdb.secret"
"broker": "${dir:mesh-state}/broker"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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" "plex-api": "${dir:state}/plex-api.secret"
}, },
"resources": [ "resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{ {
"id": "state", "id": "state",
"type": "directory", "type": "directory",
@@ -96,27 +87,6 @@
"mode": "0600", "mode": "0600",
"content": "{}\n", "content": "{}\n",
"merge": "json" "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": [], "consumes": [],
"own-secrets": { "own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": { "api-key": {
"path": "${dir:mesh-state}/api-key", "path": "${dir:mesh-state}/api-key",
"taken": "at-start" "taken": "at-start"
@@ -124,33 +123,6 @@
"api-key-init" "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", "id": "downloads-config",
"type": "file", "type": "file",
@@ -166,34 +138,23 @@
}, },
{ {
"id": "downloads", "id": "downloads",
"type": "container", "type": "process",
"name": "mesh-lidarr-downloads", "name": "lidarr-downloads",
"network": "host", "artifact": "code",
"run-once": true, "run": [
"volumes": [ "node",
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro", "downloads/index.js"
"${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"
], ],
"run-once": true,
"env": { "env": {
"MESH_DOWNLOADS_APP": "lidarr", "MESH_DOWNLOADS_APP": "lidarr",
"MESH_DOWNLOADS_API": "v1", "MESH_DOWNLOADS_API": "v1",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8686}", "MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8686}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/lidarr/config/config.xml", "MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads", "MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json", "MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json" "MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
}, },
"args": [
"run",
"/app/modules/lidarr/dist/downloads/index.js"
],
"restart-on": [ "restart-on": [
"downloads-config", "downloads-config",
"bound-nzbget-api", "bound-nzbget-api",
@@ -202,8 +163,7 @@
"secret-qbittorrent-api", "secret-qbittorrent-api",
"bound-jackett-api", "bound-jackett-api",
"secret-jackett-api" "secret-jackett-api"
], ]
"artifact": "runtime"
} }
], ],
"requires": [ "requires": [
@@ -233,23 +193,27 @@
"plex-api": "${dir:state}/plex-api.secret" "plex-api": "${dir:state}/plex-api.secret"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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": [], "consumes": [],
"own-secrets": { "own-secrets": {
"broker": "${dir:mesh-state}/broker",
"password": { "password": {
"path": "${dir:mesh-state}/password", "path": "${dir:mesh-state}/password",
"taken": "at-start" "taken": "at-start"
@@ -99,30 +98,6 @@
"mode": "0600", "mode": "0600",
"content": "{}\n", "content": "{}\n",
"merge": "json" "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": [ "provides": [
@@ -154,23 +129,25 @@
"route": "${dir:state}/route.json" "route": "${dir:state}/route.json"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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" "request.approved"
], ],
"own-secrets": { "own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": "${dir:mesh-state}/api-key" "api-key": "${dir:mesh-state}/api-key"
}, },
"listens": [ "listens": [
@@ -76,53 +75,21 @@
"content": "{}\n", "content": "{}\n",
"merge": "json" "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", "id": "connections",
"type": "container", "type": "process",
"name": "mesh-ombi-connections", "name": "ombi-connections",
"network": "host", "artifact": "code",
"run-once": true, "run": [
"volumes": [ "node",
"${dir:mesh-state}/api-key:/run/secrets/api-key:ro", "connections/index.js"
"${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"
], ],
"run-once": true,
"env": { "env": {
"MESH_OMBI_URL": "http://127.0.0.1:${port:3579}", "MESH_OMBI_URL": "http://127.0.0.1:${port:3579}",
"MESH_OMBI_API_KEY_FILE": "/run/secrets/api-key", "MESH_OMBI_API_KEY_FILE": "${dir:mesh-state}/api-key",
"MESH_CONNECTIONS_DIR": "/run/connections" "MESH_CONNECTIONS_DIR": "${dir:state}"
}, },
"args": [
"run",
"/app/modules/ombi/dist/connections/index.js"
],
"restart-on": [ "restart-on": [
"bound-sonarr-api", "bound-sonarr-api",
"secret-sonarr-api", "secret-sonarr-api",
@@ -132,8 +99,7 @@
"secret-lidarr-api", "secret-lidarr-api",
"bound-plex-api", "bound-plex-api",
"secret-plex-api" "secret-plex-api"
], ]
"artifact": "runtime"
} }
], ],
"requires": [ "requires": [
@@ -163,23 +129,25 @@
"plex-api": "${dir:state}/plex-api.secret" "plex-api": "${dir:state}/plex-api.secret"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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 { static fromEnv(env: NodeJS.ProcessEnv = process.env): PlexClient {
const url = env.MESH_PLEX_URL ?? `http://127.0.0.1:${env.PLEX_PORT ?? "32400"}`; 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 // 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. // 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 // 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. // 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"); if (!token) throw new Error("no Plex token — set MESH_PLEX_TOKEN or make the data dir readable");
return new PlexClient(url, token); return new PlexClient(url, token);
} }
/** Discover the token from the server's Preferences.xml, falling back to null. */ /** Discover the token from the server's Preferences.xml under its config directory, falling back to null. */
static detectToken(dataDir: string): string | null { static detectToken(configDir: string): string | null {
const prefs = join(dataDir, "config", "Library", "Application Support", "Plex Media Server", "Preferences.xml"); const prefs = join(configDir, "Library", "Application Support", "Plex Media Server", "Preferences.xml");
if (existsSync(prefs)) { if (existsSync(prefs)) {
const match = readFileSync(prefs, "utf8").match(/PlexOnlineToken="([^"]+)"/); const match = readFileSync(prefs, "utf8").match(/PlexOnlineToken="([^"]+)"/);
if (match) return match[1]; if (match) return match[1];
+15 -40
View File
@@ -24,9 +24,6 @@
"consumes": [ "consumes": [
"*.download.completed" "*.download.completed"
], ],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"listens": [ "listens": [
{ {
"name": "stream", "name": "stream",
@@ -107,12 +104,6 @@
} }
], ],
"resources": [ "resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{ {
"id": "state", "id": "state",
"type": "directory", "type": "directory",
@@ -180,22 +171,6 @@
"restart-on": [ "restart-on": [
"identity" "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": [ "requires": [
@@ -211,23 +186,23 @@
"route": "${dir:state}/route.json" "route": "${dir:state}/route.json"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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": [], "consumes": [],
"own-secrets": { "own-secrets": {
"broker": "${dir:mesh-state}/broker",
"password": { "password": {
"path": "${dir:mesh-state}/password", "path": "${dir:mesh-state}/password",
"taken": "at-start" "taken": "at-start"
@@ -118,30 +117,6 @@
"mode": "0600", "mode": "0600",
"content": "{}\n", "content": "{}\n",
"merge": "json" "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": [ "provides": [
@@ -173,23 +148,25 @@
"route": "${dir:state}/route.json" "route": "${dir:state}/route.json"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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": [], "consumes": [],
"own-secrets": { "own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": { "api-key": {
"path": "${dir:mesh-state}/api-key", "path": "${dir:mesh-state}/api-key",
"taken": "at-start" "taken": "at-start"
@@ -118,33 +117,6 @@
"api-key-init" "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", "id": "downloads-config",
"type": "file", "type": "file",
@@ -160,34 +132,23 @@
}, },
{ {
"id": "downloads", "id": "downloads",
"type": "container", "type": "process",
"name": "mesh-radarr-downloads", "name": "radarr-downloads",
"network": "host", "artifact": "code",
"run-once": true, "run": [
"volumes": [ "node",
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro", "downloads/index.js"
"${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"
], ],
"run-once": true,
"env": { "env": {
"MESH_DOWNLOADS_APP": "radarr", "MESH_DOWNLOADS_APP": "radarr",
"MESH_DOWNLOADS_API": "v3", "MESH_DOWNLOADS_API": "v3",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:7878}", "MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:7878}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/radarr/config/config.xml", "MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads", "MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json", "MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json" "MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
}, },
"args": [
"run",
"/app/modules/radarr/dist/downloads/index.js"
],
"restart-on": [ "restart-on": [
"downloads-config", "downloads-config",
"bound-nzbget-api", "bound-nzbget-api",
@@ -196,8 +157,7 @@
"secret-qbittorrent-api", "secret-qbittorrent-api",
"bound-jackett-api", "bound-jackett-api",
"secret-jackett-api" "secret-jackett-api"
], ]
"artifact": "runtime"
} }
], ],
"requires": [ "requires": [
@@ -227,23 +187,27 @@
"plex-api": "${dir:state}/plex-api.secret" "plex-api": "${dir:state}/plex-api.secret"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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": [], "consumes": [],
"own-secrets": { "own-secrets": {
"broker": "${dir:mesh-state}/broker",
"api-key": { "api-key": {
"path": "${dir:mesh-state}/api-key", "path": "${dir:mesh-state}/api-key",
"taken": "at-start" "taken": "at-start"
@@ -124,33 +123,6 @@
"api-key-init" "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", "id": "downloads-config",
"type": "file", "type": "file",
@@ -166,34 +138,23 @@
}, },
{ {
"id": "downloads", "id": "downloads",
"type": "container", "type": "process",
"name": "mesh-sonarr-downloads", "name": "sonarr-downloads",
"network": "host", "artifact": "code",
"run-once": true, "run": [
"volumes": [ "node",
"${dir:state}/downloads.json:/run/downloads/downloads.json:ro", "downloads/index.js"
"${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"
], ],
"run-once": true,
"env": { "env": {
"MESH_DOWNLOADS_APP": "sonarr", "MESH_DOWNLOADS_APP": "sonarr",
"MESH_DOWNLOADS_API": "v3", "MESH_DOWNLOADS_API": "v3",
"MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8989}", "MESH_DOWNLOADS_APP_URL": "http://127.0.0.1:${port:8989}",
"MESH_DOWNLOADS_APP_CONFIG": "/var/lib/sonarr/config/config.xml", "MESH_DOWNLOADS_APP_CONFIG": "${dir:config}/config.xml",
"MESH_DOWNLOADS_DIR": "/run/downloads", "MESH_DOWNLOADS_DIR": "${dir:state}",
"MESH_DOWNLOADS_SETTINGS": "/run/downloads/downloads.json", "MESH_DOWNLOADS_SETTINGS": "${dir:state}/downloads.json",
"MESH_DOWNLOADS_MEMORY": "/var/lib/downloads/memory.json" "MESH_DOWNLOADS_MEMORY": "${dir:downloads-memory}/memory.json"
}, },
"args": [
"run",
"/app/modules/sonarr/dist/downloads/index.js"
],
"restart-on": [ "restart-on": [
"downloads-config", "downloads-config",
"bound-nzbget-api", "bound-nzbget-api",
@@ -202,8 +163,7 @@
"secret-qbittorrent-api", "secret-qbittorrent-api",
"bound-jackett-api", "bound-jackett-api",
"secret-jackett-api" "secret-jackett-api"
], ]
"artifact": "runtime"
} }
], ],
"requires": [ "requires": [
@@ -233,23 +193,27 @@
"plex-api": "${dir:state}/plex-api.secret" "plex-api": "${dir:state}/plex-api.secret"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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": [ "emits": [
"watch.recorded" "watch.recorded"
], ],
"own-secrets": {
"broker": "${dir:mesh-state}/broker"
},
"capabilities": [ "capabilities": [
"container-runtime" "container-runtime"
], ],
@@ -96,53 +93,26 @@
"content": "{}\n", "content": "{}\n",
"merge": "json" "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", "id": "plex",
"type": "container", "type": "process",
"name": "mesh-tautulli-plex", "name": "tautulli-plex",
"network": "host", "artifact": "code",
"run-once": true, "run": [
"volumes": [ "node",
"${dir:state}/plex-api.json:/run/plex/plex-api.json:ro", "plex/index.js"
"${dir:state}/plex-api.secret:/run/plex/plex-api.secret:ro",
"${dir:config}:/var/lib/tautulli/config:ro"
], ],
"run-once": true,
"env": { "env": {
"MESH_TAUTULLI_URL": "http://127.0.0.1:${port:8181}", "MESH_TAUTULLI_URL": "http://127.0.0.1:${port:8181}",
"MESH_TAUTULLI_CONFIG_DIR": "/var/lib/tautulli/config", "MESH_TAUTULLI_CONFIG_DIR": "${dir:config}",
"MESH_PLEX_DIR": "/run/plex" "MESH_PLEX_DIR": "${dir:state}"
}, },
"args": [
"run",
"/app/modules/tautulli/dist/plex/index.js"
],
"restart-on": [ "restart-on": [
"server", "server",
"bound-plex-api", "bound-plex-api",
"secret-plex-api" "secret-plex-api"
], ]
"artifact": "runtime"
} }
], ],
"requires": [ "requires": [
@@ -163,23 +133,25 @@
"plex-api": "${dir:state}/plex-api.secret" "plex-api": "${dir:state}/plex-api.secret"
}, },
"build": { "build": {
"on": [
{
"arg": "BUILD_BASE",
"module": "mesh-tools",
"artifact": "build"
},
{
"arg": "RUNTIME_BASE",
"module": "mesh-tools",
"artifact": "runtime"
}
],
"artifacts": [ "artifacts": [
{ {
"name": "runtime", "name": "code",
"kind": "image", "kind": "bundle",
"from": "Dockerfile" "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}"
}
} }
] ]
} }