@@ -56,6 +56,7 @@ type configOptions struct {
5656 noConsistency bool
5757 variables bool
5858 environment bool
59+ lockImageDigests bool
5960}
6061
6162func (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 {
9899 if p .Compatibility {
99100 opts .noNormalize = true
100101 }
102+ if opts .lockImageDigests {
103+ opts .resolveImageDigests = true
104+ }
101105 return nil
102106 }),
103107 RunE : Adapt (func (ctx context.Context , args []string ) error {
@@ -133,6 +137,7 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
133137 flags := cmd .Flags ()
134138 flags .StringVar (& opts .Format , "format" , "" , "Format the output. Values: [yaml | json]" )
135139 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" )
136141 flags .BoolVarP (& opts .quiet , "quiet" , "q" , false , "Only validate the configuration, don't print anything" )
137142 flags .BoolVar (& opts .noInterpolate , "no-interpolate" , false , "Don't interpolate environment variables" )
138143 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
208213 }
209214 }
210215
216+ if opts .lockImageDigests {
217+ project = imagesOnly (project )
218+ }
219+
211220 var content []byte
212221 switch opts .Format {
213222 case "json" :
@@ -223,6 +232,18 @@ func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts confi
223232 return content , nil
224233}
225234
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+
226247func runConfigNoInterpolate (ctx context.Context , dockerCli command.Cli , opts configOptions , services []string ) ([]byte , error ) {
227248 // we can't use ToProject, so the model we render here is only partially resolved
228249 model , err := opts .ToModel (ctx , dockerCli , services )
@@ -237,6 +258,23 @@ func runConfigNoInterpolate(ctx context.Context, dockerCli command.Cli, opts con
237258 }
238259 }
239260
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+
240278 return formatModel (model , opts .Format )
241279}
242280
0 commit comments