Skip to content
This repository was archived by the owner on Oct 6, 2025. It is now read-only.
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
145 changes: 145 additions & 0 deletions commands/package.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package commands

import (
"bufio"
"encoding/json"
"fmt"
"html"
"io"
"path/filepath"

"github.com/docker/model-cli/desktop"
"github.com/docker/model-distribution/builder"
"github.com/docker/model-distribution/registry"
"github.com/spf13/cobra"
)

func newPackagedCmd() *cobra.Command {
var opts packageOptions

c := &cobra.Command{
Use: "package --gguf <path> [--license <path>...] --push TARGET",
Short: "package a model",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf(
"'docker model package' requires 1 argument.\n\n"+
"Usage: %s\n\n"+
"See 'docker model package --help' for more information",
cmd.Use,
)
}
if opts.push != true {
return fmt.Errorf(
"This version of 'docker model package' requires --push and will write the resulting package directly to the registry.\n\n" +
"See 'docker model package --help' for more information",
)
}
if opts.ggufPath == "" {
return fmt.Errorf(
"GGUF path is required.\n\n" +
"See 'docker model package --help' for more information",
)
}
if !filepath.IsAbs(opts.ggufPath) {
return fmt.Errorf(
"GGUF path must be absolute.\n\n" +
"See 'docker model package --help' for more information",
)
}
opts.ggufPath = filepath.Clean(opts.ggufPath)

for i, l := range opts.licensePaths {
if !filepath.IsAbs(l) {
return fmt.Errorf(
"license path must be absolute.\n\n" +
"See 'docker model package --help' for more information",
)
}
opts.licensePaths[i] = filepath.Clean(l)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := packageModel(cmd, args[0], opts); err != nil {
cmd.PrintErrln("Failed to package model")
return fmt.Errorf("package model: %w", err)
}
return nil
},
}

c.Flags().StringVar(&opts.ggufPath, "gguf", "", "absolute path to gguf file (required)")
c.Flags().StringArrayVarP(&opts.licensePaths, "license", "l", nil, "absolute path to a license file")
c.Flags().BoolVar(&opts.push, "push", false, "push to registry (required)")
return c
}

type packageOptions struct {
ggufPath string
licensePaths []string
push bool
}

func packageModel(cmd *cobra.Command, tag string, opts packageOptions) error {
// Parse the reference
cmd.PrintErrln("Packaging model %q\n", tag)
target, err := registry.NewClient(
registry.WithUserAgent("docker-model-cli/" + desktop.Version),
).NewTarget(tag)
if err != nil {
return err
}

// Create package builder with GGUF file
cmd.PrintErrf("Adding GGUF file from %q\n", opts.ggufPath)
pkg, err := builder.FromGGUF(opts.ggufPath)
if err != nil {
return fmt.Errorf("add gguf file: %w", err)
}

// Add license files
for _, path := range opts.licensePaths {
cmd.PrintErrf("Adding license file from %q\n", path)
pkg, err = pkg.WithLicense(path)
if err != nil {
return fmt.Errorf("add license file: %w", err)
}
}

// Write the artifact to the registry
cmd.PrintErrln("Pushing to registry...")
pr, pw := io.Pipe()
done := make(chan error, 1)
go func() {
defer pw.Close()
done <- pkg.Build(cmd.Context(), target, pw)
}()

scanner := bufio.NewScanner(pr)
for scanner.Scan() {
progressLine := scanner.Text()
if progressLine == "" {
continue
}

// Parse the progress message
var progressMsg desktop.ProgressMessage
if err := json.Unmarshal([]byte(html.UnescapeString(progressLine)), &progressMsg); err != nil {
cmd.PrintErrln("Error displaying progress:", err)
}

// Print progress messages
TUIProgress(progressMsg.Message)
}
cmd.PrintErrln("") // newline after progress

if err := scanner.Err(); err != nil {
cmd.PrintErrln("Error streaming progress:", err)
}
if err := <-done; err != nil {
return fmt.Errorf("push: %w", err)
}
cmd.PrintErrln("Model pushed successfully")
return nil
}
1 change: 1 addition & 0 deletions commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func NewRootCmd(cli *command.DockerCli) *cobra.Command {
newStatusCmd(),
newPullCmd(),
newPushCmd(),
newPackagedCmd(),
newListCmd(),
newLogsCmd(),
newRunCmd(),
Expand Down
15 changes: 13 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ require (
github.com/docker/cli v28.0.1+incompatible
github.com/docker/docker v28.0.1+incompatible
github.com/docker/go-units v0.5.0
github.com/docker/model-runner v0.0.0-20250430163045-4c3ffbfa5311
github.com/docker/model-distribution v0.0.0-20250512190053-b3792c042d57
github.com/docker/model-runner v0.0.0-20250512190413-96af7b750f88
github.com/google/go-containerregistry v0.20.3
github.com/nxadm/tail v1.4.8
github.com/olekukonko/tablewriter v0.0.5
Expand Down Expand Up @@ -37,7 +38,6 @@ require (
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/model-distribution v0.0.0-20250423075433-587f475e591d // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
Expand All @@ -47,9 +47,12 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gpustack/gguf-parser-go v0.14.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/henvic/httpretty v0.1.4 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/gorm v1.9.16 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
Expand All @@ -59,6 +62,8 @@ require (
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
Expand All @@ -69,7 +74,9 @@ require (
github.com/prometheus/common v0.60.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a // indirect
github.com/vbatts/tar-split v0.11.6 // indirect
Expand All @@ -84,11 +91,15 @@ require (
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
gonum.org/v1/gonum v0.15.1 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 // indirect
google.golang.org/grpc v1.70.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/model-distribution v0.0.0-20250423075433-587f475e591d h1:lyxdxjNHTSyQ2w1rjbuu5pbgX42AD3kmxWLNv3mdqQ4=
github.com/docker/model-distribution v0.0.0-20250423075433-587f475e591d/go.mod h1:dThpO9JoG5Px3i+rTluAeZcqLGw8C0qepuEL4gL2o/c=
github.com/docker/model-runner v0.0.0-20250430163045-4c3ffbfa5311 h1:VAN98Xol6PH2NnHe4uNxiX//7XU+uUrfr2SmjyyoKq0=
github.com/docker/model-runner v0.0.0-20250430163045-4c3ffbfa5311/go.mod h1:9x6YLoYMHOsX1ppytOCvluEtZiFxnEc9zGHfTLs3pmQ=
github.com/docker/model-distribution v0.0.0-20250512190053-b3792c042d57 h1:ZqfKknb+0/uJid8XLFwSl/osjE+WuS6o6I3dh3ZqO4U=
github.com/docker/model-distribution v0.0.0-20250512190053-b3792c042d57/go.mod h1:dThpO9JoG5Px3i+rTluAeZcqLGw8C0qepuEL4gL2o/c=
github.com/docker/model-runner v0.0.0-20250512190413-96af7b750f88 h1:NkiizYL67HsCnnlEU6BQVoeiC1bAAyJFxw02bO7JC4E=
github.com/docker/model-runner v0.0.0-20250512190413-96af7b750f88/go.mod h1:Nw+rx6RRPNdProEb9/BVJyAQn63px6WWlOv+eEpkV7Q=
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
Expand Down
49 changes: 49 additions & 0 deletions vendor/github.com/docker/model-distribution/builder/builder.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading