wip: app-deploy dependency tracking (incomplete — needs endpoint wiring)
Half-finished feature for declaring and resolving app dependencies
when deploying. Preserved here for later finishing.
What's done:
- app-templates.js: dependsOn declarations on 7 templates
(sonarr, radarr, lidarr, readarr, bazarr, overseerr, tautulli).
- routes/apps/deploy.js: helper functions checkDependencies(),
topologicalSortTemplates(), buildDefaultDepConfig().
- routes/recipes/deploy.js: wait-for-health between recipe components
via appsHelpers.waitForHealthCheck() (verify export exists).
- status/js/app-selector.js: dependency-warning modal injected into
app-selector flow, with a "deploy with deps" checkbox.
What's missing (blockers for merge):
- POST /api/v1/apps/check-dependencies endpoint — frontend calls it
(app-selector.js around line 395) but the route is never registered.
Helper functions exist; just need to expose them. Frontend currently
404s and falls back to plain deploy (line 401), so the dep-aware
flow is non-functional.
- Auto-deploy-with-dependencies handler in the modal — checkbox
exists but nothing wires the "yes deploy them" choice into actually
deploying the listed dependencies before the target app.
- No tests around topological sort behaviour (circular deps,
diamond deps, missing deps).
Lifted out of wip/cloud-backups-and-history when the cloud-backups +
resource-history features were merged to main (commit d81d118).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,9 @@ module.exports = function({ docker, credentialManager: _credentialManager, servi
|
||||
const deployedComponents = [];
|
||||
const errors = [];
|
||||
|
||||
// Lazy-load apps helpers once for health waits between components
|
||||
const appsHelpers = require('../apps/helpers')(ctx);
|
||||
|
||||
try {
|
||||
for (const component of componentsToDeploy) {
|
||||
try {
|
||||
@@ -84,6 +87,18 @@ module.exports = function({ docker, credentialManager: _credentialManager, servi
|
||||
log.info('recipe', `Component deployed: ${component.id}`, {
|
||||
containerId: result.containerId?.substring(0, 12)
|
||||
});
|
||||
|
||||
// Wait for the component to become healthy before deploying the next one
|
||||
// (so dependent components — Sonarr after Prowlarr — see a working dep)
|
||||
if (result.containerId && result.healthPort) {
|
||||
try {
|
||||
await appsHelpers.waitForHealthCheck(result.containerId, result.healthPath, result.healthPort);
|
||||
log.info('recipe', `Component healthy: ${component.id}`);
|
||||
} catch (healthErr) {
|
||||
log.warn('recipe', `Component health wait failed: ${component.id}`, { error: healthErr.message });
|
||||
// Don't abort the recipe — continue with the next component
|
||||
}
|
||||
}
|
||||
} catch (componentError) {
|
||||
log.error('recipe', `Component failed: ${component.id}`, {
|
||||
error: componentError.message
|
||||
@@ -349,6 +364,17 @@ module.exports = function({ docker, credentialManager: _credentialManager, servi
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve health check path + port for the waiter
|
||||
let healthPath = null;
|
||||
let healthPort = null;
|
||||
if (component.templateRef) {
|
||||
const tpl = ctx.APP_TEMPLATES[component.templateRef];
|
||||
healthPath = tpl?.healthCheck || null;
|
||||
healthPort = port || tpl?.defaultPort || null;
|
||||
} else if (dockerConfig.ports?.length > 0) {
|
||||
healthPort = port || dockerConfig.ports[0].split(/[:/]/)[0];
|
||||
}
|
||||
|
||||
return {
|
||||
id: component.id,
|
||||
role: component.role,
|
||||
@@ -358,7 +384,9 @@ module.exports = function({ docker, credentialManager: _credentialManager, servi
|
||||
internal: component.internal || false,
|
||||
templateRef: component.templateRef,
|
||||
logo,
|
||||
url
|
||||
url,
|
||||
healthPath,
|
||||
healthPort
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user