mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
fix(dependency-map): stop flagging env-var bind mounts as missing volumes (#1468)
* fix(dependency-map): stop flagging env-var bind mounts as missing volumes
The Fleet Map "Missing dependencies" anomaly fired false positives for
services whose volumes use env-var-interpolated bind sources such as
${BACKUPS_PATH}:/backups. The compose parser classified the source as a
named volume because the ${VAR} token contains no slash, then the runtime
presence check found no matching volume and flagged it.
A compose named-volume key can never contain $, so any source with an env
var is a bind path whose value is unresolvable at parse time. Exclude it
from named-volume classification.
Closes #1464
* docs(dependency-map): clarify the env-var volume guard comment
Note that the $ check also covers the $$ literal-dollar Compose escape, and
state the named-volume key charset that makes the guard safe. No behavior change.
This commit is contained in:
@@ -37,6 +37,42 @@ describe('parseComposeDependencies - volumes', () => {
|
||||
const r = parseComposeDependencies('services:\n web:\n volumes:\n - type: volume\n source: data\n target: /d\n - type: bind\n source: /host\n target: /b\n');
|
||||
expect(r.services[0].volumes).toEqual(['data']);
|
||||
});
|
||||
|
||||
it('drops env-var-interpolated bind sources and keeps real named volumes', () => {
|
||||
const r = parseComposeDependencies(svc('web', 'volumes:\n - ${BACKUPS_PATH}:/backups\n - $LEGACY_PATH:/legacy\n - db_data:/var/lib'));
|
||||
expect(r.services[0].volumes).toEqual(['db_data']);
|
||||
});
|
||||
|
||||
it('drops a bind source with an embedded (non-leading) env var', () => {
|
||||
const r = parseComposeDependencies(svc('web', 'volumes:\n - prefix-${SUB}:/data'));
|
||||
expect(r.services[0].volumes).toEqual([]);
|
||||
});
|
||||
|
||||
it('passes a long-form type: volume env-var source through unchanged', () => {
|
||||
// The long-form path never calls isNamedVolumeSource, so its contract is
|
||||
// unaffected by the short-form env-var fix; this guards that.
|
||||
const r = parseComposeDependencies('services:\n web:\n volumes:\n - type: volume\n source: ${MY_VOLUME}\n target: /d\n');
|
||||
expect(r.services[0].volumes).toEqual(['${MY_VOLUME}']);
|
||||
});
|
||||
|
||||
it('does not flag env-var bind mounts as named volumes (issue #1464 repro)', () => {
|
||||
const r = parseComposeDependencies(
|
||||
'services:\n' +
|
||||
' core:\n' +
|
||||
' volumes:\n' +
|
||||
' - ${COMPOSE_KOMODO_BACKUPS_PATH}:/backups\n' +
|
||||
' - keys:/config/keys\n' +
|
||||
' periphery:\n' +
|
||||
' volumes:\n' +
|
||||
' - /var/run/docker.sock:/var/run/docker.sock\n' +
|
||||
' - ${PERIPHERY_ROOT_DIRECTORY}:/root\n' +
|
||||
' - ${PERIPHERY_STACK_DIR}:/stack\n',
|
||||
);
|
||||
const core = r.services.find((s) => s.name === 'core');
|
||||
const periphery = r.services.find((s) => s.name === 'periphery');
|
||||
expect(core?.volumes).toEqual(['keys']);
|
||||
expect(periphery?.volumes).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseComposeDependencies - ports', () => {
|
||||
|
||||
@@ -66,6 +66,9 @@ function collectKeys(value: unknown): string[] {
|
||||
/** True when a volume source is a named volume (not a bind mount or anonymous). */
|
||||
function isNamedVolumeSource(source: string): boolean {
|
||||
if (!source) return false;
|
||||
// A named-volume key is [a-zA-Z0-9._-]+, so any '$' marks an env-var bind path
|
||||
// (or the '$$' literal-dollar escape) whose value is unresolvable at parse time.
|
||||
if (source.includes('$')) return false;
|
||||
if (source.includes('/')) return false; // bind mount path
|
||||
if (source.startsWith('.') || source.startsWith('~')) return false;
|
||||
if (/^[a-zA-Z]:[\\/]/.test(source)) return false; // Windows drive path
|
||||
|
||||
Reference in New Issue
Block a user