fix: [#382] guard all service networks blocks in docker-compose template#384
Merged
josecelano merged 5 commits intomainfrom Feb 24, 2026
Merged
Conversation
Fixes #382. Any service with an empty networks list would render an invalid empty `networks:` key, which Docker Compose rejects with: services.<name>.networks must be a list The backup service already had the correct guard. This commit applies the same defensive pattern to every remaining service block. Changes in templates/docker-compose/docker-compose.yml.tera: - tracker: add `{%- if tracker.networks | length > 0 %}` guard - caddy: add `{%- if caddy.networks | length > 0 %}` guard - prometheus: add `{%- if prometheus.networks | length > 0 %}` guard - grafana: add `{%- if grafana.networks | length > 0 %}` guard - mysql: add `{%- if mysql.networks | length > 0 %}` guard All five service blocks now follow the same pattern as the pre-existing backup service guard. The top-level `networks:` section was already wrapped in `{%- if required_networks | length > 0 %}` and is unchanged. Changes in template.rs: - it_should_not_render_empty_networks_key_for_tracker_when_no_optional_services_are_configured Verifies a minimal config (SQLite, no Caddy/Prometheus/MySQL) produces no empty `networks:` key for the tracker service. - it_should_render_networks_key_for_tracker_when_prometheus_is_enabled Verifies `networks:` is present and contains `metrics_network` when Prometheus is enabled.
Mark completed tasks: - Phase 1: all service networks blocks guarded (tracker, caddy, prometheus, grafana, mysql) - Phase 2: two unit tests added for minimal-config and Prometheus-enabled rendering scenarios - Phase 4: no template doc changes needed Update the affected-services table to show all services are now guarded. Phase 3 (docker compose config --quiet validation at configure time) remains open.
…ring
Add local_validator.rs with validate_docker_compose_file() which runs
`docker compose config --quiet` on the build directory immediately after
the docker-compose.yml template is rendered.
- New module: src/infrastructure/templating/docker_compose/local_validator.rs
- validate_docker_compose_file(compose_dir: &Path) -> Result<()>
- DockerComposeLocalValidationError with CommandExecutionFailed and
InvalidDockerComposeFile variants, each with a help() message
- 4 unit tests: valid file passes, empty networks key fails,
help messages are non-empty
- DockerComposeProjectGeneratorError gains DockerComposeValidationFailed
variant that wraps DockerComposeLocalValidationError
- validate_docker_compose_file() called in DockerComposeProjectGenerator::render()
after both env and docker-compose.yml are written, before returning the build path
This provides fast-fail detection at configure time rather than waiting for
Docker to reject the file at run time on the remote VM.
Document the decision to call validate_docker_compose_file() inside DockerComposeProjectGenerator::render() (infrastructure layer) rather than in the application service or step layers. Key rationale: - The generator is the earliest point where the complete artifact (both docker-compose.yml and .env) exists, making validation accurate - Keeps process-spawning (docker CLI) in the infrastructure layer, preserving DDD boundaries - DockerComposeRenderer::render() alone is not sufficient because .env is not yet written at that point
…tion Ran the full workflow (create → provision → configure → release → run → test) with a minimal config (SQLite, no domains, no Prometheus) — the exact configuration that previously failed with: services.tracker.networks must be a list All steps completed successfully. The 'run' command now works with this configuration. All goals and acceptance criteria are now complete. Also added: - Reference to the new ADR (docker-compose-local-validation-placement.md) in the Related Documentation section - Reference to local_validator.rs in the Related Documentation section
Member
Author
|
ACK d75b7cd |
josecelano
added a commit
that referenced
this pull request
Feb 24, 2026
PR #384 has been merged to main. The issue tracking file is no longer needed.
josecelano
added a commit
that referenced
this pull request
Mar 4, 2026
… not in PATH) The local docker-compose.yml validator added in PR #384 runs 'docker compose config --quiet' on the host. When the deployer is invoked via its Docker container (the standard usage), the docker binary is not installed inside the container and the command fails with ENOENT, aborting the release. Documents: - Root cause: local validator assumes docker is in PATH, but the deployer container has no docker binary installed - Fix applied: handle ErrorKind::NotFound gracefully (skip with warning) - State recovery: how environment.json was manually reset from ReleaseFailed to Configured so release could be retried Also adds 'ENOENT' to project-words.txt. Refs #405
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #382 — the
docker-compose.yml.teratemplate rendered an invalid emptynetworks:key for the tracker service (and other services) when no optional services were enabled, causingdocker compose upto fail with:This failure only surfaced at
runtime on the remote VM — after the fullcreate → provision → configure → releaseworkflow had already completed.Changes
Fix (Phase 1)
Guard all service
networks:blocks with{%- if <service>.networks | length > 0 %}, matching the pre-existingbackupservice pattern. Affected services:tracker,caddy,prometheus,grafana,mysql.Tests (Phase 2)
Added unit tests in
template.rsandlocal_validator.rs:it_should_not_render_empty_networks_key_for_tracker_when_no_optional_services_are_configuredit_should_render_networks_key_for_tracker_when_prometheus_is_enabledvalidate_docker_compose_file()(valid file passes, emptynetworks:key fails)Local validation (Phase 3)
New module
src/infrastructure/templating/docker_compose/local_validator.rs— runsdocker compose config --quieton the build directory immediately after bothdocker-compose.ymland.envare rendered. Any structural error in the generated file now fails fast atconfiguretime with a clear, actionable error message.The validator is called inside
DockerComposeProjectGenerator::render()— the earliest point where the complete artifact exists, and still within the infrastructure layer (keeping the application layer free of process-spawning logic). This decision is documented in a new ADR.Documentation (Phase 4)
docs/decisions/docker-compose-local-validation-placement.mdVerification
Full E2E workflow run with a minimal config (SQLite, no domains, no Prometheus — the exact reproduction case from the issue):
All steps passed. The
runcommand no longer fails with the previously broken configuration.Pre-commit checks pass:
./scripts/pre-commit.sh✅All linters pass:
cargo run --bin linter all✅2353 unit tests pass ✅