Repository: docker/compose (go)

Issue:
Fallback build after failed image pull for service produces scary error message (but works)
**Description**

### Building a docker image with name format outputs an error message.


When building a docker image locally, while using a formatted image name, results in the following error message:

```Shell
λ  /tmp/demo  docker compose up
[+] Running 0/1
 ⠿ example Error                                                                                                                                                      1.4s
...
```

While the command finishes successfully, this error message is logged to the console. 

The assumption is that docker is attempting to pull the image from a registry. However, that image does not exist.

**Steps to reproduce the issue:**

Using the following compose.yaml file:
```yaml
services:
  example:
    build:
      context: .
      target: build
    image: demo/example

```

With this Dockerfile:
```dockerfile
FROM node:18.7.0 AS build
```

Will reproduce the issue in the console. Removing `image` from `compose.yaml` will result in no error message.

**Describe the results you received:**

This is the complete console output that includes the error message:

```Shell
λ  /tmp/demo  docker compose up
[+] Running 0/1
 ⠿ example Error                                                                                                                                                      1.4s
[+] Building 0.1s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                  0.0s
 => => transferring dockerfile: 63B                                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                                     0.0s
 => => transferring context: 2B                                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/node:18.7.0                                                                                                        0.0s
 => CACHED [1/1] FROM docker.io/library/node:18.7.0                                                                                                                   0.0s
 => exporting to image                                                                                                                                                0.0s
 => => exporting layers                                                                                                                                               0.0s
 => => writing image sha256:80440b842c0d99a26be8529f0f0d4d33104da161355e1f6890b11bbedd6a3a6a                                                                          0.0s
 => => naming to docker.io/demo/example:latest                                                                                                                        0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 2/1
 ⠿ Network demo_default      Created                                                                                                                                  0.0s
 ⠿ Container demo-example-1  Created                                                                                                                                  0.1s
Attaching to demo-example-1
demo-example-1 exited with code 0
```

**Describe the results you expected:**

Removing `image` from `compose.yaml` will result in no error message. In other words, what we expect:

```Shell
λ  /tmp/demo  docker compose up
[+] Running 0/1
 ⠿ example Error                                                                                                                                                      1.3s
[+] Building 0.1s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                  0.0s
 => => transferring dockerfile: 63B                                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                                     0.0s
 => => transferring context: 2B                                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/node:18.7.0                                                                                                        0.0s
 => CACHED [1/1] FROM docker.io/library/node:18.7.0                                                                                                                   0.0s
 => exporting to image                                                                                                                                                0.0s
 => => exporting layers                                                                                                                                               0.0s
 => => writing image sha256:80440b842c0d99a26be8529f0f0d4d33104da161355e1f6890b11bbedd6a3a6a                                                                          0.0s
 => => naming to docker.io/demo/example                                                                                                                               0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 2/0
 ⠿ Network demo_default      Created                                                                                                                                  0.0s
 ⠿ Container demo-example-1  Created                                                                                                                                  0.0s
Attaching to demo-example-1
demo-example-1 exited with code 0
```

**Additional information you deem important (e.g. issue happens only occasionally):**

The assumption is that docker is attempting to pull the image from a registry. However, that image does not exist. As a result, it is using the `build` value as fallback. It might help if we eliminated this error message or perhaps logged a notice if this was intentional. That is, we want to build a custom image and name it.

**Output of `docker compose version`:**

```Shell
Docker Compose version v2.10.2
```

**Output of `docker info`:**

```Shell
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc., v0.9.1)
  compose: Docker Compose (Docker Inc., v2.10.2)
  extension: Manages Docker extensions (Docker Inc., v0.2.9)
  sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc., 0.6.0)
  scan: Docker Scan (Docker Inc., v0.19.0)

Server:
 Containers: 1
  Running: 0
  Paused: 0
  Stopped: 1
 Images: 10
 Server Version: 20.10.17
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 2
 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 io.containerd.runtime.v1.linux runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 9cd3357b7fd7218e4aec3eae239db1f68a5a6ec6
 runc version: v1.1.4-0-g5fd4c4d
 init version: de40ad0
 Security Options:
  seccomp
   Profile: default
  cgroupns
 Kernel Version: 5.10.124-linuxkit
 Operating System: Docker Desktop
 OSType: linux
 Architecture: x86_64
 CPUs: 6
 Total Memory: 7.773GiB
 Name: docker-desktop
 ID: PZFN:3UMW:F4KK:I5BZ:VUXQ:PCVH:AIBQ:KJET:5QIW:TCQ3:XZTZ:GU2S
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 HTTP Proxy: http.docker.internal:3128
 HTTPS Proxy: http.docker.internal:3128
 No Proxy: hubproxy.docker.internal
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  hubproxy.docker.internal:5000
  127.0.0.0/8
 Live Restore Enabled: false
```

**Additional environment details:**

macOS Big Sur Version 11.6.8


Pull request:
chore: add improvement output warning when pulling image
**What I did**

Related to [this](https://github.com/docker/compose/issues/9820) issue, I added some improvements in the console output, where I added a `warning` status instead of an `error` when pulling an image produces an error but in the service has a build section.

Before Changes:
<img width="993" alt="Screen Shot 2022-09-11 at 23 44 04" src="https://user-images.githubusercontent.com/36788585/189539437-5337c408-5577-4015-aa8a-8e852b1781d9.png">

After Changes:
<img width="1001" alt="Screen Shot 2022-09-11 at 23 45 06" src="https://user-images.githubusercontent.com/36788585/189539455-8aeb2b34-b891-47f6-8f87-17a1dff14c1b.png">


**Related issue**
fixes #9820 

**(not mandatory) A picture of a cute animal, if possible in relation with what you did**
![image](https://user-images.githubusercontent.com/36788585/189539520-f2913a5e-37e9-4cda-bdbc-268fdde6920c.png)



Patch:
diff --git a/README.md b/README.md
index 2007d9987..43af9f6df 100644
--- a/README.md
+++ b/README.md
@@ -35,12 +35,12 @@ You can download Docker Compose binaries from the
 
 Rename the relevant binary for your OS to `docker-compose` and copy it to `$HOME/.docker/cli-plugins` 
 
-Or copy it into one of these folders for installing it system-wide:
+Or copy it into one of these folders to install it system-wide:
 
 * `/usr/local/lib/docker/cli-plugins` OR `/usr/local/libexec/docker/cli-plugins`
 * `/usr/lib/docker/cli-plugins` OR `/usr/libexec/docker/cli-plugins`
 
-(might require to make the downloaded file executable with `chmod +x`)
+(might require making the downloaded file executable with `chmod +x`)
 
 
 Quick Start
diff --git a/pkg/compose/pull.go b/pkg/compose/pull.go
index fbeacacec..88cfefbf6 100644
--- a/pkg/compose/pull.go
+++ b/pkg/compose/pull.go
@@ -181,6 +181,18 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
 		RegistryAuth: base64.URLEncoding.EncodeToString(buf),
 		Platform:     service.Platform,
 	})
+
+	// check if has error and the service has a build section
+	// then the status should be warning instead of error
+	if err != nil && service.Build != nil {
+		w.Event(progress.Event{
+			ID:     service.Name,
+			Status: progress.Warning,
+			Text:   "Warning",
+		})
+		return "", WrapCategorisedComposeError(err, PullFailure)
+	}
+
 	if err != nil {
 		w.Event(progress.Event{
 			ID:     service.Name,
diff --git a/pkg/progress/event.go b/pkg/progress/event.go
index 0ead54dd4..8603410e9 100644
--- a/pkg/progress/event.go
+++ b/pkg/progress/event.go
@@ -28,6 +28,8 @@ const (
 	Done
 	// Error means that the current task has errored
 	Error
+	// Warning means that the current task has warning
+	Warning
 )
 
 // Event represents a progress event.
diff --git a/pkg/progress/tty.go b/pkg/progress/tty.go
index 09338fb83..8729571ae 100644
--- a/pkg/progress/tty.go
+++ b/pkg/progress/tty.go
@@ -75,7 +75,7 @@ func (w *ttyWriter) Event(e Event) {
 	if _, ok := w.events[e.ID]; ok {
 		last := w.events[e.ID]
 		switch e.Status {
-		case Done, Error:
+		case Done, Error, Warning:
 			if last.Status != e.Status {
 				last.stop()
 			}
@@ -222,6 +222,9 @@ func lineText(event Event, pad string, terminalWidth, statusPadding int, color b
 		if event.Status == Error {
 			color = aec.RedF
 		}
+		if event.Status == Warning {
+			color = aec.YellowF
+		}
 		return aec.Apply(o, color)
 	}
 


Test patch:
diff --git a/pkg/progress/tty_test.go b/pkg/progress/tty_test.go
index f9b4071d1..e9e0c7e2d 100644
--- a/pkg/progress/tty_test.go
+++ b/pkg/progress/tty_test.go
@@ -54,6 +54,10 @@ func TestLineText(t *testing.T) {
 	ev.Status = Error
 	out = lineText(ev, "", 50, lineWidth, true)
 	assert.Equal(t, out, "\x1b[31m . id Text Status                            0.0s\n\x1b[0m")
+
+	ev.Status = Warning
+	out = lineText(ev, "", 50, lineWidth, true)
+	assert.Equal(t, out, "\x1b[33m . id Text Status                            0.0s\n\x1b[0m")
 }
 
 func TestLineTextSingleEvent(t *testing.T) {
@@ -103,3 +107,32 @@ func TestErrorEvent(t *testing.T) {
 	assert.Assert(t, ok)
 	assert.Assert(t, event.endTime.After(time.Now().Add(-10*time.Second)))
 }
+
+func TestWarningEvent(t *testing.T) {
+	w := &ttyWriter{
+		events: map[string]Event{},
+		mtx:    &sync.Mutex{},
+	}
+	e := Event{
+		ID:         "id",
+		Text:       "Text",
+		Status:     Working,
+		StatusText: "Working",
+		startTime:  time.Now(),
+		spinner: &spinner{
+			chars: []string{"."},
+		},
+	}
+	// Fire "Working" event and check end time isn't touched
+	w.Event(e)
+	event, ok := w.events[e.ID]
+	assert.Assert(t, ok)
+	assert.Assert(t, event.endTime.Equal(time.Time{}))
+
+	// Fire "Warning" event and check end time is set
+	e.Status = Warning
+	w.Event(e)
+	event, ok = w.events[e.ID]
+	assert.Assert(t, ok)
+	assert.Assert(t, event.endTime.After(time.Now().Add(-10*time.Second)))
+}