@@ -10,71 +10,103 @@ import (
1010 "github.com/spf13/cobra"
1111)
1212
13- // NewBuildCommand creates a new command to build services from a Compose file.
13+ type buildOptions struct {
14+ cli.BuildServicesOptions
15+ files []string
16+ profiles []string
17+ }
18+
19+ // NewBuildCommand creates a new command to build images for services from a Compose file.
1420func NewBuildCommand () * cobra.Command {
15- opts := cli. BuildOptions {}
21+ opts := buildOptions {}
1622 cmd := & cobra.Command {
1723 Use : "build [FLAGS] [SERVICE...]" ,
1824 Short : "Build services from a Compose file." ,
25+ Long : `Build images for services from a Compose file using local Docker.
26+
27+ By default, built images remain on the local Docker host. Use --push to upload them
28+ to cluster machines or --push-registry to upload them to external registries.` ,
29+ Example : ` # Build all services that have a build section in compose.yaml.
30+ uc build
31+
32+ # Build specific services that have a build section.
33+ uc build web api
34+
35+ # Build services and push images to all cluster machines or service x-machines if specified.
36+ uc build --push
37+
38+ # Build services and push images to specific machines.
39+ uc build --push -m machine1,machine2
40+
41+ # Build services and push images to external registries (e.g., Docker Hub).
42+ uc build --push-registry
43+
44+ # Build services with build arguments, pull newer base images before building, and don't use cache.
45+ uc build --build-arg NODE_VERSION=24 --build-arg ENV=production --no-cache --pull` ,
1946 RunE : func (cmd * cobra.Command , args []string ) error {
2047 uncli := cmd .Context ().Value ("cli" ).(* cli.CLI )
21-
22- if len (args ) > 0 {
23- opts .Services = args
24- }
48+ opts .Services = args
2549
2650 return runBuild (cmd .Context (), uncli , opts )
2751 },
2852 }
2953
30- cmd .Flags ().StringSliceVarP (& opts .Files , "file" , "f" , nil ,
31- "One or more Compose files to build (default compose.yaml)" )
32- cmd .Flags ().StringSliceVarP (& opts .Profiles , "profile" , "p" , nil ,
33- "One or more Compose profiles to enable." )
34- cmd .Flags ().BoolVarP (& opts .Push , "push" , "P" , false ,
35- "Push built images to the registry after building. (default false)" )
54+ cmd .Flags ().StringArrayVar (& opts .BuildArgs , "build-arg" , nil ,
55+ "Set a build-time variable for services. Used in Dockerfiles that declare the variable with ARG.\n " +
56+ "Can be specified multiple times. Format: --build-arg VAR=VALUE" )
57+ cmd .Flags ().BoolVar (& opts .Check , "check" , false ,
58+ "Check the build configuration for services without building them." )
59+ cmd .Flags ().BoolVar (& opts .Deps , "deps" , false ,
60+ "Also build services declared as dependencies of the selected services." )
61+ cmd .Flags ().StringSliceVarP (& opts .files , "file" , "f" , nil ,
62+ "One or more Compose files to build. (default compose.yaml)" )
63+ cmd .Flags ().StringSliceVarP (& opts .Machines , "machine" , "m" , nil ,
64+ "Machine names or IDs to push the built images to (requires --push).\n " +
65+ "Can be specified multiple times or as a comma-separated list. (default is all machines or x-machines)" )
3666 cmd .Flags ().BoolVar (& opts .NoCache , "no-cache" , false ,
37- "Do not use cache when building images. (default false)" )
67+ "Do not use cache when building images." )
68+ cmd .Flags ().StringSliceVarP (& opts .profiles , "profile" , "p" , nil ,
69+ "One or more Compose profiles to enable." )
70+ cmd .Flags ().BoolVar (& opts .Pull , "pull" , false ,
71+ "Always attempt to pull newer versions of base images before building." )
72+ cmd .Flags ().BoolVar (& opts .PushCluster , "push" , false ,
73+ "Upload the built images to cluster machines after building.\n " +
74+ "Use --machine to specify which machines. (default is all machines)" )
75+ cmd .Flags ().BoolVar (& opts .PushRegistry , "push-registry" , false ,
76+ "Upload the built images to external registries (e.g., Docker Hub) after building." )
77+ cmd .Flags ().StringVarP (& opts .Context , "context" , "c" , "" ,
78+ "Name of the cluster context. (default is the current context)" )
3879
3980 return cmd
4081}
4182
42- // TODO: deduplicate with a similar functino for deploy options
43- // projectOpts returns the project options for the Compose file(s).
44- func projectOptsFromBuildOpts (opts cli.BuildOptions ) []composecli.ProjectOptionsFn {
45- projectOpts := []composecli.ProjectOptionsFn {}
46-
47- if len (opts .Profiles ) > 0 {
48- projectOpts = append (projectOpts , composecli .WithDefaultProfiles (opts .Profiles ... ))
83+ // runBuild parses the Compose file(s) and builds the images for selected services.
84+ func runBuild (ctx context.Context , uncli * cli.CLI , opts buildOptions ) error {
85+ // Validate push flags.
86+ if opts .PushCluster && opts .PushRegistry {
87+ return fmt .Errorf ("cannot specify both --push and --push-registry: choose one push target" )
4988 }
5089
51- return projectOpts
52- }
90+ machines := cli .ExpandCommaSeparatedValues (opts .Machines )
91+ // Special handling for an explicit "all" keyword to push to all machines.
92+ if len (machines ) == 1 && machines [0 ] == "all" {
93+ machines = nil
94+ }
95+ opts .Machines = machines
5396
54- // runBuild parses the Compose file(s), builds the services, and pushes them if requested.
55- func runBuild (ctx context.Context , uncli * cli.CLI , opts cli.BuildOptions ) error {
56- projectOpts := projectOptsFromBuildOpts (opts )
57- project , err := compose .LoadProject (ctx , opts .Files , projectOpts ... )
97+ project , err := compose .LoadProject (ctx , opts .files , composecli .WithDefaultProfiles (opts .profiles ... ))
5898 if err != nil {
5999 return fmt .Errorf ("load compose file(s): %w" , err )
60100 }
61101
62- if len (opts .Services ) > 0 {
63- project , err = project .WithSelectedServices (opts .Services )
64- if err != nil {
65- return fmt .Errorf ("select services: %w" , err )
66- }
67- }
68-
69- servicesToBuild , err := cli .ServicesThatNeedBuild (project , opts .Services , false )
102+ servicesToBuild , err := cli .ServicesThatNeedBuild (project , opts .Services , opts .Deps )
70103 if err != nil {
71104 return fmt .Errorf ("determine services to build: %w" , err )
72105 }
73-
74106 if len (servicesToBuild ) == 0 {
75107 fmt .Println ("No services to build." )
76108 return nil
77109 }
78110
79- return cli .BuildServices (ctx , servicesToBuild , opts )
111+ return uncli .BuildServices (ctx , project , opts . BuildServicesOptions )
80112}
0 commit comments