Skip to content

refactor: extract inspect and projectloader from pkg/compose into internal packages - #14331

Merged
glours merged 2 commits into
docker:mainfrom
ndeloof:refactor/extract-inspect
Oct 13, 2026
Merged

glours merged 2 commits into
docker:mainfrom
ndeloof:refactor/extract-inspect

Conversation

@ndeloof

@ndeloof ndeloof commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does, in one sentence

Moves two self-contained groups of operations out of the flat pkg/compose package into their own internal packages — the read-only ones (ps, ls, top, port, volumes, events, viz) into internal/inspect, and project loading and generation (LoadProject, Generate) into internal/projectloader — as the first steps of splitting pkg/compose into focused sub-packages.

Context

pkg/compose is one package of about seventy files and 21k lines, where every operation is a method of the same composeService and can therefore reach anything: the Docker client, the event bus, the prompt, any other operation. Nothing but convention keeps a read-only command from depending on lifecycle code, and every test builds a whole service. The recent preparation work (the api.Compose split into focused interfaces, the removal of the package-level state, the inversion of the CLI dependency) removed what made a split impossible; this PR is the first actual move, and each of the two packages matches one of those interfaces (api.Inspector, api.ProjectLoader).

What the PR brings

  • internal/inspect and internal/projectloader, each declaring what it needs from the engine as a small Backend interface (the Docker client and the concurrency bound for the former, the Docker client and the Docker CLI for the latter) and unable to reach anything else: importing pkg/compose back would be an import cycle.
  • composeService implements those interfaces in two new files of pkg/compose and forwards the corresponding api.Compose methods. Behavior is unchanged: the service-level tests of ps, port, viz and volumes are untouched and run through the forwarders, and the loader tests moved with their code with the same assertions.
  • A tiny internal/naming package for the one helper both packages need (the canonical container name), rather than copying it a third time. The few other helpers still copied belong to a shared kernel that a later step extracts.
  • No other existing file of pkg/compose changes, so the open pull requests don't have to be rebased; the only one touching a moved file (the unsupported-attributes detection) merges cleanly with this change and still builds and passes its tests.

Why this is the right first brick

These are the most isolated clusters of pkg/compose: together about 1,400 lines that nothing else in the package calls, with no API beyond the interfaces they implement, and two or three things each needed from the engine. That makes them the cheapest place to settle the pattern — sub-package, narrow Backend interface, forwarders in pkg/compose — that the following moves reuse, with the heavier clusters (provider plugins, images, then the lifecycle ones once the start-phase series has landed) coming after.

🤖 Generated with Claude Code

ps, ls, top, port, volumes, events and viz only read the state of a
project: they need the Docker API, the list of the project's containers
and the concurrency bound, nothing else of composeService.

They now live in internal/inspect, which declares that need as a Backend
interface. composeService implements it in a new file of pkg/compose and
forwards the seven api.Inspector methods, so no other file of pkg/compose
changes, and neither does the behavior: the service-level tests stay in
pkg/compose and run unchanged through the forwarders.

The few tiny helpers the operations share with the rest of pkg/compose
(canonical container name, concurrency-limited errgroup, project filter)
are copied for now: the shared kernel they belong to is a later step.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
@ndeloof
ndeloof requested review from a team as code owners October 13, 2026 06:55

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟢 APPROVE

The refactor correctly moves seven read-only Compose operations (ps, ls, top, port, volumes, events, viz) from pkg/compose into the new internal/inspect package. The Backend interface is well-designed and the inspectBackend adapter faithfully translates all types (including the OneOff enum via toOneOff). Forwarding methods pass all arguments and return values correctly. The copied helpers (canonicalContainerName, limitedGroup, projectFilter) are faithful copies of the originals, with limitedGroup correctly guarding against SetLimit(0) deadlock. Goroutine closures in ps.go and top.go are safe under Go 1.22+ per-iteration loop variable semantics.

Lower-confidence findings (not posted inline)

  • [low] internal/inspect/top.go:48 — Variable s in errgroup closure shadows method receiver (low severity, not verified; cosmetic concern, no behavioral impact)
  • [low] internal/inspect/ps.go:43 — Double service-filter application: Containers() already receives the service list, and inServices() is applied again in-memory (low severity, not verified; result is correct but subtly widens filter to single-service case without documentation)
  • [low] pkg/compose/inspect.go:34 — inspector() allocates a new inspect.Service on every forwarding call (low severity, not verified; correct behavior, minor allocation inefficiency)

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟢 APPROVE

Clean refactor. The Backend interface correctly covers all dependencies the moved implementations use, the forwarding wrappers in pkg/compose/inspect.go are correctly wired, error propagation is intact, and tests properly use t.Context().

Lower-confidence findings (not posted inline)

  • [low] internal/inspect/inspect.go:97 — canonicalContainerName slices c.ID[:12] without a length guard (not verified; Docker IDs are always 64-hex chars, so unreachable in practice — only a risk with synthetic test stubs)
  • [low] internal/inspect/ps.go:41 — Ps filters by service names twice (once via backend.Containers, then again via inServices); the second pass is a no-op but adds minor redundancy
  • [low] pkg/compose/inspect.go:37 — inspector() allocates a new inspect.Service + inspectBackend wrapper on every forwarded call; stateless, so storing it as a field of composeService would avoid repeated allocation on hot paths like polling Ps

@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

…nal/projectloader

LoadProject and Generate (the api.ProjectLoader operations) need two things
from the engine: the Docker API, to read the containers a project is
generated from, and the Docker CLI, which resolves remote Compose files.

They now live in internal/projectloader, along with the unsupported
attributes detection only the loader uses, behind a Backend interface that
composeService implements in a new file of pkg/compose, like internal/inspect.
The tests move with the code; their assertions are unchanged, only the way
the service is built differs.

The canonical container name, which both this package and internal/inspect
need, moves to a tiny internal/naming package instead of being copied a
third time. pkg/compose keeps its own copy until the shared kernel is
extracted.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
@ndeloof ndeloof changed the title refactor(inspect): move the read-only operations to internal/inspect refactor: extract inspect and projectloader from pkg/compose into internal packages Oct 13, 2026
@ndeloof
ndeloof requested a review from docker-agent October 13, 2026 07:23

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review covers only the commits pushed since d965367.

Assessment: 🟢 APPROVE

@glours glours left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@glours
glours merged commit 6698708 into docker:main Oct 13, 2026
63 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.

3 participants