Skip to content

[DockerComposeConfigurator] Fix duplicate top-level key on reconfigure#1090

Merged
nicolas-grekas merged 1 commit into
symfony:2.xfrom
mistyfiky:fix/docker-compose-duplicate-top-level-key
May 29, 2026
Merged

[DockerComposeConfigurator] Fix duplicate top-level key on reconfigure#1090
nicolas-grekas merged 1 commit into
symfony:2.xfrom
mistyfiky:fix/docker-compose-duplicate-top-level-key

Conversation

@mistyfiky

Copy link
Copy Markdown
Contributor
Q A
Bug fix? yes
New feature? no
Deprecations? no
Issues
License MIT

Problem

composer recipes:install --reset --force (or any re-invocation of configure against an already-marked compose.yaml) can produce a duplicate top-level key:

###< some/recipe ###

volumes:
volumes:
###> other/recipe ###
  some-data:
###< other/recipe ###

docker compose config then rejects the file with mapping key "volumes" already defined.

Triggered when a recipe touches a non-last top-level section (e.g. symfony/mailer contributing only services: on a file that already has volumes:) and that recipe's markers are already present.

Root cause

Two interacting bugs in DockerComposeConfigurator::configureDockerCompose. Either alone is harmless; the duplicate top-level key requires both.

$nodesLines accounting. The parser appends each line to $nodesLines[$node] before checking whether the line itself starts a new top-level key. So when the loop reaches volumes:, that header is recorded under the outgoing services node. When services is later updated and spliced back, the replacement carries a trailing volumes:; the original volumes: header sits one index past the splice range and survives — duplicate.

Position-adjustment math. After the in-place splice, $startAt/$endAt for later sections are adjusted by -$length - 1 and -$length respectively. The correct shift is -($length - 1).

Why they mask each other. Fix only the accounting bug and the next section's splice (with the off-by-two startAt) deletes that section's header. Fix only the math and the leftover key compounds — three consecutive volumes: lines. Together, the splice range matches what $nodesLines[old node] mirrors, and subsequent sections land at correct positions.

Fix

  1. On node transition, unset $nodesLines[$node][$i] (plus [$i - 1] if the previous line was blank) so recorded content matches the splice range [startAt..endAt-1].
  2. Use $shift = $length - 1 for both $startAt and $endAt adjustments.

Test

testReconfigureDoesNotDuplicateLaterTopLevelKey — fails on 2.x, passes here.

@mistyfiky mistyfiky force-pushed the fix/docker-compose-duplicate-top-level-key branch 3 times, most recently from 4c18fc2 to ec6489a Compare May 18, 2026 21:29
@nicolas-grekas nicolas-grekas force-pushed the fix/docker-compose-duplicate-top-level-key branch from ec6489a to 3446c0f Compare May 29, 2026 17:25
@nicolas-grekas

Copy link
Copy Markdown
Member

Thank you @mistyfiky.

@nicolas-grekas nicolas-grekas merged commit 4a6d98e into symfony:2.x May 29, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants