17
17
package main
18
18
19
19
import (
20
- "fmt "
20
+ "io "
21
21
"os"
22
22
"os/exec"
23
+ "strconv"
23
24
"strings"
24
25
25
26
"path/filepath"
@@ -69,7 +70,7 @@ var buildCommand = &cli.Command{
69
70
& cli.StringFlag {
70
71
Name : "output" ,
71
72
Aliases : []string {"o" },
72
- Value : "type=docker " ,
73
+ Usage : "Output destination (format: type=local,dest=path) " ,
73
74
},
74
75
& cli.StringFlag {
75
76
Name : "progress" ,
@@ -84,6 +85,19 @@ var buildCommand = &cli.Command{
84
85
Name : "ssh" ,
85
86
Usage : "SSH agent socket or keys to expose to the build (format: default|<id>[=<socket>|<key>[,<key>]])" ,
86
87
},
88
+ & cli.BoolFlag {
89
+ Name : "quiet" ,
90
+ Aliases : []string {"q" },
91
+ Usage : "Suppress the build output and print image ID on success" ,
92
+ },
93
+ & cli.StringSliceFlag {
94
+ Name : "cache-from" ,
95
+ Usage : "External cache sources (eg. user/app:cache, type=local,src=path/to/dir)" ,
96
+ },
97
+ & cli.StringSliceFlag {
98
+ Name : "cache-to" ,
99
+ Usage : "Cache export destinations (eg. user/app:cache, type=local,dest=path/to/dir)" ,
100
+ },
87
101
},
88
102
}
89
103
@@ -93,31 +107,37 @@ func buildAction(clicontext *cli.Context) error {
93
107
return err
94
108
}
95
109
96
- buildctlBinary , buildctlArgs , err := generateBuildctlArgs (clicontext )
110
+ buildctlBinary , buildctlArgs , needsLoading , err := generateBuildctlArgs (clicontext )
97
111
if err != nil {
98
112
return err
99
113
}
100
114
115
+ quiet := clicontext .Bool ("quiet" )
116
+
101
117
logrus .Debugf ("running %s %v" , buildctlBinary , buildctlArgs )
102
118
buildctlCmd := exec .Command (buildctlBinary , buildctlArgs ... )
103
119
buildctlCmd .Env = os .Environ ()
104
120
105
- buildctlStdout , err := buildctlCmd .StdoutPipe ()
106
- if err != nil {
107
- return err
121
+ var buildctlStdout io.Reader
122
+ if needsLoading {
123
+ buildctlStdout , err = buildctlCmd .StdoutPipe ()
124
+ if err != nil {
125
+ return err
126
+ }
127
+ } else {
128
+ buildctlCmd .Stdout = clicontext .App .Writer
108
129
}
109
- buildctlCmd .Stderr = clicontext .App .ErrWriter
110
130
111
- if err := buildctlCmd . Start (); err != nil {
112
- return err
131
+ if ! quiet {
132
+ buildctlCmd . Stderr = clicontext . App . ErrWriter
113
133
}
114
134
115
- localBuild , err := isLocalBuild (clicontext )
116
- if err != nil {
135
+ if err := buildctlCmd .Start (); err != nil {
117
136
return err
118
137
}
119
- if ! localBuild {
120
- if err = loadImage (buildctlStdout , clicontext ); err != nil {
138
+
139
+ if needsLoading {
140
+ if err = loadImage (buildctlStdout , clicontext , quiet ); err != nil {
121
141
return err
122
142
}
123
143
}
@@ -129,24 +149,29 @@ func buildAction(clicontext *cli.Context) error {
129
149
return nil
130
150
}
131
151
132
- func generateBuildctlArgs (clicontext * cli.Context ) (string , []string , error ) {
152
+ func generateBuildctlArgs (clicontext * cli.Context ) (string , []string , bool , error ) {
153
+ var needsLoading bool
133
154
if clicontext .NArg () < 1 {
134
- return "" , nil , errors .New ("context needs to be specified" )
155
+ return "" , nil , false , errors .New ("context needs to be specified" )
135
156
}
136
157
buildContext := clicontext .Args ().First ()
137
158
if buildContext == "-" || strings .Contains (buildContext , "://" ) {
138
- return "" , nil , errors .Errorf ("unsupported build context: %q" , buildContext )
159
+ return "" , nil , false , errors .Errorf ("unsupported build context: %q" , buildContext )
139
160
}
140
161
141
162
buildctlBinary , err := buildkitutil .BuildctlBinary ()
142
163
if err != nil {
143
- return "" , nil , err
164
+ return "" , nil , false , err
144
165
}
145
166
146
- output := fmt .Sprintf ("--output=%s" , clicontext .String ("output" ))
167
+ output := clicontext .String ("output" )
168
+ if output == "" {
169
+ output = "type=docker"
170
+ needsLoading = true
171
+ }
147
172
if tagSlice := strutil .DedupeStrSlice (clicontext .StringSlice ("tag" )); len (tagSlice ) > 0 {
148
173
if len (tagSlice ) > 1 {
149
- return "" , nil , errors .Errorf ("specifying multiple -t is not supported yet" )
174
+ return "" , nil , false , errors .Errorf ("specifying multiple -t is not supported yet" )
150
175
}
151
176
output += ",name=" + tagSlice [0 ]
152
177
}
@@ -159,7 +184,7 @@ func generateBuildctlArgs(clicontext *cli.Context) (string, []string, error) {
159
184
"--frontend=dockerfile.v0" ,
160
185
"--local=context=" + buildContext ,
161
186
"--local=dockerfile=" + buildContext ,
162
- output ,
187
+ "--output=" + output ,
163
188
}... )
164
189
165
190
if filename := clicontext .String ("file" ); filename != "" {
@@ -176,6 +201,20 @@ func generateBuildctlArgs(clicontext *cli.Context) (string, []string, error) {
176
201
177
202
for _ , ba := range strutil .DedupeStrSlice (clicontext .StringSlice ("build-arg" )) {
178
203
buildctlArgs = append (buildctlArgs , "--opt=build-arg:" + ba )
204
+
205
+ // Support `--build-arg BUILDKIT_INLINE_CACHE=1` for compatibility with `docker buildx build`
206
+ // https://github.com/docker/buildx/blob/v0.6.3/docs/reference/buildx_build.md#-export-build-cache-to-an-external-cache-destination---cache-to
207
+ if strings .HasPrefix (ba , "BUILDKIT_INLINE_CACHE=" ) {
208
+ bic := strings .TrimPrefix (ba , "BUILDKIT_INLINE_CACHE=" )
209
+ bicParsed , err := strconv .ParseBool (bic )
210
+ if err == nil {
211
+ if bicParsed {
212
+ buildctlArgs = append (buildctlArgs , "--export-cache=type=inline" )
213
+ }
214
+ } else {
215
+ logrus .WithError (err ).Warnf ("invalid BUILDKIT_INLINE_CACHE: %q" , bic )
216
+ }
217
+ }
179
218
}
180
219
181
220
if clicontext .Bool ("no-cache" ) {
@@ -190,18 +229,19 @@ func generateBuildctlArgs(clicontext *cli.Context) (string, []string, error) {
190
229
buildctlArgs = append (buildctlArgs , "--ssh=" + s )
191
230
}
192
231
193
- return buildctlBinary , buildctlArgs , nil
194
- }
195
-
196
- func isLocalBuild (clicontext * cli.Context ) (bool , error ) {
197
- opts , err := strutil .ParseCSVMap (clicontext .String ("output" ))
198
- if err != nil {
199
- return false , err
232
+ for _ , s := range strutil .DedupeStrSlice (clicontext .StringSlice ("cache-from" )) {
233
+ if ! strings .Contains (s , "type=" ) {
234
+ s = "type=registry,ref=" + s
235
+ }
236
+ buildctlArgs = append (buildctlArgs , "--import-cache=" + s )
200
237
}
201
- if v , ok := opts ["type" ]; ok {
202
- if strings .TrimSpace (strings .ToLower (v )) == "local" {
203
- return true , nil
238
+
239
+ for _ , s := range strutil .DedupeStrSlice (clicontext .StringSlice ("cache-to" )) {
240
+ if ! strings .Contains (s , "type=" ) {
241
+ s = "type=registry,ref=" + s
204
242
}
243
+ buildctlArgs = append (buildctlArgs , "--export-cache=" + s )
205
244
}
206
- return false , nil
245
+
246
+ return buildctlBinary , buildctlArgs , needsLoading , nil
207
247
}
0 commit comments