Skip to content

Commit 76aace8

Browse files
authored
docs(adr): compress to 6 records in the domain-modeling format (#102)
Drop 0004, 0005, 0007, 0010, 0011, 0012, 0013 as separate records and fold what still holds into the survivors; merge 0003/0004/0005 into the shared-namespace record, 0006/0007 into the raw-dict seam record, 0008/0010/0013 into the owning-modules record, 0011/0012 into the parity record. Renumber the six survivors 0001-0006 in their original order and move every citation (CONTEXT.md, graph.py, values.py, three tests).
1 parent 1d90f64 commit 76aace8

24 files changed

Lines changed: 79 additions & 816 deletions

‎CONTEXT.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ A supported service key handled *outside* the service-key registry, listed in
2626
`project_dir` (`env_file`, `volumes`), spans keys, or occupies the image/command slot
2727
(`entrypoint`). Structural keys keep their own validate/emit machinery, in the module
2828
that owns the concern. Which side of this line a key falls on is a design ruling, not
29-
a convenience: see [ADR-0008](docs/adr/0008-reject-structural-key-registry.md) and
30-
[ADR-0013](docs/adr/0013-volumes-stays-hand-rolled.md).
29+
a convenience: see
30+
[ADR-0005](docs/adr/0005-structural-keys-and-schema-validators-stay-in-their-owning-modules.md).
3131

3232
**Token**:
3333
The result of rendering one Compose value into a `podman run`/`pod create` argument:
@@ -53,7 +53,7 @@ document-wide.
5353

5454
**Rule one / rule two**:
5555
The two directions of the Docker-rejection parity rule
56-
([ADR-0009](docs/adr/0009-docker-rejection-parity.md)). "Rule two" is named bare
56+
([ADR-0006](docs/adr/0006-docker-rejection-parity.md)). "Rule two" is named bare
5757
in `parsing.py` comments and in tests, with no restatement at the call site.
5858
**Rule one**: a document `docker compose config` rejects, compose2pod
5959
rejects too — hard, no exceptions. **Rule two**: a document Docker accepts,

‎compose2pod/graph.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def _depends_on_entry_condition(dep: str, spec: dict[str, Any]) -> str:
5858
# defaults to service_started -- that default is Docker's own and
5959
# stays. This one was compose2pod's own invention (`spec.get(...,
6060
# "service_started")`) and was a false green against the hard rule
61-
# in `docs/adr/0009-docker-rejection-parity.md`.
61+
# in `docs/adr/0006-docker-rejection-parity.md`.
6262
msg = f"depends_on entry {dep!r}: missing required key 'condition'"
6363
raise UnsupportedComposeError(msg)
6464
condition = spec["condition"]

‎compose2pod/values.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Value grammars: the shapes `docker compose config` accepts for a scalar key.
22
33
compose2pod refuses every document Docker refuses
4-
(`docs/adr/0009-docker-rejection-parity.md`), which means
4+
(`docs/adr/0006-docker-rejection-parity.md`), which means
55
matching Docker's *value* grammars, not just its types: `mem_limit: ""` and
66
`cpus: somevalue` are documents Docker will not run.
77

‎docs/adr/0001-healthcheck-start-period-retries-passthrough.md‎

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# The healthcheck wait budget is not coupled to `retries`
2+
3+
`run_flags` passes a healthcheck's `start_period` and `retries` through as `--health-start-period`
4+
and `--health-retries`, but the emitted script's `wait_healthy` loop keeps its fixed
5+
`HEALTHY_WAIT_BUDGET_SECONDS` rather than deriving `retries × interval`. `retries` counts the
6+
consecutive failures podman tolerates, not the time until the first check is meaningful, so a
7+
budget derived from it can expire before a long `start_period` has elapsed. The fixed 120 s errs
8+
toward slow starters at the cost of a slower failure signal; a service that needs more makes the
9+
budget configurable, not coupled.
Lines changed: 6 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,7 @@
1-
# Zero-dependency core, no compose-parser dependency
1+
# Zero-dependency core
22

3-
**Decision:** The `compose2pod` core package has zero runtime dependencies
4-
(stdlib only). PyYAML is shipped only behind the optional `[yaml]` extra.
5-
The core does not depend on any compose-spec parser library.
6-
7-
## Context
8-
9-
`compose2pod` reads a Docker Compose document and must decide how it is
10-
loaded (JSON/YAML) and validated against the supported subset. Two
11-
dependency questions came up while designing the package:
12-
13-
1. Should YAML parsing be a hard dependency, so the CLI always accepts
14-
`docker-compose.yml` directly?
15-
2. Should the core adopt an existing compose-spec parser library
16-
(`compose-spec`, `compose-pydantic`) instead of hand-rolled subset
17-
validation, to get broader spec coverage for free?
18-
19-
The primary differentiator for `compose2pod` is that it installs with no
20-
compiled wheels and runs in minimal Python images (the same CI containers
21-
that motivate the tool in the first place — see the design's "Why this
22-
exists"). Any hard dependency, especially one with a compiled wheel,
23-
undermines that.
24-
25-
## Decision & rationale
26-
27-
- **YAML stays optional.** JSON parsing via stdlib `json` is always
28-
available. YAML parsing via PyYAML is the optional `[yaml]` extra; without
29-
it, `--format yaml` errors with an actionable message pointing at `pip
30-
install compose2pod[yaml]` or piping through `yq -o=json`. This keeps the
31-
dependency-constrained-CI path (where even PyYAML may not be installable)
32-
fully functional.
33-
- **No compose-spec parser dependency.** Researched candidates
34-
(`compose-spec`, `compose-pydantic`) both require pydantic v2 (a compiled
35-
`pydantic-core` wheel) and are early-stage single-maintainer 0.x projects.
36-
Adopting one would:
37-
(a) break the zero-dependency differentiator for the core,
38-
(b) not actually remove the subset validation — full-spec parsers
39-
permissively accept constructs (`configs`, `secrets`, `profiles`,
40-
`deploy`, long-form volumes, ...) that `compose2pod` cannot turn into a
41-
single pod, so a subset gate is still required regardless of what parses
42-
the document, and
43-
(c) add supply-chain risk (compiled wheel, single maintainer, early 0.x)
44-
to a package whose whole pitch is minimal-footprint installability.
45-
- A future optional `[strict]` extra could cross-validate against
46-
`compose-spec` for users who want full-spec checking, but that is
47-
deliberately out of scope for v1 (YAGNI) — no user need has been
48-
identified yet.
49-
50-
**Revisit trigger:** any of —
51-
52-
- A mature, pure-Python (no compiled wheel) compose-spec parser reaches a
53-
stable 1.x release with more than one maintainer, and a concrete user
54-
need for full-spec validation (beyond the subset `compose2pod` supports)
55-
emerges.
56-
- PyYAML itself becomes uninstallable in a target CI environment that
57-
`compose2pod` needs to support, forcing a rethink of the YAML story.
3+
The core has no runtime dependencies: JSON via the stdlib always works and PyYAML sits behind the
4+
`[yaml]` extra, because the tool exists for minimal CI images where even a compiled wheel may not
5+
install. No compose-spec parser library is used either. The candidates require `pydantic-core`,
6+
are early single-maintainer projects, and would not remove the subset gate anyway, since a
7+
full-spec parser accepts constructs a single pod cannot express.

‎docs/adr/0003-reject-namespace-network-keys.md‎

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# The shared namespace decides which keys are refused, inert, or pod-level
2+
3+
Every service runs in one pod sharing `net`, `uts`, `ipc` and `cgroup`, and the shared network
4+
namespace, with localhost discovery through per-container `--add-host`, is the reason the tool
5+
exists. Keys that pull a container out of it (`network_mode`, `links`, `external_links`,
6+
`expose`) are refused permanently; per-container namespace overrides (`ipc`, `uts`, `domainname`,
7+
`cgroup`, `userns_mode`) are refused while the pod keeps its default `--share`. `dns*` and
8+
`sysctls` are pod-level, unioned and conflict-checked across the closure onto
9+
`podman pod create`, because a container that joined the pod owns neither namespace and podman
10+
rejects the per-container flag. `stop_signal` and `stop_grace_period` are accepted but inert, in
11+
`IGNORED_SERVICE_KEYS` with a warning: the script tears down with `pod rm -f` and never runs
12+
`podman stop`, so the flags would set metadata nothing consults.

‎docs/adr/0004-stop-lifecycle-keys-inert.md‎

Lines changed: 0 additions & 40 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# `validate` and `emit` both read the raw dict
2+
3+
`validate(compose)` and `emit_script(compose, options)` both take the raw compose dict. There is
4+
no typed `CheckedDocument` produced by one and consumed by the other, and the graph queries in
5+
`graph.py` (`depends_on`, `hostnames`, `startup_order`) normalise and raise on bad shape wherever
6+
they are called rather than being computed once and threaded through. The gate is
7+
target-agnostic and emit is target-scoped, so one shared model cannot span them, and the typed
8+
model would cost a registry rewrite plus a breaking API for a gap that is closed differently:
9+
`emit._plan`, the single traversal both `emit_script` and `referenced_variables` project from,
10+
calls `validate(compose)` itself, so a library caller cannot reach a raw `KeyError` or a corrupted
11+
flag. A second emit consumer is what would make the typed model pay.

0 commit comments

Comments
 (0)