Repository: docker/buildx

Issue:
host-gateway no longer supported in docker build
### Description

Hi all, I've noticed that once I upgraded my docker to the new version I'm unable to use `host-gateway` during the build process.

I get the following error when using the `--add-host=host.docker.internal:host-gateway` argument for `docker build`.

```
[+] Building 0.0s (0/0)
 ERROR: failed to solve: failed to parse additional hosts: failed to parse IP host-gateway
```

This used to work before the upgrade and what's interesting is that the `docker run` command and even `docker comnpose` still support the argument.

### Reproduce

1. Run a docker build command with the `--add-host=host.docker.internal:host-gateway`

For example

```
docker build --add-host=host.docker.internal:host-gateway .
```

### Expected behavior

`docker build` is able to use the `host-gateway` special string to bridge the host network with the containers network.

### docker version

```bash
Client: Docker Engine - Community
  Version:           23.0.0
  API version:       1.42
  Go version:        go1.19.5
  Git commit:        e92dd87
  Built:             Wed Feb  1 17:49:08 2023
  OS/Arch:           linux/amd64
  Context:           default
 
 Server: Docker Engine - Community
  Engine:
   Version:          23.0.0
   API version:      1.42 (minimum version 1.12)
   Go version:       go1.19.5
   Git commit:       d7573ab
   Built:            Wed Feb  1 17:49:08 2023
   OS/Arch:          linux/amd64
   Experimental:     false
  containerd:
   Version:          1.6.16
   GitCommit:        31aa4358a36870b21a992d3ad2bef29e1d693bec
  runc:
   Version:          1.1.4
   GitCommit:        v1.1.4-0-g5fd4c4d
  docker-init:
   Version:          0.19.0
   GitCommit:        de40ad0
```


### docker info

```bash
Client:
  Context:    default
  Debug Mode: false
  Plugins:
   buildx: Docker Buildx (Docker Inc.)
     Version:  v0.10.2
     Path:     /usr/libexec/docker/cli-plugins/docker-buildx
   compose: Docker Compose (Docker Inc.)
     Version:  v2.15.1
     Path:     /usr/libexec/docker/cli-plugins/docker-compose
   scan: Docker Scan (Docker Inc.)
     Version:  v0.23.0
     Path:     /usr/libexec/docker/cli-plugins/docker-scan
 
 Server:
  Containers: 9
   Running: 7
   Paused: 0
   Stopped: 2
  Images: 115
  Server Version: 23.0.0
  Storage Driver: overlay2
   Backing Filesystem: extfs
   Supports d_type: true
   Using metacopy: false
   Native Overlay Diff: true
   userxattr: false
  Logging Driver: json-file
  Cgroup Driver: cgroupfs
  Cgroup Version: 1
  Plugins:
   Volume: local
   Network: bridge host ipvlan macvlan null overlay
   Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
  Swarm: inactive
  Runtimes: io.containerd.runc.v2 runc
  Default Runtime: runc
  Init Binary: docker-init
  containerd version: 31aa4358a36870b21a992d3ad2bef29e1d693bec
  runc version: v1.1.4-0-g5fd4c4d
  init version: de40ad0
  Security Options:
   apparmor
   seccomp
    Profile: builtin
  Kernel Version: 5.4.0-121-generic
  Operating System: Ubuntu 20.04.5 LTS
  OSType: linux
  Architecture: x86_64
  CPUs: 4
  Total Memory: 3.666GiB
  Name: dev-testing-amlesh
  ID: HYQS:EF3Y:2IJR:XJHT:IPNI:2M3H:SPSX:3UBG:7OFQ:SE77:7U7J:WHYU
  Docker Root Dir: /var/lib/docker
  Debug Mode: false
  Registry: https://index.docker.io/v1/
  Experimental: false
  Insecure Registries:
   127.0.0.0/8
  Live Restore Enabled: false
 
 WARNING: No swap limit support
```


### Additional Info

_No response_

Discussion:
@bsousaa: There are changes in this area that could impact this functionality: 
- https://github.com/moby/moby/pull/44574 
- https://github.com/moby/moby/pull/44537

cc @crazy-max 
@zamlz: Thanks @bsousaa for sharing those two PRs. I'll read into those and see if explains why this functionality doesn't work anymore or if it's being added back in! While I go through that, could I get some clarification on what the `moby/moby` project is really and how it relates to docker?@zamlz: @bsousaa finished reviewing those two PR threads. Unless I'm misunderstanding something, it looks like both of them are adding the functionality I'm describing that appears to be missing my docker build command (one is a backport for the newer version).

```go
// If the IP Address is a "host-gateway", replace this value with the
// IP address stored in the daemon level HostGatewayIP config variable.
if ip == opts.HostGatewayName {
    gateway := hostGatewayIP.String()
    if gateway == "" {
        return "", fmt.Errorf("unable to derive the IP value for host-gateway")
    }
    ip = gateway
}
```@thaJeztah: Yes, those pull requests were meant to fix issues around this (and are part of the 23.0 release). It's possible though that other changes are still needed to either BuildKit, or `buildx` (which provides the client for `docker build`), or the Dockefile front-end (parser).

> Could I get some clarification on what the moby/moby project is really and how it relates to docker?

Docker uses a client/server model with an API. Docker Consists of various components. On a Linux installation of Docker Engine;

- The `docker/cli` (this repository) repository contains the source code for the command-line client (`docker`). There's additional plugins installed that handle specific parts of the CLI (`docker compose`, `docker build` and `docker buildx`).
- The `moby/moby` repository contains the source-code used to build the Docker Engine (the `dockerd` "daemon"), which provides the API, and contains the server-side components to run your containers, pull images, and build images.
- For building images, the `dockerd` daemon contains the "builder", which is code embedded from the `moby/buildkit` repository.
- In addition, there's further "lower level" components, such as `containerd` (OCI intermediate runtime), and `runc` (OCI runtime)

All of the above is Open Source. If you run Docker Desktop, there's additional components and features, some of which are not open source, which includes a VM and LinuxKit Linux distribution (to allow you to run Linux containers on macOS and Windows, and provide additional security boundaries), but also includes a GUI / Dashboard, Management tools, Extensions, etc.
@zamlz: Thank you @thaJeztah for the explanation. That all makes sense. 

That is unfortunate that this feature is broken at the moment. Can we confirm that this is a bug that is effecting multiple people and not just me? This will help me decide whether I should focus on debugging my system/package configuration on ubuntu or into looking for a temporary workaround.

Your work is much appreciated!@crazy-max: > @bsousaa finished reviewing those two PR threads. Unless I'm misunderstanding something, it looks like both of them are adding the functionality I'm describing that appears to be missing my docker build command (one is a backport for the newer version).
> 
> ```go
> // If the IP Address is a "host-gateway", replace this value with the
> // IP address stored in the daemon level HostGatewayIP config variable.
> if ip == opts.HostGatewayName {
>     gateway := hostGatewayIP.String()
>     if gateway == "" {
>         return "", fmt.Errorf("unable to derive the IP value for host-gateway")
>     }
>     ip = gateway
> }
> ```

There's something fishy. You're on Docker 23.0.0 and you're using Buildx 0.10.2 so it should work as intended.

Can you show me the output of `docker build --help` and also `docker buildx ls` please?@zamlz: Thanks @crazy-max for taking a look at this!

Here is the output of `docker build --help`

```
Usage:  docker buildx build [OPTIONS] PATH | URL | -

Start a build

Aliases:
  docker buildx build, docker buildx b

Options:
      --add-host strings              Add a custom host-to-IP mapping (format: "host:ip")
      --allow strings                 Allow extra privileged entitlement (e.g., "network.host", "security.insecure")
      --attest stringArray            Attestation parameters (format: "type=sbom,generator=image")
      --build-arg stringArray         Set build-time variables
      --build-context stringArray     Additional build contexts (e.g., name=path)
      --builder string                Override the configured builder instance (default "default")
      --cache-from stringArray        External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")
      --cache-to stringArray          Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")
      --cgroup-parent string          Optional parent cgroup for the container
  -f, --file string                   Name of the Dockerfile (default: "PATH/Dockerfile")
      --iidfile string                Write the image ID to the file
      --label stringArray             Set metadata for an image
      --load                          Shorthand for "--output=type=docker"
      --metadata-file string          Write build result metadata to the file
      --network string                Set the networking mode for the "RUN" instructions during build (default "default")
      --no-cache                      Do not use cache when building the image
      --no-cache-filter stringArray   Do not cache specified stages
  -o, --output stringArray            Output destination (format: "type=local,dest=path")
      --platform stringArray          Set target platform for build
      --progress string               Set type of progress output ("auto", "plain", "tty"). Use plain to show container output (default "auto")
      --provenance string             Shortand for "--attest=type=provenance"
      --pull                          Always attempt to pull all referenced images
      --push                          Shorthand for "--output=type=registry"
  -q, --quiet                         Suppress the build output and print image ID on success
      --sbom string                   Shorthand for "--attest=type=sbom"
      --secret stringArray            Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")
      --shm-size bytes                Size of "/dev/shm"
      --ssh stringArray               SSH agent socket or keys to expose to the build (format: "default|<id>[=<socket>|<key>[,<key>]]")
  -t, --tag stringArray               Name and optionally a tag (format: "name:tag")
      --target string                 Set the target build stage to build
      --ulimit ulimit                 Ulimit options (default [])
```

And here is the output of `docker buildx ls`

```
NAME/NODE DRIVER/ENDPOINT STATUS  BUILDKIT PLATFORMS
default * docker
  default default         running 23.0.0   linux/amd64, linux/amd64/v2, linux/386
```
@crazy-max: Ok it seems to be a bug, I was able to repro on current 23.0.1:

```
$ docker build --add-host=host.docker.internal:host-gateway .
[+] Building 0.0s (0/0)                                                                                                                                                                                                                       
ERROR: failed to solve: failed to parse additional hosts: failed to parse IP host-gateway
```

I will take a look.@crazy-max: It seems https://github.com/docker/buildx/pull/1446 and https://github.com/moby/moby/pull/44537 are not enough. The error `failed to parse additional hosts` comes from BuildKit when build is forwarded through the gateway: https://github.com/moby/buildkit/blob/v0.10/frontend/dockerfile/builder/build.go#L122.@dboreham: Also affected by this. Presumably there is no test coverage for this feature, since it doesn't work in 23.0.0 and 23.0.1?@neersighted: @zamlz There were some improvements here with BuildKit 0.11 (shipped in v24.0.y) -- could you please try reproducing again on e.g. 24.0.1?@dboreham: fwiw, problem is still present in 24.0.1 for me:

```
$ docker version
Client: Docker Engine - Community
 Version:           24.0.1
 API version:       1.43
 Go version:        go1.20.4
 Git commit:        6802122
 Built:             Fri May 19 18:06:21 2023
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          24.0.1
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.4
  Git commit:       463850e
  Built:            Fri May 19 18:06:21 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.21
  GitCommit:        3dce8eb055cbb6872793272b4f20ed16117344f8
 runc:
  Version:          1.1.7
  GitCommit:        v1.1.7-0-g860f061
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

Then:

```
ERROR: failed to solve: failed to parse additional hosts: failed to parse IP host-gateway
```@PedramMarandi: I still have the same issue as well. 

```

(base) ➜  kubernetes git:(master) ✗ docker version
Client: Docker Engine - Community
 Version:           24.0.1
 API version:       1.43
 Go version:        go1.20.4
 Git commit:        6802122
 Built:             Fri May 19 18:06:21 2023
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          24.0.1
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.4
  Git commit:       463850e
  Built:            Fri May 19 18:06:21 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.21
  GitCommit:        3dce8eb055cbb6872793272b4f20ed16117344f8
 runc:
  Version:          1.1.7
  GitCommit:        v1.1.7-0-g860f061
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```
@zamlz: @neersighted Thank you! I will be able to check this tomorrow! Unfortunately, it looks like others have gotten to it before me and it appears that it hasn't been fixed yet. I will still confirm on my end.@thaJeztah: Thanks for trying! So from a quick glance,  I think https://github.com/moby/moby/pull/44537 would've fixed the situation where the CLI would use the JSON / REST API to perform builds, but buildx uses the BuildKit gRPC API, so probably some fix elsewhere is still needed now that the default is to use the gRPC API for BuildKit builds 🤔 @dvictori: I'm facing this same issue, using docker 24.0.1 on Ubuntu. I noticed it when I tried to build a docker-compose with `extra_hosts` on the build section. I need to access a postgres sever that is running on the host machine. So I'm using extra_hosts "host.docker.internal:host-gateway" to make the connection.

--- Addendum

I'm running on Linux. So I changed extra_hosts to point to the docker-host IP and things worked. Here is what I placed on my docker-compose:

```
extra_hosts:
            - "host.docker.internal:172.17.0.1"
 ```

source: https://forums.docker.com/t/how-to-reach-localhost-on-host-from-docker-container/113321/4@tschallacka: For linux i've suddenly run into this as well as my xdebug stopped connecting after an update.

Luckely we use an internal stitching for docker compose files, and it's easy to add to a shell script.

This command can be added to a bashrc or bash  profile or any other 'autoexec' file that starts on a terminal session, adapt as needed. This supports mac and linux.

```bash
case "$(uname -sr)" in

   Darwin*)
     IPADDRESS=$(ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}')
     ;;

   Linux*Microsoft*)
     IPADDRESS=$(hostname -I | awk '{print $1}')
     ;;

   Linux*)
     IPADDRESS=$(hostname -I | awk '{print $1}')
     ;;

   CYGWIN*|MINGW*|MINGW32*|MSYS*)
     ;;

   # Add here more strings to compare
   # See correspondence table at the bottom of this answer

   *)
     echo "Unknown platform, aborting";exit
     ;;
esac
export IPADDRESS
```

```yaml
        extra_hosts:
            - 'host.docker.internal:${IPADDRESS}'
```@zamlz: > I'm facing this same issue, using docker 24.0.1 on Ubuntu. I noticed it when I tried to build a docker-compose with `extra_hosts` on the build section. I need to access a postgres sever that is running on the host machine. So I'm using extra_hosts "host.docker.internal:host-gateway" to make the connection.
> 
> --- Addendum
> 
> I'm running on Linux. So I changed extra_hosts to point to the docker-host IP and things worked. Here is what I placed on my docker-compose:
> 
> ```
> extra_hosts:
>             - "host.docker.internal:172.17.0.1"
> ```
> 
> source: https://forums.docker.com/t/how-to-reach-localhost-on-host-from-docker-container/113321/4

Decent solution, but I can't guarantee all the containers I'm running are on the same docker network bridge. They'd have different IPs. We do not wish to hardcode these.@dvictori: > 
> Decent solution, but I can't guarantee all the containers I'm running are on the same docker network bridge. They'd have different IPs. We do not wish to hardcode these.

I agree. It was just a quick fix that I used. But by no means a definitive solution.@Roemer: This is really annoying. Using a hardcoded ip won't work as it can change. For me it is `172.18.0.1` and for others `172.17.0.1`.  The only workaround which might work would be to add an environment variable in the docker host which is regularly updated and reference that one.@zamlz: I finally got around to testing this on my end. The issue is closed for some reason, but I can't seem to get it to work still.

```
$ docker --version
Docker version 24.0.2, build cb74dfcd85
```

Unfortunately, this particular version of `docker` does not appear to be fixed. Am I using an older version still?

```
ERROR: failed to solve: failed to parse additional hosts: failed to parse IP host-gateway
```@neersighted: The fix requires buildx v0.11.1, and daemon v24.0.3 (which were released together). The version you have provided is the CLI, which does not need to be updated to get this fix; however I find it unlikely that you have the correct daemon and buildx version given that CLI version.@thaJeztah: Looks like fixes went into 24.0.3 https://github.com/moby/moby/pull/45790

And buildx v0.11.1 through https://github.com/docker/buildx/pull/1926

You need both updated to at least those versions to get the fix@thaJeztah: Ah @neersighted beat me to it 😅@zamlz: Ahhh I see, cool okay that's what I figured happened, but wanted to make sure. I saw that issue link before but I didn't see a version number so I had no clue if the version I currently updated to had it or not.

However, looking at the page again, I see that there is a milestones on the right side that indicated the version. Cool, will look at that next time! Thanks for your help!@neersighted: You can also review https://docs.docker.com/engine/release-notes when you update, and look for the fix there 😉 @zamlz: Awesome! Thanks for sharing! I forgot about the release notes document. I'll definitely refer to that doc next time!@elichai: This issue still persists, you can easily re-create it with in github action: https://github.com/elichai/docker-bug/actions/runs/12456873279/job/34770817326@CHN-beta: > This issue still persists, you can easily re-create it with in github action: https://github.com/elichai/docker-bug/actions/runs/12456873279/job/34770817326

Same issue, with docker 27.3.1.@adamdicarlo0: so... host-gateway works with Docker Desktop, but not the cli engine?

Solution patch:
diff --git a/bake/compose.go b/bake/compose.go
index 457fe2c1..6d207eda 100644
--- a/bake/compose.go
+++ b/bake/compose.go
@@ -37,6 +37,7 @@ func ParseCompose(cfgs []compose.ConfigFile, envs map[string]string) (*Config, e
 	}, func(options *loader.Options) {
 		options.SetProjectName("bake", false)
 		options.SkipNormalization = true
+		options.Profiles = []string{"*"}
 	})
 	if err != nil {
 		return nil, err
diff --git a/build/build.go b/build/build.go
index b480187a..0d7ef093 100644
--- a/build/build.go
+++ b/build/build.go
@@ -645,7 +645,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
 	}
 
 	// setup extrahosts
-	extraHosts, err := toBuildkitExtraHosts(opt.ExtraHosts, nodeDriver.IsMobyDriver())
+	extraHosts, err := toBuildkitExtraHosts(ctx, opt.ExtraHosts, nodeDriver)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/build/utils.go b/build/utils.go
index 03a85ca4..ee86ff93 100644
--- a/build/utils.go
+++ b/build/utils.go
@@ -3,10 +3,12 @@ package build
 import (
 	"archive/tar"
 	"bytes"
+	"context"
 	"net"
 	"os"
 	"strings"
 
+	"github.com/docker/buildx/driver"
 	"github.com/docker/cli/opts"
 	"github.com/docker/docker/builder/remotecontext/urlutil"
 	"github.com/moby/buildkit/util/gitutil"
@@ -57,7 +59,7 @@ func isArchive(header []byte) bool {
 }
 
 // toBuildkitExtraHosts converts hosts from docker key:value format to buildkit's csv format
-func toBuildkitExtraHosts(inp []string, mobyDriver bool) (string, error) {
+func toBuildkitExtraHosts(ctx context.Context, inp []string, nodeDriver *driver.DriverHandle) (string, error) {
 	if len(inp) == 0 {
 		return "", nil
 	}
@@ -67,11 +69,16 @@ func toBuildkitExtraHosts(inp []string, mobyDriver bool) (string, error) {
 		if !ok || host == "" || ip == "" {
 			return "", errors.Errorf("invalid host %s", h)
 		}
-		// Skip IP address validation for "host-gateway" string with moby driver
-		if !mobyDriver || ip != mobyHostGatewayName {
-			if net.ParseIP(ip) == nil {
-				return "", errors.Errorf("invalid host %s", h)
+		// If the IP Address is a "host-gateway", replace this value with the
+		// IP address provided by the worker's label.
+		if ip == mobyHostGatewayName {
+			hgip, err := nodeDriver.HostGatewayIP(ctx)
+			if err != nil {
+				return "", errors.Wrap(err, "unable to derive the IP value for host-gateway")
 			}
+			ip = hgip.String()
+		} else if net.ParseIP(ip) == nil {
+			return "", errors.Errorf("invalid host %s", h)
 		}
 		hosts = append(hosts, host+"="+ip)
 	}
diff --git a/controller/build/build.go b/controller/build/build.go
index 64069d78..bdf6d8d1 100644
--- a/controller/build/build.go
+++ b/controller/build/build.go
@@ -54,6 +54,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
 			NamedContexts:  contexts,
 		},
 		BuildArgs:     in.BuildArgs,
+		CgroupParent:  in.CgroupParent,
 		ExtraHosts:    in.ExtraHosts,
 		Labels:        in.Labels,
 		NetworkMode:   in.NetworkMode,
diff --git a/docs/reference/buildx.md b/docs/reference/buildx.md
index 735912b9..324cb013 100644
--- a/docs/reference/buildx.md
+++ b/docs/reference/buildx.md
@@ -9,24 +9,21 @@ Extended build capabilities with BuildKit
 
 ### Subcommands
 
-| Name                                           | Description                                |
-|:-----------------------------------------------|:-------------------------------------------|
-| [`_INTERNAL_SERVE`](buildx__INTERNAL_SERVE.md) |                                            |
-| [`bake`](buildx_bake.md)                       | Build from a file                          |
-| [`build`](buildx_build.md)                     | Start a build                              |
-| [`create`](buildx_create.md)                   | Create a new builder instance              |
-| [`debug-shell`](buildx_debug-shell.md)         | Start a monitor                            |
-| [`du`](buildx_du.md)                           | Disk usage                                 |
-| [`imagetools`](buildx_imagetools.md)           | Commands to work on images in registry     |
-| [`inspect`](buildx_inspect.md)                 | Inspect current builder instance           |
-| [`install`](buildx_install.md)                 | Install buildx as a 'docker builder' alias |
-| [`ls`](buildx_ls.md)                           | List builder instances                     |
-| [`prune`](buildx_prune.md)                     | Remove build cache                         |
-| [`rm`](buildx_rm.md)                           | Remove a builder instance                  |
-| [`stop`](buildx_stop.md)                       | Stop builder instance                      |
-| [`uninstall`](buildx_uninstall.md)             | Uninstall the 'docker builder' alias       |
-| [`use`](buildx_use.md)                         | Set the current builder instance           |
-| [`version`](buildx_version.md)                 | Show buildx version information            |
+| Name                                   | Description                            |
+|:---------------------------------------|:---------------------------------------|
+| [`bake`](buildx_bake.md)               | Build from a file                      |
+| [`build`](buildx_build.md)             | Start a build                          |
+| [`create`](buildx_create.md)           | Create a new builder instance          |
+| [`debug-shell`](buildx_debug-shell.md) | Start a monitor                        |
+| [`du`](buildx_du.md)                   | Disk usage                             |
+| [`imagetools`](buildx_imagetools.md)   | Commands to work on images in registry |
+| [`inspect`](buildx_inspect.md)         | Inspect current builder instance       |
+| [`ls`](buildx_ls.md)                   | List builder instances                 |
+| [`prune`](buildx_prune.md)             | Remove build cache                     |
+| [`rm`](buildx_rm.md)                   | Remove a builder instance              |
+| [`stop`](buildx_stop.md)               | Stop builder instance                  |
+| [`use`](buildx_use.md)                 | Set the current builder instance       |
+| [`version`](buildx_version.md)         | Show buildx version information        |
 
 
 ### Options
diff --git a/docs/reference/buildx_install.md b/docs/reference/buildx_install.md
deleted file mode 100644
index 4f8f370d..00000000
--- a/docs/reference/buildx_install.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# buildx install
-
-```
-docker buildx install
-```
-
-<!---MARKER_GEN_START-->
-Install buildx as a 'docker builder' alias
-
-
-<!---MARKER_GEN_END-->
diff --git a/docs/reference/buildx_uninstall.md b/docs/reference/buildx_uninstall.md
deleted file mode 100644
index 524762e3..00000000
--- a/docs/reference/buildx_uninstall.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# buildx uninstall
-
-```
-docker buildx uninstall
-```
-
-<!---MARKER_GEN_START-->
-Uninstall the 'docker builder' alias
-
-
-<!---MARKER_GEN_END-->
diff --git a/driver/manager.go b/driver/manager.go
index 657e5094..b4ec318f 100644
--- a/driver/manager.go
+++ b/driver/manager.go
@@ -2,17 +2,17 @@ package driver
 
 import (
 	"context"
+	"net"
 	"os"
 	"sort"
 	"strings"
 	"sync"
 
-	"k8s.io/client-go/rest"
-
 	dockerclient "github.com/docker/docker/client"
 	"github.com/moby/buildkit/client"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
+	"k8s.io/client-go/rest"
 )
 
 type Factory interface {
@@ -154,6 +154,9 @@ type DriverHandle struct {
 	features                map[Feature]bool
 	historyAPISupportedOnce sync.Once
 	historyAPISupported     bool
+	hostGatewayIPOnce       sync.Once
+	hostGatewayIP           net.IP
+	hostGatewayIPErr        error
 }
 
 func (d *DriverHandle) Client(ctx context.Context) (*client.Client, error) {
@@ -178,3 +181,36 @@ func (d *DriverHandle) HistoryAPISupported(ctx context.Context) bool {
 	})
 	return d.historyAPISupported
 }
+
+func (d *DriverHandle) HostGatewayIP(ctx context.Context) (net.IP, error) {
+	d.hostGatewayIPOnce.Do(func() {
+		if !d.Driver.IsMobyDriver() {
+			d.hostGatewayIPErr = errors.New("host-gateway is only supported with the docker driver")
+			return
+		}
+		c, err := d.Client(ctx)
+		if err != nil {
+			d.hostGatewayIPErr = err
+			return
+		}
+		workers, err := c.ListWorkers(ctx)
+		if err != nil {
+			d.hostGatewayIPErr = errors.Wrap(err, "listing workers")
+			return
+		}
+		for _, w := range workers {
+			// should match github.com/docker/docker/builder/builder-next/worker/label.HostGatewayIP const
+			if v, ok := w.Labels["org.mobyproject.buildkit.worker.moby.host-gateway-ip"]; ok && v != "" {
+				ip := net.ParseIP(v)
+				if ip == nil {
+					d.hostGatewayIPErr = errors.Errorf("failed to parse host-gateway IP: %s", v)
+					return
+				}
+				d.hostGatewayIP = ip
+				return
+			}
+		}
+		d.hostGatewayIPErr = errors.New("host-gateway IP not found")
+	})
+	return d.hostGatewayIP, d.hostGatewayIPErr
+}
diff --git a/go.mod b/go.mod
index 8fabb7d3..0b77430f 100644
--- a/go.mod
+++ b/go.mod
@@ -10,8 +10,9 @@ require (
 	github.com/containerd/containerd v1.7.2
 	github.com/containerd/continuity v0.4.1
 	github.com/containerd/typeurl/v2 v2.1.1
+	github.com/creack/pty v1.1.18
 	github.com/docker/cli v24.0.2+incompatible
-	github.com/docker/cli-docs-tool v0.5.1
+	github.com/docker/cli-docs-tool v0.6.0
 	github.com/docker/distribution v2.8.2+incompatible
 	github.com/docker/docker v24.0.2+incompatible
 	github.com/docker/go-units v0.5.0
diff --git a/go.sum b/go.sum
index 0edce880..3c520052 100644
--- a/go.sum
+++ b/go.sum
@@ -142,6 +142,7 @@ github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3H
 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/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
+github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
 github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -151,8 +152,8 @@ github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa h1:L9
 github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
 github.com/docker/cli v24.0.2+incompatible h1:QdqR7znue1mtkXIJ+ruQMGQhpw2JzMJLRXp6zpzF6tM=
 github.com/docker/cli v24.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/cli-docs-tool v0.5.1 h1:jIk/cCZurZERhALPVKhqlNxTQGxn2kcI+56gE57PQXg=
-github.com/docker/cli-docs-tool v0.5.1/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
+github.com/docker/cli-docs-tool v0.6.0 h1:Z9x10SaZgFaB6jHgz3OWooynhSa40CsWkpe5hEnG/qA=
+github.com/docker/cli-docs-tool v0.6.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
 github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
 github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg=
diff --git a/util/desktop/desktop.go b/util/desktop/desktop.go
index 6ddf74a5..7fa4ee6d 100644
--- a/util/desktop/desktop.go
+++ b/util/desktop/desktop.go
@@ -81,6 +81,6 @@ func (e *ErrorWithBuildRef) Print(w io.Writer) error {
 	if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
 		term = true
 	}
-	fmt.Fprintf(w, "\n%s", BuildDetailsOutput(map[string]string{"default": e.Ref}, term))
+	fmt.Fprintf(w, "\n%s\n", BuildDetailsOutput(map[string]string{"default": e.Ref}, term))
 	return nil
 }
diff --git a/vendor/github.com/creack/pty/.gitignore b/vendor/github.com/creack/pty/.gitignore
new file mode 100644
index 00000000..1f0a99f2
--- /dev/null
+++ b/vendor/github.com/creack/pty/.gitignore
@@ -0,0 +1,4 @@
+[568].out
+_go*
+_test*
+_obj
diff --git a/vendor/github.com/creack/pty/Dockerfile.golang b/vendor/github.com/creack/pty/Dockerfile.golang
new file mode 100644
index 00000000..2ee82a3a
--- /dev/null
+++ b/vendor/github.com/creack/pty/Dockerfile.golang
@@ -0,0 +1,17 @@
+ARG GOVERSION=1.14
+FROM golang:${GOVERSION}
+
+# Set base env.
+ARG GOOS=linux
+ARG GOARCH=amd64
+ENV GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
+
+# Pre compile the stdlib for 386/arm (32bits).
+RUN go build -a std
+
+# Add the code to the image.
+WORKDIR pty
+ADD . .
+
+# Build the lib.
+RUN go build
diff --git a/vendor/github.com/creack/pty/Dockerfile.riscv b/vendor/github.com/creack/pty/Dockerfile.riscv
new file mode 100644
index 00000000..7a30c94d
--- /dev/null
+++ b/vendor/github.com/creack/pty/Dockerfile.riscv
@@ -0,0 +1,23 @@
+# NOTE: Using 1.13 as a base to build the RISCV compiler, the resulting version is based on go1.6.
+FROM golang:1.13
+
+# Clone and complie a riscv compatible version of the go compiler.
+RUN git clone https://review.gerrithub.io/riscv/riscv-go /riscv-go
+# riscvdev branch HEAD as of 2019-06-29.
+RUN cd /riscv-go && git checkout 04885fddd096d09d4450726064d06dd107e374bf
+ENV PATH=/riscv-go/misc/riscv:/riscv-go/bin:$PATH
+RUN cd /riscv-go/src && GOROOT_BOOTSTRAP=$(go env GOROOT) ./make.bash
+ENV GOROOT=/riscv-go
+
+# Set the base env.
+ENV GOOS=linux GOARCH=riscv CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
+
+# Pre compile the stdlib.
+RUN go build -a std
+
+# Add the code to the image.
+WORKDIR pty
+ADD . .
+
+# Build the lib.
+RUN go build
diff --git a/vendor/github.com/creack/pty/LICENSE b/vendor/github.com/creack/pty/LICENSE
new file mode 100644
index 00000000..6b7558b6
--- /dev/null
+++ b/vendor/github.com/creack/pty/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2011 Keith Rarick
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall
+be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/creack/pty/README.md b/vendor/github.com/creack/pty/README.md
new file mode 100644
index 00000000..a4fe7670
--- /dev/null
+++ b/vendor/github.com/creack/pty/README.md
@@ -0,0 +1,107 @@
+# pty
+
+Pty is a Go package for using unix pseudo-terminals.
+
+## Install
+
+```sh
+go get github.com/creack/pty
+```
+
+## Examples
+
+Note that those examples are for demonstration purpose only, to showcase how to use the library. They are not meant to be used in any kind of production environment.
+
+### Command
+
+```go
+package main
+
+import (
+	"io"
+	"os"
+	"os/exec"
+
+	"github.com/creack/pty"
+)
+
+func main() {
+	c := exec.Command("grep", "--color=auto", "bar")
+	f, err := pty.Start(c)
+	if err != nil {
+		panic(err)
+	}
+
+	go func() {
+		f.Write([]byte("foo\n"))
+		f.Write([]byte("bar\n"))
+		f.Write([]byte("baz\n"))
+		f.Write([]byte{4}) // EOT
+	}()
+	io.Copy(os.Stdout, f)
+}
+```
+
+### Shell
+
+```go
+package main
+
+import (
+        "io"
+        "log"
+        "os"
+        "os/exec"
+        "os/signal"
+        "syscall"
+
+        "github.com/creack/pty"
+        "golang.org/x/term"
+)
+
+func test() error {
+        // Create arbitrary command.
+        c := exec.Command("bash")
+
+        // Start the command with a pty.
+        ptmx, err := pty.Start(c)
+        if err != nil {
+                return err
+        }
+        // Make sure to close the pty at the end.
+        defer func() { _ = ptmx.Close() }() // Best effort.
+
+        // Handle pty size.
+        ch := make(chan os.Signal, 1)
+        signal.Notify(ch, syscall.SIGWINCH)
+        go func() {
+                for range ch {
+                        if err := pty.InheritSize(os.Stdin, ptmx); err != nil {
+                                log.Printf("error resizing pty: %s", err)
+                        }
+                }
+        }()
+        ch <- syscall.SIGWINCH // Initial resize.
+        defer func() { signal.Stop(ch); close(ch) }() // Cleanup signals when done.
+
+        // Set stdin in raw mode.
+        oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
+        if err != nil {
+                panic(err)
+        }
+        defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
+
+        // Copy stdin to the pty and the pty to stdout.
+        // NOTE: The goroutine will keep reading until the next keystroke before returning.
+        go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
+        _, _ = io.Copy(os.Stdout, ptmx)
+
+        return nil
+}
+
+func main() {
+        if err := test(); err != nil {
+                log.Fatal(err)
+        }
+}
+```
diff --git a/vendor/github.com/creack/pty/asm_solaris_amd64.s b/vendor/github.com/creack/pty/asm_solaris_amd64.s
new file mode 100644
index 00000000..7fbef8ee
--- /dev/null
+++ b/vendor/github.com/creack/pty/asm_solaris_amd64.s
@@ -0,0 +1,18 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+//+build gc
+
+#include "textflag.h"
+
+//
+// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
+//
+
+TEXT ·sysvicall6(SB),NOSPLIT,$0-88
+	JMP	syscall·sysvicall6(SB)
+
+TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
+	JMP	syscall·rawSysvicall6(SB)
diff --git a/vendor/github.com/creack/pty/doc.go b/vendor/github.com/creack/pty/doc.go
new file mode 100644
index 00000000..3c8b3244
--- /dev/null
+++ b/vendor/github.com/creack/pty/doc.go
@@ -0,0 +1,16 @@
+// Package pty provides functions for working with Unix terminals.
+package pty
+
+import (
+	"errors"
+	"os"
+)
+
+// ErrUnsupported is returned if a function is not
+// available on the current platform.
+var ErrUnsupported = errors.New("unsupported")
+
+// Open a pty and its corresponding tty.
+func Open() (pty, tty *os.File, err error) {
+	return open()
+}
diff --git a/vendor/github.com/creack/pty/ioctl.go b/vendor/github.com/creack/pty/ioctl.go
new file mode 100644
index 00000000..3cabedd9
--- /dev/null
+++ b/vendor/github.com/creack/pty/ioctl.go
@@ -0,0 +1,19 @@
+//go:build !windows && !solaris && !aix
+// +build !windows,!solaris,!aix
+
+package pty
+
+import "syscall"
+
+const (
+	TIOCGWINSZ = syscall.TIOCGWINSZ
+	TIOCSWINSZ = syscall.TIOCSWINSZ
+)
+
+func ioctl(fd, cmd, ptr uintptr) error {
+	_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
+	if e != 0 {
+		return e
+	}
+	return nil
+}
diff --git a/vendor/github.com/creack/pty/ioctl_bsd.go b/vendor/github.com/creack/pty/ioctl_bsd.go
new file mode 100644
index 00000000..db3bf845
--- /dev/null
+++ b/vendor/github.com/creack/pty/ioctl_bsd.go
@@ -0,0 +1,40 @@
+//go:build darwin || dragonfly || freebsd || netbsd || openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package pty
+
+// from <sys/ioccom.h>
+const (
+	_IOC_VOID    uintptr = 0x20000000
+	_IOC_OUT     uintptr = 0x40000000
+	_IOC_IN      uintptr = 0x80000000
+	_IOC_IN_OUT  uintptr = _IOC_OUT | _IOC_IN
+	_IOC_DIRMASK         = _IOC_VOID | _IOC_OUT | _IOC_IN
+
+	_IOC_PARAM_SHIFT = 13
+	_IOC_PARAM_MASK  = (1 << _IOC_PARAM_SHIFT) - 1
+)
+
+func _IOC_PARM_LEN(ioctl uintptr) uintptr {
+	return (ioctl >> 16) & _IOC_PARAM_MASK
+}
+
+func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
+}
+
+func _IO(group byte, ioctl_num uintptr) uintptr {
+	return _IOC(_IOC_VOID, group, ioctl_num, 0)
+}
+
+func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return _IOC(_IOC_OUT, group, ioctl_num, param_len)
+}
+
+func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return _IOC(_IOC_IN, group, ioctl_num, param_len)
+}
+
+func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
+}
diff --git a/vendor/github.com/creack/pty/ioctl_solaris.go b/vendor/github.com/creack/pty/ioctl_solaris.go
new file mode 100644
index 00000000..bff22dad
--- /dev/null
+++ b/vendor/github.com/creack/pty/ioctl_solaris.go
@@ -0,0 +1,48 @@
+//go:build solaris
+// +build solaris
+
+package pty
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+//go:cgo_import_dynamic libc_ioctl ioctl "libc.so"
+//go:linkname procioctl libc_ioctl
+var procioctl uintptr
+
+const (
+	// see /usr/include/sys/stropts.h
+	I_PUSH = uintptr((int32('S')<<8 | 002))
+	I_STR  = uintptr((int32('S')<<8 | 010))
+	I_FIND = uintptr((int32('S')<<8 | 013))
+
+	// see /usr/include/sys/ptms.h
+	ISPTM   = (int32('P') << 8) | 1
+	UNLKPT  = (int32('P') << 8) | 2
+	PTSSTTY = (int32('P') << 8) | 3
+	ZONEPT  = (int32('P') << 8) | 4
+	OWNERPT = (int32('P') << 8) | 5
+
+	// see /usr/include/sys/termios.h
+	TIOCSWINSZ = (uint32('T') << 8) | 103
+	TIOCGWINSZ = (uint32('T') << 8) | 104
+)
+
+type strioctl struct {
+	icCmd     int32
+	icTimeout int32
+	icLen     int32
+	icDP      unsafe.Pointer
+}
+
+// Defined in asm_solaris_amd64.s.
+func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
+
+func ioctl(fd, cmd, ptr uintptr) error {
+	if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, fd, cmd, ptr, 0, 0, 0); errno != 0 {
+		return errno
+	}
+	return nil
+}
diff --git a/vendor/github.com/creack/pty/ioctl_unsupported.go b/vendor/github.com/creack/pty/ioctl_unsupported.go
new file mode 100644
index 00000000..2449a27e
--- /dev/null
+++ b/vendor/github.com/creack/pty/ioctl_unsupported.go
@@ -0,0 +1,13 @@
+//go:build aix
+// +build aix
+
+package pty
+
+const (
+	TIOCGWINSZ = 0
+	TIOCSWINSZ = 0
+)
+
+func ioctl(fd, cmd, ptr uintptr) error {
+	return ErrUnsupported
+}
diff --git a/vendor/github.com/creack/pty/mktypes.bash b/vendor/github.com/creack/pty/mktypes.bash
new file mode 100644
index 00000000..7f71bda6
--- /dev/null
+++ b/vendor/github.com/creack/pty/mktypes.bash
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+GOOSARCH="${GOOS}_${GOARCH}"
+case "$GOOSARCH" in
+_* | *_ | _)
+	echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
+	exit 1
+	;;
+esac
+
+GODEFS="go tool cgo -godefs"
+
+$GODEFS types.go |gofmt > ztypes_$GOARCH.go
+
+case $GOOS in
+freebsd|dragonfly|netbsd|openbsd)
+	$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
+	;;
+esac
diff --git a/vendor/github.com/creack/pty/pty_darwin.go b/vendor/github.com/creack/pty/pty_darwin.go
new file mode 100644
index 00000000..9bdd71d0
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_darwin.go
@@ -0,0 +1,68 @@
+//go:build darwin
+// +build darwin
+
+package pty
+
+import (
+	"errors"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+	pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	p := os.NewFile(uintptr(pFD), "/dev/ptmx")
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
+	sname, err := ptsname(p)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if err := grantpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	if err := unlockpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+	n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
+
+	err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
+	if err != nil {
+		return "", err
+	}
+
+	for i, c := range n {
+		if c == 0 {
+			return string(n[:i]), nil
+		}
+	}
+	return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
+}
+
+func grantpt(f *os.File) error {
+	return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
+}
+
+func unlockpt(f *os.File) error {
+	return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
+}
diff --git a/vendor/github.com/creack/pty/pty_dragonfly.go b/vendor/github.com/creack/pty/pty_dragonfly.go
new file mode 100644
index 00000000..aa916aad
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_dragonfly.go
@@ -0,0 +1,83 @@
+//go:build dragonfly
+// +build dragonfly
+
+package pty
+
+import (
+	"errors"
+	"os"
+	"strings"
+	"syscall"
+	"unsafe"
+)
+
+// same code as pty_darwin.go
+func open() (pty, tty *os.File, err error) {
+	p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
+	sname, err := ptsname(p)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if err := grantpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	if err := unlockpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	t, err := os.OpenFile(sname, os.O_RDWR, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	return p, t, nil
+}
+
+func grantpt(f *os.File) error {
+	_, err := isptmaster(f.Fd())
+	return err
+}
+
+func unlockpt(f *os.File) error {
+	_, err := isptmaster(f.Fd())
+	return err
+}
+
+func isptmaster(fd uintptr) (bool, error) {
+	err := ioctl(fd, syscall.TIOCISPTMASTER, 0)
+	return err == nil, err
+}
+
+var (
+	emptyFiodgnameArg fiodgnameArg
+	ioctl_FIODNAME    = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
+)
+
+func ptsname(f *os.File) (string, error) {
+	name := make([]byte, _C_SPECNAMELEN)
+	fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
+
+	err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
+	if err != nil {
+		return "", err
+	}
+
+	for i, c := range name {
+		if c == 0 {
+			s := "/dev/" + string(name[:i])
+			return strings.Replace(s, "ptm", "pts", -1), nil
+		}
+	}
+	return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
+}
diff --git a/vendor/github.com/creack/pty/pty_freebsd.go b/vendor/github.com/creack/pty/pty_freebsd.go
new file mode 100644
index 00000000..bcd3b6f9
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_freebsd.go
@@ -0,0 +1,81 @@
+//go:build freebsd
+// +build freebsd
+
+package pty
+
+import (
+	"errors"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func posixOpenpt(oflag int) (fd int, err error) {
+	r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = e1
+	}
+	return fd, err
+}
+
+func open() (pty, tty *os.File, err error) {
+	fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC)
+	if err != nil {
+		return nil, nil, err
+	}
+	p := os.NewFile(uintptr(fd), "/dev/pts")
+	// In case of error after this point, make sure we close the pts fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
+	sname, err := ptsname(p)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	return p, t, nil
+}
+
+func isptmaster(fd uintptr) (bool, error) {
+	err := ioctl(fd, syscall.TIOCPTMASTER, 0)
+	return err == nil, err
+}
+
+var (
+	emptyFiodgnameArg fiodgnameArg
+	ioctlFIODGNAME    = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
+)
+
+func ptsname(f *os.File) (string, error) {
+	master, err := isptmaster(f.Fd())
+	if err != nil {
+		return "", err
+	}
+	if !master {
+		return "", syscall.EINVAL
+	}
+
+	const n = _C_SPECNAMELEN + 1
+	var (
+		buf = make([]byte, n)
+		arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
+	)
+	if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
+		return "", err
+	}
+
+	for i, c := range buf {
+		if c == 0 {
+			return string(buf[:i]), nil
+		}
+	}
+	return "", errors.New("FIODGNAME string not NUL-terminated")
+}
diff --git a/vendor/github.com/creack/pty/pty_linux.go b/vendor/github.com/creack/pty/pty_linux.go
new file mode 100644
index 00000000..a3b368f5
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_linux.go
@@ -0,0 +1,54 @@
+//go:build linux
+// +build linux
+
+package pty
+
+import (
+	"os"
+	"strconv"
+	"syscall"
+	"unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+	p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
+	sname, err := ptsname(p)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if err := unlockpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) //nolint:gosec // Expected Open from a variable.
+	if err != nil {
+		return nil, nil, err
+	}
+	return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+	var n _C_uint
+	err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) //nolint:gosec // Expected unsafe pointer for Syscall call.
+	if err != nil {
+		return "", err
+	}
+	return "/dev/pts/" + strconv.Itoa(int(n)), nil
+}
+
+func unlockpt(f *os.File) error {
+	var u _C_int
+	// use TIOCSPTLCK with a pointer to zero to clear the lock
+	return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) //nolint:gosec // Expected unsafe pointer for Syscall call.
+}
diff --git a/vendor/github.com/creack/pty/pty_netbsd.go b/vendor/github.com/creack/pty/pty_netbsd.go
new file mode 100644
index 00000000..2b20d944
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_netbsd.go
@@ -0,0 +1,69 @@
+//go:build netbsd
+// +build netbsd
+
+package pty
+
+import (
+	"errors"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+	p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
+	sname, err := ptsname(p)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if err := grantpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	// In NetBSD unlockpt() does nothing, so it isn't called here.
+
+	t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+	/*
+	 * from ptsname(3): The ptsname() function is equivalent to:
+	 * struct ptmget pm;
+	 * ioctl(fd, TIOCPTSNAME, &pm) == -1 ? NULL : pm.sn;
+	 */
+	var ptm ptmget
+	if err := ioctl(f.Fd(), uintptr(ioctl_TIOCPTSNAME), uintptr(unsafe.Pointer(&ptm))); err != nil {
+		return "", err
+	}
+	name := make([]byte, len(ptm.Sn))
+	for i, c := range ptm.Sn {
+		name[i] = byte(c)
+		if c == 0 {
+			return string(name[:i]), nil
+		}
+	}
+	return "", errors.New("TIOCPTSNAME string not NUL-terminated")
+}
+
+func grantpt(f *os.File) error {
+	/*
+	 * from grantpt(3): Calling grantpt() is equivalent to:
+	 * ioctl(fd, TIOCGRANTPT, 0);
+	 */
+	return ioctl(f.Fd(), uintptr(ioctl_TIOCGRANTPT), 0)
+}
diff --git a/vendor/github.com/creack/pty/pty_openbsd.go b/vendor/github.com/creack/pty/pty_openbsd.go
new file mode 100644
index 00000000..031367a8
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_openbsd.go
@@ -0,0 +1,36 @@
+//go:build openbsd
+// +build openbsd
+
+package pty
+
+import (
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+	/*
+	 * from ptm(4):
+	 * The PTMGET command allocates a free pseudo terminal, changes its
+	 * ownership to the caller, revokes the access privileges for all previous
+	 * users, opens the file descriptors for the pty and tty devices and
+	 * returns them to the caller in struct ptmget.
+	 */
+
+	p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	defer p.Close()
+
+	var ptm ptmget
+	if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
+		return nil, nil, err
+	}
+
+	pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm")
+	tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm")
+
+	return pty, tty, nil
+}
diff --git a/vendor/github.com/creack/pty/pty_solaris.go b/vendor/github.com/creack/pty/pty_solaris.go
new file mode 100644
index 00000000..37f933e6
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_solaris.go
@@ -0,0 +1,152 @@
+//go:build solaris
+// +build solaris
+
+package pty
+
+/* based on:
+http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c
+*/
+
+import (
+	"errors"
+	"os"
+	"strconv"
+	"syscall"
+	"unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+	ptmxfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx")
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+
+	sname, err := ptsname(p)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if err := grantpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	if err := unlockpt(p); err != nil {
+		return nil, nil, err
+	}
+
+	ptsfd, err := syscall.Open(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+	if err != nil {
+		return nil, nil, err
+	}
+	t := os.NewFile(uintptr(ptsfd), sname)
+
+	// In case of error after this point, make sure we close the pts fd.
+	defer func() {
+		if err != nil {
+			_ = t.Close() // Best effort.
+		}
+	}()
+
+	// pushing terminal driver STREAMS modules as per pts(7)
+	for _, mod := range []string{"ptem", "ldterm", "ttcompat"} {
+		if err := streamsPush(t, mod); err != nil {
+			return nil, nil, err
+		}
+	}
+
+	return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+	dev, err := ptsdev(f.Fd())
+	if err != nil {
+		return "", err
+	}
+	fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10)
+
+	if err := syscall.Access(fn, 0); err != nil {
+		return "", err
+	}
+	return fn, nil
+}
+
+func unlockpt(f *os.File) error {
+	istr := strioctl{
+		icCmd:     UNLKPT,
+		icTimeout: 0,
+		icLen:     0,
+		icDP:      nil,
+	}
+	return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
+}
+
+func minor(x uint64) uint64 { return x & 0377 }
+
+func ptsdev(fd uintptr) (uint64, error) {
+	istr := strioctl{
+		icCmd:     ISPTM,
+		icTimeout: 0,
+		icLen:     0,
+		icDP:      nil,
+	}
+
+	if err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
+		return 0, err
+	}
+	var status syscall.Stat_t
+	if err := syscall.Fstat(int(fd), &status); err != nil {
+		return 0, err
+	}
+	return uint64(minor(status.Rdev)), nil
+}
+
+type ptOwn struct {
+	rUID int32
+	rGID int32
+}
+
+func grantpt(f *os.File) error {
+	if _, err := ptsdev(f.Fd()); err != nil {
+		return err
+	}
+	pto := ptOwn{
+		rUID: int32(os.Getuid()),
+		// XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty"
+		rGID: int32(os.Getgid()),
+	}
+	istr := strioctl{
+		icCmd:     OWNERPT,
+		icTimeout: 0,
+		icLen:     int32(unsafe.Sizeof(strioctl{})),
+		icDP:      unsafe.Pointer(&pto),
+	}
+	if err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
+		return errors.New("access denied")
+	}
+	return nil
+}
+
+// streamsPush pushes STREAMS modules if not already done so.
+func streamsPush(f *os.File, mod string) error {
+	buf := []byte(mod)
+
+	// XXX I_FIND is not returning an error when the module
+	// is already pushed even though truss reports a return
+	// value of 1. A bug in the Go Solaris syscall interface?
+	// XXX without this we are at risk of the issue
+	// https://www.illumos.org/issues/9042
+	// but since we are not using libc or XPG4.2, we should not be
+	// double-pushing modules
+
+	if err := ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil {
+		return nil
+	}
+	return ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
+}
diff --git a/vendor/github.com/creack/pty/pty_unsupported.go b/vendor/github.com/creack/pty/pty_unsupported.go
new file mode 100644
index 00000000..c771020f
--- /dev/null
+++ b/vendor/github.com/creack/pty/pty_unsupported.go
@@ -0,0 +1,12 @@
+//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris
+// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris
+
+package pty
+
+import (
+	"os"
+)
+
+func open() (pty, tty *os.File, err error) {
+	return nil, nil, ErrUnsupported
+}
diff --git a/vendor/github.com/creack/pty/run.go b/vendor/github.com/creack/pty/run.go
new file mode 100644
index 00000000..47553662
--- /dev/null
+++ b/vendor/github.com/creack/pty/run.go
@@ -0,0 +1,57 @@
+package pty
+
+import (
+	"os"
+	"os/exec"
+	"syscall"
+)
+
+// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// Starts the process in a new session and sets the controlling terminal.
+func Start(cmd *exec.Cmd) (*os.File, error) {
+	return StartWithSize(cmd, nil)
+}
+
+// StartWithAttrs assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// This will resize the pty to the specified size before starting the command if a size is provided.
+// The `attrs` parameter overrides the one set in c.SysProcAttr.
+//
+// This should generally not be needed. Used in some edge cases where it is needed to create a pty
+// without a controlling terminal.
+func StartWithAttrs(c *exec.Cmd, sz *Winsize, attrs *syscall.SysProcAttr) (*os.File, error) {
+	pty, tty, err := Open()
+	if err != nil {
+		return nil, err
+	}
+	defer func() { _ = tty.Close() }() // Best effort.
+
+	if sz != nil {
+		if err := Setsize(pty, sz); err != nil {
+			_ = pty.Close() // Best effort.
+			return nil, err
+		}
+	}
+	if c.Stdout == nil {
+		c.Stdout = tty
+	}
+	if c.Stderr == nil {
+		c.Stderr = tty
+	}
+	if c.Stdin == nil {
+		c.Stdin = tty
+	}
+
+	c.SysProcAttr = attrs
+
+	if err := c.Start(); err != nil {
+		_ = pty.Close() // Best effort.
+		return nil, err
+	}
+	return pty, err
+}
diff --git a/vendor/github.com/creack/pty/start.go b/vendor/github.com/creack/pty/start.go
new file mode 100644
index 00000000..9b51635f
--- /dev/null
+++ b/vendor/github.com/creack/pty/start.go
@@ -0,0 +1,25 @@
+//go:build !windows
+// +build !windows
+
+package pty
+
+import (
+	"os"
+	"os/exec"
+	"syscall"
+)
+
+// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// This will resize the pty to the specified size before starting the command.
+// Starts the process in a new session and sets the controlling terminal.
+func StartWithSize(cmd *exec.Cmd, ws *Winsize) (*os.File, error) {
+	if cmd.SysProcAttr == nil {
+		cmd.SysProcAttr = &syscall.SysProcAttr{}
+	}
+	cmd.SysProcAttr.Setsid = true
+	cmd.SysProcAttr.Setctty = true
+	return StartWithAttrs(cmd, ws, cmd.SysProcAttr)
+}
diff --git a/vendor/github.com/creack/pty/start_windows.go b/vendor/github.com/creack/pty/start_windows.go
new file mode 100644
index 00000000..7e9530ba
--- /dev/null
+++ b/vendor/github.com/creack/pty/start_windows.go
@@ -0,0 +1,19 @@
+//go:build windows
+// +build windows
+
+package pty
+
+import (
+	"os"
+	"os/exec"
+)
+
+// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// This will resize the pty to the specified size before starting the command.
+// Starts the process in a new session and sets the controlling terminal.
+func StartWithSize(cmd *exec.Cmd, ws *Winsize) (*os.File, error) {
+	return nil, ErrUnsupported
+}
diff --git a/vendor/github.com/creack/pty/test_crosscompile.sh b/vendor/github.com/creack/pty/test_crosscompile.sh
new file mode 100644
index 00000000..47e8b106
--- /dev/null
+++ b/vendor/github.com/creack/pty/test_crosscompile.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env sh
+
+# Test script checking that all expected os/arch compile properly.
+# Does not actually test the logic, just the compilation so we make sure we don't break code depending on the lib.
+
+echo2() {
+  echo $@ >&2
+}
+
+trap end 0
+end() {
+  [ "$?" = 0 ] && echo2 "Pass." || (echo2 "Fail."; exit 1)
+}
+
+cross() {
+  os=$1
+  shift
+  echo2 "Build for $os."
+  for arch in $@; do
+    echo2 "  - $os/$arch"
+    GOOS=$os GOARCH=$arch go build
+  done
+  echo2
+}
+
+set -e
+
+cross linux     amd64 386 arm arm64 ppc64 ppc64le s390x mips mipsle mips64 mips64le
+cross darwin    amd64 arm64
+cross freebsd   amd64 386 arm arm64
+cross netbsd    amd64 386 arm arm64
+cross openbsd   amd64 386 arm arm64
+cross dragonfly amd64
+cross solaris   amd64
+
+# Not expected to work but should still compile.
+cross windows amd64 386 arm
+
+# TODO: Fix compilation error on openbsd/arm.
+# TODO: Merge the solaris PR.
+
+# Some os/arch require a different compiler. Run in docker.
+if ! hash docker; then
+  # If docker is not present, stop here.
+  return
+fi
+
+echo2 "Build for linux."
+echo2 "  - linux/riscv"
+docker build -t creack-pty-test -f Dockerfile.riscv .
+
+# Golang dropped support for darwin 32bits since go1.15. Make sure the lib still compile with go1.14 on those archs.
+echo2 "Build for darwin (32bits)."
+echo2 "  - darwin/386"
+docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.14 --build-arg=GOOS=darwin --build-arg=GOARCH=386 .
+echo2 "  - darwin/arm"
+docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.14 --build-arg=GOOS=darwin --build-arg=GOARCH=arm .
+
+# Run a single test for an old go version. Would be best with go1.0, but not available on Dockerhub.
+# Using 1.6 as it is the base version for the RISCV compiler.
+# Would also be better to run all the tests, not just one, need to refactor this file to allow for specifc archs per version.
+echo2 "Build for linux - go1.6."
+echo2 "  - linux/amd64"
+docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.6 --build-arg=GOOS=linux --build-arg=GOARCH=amd64 .
diff --git a/vendor/github.com/creack/pty/winsize.go b/vendor/github.com/creack/pty/winsize.go
new file mode 100644
index 00000000..57323f40
--- /dev/null
+++ b/vendor/github.com/creack/pty/winsize.go
@@ -0,0 +1,27 @@
+package pty
+
+import "os"
+
+// InheritSize applies the terminal size of pty to tty. This should be run
+// in a signal handler for syscall.SIGWINCH to automatically resize the tty when
+// the pty receives a window size change notification.
+func InheritSize(pty, tty *os.File) error {
+	size, err := GetsizeFull(pty)
+	if err != nil {
+		return err
+	}
+	if err := Setsize(tty, size); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Getsize returns the number of rows (lines) and cols (positions
+// in each line) in terminal t.
+func Getsize(t *os.File) (rows, cols int, err error) {
+	ws, err := GetsizeFull(t)
+	if err != nil {
+		return 0, 0, err
+	}
+	return int(ws.Rows), int(ws.Cols), nil
+}
diff --git a/vendor/github.com/creack/pty/winsize_unix.go b/vendor/github.com/creack/pty/winsize_unix.go
new file mode 100644
index 00000000..5d99c3dd
--- /dev/null
+++ b/vendor/github.com/creack/pty/winsize_unix.go
@@ -0,0 +1,35 @@
+//go:build !windows
+// +build !windows
+
+package pty
+
+import (
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+// Winsize describes the terminal size.
+type Winsize struct {
+	Rows uint16 // ws_row: Number of rows (in cells)
+	Cols uint16 // ws_col: Number of columns (in cells)
+	X    uint16 // ws_xpixel: Width in pixels
+	Y    uint16 // ws_ypixel: Height in pixels
+}
+
+// Setsize resizes t to s.
+func Setsize(t *os.File, ws *Winsize) error {
+	//nolint:gosec // Expected unsafe pointer for Syscall call.
+	return ioctl(t.Fd(), syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws)))
+}
+
+// GetsizeFull returns the full terminal size description.
+func GetsizeFull(t *os.File) (size *Winsize, err error) {
+	var ws Winsize
+
+	//nolint:gosec // Expected unsafe pointer for Syscall call.
+	if err := ioctl(t.Fd(), syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))); err != nil {
+		return nil, err
+	}
+	return &ws, nil
+}
diff --git a/vendor/github.com/creack/pty/winsize_unsupported.go b/vendor/github.com/creack/pty/winsize_unsupported.go
new file mode 100644
index 00000000..0d210993
--- /dev/null
+++ b/vendor/github.com/creack/pty/winsize_unsupported.go
@@ -0,0 +1,23 @@
+//go:build windows
+// +build windows
+
+package pty
+
+import (
+	"os"
+)
+
+// Winsize is a dummy struct to enable compilation on unsupported platforms.
+type Winsize struct {
+	Rows, Cols, X, Y uint16
+}
+
+// Setsize resizes t to s.
+func Setsize(*os.File, *Winsize) error {
+	return ErrUnsupported
+}
+
+// GetsizeFull returns the full terminal size description.
+func GetsizeFull(*os.File) (*Winsize, error) {
+	return nil, ErrUnsupported
+}
diff --git a/vendor/github.com/creack/pty/ztypes_386.go b/vendor/github.com/creack/pty/ztypes_386.go
new file mode 100644
index 00000000..d126f4aa
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_386.go
@@ -0,0 +1,12 @@
+//go:build 386
+// +build 386
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_amd64.go b/vendor/github.com/creack/pty/ztypes_amd64.go
new file mode 100644
index 00000000..6c4a7677
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_amd64.go
@@ -0,0 +1,12 @@
+//go:build amd64
+// +build amd64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_arm.go b/vendor/github.com/creack/pty/ztypes_arm.go
new file mode 100644
index 00000000..de6fe160
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_arm.go
@@ -0,0 +1,12 @@
+//go:build arm
+// +build arm
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_arm64.go b/vendor/github.com/creack/pty/ztypes_arm64.go
new file mode 100644
index 00000000..c4f315ca
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_arm64.go
@@ -0,0 +1,12 @@
+//go:build arm64
+// +build arm64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go b/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go
new file mode 100644
index 00000000..183c4214
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go
@@ -0,0 +1,17 @@
+//go:build amd64 && dragonfly
+// +build amd64,dragonfly
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_dragonfly.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Name      *byte
+	Len       uint32
+	Pad_cgo_0 [4]byte
+}
diff --git a/vendor/github.com/creack/pty/ztypes_freebsd_386.go b/vendor/github.com/creack/pty/ztypes_freebsd_386.go
new file mode 100644
index 00000000..d80dbf71
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_freebsd_386.go
@@ -0,0 +1,16 @@
+//go:build 386 && freebsd
+// +build 386,freebsd
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Len int32
+	Buf *byte
+}
diff --git a/vendor/github.com/creack/pty/ztypes_freebsd_amd64.go b/vendor/github.com/creack/pty/ztypes_freebsd_amd64.go
new file mode 100644
index 00000000..bfab4e45
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_freebsd_amd64.go
@@ -0,0 +1,17 @@
+//go:build amd64 && freebsd
+// +build amd64,freebsd
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Len       int32
+	Pad_cgo_0 [4]byte
+	Buf       *byte
+}
diff --git a/vendor/github.com/creack/pty/ztypes_freebsd_arm.go b/vendor/github.com/creack/pty/ztypes_freebsd_arm.go
new file mode 100644
index 00000000..3a8aeae3
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_freebsd_arm.go
@@ -0,0 +1,16 @@
+//go:build arm && freebsd
+// +build arm,freebsd
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Len int32
+	Buf *byte
+}
diff --git a/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go b/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go
new file mode 100644
index 00000000..a8392491
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go
@@ -0,0 +1,16 @@
+//go:build arm64 && freebsd
+// +build arm64,freebsd
+
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0xff
+)
+
+type fiodgnameArg struct {
+	Len int32
+	Buf *byte
+}
diff --git a/vendor/github.com/creack/pty/ztypes_freebsd_ppc64.go b/vendor/github.com/creack/pty/ztypes_freebsd_ppc64.go
new file mode 100644
index 00000000..5fa102fc
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_freebsd_ppc64.go
@@ -0,0 +1,14 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Len       int32
+	Pad_cgo_0 [4]byte
+	Buf       *byte
+}
diff --git a/vendor/github.com/creack/pty/ztypes_loong64.go b/vendor/github.com/creack/pty/ztypes_loong64.go
new file mode 100644
index 00000000..3beb5c17
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_loong64.go
@@ -0,0 +1,12 @@
+//go:build loong64
+// +build loong64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_mipsx.go b/vendor/github.com/creack/pty/ztypes_mipsx.go
new file mode 100644
index 00000000..28127797
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_mipsx.go
@@ -0,0 +1,13 @@
+//go:build (mips || mipsle || mips64 || mips64le) && linux
+// +build mips mipsle mips64 mips64le
+// +build linux
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_netbsd_32bit_int.go b/vendor/github.com/creack/pty/ztypes_netbsd_32bit_int.go
new file mode 100644
index 00000000..2ab7c455
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_netbsd_32bit_int.go
@@ -0,0 +1,17 @@
+//go:build (386 || amd64 || arm || arm64) && netbsd
+// +build 386 amd64 arm arm64
+// +build netbsd
+
+package pty
+
+type ptmget struct {
+	Cfd int32
+	Sfd int32
+	Cn  [1024]int8
+	Sn  [1024]int8
+}
+
+var (
+	ioctl_TIOCPTSNAME = 0x48087448
+	ioctl_TIOCGRANTPT = 0x20007447
+)
diff --git a/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go b/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go
new file mode 100644
index 00000000..1eb09481
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go
@@ -0,0 +1,14 @@
+//go:build (386 || amd64 || arm || arm64 || mips64) && openbsd
+// +build 386 amd64 arm arm64 mips64
+// +build openbsd
+
+package pty
+
+type ptmget struct {
+	Cfd int32
+	Sfd int32
+	Cn  [16]int8
+	Sn  [16]int8
+}
+
+var ioctl_PTMGET = 0x40287401
diff --git a/vendor/github.com/creack/pty/ztypes_ppc64.go b/vendor/github.com/creack/pty/ztypes_ppc64.go
new file mode 100644
index 00000000..bbb3da83
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_ppc64.go
@@ -0,0 +1,12 @@
+//go:build ppc64
+// +build ppc64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_ppc64le.go b/vendor/github.com/creack/pty/ztypes_ppc64le.go
new file mode 100644
index 00000000..8a4fac3e
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_ppc64le.go
@@ -0,0 +1,12 @@
+//go:build ppc64le
+// +build ppc64le
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_riscvx.go b/vendor/github.com/creack/pty/ztypes_riscvx.go
new file mode 100644
index 00000000..dc5da905
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_riscvx.go
@@ -0,0 +1,12 @@
+//go:build riscv || riscv64
+// +build riscv riscv64
+
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/creack/pty/ztypes_s390x.go b/vendor/github.com/creack/pty/ztypes_s390x.go
new file mode 100644
index 00000000..3433be7c
--- /dev/null
+++ b/vendor/github.com/creack/pty/ztypes_s390x.go
@@ -0,0 +1,12 @@
+//go:build s390x
+// +build s390x
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)
diff --git a/vendor/github.com/docker/cli-docs-tool/clidocstool_md.go b/vendor/github.com/docker/cli-docs-tool/clidocstool_md.go
index 73d05c8a..1dee58c0 100644
--- a/vendor/github.com/docker/cli-docs-tool/clidocstool_md.go
+++ b/vendor/github.com/docker/cli-docs-tool/clidocstool_md.go
@@ -53,6 +53,12 @@ func (c *Client) GenMarkdownTree(cmd *cobra.Command) error {
 		return nil
 	}
 
+	// Skip hidden command
+	if cmd.Hidden {
+		log.Printf("INFO: Skipping Markdown for %q (hidden command)", cmd.CommandPath())
+		return nil
+	}
+
 	log.Printf("INFO: Generating Markdown for %q", cmd.CommandPath())
 	mdFile := mdFilename(cmd)
 	sourcePath := filepath.Join(c.source, mdFile)
@@ -208,6 +214,9 @@ func mdCmdOutput(cmd *cobra.Command, old string) (string, error) {
 		b.WriteString("### Subcommands\n\n")
 		table := newMdTable("Name", "Description")
 		for _, c := range cmd.Commands() {
+			if c.Hidden {
+				continue
+			}
 			table.AddRow(fmt.Sprintf("[`%s`](%s)", c.Name(), mdFilename(c)), c.Short)
 		}
 		b.WriteString(table.String() + "\n")
diff --git a/vendor/github.com/docker/cli-docs-tool/clidocstool_yaml.go b/vendor/github.com/docker/cli-docs-tool/clidocstool_yaml.go
index f6ae6b85..52352429 100644
--- a/vendor/github.com/docker/cli-docs-tool/clidocstool_yaml.go
+++ b/vendor/github.com/docker/cli-docs-tool/clidocstool_yaml.go
@@ -62,6 +62,7 @@ type cmdDoc struct {
 	InheritedOptions []cmdOption `yaml:"inherited_options,omitempty"`
 	Example          string      `yaml:"examples,omitempty"`
 	Deprecated       bool
+	Hidden           bool
 	MinAPIVersion    string `yaml:"min_api_version,omitempty"`
 	Experimental     bool
 	ExperimentalCLI  bool
@@ -155,6 +156,7 @@ func (c *Client) genYamlCustom(cmd *cobra.Command, w io.Writer) error {
 		Long:       forceMultiLine(cmd.Long, longMaxWidth),
 		Example:    cmd.Example,
 		Deprecated: len(cmd.Deprecated) > 0,
+		Hidden:     cmd.Hidden,
 	}
 
 	if len(cliDoc.Long) == 0 {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 453d24b1..ebdcea43 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -197,6 +197,9 @@ github.com/containerd/ttrpc
 # github.com/containerd/typeurl/v2 v2.1.1
 ## explicit; go 1.13
 github.com/containerd/typeurl/v2
+# github.com/creack/pty v1.1.18
+## explicit; go 1.13
+github.com/creack/pty
 # github.com/cyphar/filepath-securejoin v0.2.3
 ## explicit; go 1.13
 github.com/cyphar/filepath-securejoin
@@ -231,7 +234,7 @@ github.com/docker/cli/cli/streams
 github.com/docker/cli/cli/trust
 github.com/docker/cli/cli/version
 github.com/docker/cli/opts
-# github.com/docker/cli-docs-tool v0.5.1
+# github.com/docker/cli-docs-tool v0.6.0
 ## explicit; go 1.18
 github.com/docker/cli-docs-tool
 github.com/docker/cli-docs-tool/annotation



Test patch:
diff --git a/bake/compose_test.go b/bake/compose_test.go
index 5074248b..b5f9961b 100644
--- a/bake/compose_test.go
+++ b/bake/compose_test.go
@@ -36,6 +36,8 @@ services:
         - token
         - aws
   webapp2:
+    profiles:
+      - test
     build:
       context: ./dir
       dockerfile_inline: |
diff --git a/tests/bake.go b/tests/bake.go
index 130fb0bd..b76a8bb8 100644
--- a/tests/bake.go
+++ b/tests/bake.go
@@ -18,6 +18,7 @@ func bakeCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
 }
 
 var bakeTests = []func(t *testing.T, sb integration.Sandbox){
+	testBakeLocal,
 	testBakeRemote,
 	testBakeRemoteCmdContext,
 	testBakeRemoteCmdContextOverride,
@@ -26,6 +27,29 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
 	testBakeRemoteCmdContextEscapeRelative,
 }
 
+func testBakeLocal(t *testing.T, sb integration.Sandbox) {
+	dockerfile := []byte(`
+FROM scratch
+COPY foo /foo
+	`)
+	bakefile := []byte(`
+target "default" {
+}
+`)
+	dir := tmpdir(
+		t,
+		fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
+		fstest.CreateFile("Dockerfile", dockerfile, 0600),
+		fstest.CreateFile("foo", []byte("foo"), 0600),
+	)
+	dirDest := t.TempDir()
+
+	out, err := bakeCmd(sb, withDir(dir), withArgs("--set", "*.output=type=local,dest="+dirDest))
+	require.NoError(t, err, out)
+
+	require.FileExists(t, filepath.Join(dirDest, "foo"))
+}
+
 func testBakeRemote(t *testing.T, sb integration.Sandbox) {
 	bakefile := []byte(`
 target "default" {
diff --git a/tests/build.go b/tests/build.go
index 8b7140d7..87021f92 100644
--- a/tests/build.go
+++ b/tests/build.go
@@ -3,19 +3,23 @@ package tests
 import (
 	"bytes"
 	"encoding/json"
-	"errors"
 	"fmt"
+	"io"
 	"os"
+	"path"
 	"path/filepath"
+	"regexp"
 	"strings"
 	"testing"
 
 	"github.com/containerd/containerd/platforms"
 	"github.com/containerd/continuity/fs/fstest"
+	"github.com/creack/pty"
 	"github.com/moby/buildkit/util/contentutil"
 	"github.com/moby/buildkit/util/testutil"
 	"github.com/moby/buildkit/util/testutil/integration"
 	"github.com/opencontainers/go-digest"
+	"github.com/pkg/errors"
 	"github.com/stretchr/testify/require"
 )
 
@@ -33,6 +37,8 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
 	testBuildRegistryExport,
 	testBuildTarExport,
 	testBuildMobyFromLocalImage,
+	testBuildDetailsLink,
+	testBuildProgress,
 }
 
 func testBuild(t *testing.T, sb integration.Sandbox) {
@@ -187,6 +193,48 @@ RUN busybox | head -1 | grep v1.35.0
 	require.NoError(t, cmd.Run())
 }
 
+func testBuildDetailsLink(t *testing.T, sb integration.Sandbox) {
+	buildDetailsPattern := regexp.MustCompile(`(?m)^View build details: docker-desktop://dashboard/build/[^/]+/[^/]+/[^/]+\n$`)
+
+	// build simple dockerfile
+	dockerfile := []byte(`FROM busybox:latest
+RUN echo foo > /bar`)
+	dir := tmpdir(t, fstest.CreateFile("Dockerfile", dockerfile, 0600))
+	cmd := buildxCmd(sb, withArgs("build", "--output=type=cacheonly", dir))
+	out, err := cmd.CombinedOutput()
+	require.NoError(t, err, string(out))
+	require.False(t, buildDetailsPattern.MatchString(string(out)), fmt.Sprintf("build details link not expected in output, got %q", out))
+
+	// create desktop-build .lastaccess file
+	home, err := os.UserHomeDir() // TODO: sandbox should create a temp home dir and expose it through its interface
+	require.NoError(t, err)
+	dbDir := path.Join(home, ".docker", "desktop-build")
+	require.NoError(t, os.MkdirAll(dbDir, 0755))
+	dblaFile, err := os.Create(path.Join(dbDir, ".lastaccess"))
+	require.NoError(t, err)
+	defer func() {
+		dblaFile.Close()
+		if err := os.Remove(dblaFile.Name()); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	// build again
+	cmd = buildxCmd(sb, withArgs("build", "--output=type=cacheonly", dir))
+	out, err = cmd.CombinedOutput()
+	require.NoError(t, err, string(out))
+	require.True(t, buildDetailsPattern.MatchString(string(out)), fmt.Sprintf("expected build details link in output, got %q", out))
+
+	// build erroneous dockerfile
+	dockerfile = []byte(`FROM busybox:latest
+RUN exit 1`)
+	dir = tmpdir(t, fstest.CreateFile("Dockerfile", dockerfile, 0600))
+	cmd = buildxCmd(sb, withArgs("build", "--output=type=cacheonly", dir))
+	out, err = cmd.CombinedOutput()
+	require.Error(t, err, string(out))
+	require.True(t, buildDetailsPattern.MatchString(string(out)), fmt.Sprintf("expected build details link in output, got %q", out))
+}
+
 func createTestProject(t *testing.T) string {
 	dockerfile := []byte(`
 FROM busybox:latest AS base
@@ -203,3 +251,29 @@ COPY --from=base /etc/bar /bar
 	)
 	return dir
 }
+
+func testBuildProgress(t *testing.T, sb integration.Sandbox) {
+	dir := createTestProject(t)
+	driver, _, _ := strings.Cut(sb.Name(), "+")
+	name := sb.Address()
+
+	// progress=tty
+	cmd := buildxCmd(sb, withArgs("build", "--progress=tty", "--output=type=cacheonly", dir))
+	f, err := pty.Start(cmd)
+	require.NoError(t, err)
+	buf := bytes.NewBuffer(nil)
+	io.Copy(buf, f)
+	ttyOutput := buf.String()
+	require.Contains(t, ttyOutput, "[+] Building")
+	require.Contains(t, ttyOutput, fmt.Sprintf("%s:%s", driver, name))
+	require.Contains(t, ttyOutput, "=> [internal] load build definition from Dockerfile")
+	require.Contains(t, ttyOutput, "=> [base 1/3] FROM docker.io/library/busybox:latest")
+
+	// progress=plain
+	cmd = buildxCmd(sb, withArgs("build", "--progress=plain", "--output=type=cacheonly", dir))
+	plainOutput, err := cmd.CombinedOutput()
+	require.NoError(t, err)
+	require.Contains(t, string(plainOutput), fmt.Sprintf(`#0 building with "%s" instance using %s driver`, name, driver))
+	require.Contains(t, string(plainOutput), "[internal] load build definition from Dockerfile")
+	require.Contains(t, string(plainOutput), "[base 1/3] FROM docker.io/library/busybox:latest")
+}