Skip to content

Rewrite dmr as a standalone, engine-independent binary#996

Open
ericcurtin wants to merge 1 commit into
docker:mainfrom
ericcurtin:standalone-dmr
Open

Rewrite dmr as a standalone, engine-independent binary#996
ericcurtin wants to merge 1 commit into
docker:mainfrom
ericcurtin:standalone-dmr

Conversation

@ericcurtin

@ericcurtin ericcurtin commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

This rewrites cmd/dmr (previously a ~70-line hack combining the CLI command
tree with a bolted-on serve subcommand) into a small, documented,
genuinely standalone package, and adds the build/release plumbing needed to
distribute it outside this repo — laying the groundwork for adoption by
tools like sbx that
don't want a Docker Desktop or Docker Engine dependency.

What changed

  • cmd/dmr/root.go builds the full model management command tree
    (run, ls, pull, rm, ps, inspect, logs, ...) and always forces
    MODEL_RUNNER_HOST to the local daemon first. This guarantees every dmr
    invocation takes the "manual host" path in cmd/cli/desktop/context.go
    and never probes for Docker Desktop or a Docker Engine connection. A
    regression test (TestServePersistentPreRunEOverridesParent) locks in the
    cobra semantics this relies on: a child command's own PersistentPreRunE
    replaces, rather than chains after, its parent's.
  • cmd/dmr/serve.go runs pkg/server in-process (the same daemon used
    by the docker-model-runner container and Docker Desktop's bundled
    runner), with --port/--socket/--models-path flags in addition to the
    existing env vars. --port/--socket are mutually exclusive
    (MarkFlagsMutuallyExclusive), and an explicit flag clears the other
    transport's environment variable first, since pkg/server prefers
    MODEL_RUNNER_PORT over MODEL_RUNNER_SOCK whenever both happen to be
    set — otherwise an inherited env var could silently override an explicit
    flag. Covered by TestApplyServeEnv.
  • Docker-Engine-only commands that manage a docker-model-runner
    container (install-runner, start-runner, stop-runner,
    restart-runner, reinstall-runner, uninstall-runner) are now hidden
    from the dmr command tree — dmr serve replaces the container
    entirely, so these no longer apply and only added confusion.
  • pkg/envconfig.LlamaServerPath() no longer defaults to the Docker Desktop
    bundle path on non-macOS platforms (that path can never exist there); it's
    now only checked as a last-resort lookup on macOS itself, where Desktop
    vendors its own copy.
  • Confirmed dmr has zero cgo dependencies, so make build-dmr now builds
    with CGO_ENABLED=0, versioned from the dmr-vX.Y.Z tag scheme
    (git describe --match 'dmr-v*', stripped of its prefix) rather than the
    container image's v* tags. A new make build-dmr-cross target
    cross-compiles it for every platform we intend to package: macOS
    (arm64), Linux (amd64/arm64), Windows (amd64) — all verified locally.
  • Adds .github/workflows/release-dmr.yml: on a dmr-vX.Y.Z tag, the build
    job runs make build-dmr-cross directly (rather than re-encoding
    GOOS/GOARCH/ldflags in YAML, so there's a single source of truth for
    dmr's build flags), publishes a GitHub release, opens the Homebrew
    formula PR against docker/homebrew-tap, and submits the WinGet manifest
    to microsoft/winget-pkgs via wingetcreate — so dmr becomes
    installable via brew install docker/tap/dmr / winget install Docker.dmr
    on its own release cadence, independent of Docker Desktop's or Docker
    Engine's pipelines. Modeled on docker/sandboxes's publish-brew.yml /
    publish-winget.yml.
  • Documents all of the above in README.md, cmd/dmr/README.md, and the
    new packaging/README.md.

Testing

  • go build ./..., go vet ./..., gofmt -l — clean
  • make lint (golangci-lint run ./...) — 0 issues
  • go test -race ./... — all packages pass, including new tests for
    cmd/dmr (TestRemoveEngineOnlyCommands,
    TestServePersistentPreRunEOverridesParent, TestApplyServeEnv) and
    pkg/envconfig (LlamaServerPath platform behavior)
  • go mod tidy — no changes
  • Manually ran dmr serve --port 19434 with no Docker installed/running,
    confirmed /models responds and the daemon downloads llama.cpp from the
    registry on demand
  • Cross-compiled and ran dmr version/dmr --help for all four target
    platforms (darwin/arm64, linux/amd64, linux/arm64, windows/amd64 build
    only, not run), and exercised the release workflow's packaging step
    locally against real make build-dmr-cross output
  • Rebased on current main (no conflicts) and squashed to a single commit

Not in scope here

Linux .deb/.rpm packaging is unaffected — it continues through the
existing docker-model-plugin pipeline referenced in release.yml.

@sourcery-ai sourcery-ai Bot 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.

Hey - I've found 1 issue, and left some high level feedback:

  • In the Makefile, DMR_VERSION uses git describe --match 'v*', which won’t pick up dmr-v* tags used by the new release flow; consider changing the match pattern (or dropping it) so local make build-dmr builds get the same versioning as the dmr-vX.Y.Z releases.
  • The GitHub release-dmr.yml workflow re-encodes the dmr build flags, ldflags, and target matrix instead of reusing make build-dmr-cross, which risks them drifting; consider calling the Makefile target from the workflow or sharing a single source of truth for build configuration.
  • For dmr serve, double-check whether commands.NewRootCmd defines a PersistentPreRun(E) on the root; if it does, that will still run before serve even though serve sets its own PersistentPreRunE, so you may need an explicit override to avoid the Docker CLI plugin initialization path you’re trying to bypass.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the Makefile, `DMR_VERSION` uses `git describe --match 'v*'`, which won’t pick up `dmr-v*` tags used by the new release flow; consider changing the match pattern (or dropping it) so local `make build-dmr` builds get the same versioning as the `dmr-vX.Y.Z` releases.
- The GitHub `release-dmr.yml` workflow re-encodes the dmr build flags, ldflags, and target matrix instead of reusing `make build-dmr-cross`, which risks them drifting; consider calling the Makefile target from the workflow or sharing a single source of truth for build configuration.
- For `dmr serve`, double-check whether `commands.NewRootCmd` defines a `PersistentPreRun(E)` on the root; if it does, that will still run before `serve` even though `serve` sets its own `PersistentPreRunE`, so you may need an explicit override to avoid the Docker CLI plugin initialization path you’re trying to bypass.

## Individual Comments

### Comment 1
<location path="Makefile" line_range="63" />
<code_context>
 build-cli:
 	$(MAKE) -C cmd/cli

+DMR_VERSION := $(shell git describe --tags --always --dirty --match 'v*')
+DMR_LDFLAGS := -s -w \
+	-X main.Version=$(DMR_VERSION) \
</code_context>
<issue_to_address>
**issue (bug_risk):** Version discovery ignores dmr-specific tags and may not match released dmr binaries

DMR_VERSION uses `git describe --match 'v*'`, but dmr releases are now tagged `dmr-vX.Y.Z`. As a result, `build-dmr`/`build-dmr-cross` will keep picking up the generic `v*` tags (container image scheme), so locally built dmr binaries won’t match published dmr release versions. Please either drop `--match` or switch to `--match 'dmr-v*'` (and strip the prefix) so standalone dmr builds follow the dmr tag scheme.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread Makefile Outdated

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces a standalone dmr binary that bundles the inference daemon and model management CLI, removing dependencies on Docker Desktop or Docker Engine. It adds cross-compilation support, documentation, and refactors the CLI command tree to hide engine-specific commands. Feedback focuses on improving the CLI flag handling in cmd/dmr/serve.go by ensuring explicit flags correctly override conflicting environment variables and enforcing mutual exclusivity between the --port and --socket flags.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread cmd/dmr/serve.go
Comment thread cmd/dmr/serve.go
The previous cmd/dmr/main.go was a 69-line hack that reused the full
"docker model" CLI command tree and bolted on a "serve" subcommand.
It worked, but its structure gave no attention to being genuinely
engine-independent, its command surface still exposed Docker-Engine
container-management commands that don't apply to it, and there was
no build/release story for distributing it outside this repo.

This rewrites cmd/dmr from scratch as a small, documented package:

- root.go builds the CLI command tree and always forces
  MODEL_RUNNER_HOST to the local daemon before doing so, guaranteeing
  every dmr invocation takes the "manual host" code path in
  cmd/cli/desktop/context.go and never probes for Docker Desktop or a
  Docker Engine connection. A regression test locks in the cobra
  semantics this relies on (a child command's own PersistentPreRunE
  replaces, rather than chains after, its parent's).
- serve.go runs pkg/server in-process, with --port/--socket/
  --models-path flags in addition to the existing env vars. --port
  and --socket are mutually exclusive, and an explicit flag clears
  the other transport's environment variable so an inherited
  MODEL_RUNNER_PORT/MODEL_RUNNER_SOCK can never silently override it
  (pkg/server prefers MODEL_RUNNER_PORT over MODEL_RUNNER_SOCK
  whenever both happen to be set).
- Engine-only commands that manage a docker-model-runner *container*
  (install-runner, start-runner, stop-runner, restart-runner,
  reinstall-runner, uninstall-runner) are hidden from the dmr command
  tree, since "dmr serve" replaces the container entirely.

Also:

- pkg/envconfig.LlamaServerPath() no longer defaults to the Docker
  Desktop bundle path on non-macOS platforms, since that path can
  never exist there; it only applies as a last-resort lookup on
  macOS itself.
- dmr has no cgo dependencies (confirmed via CGO_ENABLED=0 builds), so
  Makefile's build-dmr now builds statically, versioned from the
  dmr-vX.Y.Z tag scheme (git describe --match 'dmr-v*', stripped of
  its prefix) rather than the container image's v* tags. A new
  build-dmr-cross target cross-compiles it for every platform we
  intend to package: macOS (arm64), Linux (amd64/arm64), and Windows
  (amd64).
- Adds .github/workflows/release-dmr.yml, which builds those targets
  by invoking make build-dmr-cross directly (a single source of truth
  for dmr's build flags shared with local/dev builds, rather than
  re-encoding them in the workflow), publishes a GitHub release, and
  opens the corresponding Homebrew formula PR (docker/homebrew-tap)
  and WinGet manifest submission (microsoft/winget-pkgs), so dmr
  becomes installable via `brew install docker/tap/dmr` and
  `winget install Docker.dmr` without depending on Docker Desktop's
  or Docker Engine's own release pipelines.
- Documents all of the above in README.md, cmd/dmr/README.md, and
  packaging/README.md.

go.mod/go.sum are tidy, golangci-lint and `go test -race ./...` both
pass across the whole repo.
@ericcurtin

Copy link
Copy Markdown
Contributor Author

Addressed all three review points from @sourcery-ai and both from @gemini-code-assist-bot in the latest push:

  1. DMR_VERSION tag matching — now uses --match 'dmr-v*' (stripped of the dmr- prefix) instead of v*, so local make build-dmr/make build-dmr-cross builds get the same version as the dmr-vX.Y.Z releases.
  2. release-dmr.yml duplicating build config — the workflow's build job now just runs make build-dmr-cross directly (with fetch-depth: 0 so git describe resolves to the pushed tag) instead of re-encoding GOOS/GOARCH/ldflags in YAML, so there's one source of truth for dmr's build flags.
  3. PersistentPreRunE override on serve — verified cobra's documented behavior (a child's own PersistentPreRunE replaces rather than chains after its parent's) and added TestServePersistentPreRunEOverridesParent as a regression test locking this in.
  4. --port/--socket mutual exclusivity — added cmd.MarkFlagsMutuallyExclusive("port", "socket").
  5. Inherited env var silently overriding an explicit flag — an explicit --socket/--port now unsets the other transport's env var first, since pkg/server prefers MODEL_RUNNER_PORT over MODEL_RUNNER_SOCK whenever both are set. Covered by a new TestApplyServeEnv.

Rebased on current main (no conflicts — main hadn't moved) and squashed back into a single commit. make lint and go test -race ./... both still pass across the whole repo.

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.

1 participant