diff --git a/go.mod b/go.mod index 3b275b13..bb72bdd8 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( require ( github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/cloudflare/circl v1.3.3 // indirect + github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gkampitakis/ciinfo v0.2.4 // indirect diff --git a/go.sum b/go.sum index aadc4253..93409a19 100644 --- a/go.sum +++ b/go.sum @@ -54,6 +54,8 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -372,6 +374,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/nodejs/__snapshots__/template_test.snap b/internal/nodejs/__snapshots__/template_test.snap index cf0c7ebf..9a309fd9 100755 --- a/internal/nodejs/__snapshots__/template_test.snap +++ b/internal/nodejs/__snapshots__/template_test.snap @@ -5,7 +5,7 @@ FROM node:18 as build ENV PORT=8080 WORKDIR /src -RUN corepack enable && corepack prepare --all +RUN corepack enable RUN yarn install @@ -25,7 +25,7 @@ FROM node:18 as build ENV PORT=8080 WORKDIR /src -RUN corepack enable && corepack prepare --all +RUN corepack enable RUN yarn install @@ -45,7 +45,7 @@ FROM node:18 as build ENV PORT=8080 WORKDIR /src -RUN corepack enable && corepack prepare --all +RUN corepack enable RUN yarn install @@ -65,7 +65,7 @@ FROM node:18 as build ENV PORT=8080 WORKDIR /src -RUN corepack enable && corepack prepare --all +RUN corepack enable RUN yarn install @@ -85,7 +85,7 @@ FROM node:18 as build ENV PORT=8080 WORKDIR /src -RUN corepack enable && corepack prepare --all +RUN corepack enable RUN yarn install @@ -111,7 +111,7 @@ WORKDIR /src COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin COPY --from=bun-runtime /usr/local/bin/bunx /usr/local/bin -RUN corepack enable && corepack prepare --all +RUN corepack enable RUN bun install diff --git a/internal/nodejs/templates/template.Dockerfile b/internal/nodejs/templates/template.Dockerfile index bbe1176d..b32af3d1 100644 --- a/internal/nodejs/templates/template.Dockerfile +++ b/internal/nodejs/templates/template.Dockerfile @@ -14,7 +14,7 @@ COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin COPY --from=bun-runtime /usr/local/bin/bunx /usr/local/bin {{- end }} -RUN corepack enable && corepack prepare --all +RUN corepack enable {{ .InstallCmd }} diff --git a/pkg/zeaburpack/image.go b/pkg/zeaburpack/image.go index bff43065..f4aa2b89 100644 --- a/pkg/zeaburpack/image.go +++ b/pkg/zeaburpack/image.go @@ -1,7 +1,7 @@ package zeaburpack import ( - "bufio" + "bytes" "fmt" "math/rand" "os" @@ -132,7 +132,13 @@ func buildImage(opt *buildImageOptions) error { if opt.PlanMeta["serverless"] == "true" || opt.PlanMeta["outputDir"] != "" || opt.PlanType == types.PlanTypeStatic { buildKitCmd = append(buildKitCmd, "--output", "type=local,dest="+path.Join(os.TempDir(), "zbpack/buildkit")) } else { - o := "type=image,name=" + opt.ResultImage + t := "image" + if !opt.PushImage { + // -> docker registry + t = "docker" + } + + o := "type=" + t + ",name=" + opt.ResultImage if opt.PushImage { o += ",push=true" } @@ -153,52 +159,23 @@ func buildImage(opt *buildImageOptions) error { buildKitCmd = append(buildKitCmd, "--progress", "tty") } - cmd := exec.Command("buildctl", buildKitCmd...) - - if opt.HandleLog == nil { - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - println("failed to run buildctl build: " + err.Error()) - return fmt.Errorf("run buildctl build: %w", err) - } - return nil - } - - errPipe, err := cmd.StderrPipe() - if err != nil { - return fmt.Errorf("get stderr pipe: %w", err) - } - - outPipe, err := cmd.StdoutPipe() + buildctlCmd := exec.Command("buildctl", buildKitCmd...) + buildctlCmd.Stderr = NewHandledWriter(os.Stderr, opt.HandleLog) + output, err := buildctlCmd.Output() if err != nil { - return fmt.Errorf("get stdout pipe: %w", err) + return fmt.Errorf("run buildctl build: %w", err) } - if err := cmd.Start(); err != nil { - return fmt.Errorf("start buildctl build: %w", err) + if opt.PushImage { + return nil // buildctl have handled push } - go func() { - scanner := bufio.NewScanner(errPipe) - for scanner.Scan() { - t := scanner.Text() - println(t) - (*opt.HandleLog)(t) - } - }() - - go func() { - scanner := bufio.NewScanner(outPipe) - for scanner.Scan() { - (*opt.HandleLog)(scanner.Text()) - } - }() - - err = cmd.Wait() - if err != nil { - return fmt.Errorf("wait buildctl build: %w", err) + dockerLoadCmd := exec.Command("docker", "load") + dockerLoadCmd.Stdin = bytes.NewReader(output) + dockerLoadCmd.Stdout = NewHandledWriter(os.Stdout, opt.HandleLog) + dockerLoadCmd.Stderr = NewHandledWriter(os.Stderr, opt.HandleLog) + if err := dockerLoadCmd.Run(); err != nil { + return fmt.Errorf("run docker load: %w", err) } return nil diff --git a/pkg/zeaburpack/writer.go b/pkg/zeaburpack/writer.go new file mode 100644 index 00000000..c8d6714d --- /dev/null +++ b/pkg/zeaburpack/writer.go @@ -0,0 +1,31 @@ +package zeaburpack + +import ( + "github.com/containerd/console" +) + +// HandledWriter is a console-compatible writer that copies the output +// to the specified log and calls handler with the log. +type HandledWriter struct { + console.File + handler func(log string) +} + +// NewHandledWriter creates a new HandledWriter. +func NewHandledWriter(w console.File, handler *func(log string)) console.File { + if handler == nil { + return w + } + + return &HandledWriter{ + File: w, + handler: *handler, + } +} + +func (h HandledWriter) Write(p []byte) (n int, err error) { + go h.handler(string(p)) + return h.File.Write(p) +} + +var _ console.File = (*HandledWriter)(nil) diff --git a/pkg/zeaburpack/writer_test.go b/pkg/zeaburpack/writer_test.go new file mode 100644 index 00000000..1a394db3 --- /dev/null +++ b/pkg/zeaburpack/writer_test.go @@ -0,0 +1,58 @@ +package zeaburpack + +import ( + "testing" + "time" + + "github.com/containerd/console" + "github.com/stretchr/testify/assert" +) + +type MockWriter struct { + written string +} + +func (m *MockWriter) Read(p []byte) (n int, err error) { + n = copy(p, m.written) + return +} + +func (m *MockWriter) Close() error { + return nil +} + +func (m *MockWriter) Fd() uintptr { + return 0 +} + +func (m *MockWriter) Name() string { + return "mock" +} + +func (m *MockWriter) Write(p []byte) (n int, err error) { + m.written = string(p) + return len(p), nil +} + +func (m *MockWriter) Flush() {} + +var _ console.File = &MockWriter{} + +func TestHandledWriter_Write(t *testing.T) { + t.Parallel() + + mockWriter := &MockWriter{} + receivedLog := "" + handler := func(log string) { + receivedLog = log + } + + handledWriter := NewHandledWriter(mockWriter, &handler) + + _, _ = handledWriter.Write([]byte("test")) + // wait for 3 ms – `go h.handler(string(p))` is async + time.Sleep(3 * time.Millisecond) + + assert.Equal(t, "test", mockWriter.written) + assert.Equal(t, "test", receivedLog) +}