11package root
22
33import (
4- "fmt"
5- "os"
6- "os/exec"
7- "path/filepath"
8- "strings"
9-
104 "github.com/spf13/cobra"
115
126 "github.com/docker/cagent/internal/telemetry"
13- "github.com/docker/cagent/pkg/config"
14- latest "github.com/docker/cagent/pkg/config/v2"
15- "github.com/docker/cagent/pkg/model/provider"
7+ "github.com/docker/cagent/pkg/oci"
168)
179
1810var push bool
1911
2012func NewBuildCmd () * cobra.Command {
2113 cmd := & cobra.Command {
22- Use : "build <agent-file> <image-name>" ,
23- Args : cobra .ExactArgs (2 ),
14+ Use : "build <agent-file> [docker-image-name]" ,
15+ Short : "Build a Docker image for the agent" ,
16+ Args : cobra .MinimumNArgs (1 ),
2417 RunE : runBuildCommand ,
2518 Hidden : true ,
2619 }
@@ -33,128 +26,11 @@ func NewBuildCmd() *cobra.Command {
3326func runBuildCommand (cmd * cobra.Command , args []string ) error {
3427 telemetry .TrackCommand ("build" , args )
3528
36- fileName := filepath .Base (args [0 ])
37- parentDir := filepath .Dir (args [0 ])
38-
39- cfg , err := config .LoadConfigSecure (fileName , parentDir )
40- if err != nil {
41- return err
42- }
43-
44- secrets := gatherRequiredEnv (cfg )
45- mcpServers := gatherMCPServers (cfg )
46-
47- tmp , err := os .MkdirTemp ("" , "build" )
48- if err != nil {
49- return err
50- }
51- defer os .RemoveAll (tmp )
52-
53- // TODO(dga): set the right entrypoint.
54- err = os .WriteFile (filepath .Join (tmp , "Dockerfile" ), fmt .Appendf (nil , `# syntax=docker/dockerfile:1
55- FROM alpine:3.22@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1
56-
57- RUN adduser -D cagent
58- ADD https://github.com/docker/cagent/releases/download/v1.0.9/cagent-linux-arm64 /cagent
59- RUN chmod +x /cagent
60- COPY agent.yaml /
61- RUN chmod 666 /agent.yaml
62- USER cagent
63- ENTRYPOINT ["/cagent", "run", "--debug", "--tui=false", "/agent.yaml", "get my username on github"]
64-
65- LABEL com.docker.agent.packaging.version="v0.0.1"
66- LABEL com.docker.agent.runtime="cagent"
67- LABEL org.opencontainers.image.description="%s"
68- LABEL org.opencontainers.image.licenses="%s"
69- LABEL com.docker.agent.mcp-servers="%s"
70- LABEL com.docker.agent.secrets="%s"
71- ` , cfg .Agents ["root" ].Description , cfg .Metadata .License , strings .Join (mcpServers , "," ), strings .Join (secrets , "," )), 0o700 )
72- if err != nil {
73- return err
74- }
75-
76- agentYaml , err := os .ReadFile (args [0 ])
77- if err != nil {
78- return err
79- }
80-
81- err = os .WriteFile (filepath .Join (tmp , "agent.yaml" ), agentYaml , 0o700 )
82- if err != nil {
83- return err
84- }
85-
86- buildArgs := []string {"build" , "-t" , args [1 ]}
87- if push {
88- buildArgs = append (buildArgs , "--push" )
89- }
90- buildArgs = append (buildArgs , tmp )
91- buildCmd := exec .CommandContext (cmd .Context (), "docker" , buildArgs ... )
92- buildCmd .Stdout = os .Stdout
93- buildCmd .Stderr = os .Stderr
94-
95- return buildCmd .Run ()
96- }
97-
98- func gatherRequiredEnv (cfg * latest.Config ) []string {
99- requiredEnv := map [string ]bool {}
100-
101- for name := range cfg .Models {
102- model := cfg .Models [name ]
103- // Use the token environment variable from the alias if available
104- if alias , exists := provider .ProviderAliases [model .Provider ]; exists {
105- if alias .TokenEnvVar != "" {
106- requiredEnv [alias .TokenEnvVar ] = true
107- }
108- } else {
109- // Fallback to hardcoded mappings for unknown providers
110- switch model .Provider {
111- case "openai" :
112- requiredEnv ["OPENAI_API_KEY" ] = true
113- case "anthropic" :
114- requiredEnv ["ANTHROPIC_API_KEY" ] = true
115- case "google" :
116- requiredEnv ["GOOGLE_API_KEY" ] = true
117- }
118- }
119- }
120-
121- for _ , agent := range cfg .Agents {
122- model := agent .Model
123- switch {
124- case strings .HasPrefix (model , "openai/" ):
125- requiredEnv ["OPENAI_API_KEY" ] = true
126- case strings .HasPrefix (model , "anthropic/" ):
127- requiredEnv ["ANTHROPIC_API_KEY" ] = true
128- case strings .HasPrefix (model , "google/" ):
129- requiredEnv ["GOOGLE_API_KEY" ] = true
130- }
131- }
132-
133- var requiredEnvList []string
134- for e := range requiredEnv {
135- requiredEnvList = append (requiredEnvList , e )
136- }
137-
138- return requiredEnvList
139- }
140-
141- func gatherMCPServers (cfg * latest.Config ) []string {
142- requiredServers := map [string ]bool {}
143-
144- for _ , agent := range cfg .Agents {
145- for i := range agent .Toolsets {
146- toolSet := agent .Toolsets [i ]
147-
148- if toolSet .Type == "mcp" && toolSet .Ref != "" {
149- requiredServers [toolSet .Ref ] = true
150- }
151- }
152- }
153-
154- var requiredServersList []string
155- for e := range requiredServers {
156- requiredServersList = append (requiredServersList , e )
29+ agentFilePath := args [0 ]
30+ dockerImageName := ""
31+ if len (args ) > 1 {
32+ dockerImageName = args [1 ]
15733 }
15834
159- return requiredServersList
35+ return oci . BuildDockerImage ( cmd . Context (), agentFilePath , dockerImageName , push )
16036}
0 commit comments