github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/cmd/buildkitapi/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "flag" 7 "io" 8 "log" 9 "net" 10 "os" 11 "path/filepath" 12 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/client" 15 "github.com/docker/docker/pkg/jsonmessage" 16 controlapi "github.com/moby/buildkit/api/services/control" 17 "github.com/moby/buildkit/identity" 18 "github.com/moby/buildkit/session" 19 "github.com/moby/buildkit/session/filesync" 20 "github.com/pkg/errors" 21 "github.com/tonistiigi/fsutil" 22 fsutiltypes "github.com/tonistiigi/fsutil/types" 23 ) 24 25 var useCache bool 26 var useLegacyAPI bool 27 var contextDir string 28 29 // A small utility for running Buildkit on the dockerfile 30 // in the current directory printing out all the buildkit api 31 // response protobufs. 32 func main() { 33 flag.BoolVar(&useCache, "cache", false, "Enable docker caching") 34 flag.BoolVar(&useLegacyAPI, "legacy", false, "Print legacy build events") 35 flag.StringVar(&contextDir, "context", "", "Context directory") 36 flag.Parse() 37 38 err := run() 39 if err != nil { 40 log.Fatal(err) 41 } 42 } 43 44 func run() error { 45 ctx := context.Background() 46 d, err := client.NewEnvClient() 47 if err != nil { 48 return err 49 } 50 51 d.NegotiateAPIVersion(ctx) 52 53 session, err := session.NewSession(ctx, "tilt", identity.NewID()) 54 if err != nil { 55 return err 56 } 57 58 fileMap := func(path string, s *fsutiltypes.Stat) fsutil.MapResult { 59 s.Uid = 0 60 s.Gid = 0 61 return fsutil.MapResultKeep 62 } 63 64 dir, _ := os.Getwd() 65 if contextDir == "" { 66 contextDir = dir 67 } 68 if !filepath.IsAbs(contextDir) { 69 contextDir = filepath.Join(dir, contextDir) 70 } 71 session.Allow(filesync.NewFSSyncProvider(filesync.StaticDirSource{ 72 "context": filesync.SyncedDir{ 73 Dir: contextDir, 74 Map: fileMap, 75 }, 76 "dockerfile": filesync.SyncedDir{ 77 Dir: dir, 78 }, 79 })) 80 81 go func() { 82 defer func() { 83 _ = session.Close() 84 }() 85 86 // Start the server 87 dialSession := func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { 88 return d.DialHijack(ctx, "/session", proto, meta) 89 } 90 _ = session.Run(ctx, dialSession) 91 }() 92 93 opts := types.ImageBuildOptions{} 94 opts.Version = types.BuilderBuildKit 95 opts.Dockerfile = "Dockerfile" 96 opts.RemoteContext = "client-session" 97 opts.SessionID = session.ID() 98 if !useCache { 99 opts.NoCache = true 100 } 101 defer session.Close() 102 103 response, err := d.ImageBuild(ctx, nil, opts) 104 if err != nil { 105 return err 106 } 107 defer func() { 108 _ = response.Body.Close() 109 }() 110 111 return readDockerOutput(ctx, response.Body) 112 } 113 114 func readDockerOutput(ctx context.Context, reader io.Reader) error { 115 decoder := json.NewDecoder(reader) 116 117 for decoder.More() { 118 message := jsonmessage.JSONMessage{} 119 err := decoder.Decode(&message) 120 if err != nil { 121 return errors.Wrap(err, "decoding docker output") 122 } 123 124 isFromBuildkit := messageIsFromBuildkit(message) 125 if isFromBuildkit && !useLegacyAPI { 126 err := writeBuildkitStatus(message.Aux) 127 if err != nil { 128 return err 129 } 130 } else if !isFromBuildkit && useLegacyAPI { 131 err := json.NewEncoder(os.Stdout).Encode(message) 132 if err != nil { 133 return err 134 } 135 } 136 } 137 return nil 138 } 139 140 func writeBuildkitStatus(aux *json.RawMessage) error { 141 var resp controlapi.StatusResponse 142 var dt []byte 143 if err := json.Unmarshal(*aux, &dt); err != nil { 144 return err 145 } 146 if err := (&resp).Unmarshal(dt); err != nil { 147 return err 148 } 149 150 return json.NewEncoder(os.Stdout).Encode(resp) 151 } 152 153 func messageIsFromBuildkit(msg jsonmessage.JSONMessage) bool { 154 return msg.ID == "moby.buildkit.trace" 155 }