github.com/pdmccormick/importable-docker-buildx@v0.0.0-20240426161518-e47091289030/util/buildflags/export.go (about) 1 package buildflags 2 3 import ( 4 "encoding/csv" 5 "regexp" 6 "strings" 7 8 "github.com/containerd/containerd/platforms" 9 controllerapi "github.com/docker/buildx/controller/pb" 10 "github.com/moby/buildkit/client" 11 "github.com/moby/buildkit/exporter/containerimage/exptypes" 12 ocispecs "github.com/opencontainers/image-spec/specs-go/v1" 13 "github.com/pkg/errors" 14 ) 15 16 func ParseExports(inp []string) ([]*controllerapi.ExportEntry, error) { 17 var outs []*controllerapi.ExportEntry 18 if len(inp) == 0 { 19 return nil, nil 20 } 21 for _, s := range inp { 22 csvReader := csv.NewReader(strings.NewReader(s)) 23 fields, err := csvReader.Read() 24 if err != nil { 25 return nil, err 26 } 27 28 out := controllerapi.ExportEntry{ 29 Attrs: map[string]string{}, 30 } 31 if len(fields) == 1 && fields[0] == s && !strings.HasPrefix(s, "type=") { 32 if s != "-" { 33 outs = append(outs, &controllerapi.ExportEntry{ 34 Type: client.ExporterLocal, 35 Destination: s, 36 }) 37 continue 38 } 39 out = controllerapi.ExportEntry{ 40 Type: client.ExporterTar, 41 Destination: s, 42 } 43 } 44 45 if out.Type == "" { 46 for _, field := range fields { 47 parts := strings.SplitN(field, "=", 2) 48 if len(parts) != 2 { 49 return nil, errors.Errorf("invalid value %s", field) 50 } 51 key := strings.TrimSpace(strings.ToLower(parts[0])) 52 value := parts[1] 53 switch key { 54 case "type": 55 out.Type = value 56 default: 57 out.Attrs[key] = value 58 } 59 } 60 } 61 if out.Type == "" { 62 return nil, errors.Errorf("type is required for output") 63 } 64 65 if out.Type == "registry" { 66 out.Type = client.ExporterImage 67 if _, ok := out.Attrs["push"]; !ok { 68 out.Attrs["push"] = "true" 69 } 70 } 71 72 if dest, ok := out.Attrs["dest"]; ok { 73 out.Destination = dest 74 delete(out.Attrs, "dest") 75 } 76 77 outs = append(outs, &out) 78 } 79 return outs, nil 80 } 81 82 func ParseAnnotations(inp []string) (map[exptypes.AnnotationKey]string, error) { 83 // TODO: use buildkit's annotation parser once it supports setting custom prefix and ":" separator 84 annotationRegexp := regexp.MustCompile(`^(?:([a-z-]+)(?:\[([A-Za-z0-9_/-]+)\])?:)?(\S+)$`) 85 annotations := make(map[exptypes.AnnotationKey]string) 86 for _, inp := range inp { 87 k, v, ok := strings.Cut(inp, "=") 88 if !ok { 89 return nil, errors.Errorf("invalid annotation %q, expected key=value", inp) 90 } 91 92 groups := annotationRegexp.FindStringSubmatch(k) 93 if groups == nil { 94 return nil, errors.Errorf("invalid annotation format, expected <type>:<key>=<value>, got %q", inp) 95 } 96 97 typ, platform, key := groups[1], groups[2], groups[3] 98 switch typ { 99 case "": 100 case exptypes.AnnotationIndex, exptypes.AnnotationIndexDescriptor, exptypes.AnnotationManifest, exptypes.AnnotationManifestDescriptor: 101 default: 102 return nil, errors.Errorf("unknown annotation type %q", typ) 103 } 104 105 var ociPlatform *ocispecs.Platform 106 if platform != "" { 107 p, err := platforms.Parse(platform) 108 if err != nil { 109 return nil, errors.Wrapf(err, "invalid platform %q", platform) 110 } 111 ociPlatform = &p 112 } 113 114 ak := exptypes.AnnotationKey{ 115 Type: typ, 116 Platform: ociPlatform, 117 Key: key, 118 } 119 annotations[ak] = v 120 } 121 return annotations, nil 122 }