Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ jobs:
platforms: linux/amd64, linux/arm64
build-args: |
"LLAMA_SERVER_VERSION=${{ env.LLAMA_SERVER_VERSION }}"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand All @@ -323,6 +324,7 @@ jobs:
"LLAMA_SERVER_VERSION=${{ env.LLAMA_SERVER_VERSION }}"
"LLAMA_SERVER_VARIANT=cuda"
"BASE_IMAGE=nvidia/cuda:12.9.0-runtime-ubuntu24.04"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand All @@ -341,6 +343,7 @@ jobs:
"VLLM_VERSION=${{ env.VLLM_VERSION }}"
"VLLM_CUDA_VERSION=cu130"
"VLLM_PYTHON_TAG=cp38-abi3"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand All @@ -357,6 +360,7 @@ jobs:
"LLAMA_SERVER_VARIANT=cuda"
"BASE_IMAGE=nvidia/cuda:12.9.0-runtime-ubuntu24.04"
"SGLANG_VERSION=${{ env.SGLANG_VERSION }}"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand All @@ -370,6 +374,7 @@ jobs:
platforms: linux/amd64, linux/arm64
build-args: |
"LLAMA_SERVER_VERSION=${{ env.LLAMA_SERVER_VERSION }}"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand All @@ -385,6 +390,7 @@ jobs:
"LLAMA_SERVER_VERSION=${{ env.LLAMA_SERVER_VERSION }}"
"LLAMA_SERVER_VARIANT=rocm"
"BASE_IMAGE=rocm/dev-ubuntu-22.04"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand All @@ -401,6 +407,7 @@ jobs:
"LLAMA_SERVER_VERSION=${{ env.LLAMA_SERVER_VERSION }}"
"LLAMA_SERVER_VARIANT=musa"
"BASE_IMAGE=mthreads/musa:rc4.3.0-runtime-ubuntu22.04-amd64"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand All @@ -417,6 +424,7 @@ jobs:
"LLAMA_SERVER_VERSION=${{ env.LLAMA_SERVER_VERSION }}"
"LLAMA_SERVER_VARIANT=cann"
"BASE_IMAGE=ascendai/cann:8.2.rc2-910b-ubuntu22.04-py3.11"
"VERSION=${{ env.RELEASE_TAG }}"
push: true
sbom: true
provenance: mode=max
Expand Down Expand Up @@ -677,26 +685,19 @@ jobs:
SUMMARY

# ---------------------------------------------------------------------------
# Verify Docker CE installation — run test-docker-ce-installation.sh
# to confirm the CLI version is available via get.docker.com
# Verify Docker CE installation and server version — install Docker CE,
# start the released model-runner image, and use `docker model version`
# to confirm both client and server versions match the release tag.
# ---------------------------------------------------------------------------
verify-docker-ce:
needs: [prepare, release-cli-docker-ce, build]
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
base_image:
- ubuntu:24.04
- debian:12
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

- name: Verify Docker CE installation
env:
BASE_IMAGE: ${{ matrix.base_image }}
- name: Verify client and server versions
run: |
chmod +x scripts/test-docker-ce-installation.sh
./scripts/test-docker-ce-installation.sh "${{ needs.prepare.outputs.release_tag }}"
Expand Down
9 changes: 7 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ ARG LLAMA_BINARY_PATH=/com.docker.llama-server.native.linux.${LLAMA_SERVER_VARIA
# use 22.04 for gpu variants to match ROCm/CUDA base images
ARG BASE_IMAGE=ubuntu:26.04

ARG VERSION=dev

FROM docker.io/library/golang:${GO_VERSION}-bookworm AS builder

ARG VERSION

# Install git for go mod download if needed
RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*

Expand All @@ -30,13 +34,14 @@ COPY --link . .
# Build the Go binary (static build)
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=1 GOOS=linux go build -ldflags="-s -w" -o model-runner .
CGO_ENABLED=1 GOOS=linux go build -ldflags="-s -w -X main.Version=${VERSION}" -o model-runner .

# Build the Go binary for SGLang (without vLLM)
FROM builder AS builder-sglang
ARG VERSION
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=1 GOOS=linux go build -tags=novllm -ldflags="-s -w" -o model-runner .
CGO_ENABLED=1 GOOS=linux go build -tags=novllm -ldflags="-s -w -X main.Version=${VERSION}" -o model-runner .

# --- Get llama.cpp binary ---
FROM docker/docker-model-backend-llamacpp:${LLAMA_SERVER_VERSION}-${LLAMA_SERVER_VARIANT} AS llama-server
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ BUILD_DMR ?= 1

# Build the Go application
build:
CGO_ENABLED=1 go build -ldflags="-s -w" -o $(APP_NAME) .
CGO_ENABLED=1 go build -ldflags="-s -w -X main.Version=$(shell git describe --tags --always --dirty --match 'v*')" -o $(APP_NAME) .

build-cli:
$(MAKE) -C cmd/cli
Expand Down
22 changes: 20 additions & 2 deletions cmd/cli/commands/version.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package commands

import (
"runtime"

"github.com/docker/model-runner/cmd/cli/commands/completion"
"github.com/docker/model-runner/cmd/cli/desktop"
"github.com/spf13/cobra"
Expand All @@ -11,8 +13,24 @@ func newVersionCmd() *cobra.Command {
Use: "version",
Short: "Show the Docker Model Runner version",
Run: func(cmd *cobra.Command, args []string) {
cmd.Printf("Docker Model Runner version %s\n", desktop.Version)
cmd.Printf("Docker Engine Kind: %s\n", modelRunner.EngineKind())
cmd.Println("Client:")
cmd.Printf(" Version: %s\n", desktop.Version)
cmd.Printf(" OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)

cmd.Println()
cmd.Println("Server:")
serverVersion := "(not reachable)"
if desktopClient != nil {
if sv, err := desktopClient.ServerVersion(); err == nil {
serverVersion = sv.Version
}
}
cmd.Printf(" Version: %s\n", serverVersion)
if modelRunner != nil {
cmd.Printf(" Engine: %s\n", modelRunner.EngineKind())
} else {
cmd.Println(" Engine: (not reachable)")
}
},
ValidArgsFunction: completion.NoComplete,
}
Expand Down
28 changes: 28 additions & 0 deletions cmd/cli/desktop/desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,34 @@ func (c *Client) Remove(modelArgs []string, force bool) (string, error) {
return modelRemoved, nil
}

type ServerVersionResponse struct {
Version string `json:"version"`
}

func (c *Client) ServerVersion() (ServerVersionResponse, error) {
resp, err := c.doRequest(http.MethodGet, "/version", nil)
if err != nil {
return ServerVersionResponse{}, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return ServerVersionResponse{}, fmt.Errorf("failed to get server version: %s", resp.Status)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return ServerVersionResponse{}, fmt.Errorf("failed to read response body: %w", err)
}

var version ServerVersionResponse
if err := json.Unmarshal(body, &version); err != nil {
return ServerVersionResponse{}, fmt.Errorf("failed to unmarshal response body: %w", err)
}

return version, nil
}

// BackendStatus to be imported from docker/model-runner when https://github.com/docker/model-runner/pull/42 is merged.
type BackendStatus struct {
BackendName string `json:"backend_name"`
Expand Down
9 changes: 9 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"crypto/tls"
"encoding/json"
"net"
"net/http"
"os"
Expand Down Expand Up @@ -264,6 +265,14 @@ func main() {
anthropicHandler := anthropic.NewHandler(log, schedulerHTTP, nil, modelManager)
router.Handle(anthropic.APIPrefix+"/", anthropicHandler)

// Register /version endpoint
router.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]string{"version": Version}); err != nil {
log.Warnf("failed to write version response: %v", err)
}
})

// Register root handler LAST - it will only catch exact "/" requests that don't match other patterns
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Only respond to exact root path
Expand Down
14 changes: 3 additions & 11 deletions pkg/inference/models/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,6 @@ func TestPullModel(t *testing.T) {
}

func TestHandleGetModel(t *testing.T) {
tempDir := t.TempDir()

// Create a test registry
server := httptest.NewServer(testregistry.New())
defer server.Close()
Expand Down Expand Up @@ -207,7 +205,9 @@ func TestHandleGetModel(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
log := logrus.NewEntry(logrus.StandardLogger())
tempDir := t.TempDir()
logger := logrus.New()
log := logrus.NewEntry(logger)
manager := NewManager(log.WithFields(logrus.Fields{"component": "model-manager"}), ClientConfig{
StoreRootPath: tempDir,
Logger: log.WithFields(logrus.Fields{"component": "model-manager"}),
Expand Down Expand Up @@ -263,14 +263,6 @@ func TestHandleGetModel(t *testing.T) {
t.Errorf("Failed to decode response body: %v", err)
}
}

// Clean tempDir after each test
if err := os.RemoveAll(tempDir); err != nil {
t.Fatalf("Failed to clean temp directory: %v", err)
}
if err := os.MkdirAll(tempDir, 0755); err != nil {
t.Fatalf("Failed to recreate temp directory: %v", err)
}
})
}
}
Expand Down
28 changes: 24 additions & 4 deletions scripts/test-docker-ce-in-container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,30 @@ echo "Testing docker model version..."
version_output=$(docker model version 2>&1 || true)
echo "Output: $version_output"

if echo "$version_output" | grep -q "version $EXPECTED_VERSION"; then
echo "✓ Success: Found expected version $EXPECTED_VERSION"
exit 0
# Extract client version from the "Client:" section (first "Version:" after "Client:")
client_version=$(echo "$version_output" | awk '/^Client:/{found=1} found && /Version:/{print $2; exit}')

# Extract server version from the "Server:" section (first "Version:" after "Server:")
server_version=$(echo "$version_output" | awk '/^Server:/{found=1} found && /Version:/{print $2; exit}')

errors=0

if [ "$client_version" = "$EXPECTED_VERSION" ]; then
echo "✓ Client version matches expected $EXPECTED_VERSION"
else
echo "✗ Error: Expected version $EXPECTED_VERSION not found in output"
echo "✗ Error: Expected client version $EXPECTED_VERSION, got '$client_version'"
errors=$((errors + 1))
fi

if [ "$server_version" = "$EXPECTED_VERSION" ]; then
echo "✓ Server version matches expected $EXPECTED_VERSION"
else
echo "✗ Error: Expected server version $EXPECTED_VERSION, got '$server_version'"
errors=$((errors + 1))
fi

if [ "$errors" -gt 0 ]; then
exit 1
fi

echo "✓ All version checks passed!"
43 changes: 35 additions & 8 deletions scripts/test-docker-ce-installation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,51 @@ main() {
fi
fi

echo "Testing Docker CE installation with expected CLI version: $cli_version"
echo "Testing Docker CE installation with expected version: $cli_version"

if [ -z "${BASE_IMAGE:-}" ]; then
echo "Error: BASE_IMAGE is not set" >&2
exit 1
fi
local base_image="${BASE_IMAGE:-ubuntu:24.04}"
echo "Using base image: $base_image"

local server_image="docker/model-runner:$cli_version"
echo "Using server image: $server_image"

# Start the model-runner server container
echo "Starting model-runner server..."
docker run -d --name dmr-version-test -p 12434:12434 "$server_image"

echo "Using base image: $BASE_IMAGE"
# Ensure cleanup on exit
cleanup() {
echo "Stopping model-runner server..."
docker stop dmr-version-test 2>/dev/null || true
docker rm dmr-version-test 2>/dev/null || true
}
trap cleanup EXIT

echo "Starting container and installing Docker CE..."
# Wait for server to be ready
echo "Waiting for server to be ready..."
for i in $(seq 1 30); do
if curl -sf http://localhost:12434/version > /dev/null 2>&1; then
echo "Server is ready"
break
fi
if [ "$i" -eq 30 ]; then
echo "Error: Server did not become ready in time" >&2
docker logs dmr-version-test
exit 1
fi
sleep 1
done

local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

echo "Starting test container..."
docker run --rm \
--network host \
-e "EXPECTED_VERSION=$cli_version" \
-e "MODEL_RUNNER_HOST=http://localhost:12434" \
-v "$script_dir/test-docker-ce-in-container.sh:/test.sh:ro" \
"$BASE_IMAGE" \
"$base_image" \
/test.sh

echo "✓ Docker CE installation test passed!"
Expand Down
3 changes: 3 additions & 0 deletions version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package main

var Version = "dev"
Loading