@@ -56,6 +56,7 @@ type configOptions struct {
56
56
noConsistency bool
57
57
variables bool
58
58
environment bool
59
+ lockImageDigests bool
59
60
}
60
61
61
62
func (o * configOptions ) ToProject (ctx context.Context , dockerCli command.Cli , services []string , po ... cli.ProjectOptionsFn ) (* types.Project , error ) {
@@ -98,6 +99,9 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
98
99
if p .Compatibility {
99
100
opts .noNormalize = true
100
101
}
102
+ if opts .lockImageDigests {
103
+ opts .resolveImageDigests = true
104
+ }
101
105
return nil
102
106
}),
103
107
RunE : Adapt (func (ctx context.Context , args []string ) error {
@@ -133,6 +137,7 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
133
137
flags := cmd .Flags ()
134
138
flags .StringVar (& opts .Format , "format" , "" , "Format the output. Values: [yaml | json]" )
135
139
flags .BoolVar (& opts .resolveImageDigests , "resolve-image-digests" , false , "Pin image tags to digests" )
140
+ flags .BoolVar (& opts .lockImageDigests , "lock-image-digests" , false , "Produces an override file with image digests" )
136
141
flags .BoolVarP (& opts .quiet , "quiet" , "q" , false , "Only validate the configuration, don't print anything" )
137
142
flags .BoolVar (& opts .noInterpolate , "no-interpolate" , false , "Don't interpolate environment variables" )
138
143
flags .BoolVar (& opts .noNormalize , "no-normalize" , false , "Don't normalize compose model" )
@@ -208,6 +213,10 @@ func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts confi
208
213
}
209
214
}
210
215
216
+ if opts .lockImageDigests {
217
+ project = imagesOnly (project )
218
+ }
219
+
211
220
var content []byte
212
221
switch opts .Format {
213
222
case "json" :
@@ -223,6 +232,18 @@ func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts confi
223
232
return content , nil
224
233
}
225
234
235
+ // imagesOnly return project with all attributes removed but service.images
236
+ func imagesOnly (project * types.Project ) * types.Project {
237
+ digests := types.Services {}
238
+ for name , config := range project .Services {
239
+ digests [name ] = types.ServiceConfig {
240
+ Image : config .Image ,
241
+ }
242
+ }
243
+ project = & types.Project {Services : digests }
244
+ return project
245
+ }
246
+
226
247
func runConfigNoInterpolate (ctx context.Context , dockerCli command.Cli , opts configOptions , services []string ) ([]byte , error ) {
227
248
// we can't use ToProject, so the model we render here is only partially resolved
228
249
model , err := opts .ToModel (ctx , dockerCli , services )
@@ -237,6 +258,23 @@ func runConfigNoInterpolate(ctx context.Context, dockerCli command.Cli, opts con
237
258
}
238
259
}
239
260
261
+ if opts .lockImageDigests {
262
+ for key , e := range model {
263
+ if key != "services" {
264
+ delete (model , key )
265
+ } else {
266
+ for _ , s := range e .(map [string ]any ) {
267
+ service := s .(map [string ]any )
268
+ for key := range service {
269
+ if key != "image" {
270
+ delete (service , key )
271
+ }
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
277
+
240
278
return formatModel (model , opts .Format )
241
279
}
242
280
0 commit comments