dap: support evaluate request to invoke a container

Supports using the `evaluate` request in REPL mode to start a container
with the `exec` command. Presently doesn't support any arguments.

This improves the dap server so it is capable of sending reverse
requests and receiving the response. It also adds a hidden command
`dap attach` that attaches to the socket created by `evaluate`.

This requires the client to support `runInTerminal`.

Likely needs some additional work to make sure resources are cleaned up
cleanly especially when the build is unpaused or terminated, but it
should work as a decent base.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
This commit is contained in:
Jonathan A. Sternberg
2025-07-15 10:45:25 -05:00
parent d5b9564c04
commit 3453f3b00a
6 changed files with 280 additions and 17 deletions
+43
View File
@@ -2,12 +2,17 @@ package commands
import (
"context"
"io"
"net"
"os"
"github.com/containerd/console"
"github.com/docker/buildx/dap"
"github.com/docker/buildx/dap/common"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/buildx/util/ioset"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -24,6 +29,8 @@ func dapCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
dapBuildCmd := buildCmd(dockerCli, rootOpts, &options)
dapBuildCmd.Args = cobra.RangeArgs(0, 1)
cmd.AddCommand(dapBuildCmd)
cmd.AddCommand(dapAttachCmd())
return cmd
}
@@ -71,3 +78,39 @@ func (d *adapterProtocolDebugger) Stop() error {
defer d.conn.Close()
return d.Adapter.Stop()
}
func dapAttachCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "attach PATH",
Short: "Attach to a container created by the dap evaluate request",
Args: cli.ExactArgs(1),
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
c, err := console.ConsoleFromFile(os.Stdout)
if err != nil {
return err
}
if err := c.SetRaw(); err != nil {
return err
}
conn, err := net.Dial("unix", args[0])
if err != nil {
return err
}
fwd := ioset.NewSingleForwarder()
fwd.SetReader(os.Stdin)
fwd.SetWriter(conn, func() io.WriteCloser {
return conn
})
if _, err := io.Copy(os.Stdout, conn); err != nil && !errors.Is(err, io.EOF) {
return err
}
return nil
},
}
return cmd
}