Skip to content

Commit 283bd44

Browse files
committed
[wip] capture output from run to use elsewhere
1 parent 134b46d commit 283bd44

File tree

6 files changed

+108
-9
lines changed

6 files changed

+108
-9
lines changed

builtin/builtin.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codegen/chain.go

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,13 +349,22 @@ func (cg *CodeGen) EmitFilesystemBuiltinChainStmt(ctx context.Context, scope *pa
349349
// to be in the context of a specific function run is in.
350350
case with.Expr.FuncLit != nil:
351351
for _, stmt := range with.Expr.FuncLit.Body.NonEmptyStmts() {
352-
if stmt.Call.Func.Name() != "mount" || stmt.Call.Alias == nil {
352+
if stmt.Call.Alias == nil {
353353
continue
354354
}
355355

356-
target, err := cg.EmitStringExpr(ctx, scope, stmt.Call.Args[1])
357-
if err != nil {
358-
return fc, err
356+
var target string
357+
switch stmt.Call.Func.Name() {
358+
case "mount":
359+
target, err = cg.EmitStringExpr(ctx, scope, stmt.Call.Args[1])
360+
if err != nil {
361+
return fc, err
362+
}
363+
case "capture":
364+
target = "capture"
365+
}
366+
if target == "" {
367+
continue
359368
}
360369

361370
calls[target] = stmt.Call
@@ -381,7 +390,43 @@ func (cg *CodeGen) EmitFilesystemBuiltinChainStmt(ctx context.Context, scope *pa
381390
for _, target := range targets {
382391
// Mounts are unique by its mountpoint, and its vertex representing the
383392
// mount after execing can be aliased.
384-
cont := ac(calls[target], exec.GetMount(target))
393+
var cont bool
394+
switch calls[target].Func.Name() {
395+
case "mount":
396+
cont = ac(calls[target], exec.GetMount(target))
397+
case "capture":
398+
cont = ac(calls[target], func() (string, error) {
399+
st := exec.Root()
400+
pw := cg.mw.WithPrefix("", false)
401+
402+
s, err := cg.newSession(ctx)
403+
if err != nil {
404+
return "", err
405+
}
406+
407+
g, ctx := errgroup.WithContext(ctx)
408+
409+
g.Go(func() error {
410+
return s.Run(ctx, cg.cln.Dialer())
411+
})
412+
413+
var captureBuf strings.Builder
414+
g.Go(func() error {
415+
opts, err := cg.SolveOptions(ctx, st)
416+
if err != nil {
417+
return err
418+
}
419+
opts = append(opts, solver.WithOutputCapture(&captureBuf))
420+
def, err := st.Marshal(ctx, llb.LinuxAmd64)
421+
if err != nil {
422+
return err
423+
}
424+
return solver.Solve(ctx, cg.cln, s, pw, def, opts...)
425+
})
426+
err = g.Wait()
427+
return captureBuf.String(), err
428+
})
429+
}
385430
if !cont {
386431
return exec.Root(), ErrAliasReached
387432
}
@@ -940,11 +985,13 @@ func (cg *CodeGen) EmitStringChainStmt(ctx context.Context, scope *parser.Scope,
940985
return nil, err
941986
}
942987
return func(_ string) (string, error) {
943-
str, ok := v.(string)
944-
if !ok {
945-
return str, errors.WithStack(ErrCodeGen{obj.Node, ErrBadCast})
988+
switch s := v.(type) {
989+
case string:
990+
return s, nil
991+
case func() (string, error):
992+
return s()
946993
}
947-
return str, nil
994+
return "", errors.WithStack(ErrCodeGen{obj.Node, ErrBadCast})
948995
}, nil
949996
}
950997
}

codegen/codegen.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,8 @@ func (cg *CodeGen) EmitExecOptions(ctx context.Context, scope *parser.Scope, op
10751075
opts = append(opts, llb.Security(securityMode))
10761076
case "shlex":
10771077
opts = append(opts, &shlexOption{})
1078+
case "capture":
1079+
// no op, only relevant if aliased, handled in alias callback
10781080
case "host":
10791081
host, err := cg.EmitStringExpr(ctx, scope, args[0])
10801082
if err != nil {

docs/reference.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ If more than one arg is given, it will be executed directly, without a shell.
480480
#!hlb
481481
fs default() {
482482
run "arg" with option {
483+
capture
483484
dir "path"
484485
env "key" "value"
485486
forward "src" "dest"
@@ -497,6 +498,11 @@ If more than one arg is given, it will be executed directly, without a shell.
497498
}
498499

499500

501+
#### <span class='hlb-type'>option::run</span> <span class='hlb-name'>capture</span>()
502+
503+
504+
505+
500506
#### <span class='hlb-type'>option::run</span> <span class='hlb-name'>dir</span>(<span class='hlb-type'>string</span> <span class='hlb-variable'>path</span>)
501507

502508
!!! info "<span class='hlb-type'>string</span> <span class='hlb-variable'>path</span>"

language/builtin.hlb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ fs shell(variadic string arg)
120120
# @return the filesystem after the command has executed.
121121
fs run(variadic string arg)
122122

123+
option::run capture()
124+
123125
# Sets the rootfs as read-only for the duration of the run command.
124126
#
125127
# @return an option to set the rootfs as read-only.

solver/solve.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package solver
33
import (
44
"context"
55
"encoding/json"
6+
"io"
67

78
"github.com/docker/buildx/util/progress"
89
"github.com/moby/buildkit/client"
@@ -26,6 +27,7 @@ type SolveInfo struct {
2627
Callbacks []func() error `json:"-"`
2728
ImageSpec *specs.Image
2829
Entitlements []entitlements.Entitlement
30+
OutputCapture io.Writer
2931
}
3032

3133
func WithDownloadDockerTarball(ref string) SolveOption {
@@ -63,6 +65,13 @@ func WithDownloadOCITarball() SolveOption {
6365
}
6466
}
6567

68+
func WithOutputCapture(w io.Writer) SolveOption {
69+
return func(info *SolveInfo) error {
70+
info.OutputCapture = w
71+
return nil
72+
}
73+
}
74+
6675
func WithCallback(fn func() error) SolveOption {
6776
return func(info *SolveInfo) error {
6877
info.Callbacks = append(info.Callbacks, fn)
@@ -174,6 +183,36 @@ func Build(ctx context.Context, c *client.Client, s *session.Session, pw progres
174183
statusCh = pw.Status()
175184
}
176185

186+
if info.OutputCapture != nil {
187+
captureStatusCh := make(chan *client.SolveStatus)
188+
go func(origStatusCh chan *client.SolveStatus) {
189+
defer func() {
190+
if origStatusCh != nil {
191+
close(origStatusCh)
192+
}
193+
}()
194+
for {
195+
select {
196+
case <-pw.Done():
197+
return
198+
case <-ctx.Done():
199+
return
200+
case status, ok := <-captureStatusCh:
201+
if !ok {
202+
return
203+
}
204+
for _, log := range status.Logs {
205+
info.OutputCapture.Write(log.Data)
206+
}
207+
if origStatusCh != nil {
208+
origStatusCh <- status
209+
}
210+
}
211+
}
212+
}(statusCh)
213+
statusCh = captureStatusCh
214+
}
215+
177216
g.Go(func() error {
178217
_, err := c.Build(ctx, solveOpt, "", f, statusCh)
179218
return err

0 commit comments

Comments
 (0)