@@ -425,7 +425,7 @@ func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solve
425425 pr .Invocation .Parameters .Secrets = nil
426426 pr .Invocation .Parameters .SSH = nil
427427 case "max" :
428- dgsts , err := provenance . AddBuildConfig (ctx , pr , res )
428+ dgsts , err := AddBuildConfig (ctx , pr , res )
429429 if err != nil {
430430 return nil , err
431431 }
@@ -571,3 +571,157 @@ func resolveRemotes(ctx context.Context, res solver.Result) ([]*solver.Remote, e
571571 }
572572 return remotes , nil
573573}
574+
575+ func AddBuildConfig (ctx context.Context , p * provenance.ProvenancePredicate , rp solver.ResultProxy ) (map [digest.Digest ]int , error ) {
576+ def := rp .Definition ()
577+ steps , indexes , err := toBuildSteps (def )
578+ if err != nil {
579+ return nil , err
580+ }
581+
582+ bc := & provenance.BuildConfig {
583+ Definition : steps ,
584+ DigestMapping : digestMap (indexes ),
585+ }
586+
587+ p .BuildConfig = bc
588+
589+ if def .Source != nil {
590+ sis := make ([]provenance.SourceInfo , len (def .Source .Infos ))
591+ for i , si := range def .Source .Infos {
592+ steps , indexes , err := toBuildSteps (si .Definition )
593+ if err != nil {
594+ return nil , err
595+ }
596+ s := provenance.SourceInfo {
597+ Filename : si .Filename ,
598+ Data : si .Data ,
599+ Language : si .Language ,
600+ Definition : steps ,
601+ DigestMapping : digestMap (indexes ),
602+ }
603+ sis [i ] = s
604+ }
605+
606+ if len (def .Source .Infos ) != 0 {
607+ locs := map [string ]* pb.Locations {}
608+ for k , l := range def .Source .Locations {
609+ idx , ok := indexes [digest .Digest (k )]
610+ if ! ok {
611+ continue
612+ }
613+ locs [fmt .Sprintf ("step%d" , idx )] = l
614+ }
615+
616+ if p .Metadata == nil {
617+ p .Metadata = & provenance.ProvenanceMetadata {}
618+ }
619+ p .Metadata .BuildKitMetadata .Source = & provenance.Source {
620+ Infos : sis ,
621+ Locations : locs ,
622+ }
623+ }
624+ }
625+
626+ return indexes , nil
627+ }
628+
629+ func digestMap (idx map [digest.Digest ]int ) map [digest.Digest ]string {
630+ m := map [digest.Digest ]string {}
631+ for k , v := range idx {
632+ m [k ] = fmt .Sprintf ("step%d" , v )
633+ }
634+ return m
635+ }
636+
637+ func toBuildSteps (def * pb.Definition ) ([]provenance.BuildStep , map [digest.Digest ]int , error ) {
638+ if def == nil || len (def .Def ) == 0 {
639+ return nil , nil , nil
640+ }
641+
642+ ops := make (map [digest.Digest ]* pb.Op )
643+ defs := make (map [digest.Digest ][]byte )
644+
645+ var dgst digest.Digest
646+ for _ , dt := range def .Def {
647+ var op pb.Op
648+ if err := (& op ).Unmarshal (dt ); err != nil {
649+ return nil , nil , errors .Wrap (err , "failed to parse llb proto op" )
650+ }
651+ if src := op .GetSource (); src != nil {
652+ for k := range src .Attrs {
653+ if k == "local.session" || k == "local.unique" {
654+ delete (src .Attrs , k )
655+ }
656+ }
657+ }
658+ dgst = digest .FromBytes (dt )
659+ ops [dgst ] = & op
660+ defs [dgst ] = dt
661+ }
662+
663+ if dgst == "" {
664+ return nil , nil , nil
665+ }
666+
667+ // depth first backwards
668+ dgsts := make ([]digest.Digest , 0 , len (def .Def ))
669+ op := ops [dgst ]
670+
671+ if op .Op != nil {
672+ return nil , nil , errors .Errorf ("invalid last vertex: %T" , op .Op )
673+ }
674+
675+ if len (op .Inputs ) != 1 {
676+ return nil , nil , errors .Errorf ("invalid last vertex inputs: %v" , len (op .Inputs ))
677+ }
678+
679+ visited := map [digest.Digest ]struct {}{}
680+ dgsts , err := walkDigests (dgsts , ops , dgst , visited )
681+ if err != nil {
682+ return nil , nil , err
683+ }
684+ indexes := map [digest.Digest ]int {}
685+ for i , dgst := range dgsts {
686+ indexes [dgst ] = i
687+ }
688+
689+ out := make ([]provenance.BuildStep , 0 , len (dgsts ))
690+ for i , dgst := range dgsts {
691+ op := * ops [dgst ]
692+ inputs := make ([]string , len (op .Inputs ))
693+ for i , inp := range op .Inputs {
694+ inputs [i ] = fmt .Sprintf ("step%d:%d" , indexes [inp .Digest ], inp .Index )
695+ }
696+ op .Inputs = nil
697+ out = append (out , provenance.BuildStep {
698+ ID : fmt .Sprintf ("step%d" , i ),
699+ Inputs : inputs ,
700+ Op : op ,
701+ })
702+ }
703+ return out , indexes , nil
704+ }
705+
706+ func walkDigests (dgsts []digest.Digest , ops map [digest.Digest ]* pb.Op , dgst digest.Digest , visited map [digest.Digest ]struct {}) ([]digest.Digest , error ) {
707+ if _ , ok := visited [dgst ]; ok {
708+ return dgsts , nil
709+ }
710+ op , ok := ops [dgst ]
711+ if ! ok {
712+ return nil , errors .Errorf ("failed to find input %v" , dgst )
713+ }
714+ if op == nil {
715+ return nil , errors .Errorf ("invalid nil input %v" , dgst )
716+ }
717+ visited [dgst ] = struct {}{}
718+ for _ , inp := range op .Inputs {
719+ var err error
720+ dgsts , err = walkDigests (dgsts , ops , inp .Digest , visited )
721+ if err != nil {
722+ return nil , err
723+ }
724+ }
725+ dgsts = append (dgsts , dgst )
726+ return dgsts , nil
727+ }
0 commit comments