Skip to content

Commit e5850b2

Browse files
Add persistDappmanagerSettings function to preserve critical env vars during updates
1 parent a2c45c7 commit e5850b2

File tree

1 file changed

+83
-2
lines changed

1 file changed

+83
-2
lines changed

packages/installer/src/installer/getInstallerPackageData.ts

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import deepmerge from "deepmerge";
22
import { orderInstallPackages } from "./orderInstallPackages.js";
3-
import { ComposeEditor, ComposeFileEditor } from "@dappnode/dockercompose";
3+
import {
4+
ComposeEditor,
5+
ComposeFileEditor,
6+
parseVolumeMappings,
7+
stringifyVolumeMappings
8+
} from "@dappnode/dockercompose";
49
import { getContainersStatus, listPackages } from "@dappnode/dockerapi";
510
import { parseTimeoutSeconds } from "../utils.js";
611
import {
@@ -12,9 +17,10 @@ import {
1217
NotificationsConfig,
1318
NotificationsSettingsAllDnps
1419
} from "@dappnode/types";
15-
import { getBackupPath, getDockerComposePath, getImagePath, getManifestPath } from "@dappnode/utils";
20+
import { getBackupPath, getDockerComposePath, getImagePath, getManifestPath, isNotFoundError } from "@dappnode/utils";
1621
import { gt } from "semver";
1722
import { logs } from "@dappnode/logger";
23+
import { params } from "@dappnode/params";
1824

1925
interface GetInstallerPackageDataArg {
2026
releases: PackageRelease[];
@@ -89,6 +95,9 @@ function getInstallerPackageData(
8995
const compose = new ComposeEditor(release.compose, { dnpName });
9096
compose.applyUserSettings(nextUserSet, { dnpName });
9197

98+
// Persist critical dappmanager env vars and volume paths across updates
99+
persistDappmanagerSettings(compose, dnpName, isCore);
100+
92101
const dockerTimeout = parseTimeoutSeconds(release.manifest.dockerTimeout);
93102

94103
return {
@@ -116,6 +125,78 @@ function getInstallerPackageData(
116125
};
117126
}
118127

128+
/**
129+
* When updating the dappmanager package, certain environment variables and volume
130+
* settings from the currently installed compose must be preserved, even if the new
131+
* compose being installed doesn't define them. This ensures:
132+
* - DISABLE_HOST_SCRIPTS is not lost during upgrades
133+
* - DAPPNODE_CORE_DIR is not lost during upgrades
134+
* - The DNCORE volume bind mount uses the correct host path from DAPPNODE_CORE_DIR
135+
*/
136+
function persistDappmanagerSettings(compose: ComposeEditor, dnpName: string, isCore: boolean): void {
137+
if (dnpName !== params.dappmanagerDnpName) return;
138+
139+
// Read the currently installed compose to get persisted env values
140+
let installedCompose: ComposeFileEditor;
141+
try {
142+
installedCompose = new ComposeFileEditor(dnpName, isCore);
143+
} catch (e) {
144+
if (!isNotFoundError(e)) throw e;
145+
// Fresh install - no existing compose to read from
146+
logs.info("No installed dappmanager compose found, skipping settings persistence");
147+
return;
148+
}
149+
150+
const envsToPreserve = ["DISABLE_HOST_SCRIPTS", "DAPPNODE_CORE_DIR"];
151+
const DNCORE_CONTAINER_PATH = "/usr/src/app/DNCORE";
152+
153+
// Collect env values from the installed compose services
154+
const installedEnvs: Record<string, string> = {};
155+
for (const serviceEditor of Object.values(installedCompose.services())) {
156+
const envs = serviceEditor.getEnvs();
157+
for (const envName of envsToPreserve) {
158+
if (envs[envName] !== undefined && envs[envName] !== "") {
159+
installedEnvs[envName] = envs[envName];
160+
}
161+
}
162+
}
163+
164+
if (Object.keys(installedEnvs).length === 0) return;
165+
166+
logs.info("Persisting dappmanager settings from installed compose", installedEnvs);
167+
168+
// Apply persisted envs and volume mapping to new compose services
169+
for (const serviceEditor of Object.values(compose.services())) {
170+
// Merge preserved envs into the new compose (installed values take priority)
171+
const envsToInject: Record<string, string> = {};
172+
for (const envName of envsToPreserve) {
173+
if (installedEnvs[envName] !== undefined) {
174+
envsToInject[envName] = installedEnvs[envName];
175+
}
176+
}
177+
if (Object.keys(envsToInject).length > 0) {
178+
serviceEditor.mergeEnvs(envsToInject);
179+
}
180+
181+
// Update the DNCORE volume host path to match DAPPNODE_CORE_DIR
182+
if (installedEnvs["DAPPNODE_CORE_DIR"]) {
183+
const dncoreHostDir = installedEnvs["DAPPNODE_CORE_DIR"];
184+
const service = serviceEditor.get();
185+
if (service.volumes) {
186+
const volumeMappings = parseVolumeMappings(service.volumes);
187+
const updatedVolumes = volumeMappings.map((vol) => {
188+
// Match the volume whose container side is /usr/src/app/DNCORE
189+
if (vol.container === DNCORE_CONTAINER_PATH) {
190+
return { ...vol, host: dncoreHostDir, name: undefined };
191+
}
192+
return vol;
193+
});
194+
service.volumes = stringifyVolumeMappings(updatedVolumes);
195+
}
196+
}
197+
}
198+
}
199+
119200
/**
120201
* Migrates the user settings from the old service name to the new service name
121202
*

0 commit comments

Comments
 (0)