Repository: tensorchord/envd (language: python)

Issue:
bug: envd --debug logs will be overwritten by progress display
## Description

![image](https://user-images.githubusercontent.com/12974685/205233927-6475993b-d28d-4dd5-b9ff-315448542f28.png)


## Reproduction

## Additional Info

<!-- It will be very helpful if you can provide the version info by running the command `envd version`. -->

---
<!-- Issue Author: Don't delete this message to encourage other users to support your issue! -->
**Message from the maintainers**:

Impacted by this bug? Give it a 👍. We prioritise the issues with the most 👍.



Discussion / hints:
https://github.com/tensorchord/envd/issues/1244
> #1244

They are not the same. The blue log will overwrite others.

Solution: remember to add `\n` if you are using logrus Debugf/Infof/etc.

I'm not sure if it's a bug or a feature.
This issue relates to the code here https://github.com/tensorchord/envd/blob/main/pkg/progress/compileui/display.go
The compileui code base is a simplified version of https://github.com/tensorchord/envd/blob/main/pkg/progress/progressui/printer.go

But it does not work as expected.
The code progressui is copied from buildkit. And to show oh-my-zsh and vscode extensions download progress, I made a simplified version compileui. I may miss the core logic about how to deal with plain text mode printing.

```go
func New(ctx context.Context, out console.File, mode string) (Writer, error) {
	var c console.Console
	switch mode {
	case "auto", "tty", "":
		if cons, err := console.ConsoleFromFile(out); err == nil {
			c = cons
		} else {
			if mode == "tty" {
				return nil, errors.Wrap(err, "failed to get console")
			}
		}
	case "plain":
	default:
		return nil, errors.Errorf("invalid progress mode %s", mode)
	}
```

I used to think this code can work with plain test. But it does not. 

The terminal will always flush, thus it overwrites some debug logs



Fix patch:
diff --git a/pkg/app/build/build.go b/pkg/app/build/build.go
index 8c5d8337f..a9d3c206a 100644
--- a/pkg/app/build/build.go
+++ b/pkg/app/build/build.go
@@ -28,6 +28,7 @@ import (
 	"github.com/tensorchord/envd/pkg/driver/docker"
 	"github.com/tensorchord/envd/pkg/envd"
 	"github.com/tensorchord/envd/pkg/home"
+	progressmode "github.com/tensorchord/envd/pkg/progress/mode"
 	"github.com/tensorchord/envd/pkg/util/fileutil"
 )
 
@@ -143,7 +144,7 @@ func ParseBuildOpt(clicontext *cli.Context) (builder.Options, error) {
 		Tag:              tag,
 		OutputOpts:       output,
 		PubKeyPath:       clicontext.Path("public-key"),
-		ProgressMode:     "auto",
+		ProgressMode:     progressmode.AUTO,
 		ExportCache:      exportCache,
 		ImportCache:      importCache,
 		UseHTTPProxy:     useProxy,
@@ -151,7 +152,7 @@ func ParseBuildOpt(clicontext *cli.Context) (builder.Options, error) {
 
 	debug := clicontext.Bool("debug")
 	if debug {
-		opt.ProgressMode = "plain"
+		opt.ProgressMode = progressmode.PLAIN
 	}
 	return opt, nil
 }
diff --git a/pkg/builder/build.go b/pkg/builder/build.go
index 63441fa5a..c9db810c1 100644
--- a/pkg/builder/build.go
+++ b/pkg/builder/build.go
@@ -157,7 +157,7 @@ func (b generalBuilder) Interpret() error {
 
 func (b generalBuilder) Compile(ctx context.Context) (*llb.Definition, error) {
 	envName := filepath.Base(b.BuildContextDir)
-	def, err := b.graph.Compile(ctx, envName, b.PubKeyPath)
+	def, err := b.graph.Compile(ctx, envName, b.PubKeyPath, b.Options.ProgressMode)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to compile build.envd")
 	}
diff --git a/pkg/lang/ir/graph.go b/pkg/lang/ir/graph.go
index a19084818..6600eebd7 100644
--- a/pkg/lang/ir/graph.go
+++ b/pkg/lang/ir/graph.go
@@ -23,7 +23,7 @@ import (
 )
 
 type Graph interface {
-	Compile(ctx context.Context, envName string, pub string) (*llb.Definition, error)
+	Compile(ctx context.Context, envName string, pub string, progressMode string) (*llb.Definition, error)
 
 	graphDebugger
 	graphVisitor
diff --git a/pkg/lang/ir/v0/compile.go b/pkg/lang/ir/v0/compile.go
index 6c2dc93c8..fd0359301 100644
--- a/pkg/lang/ir/v0/compile.go
+++ b/pkg/lang/ir/v0/compile.go
@@ -106,8 +106,8 @@ func (g generalGraph) GetUser() string {
 	return "envd"
 }
 
-func (g *generalGraph) Compile(ctx context.Context, envName string, pub string) (*llb.Definition, error) {
-	w, err := compileui.New(ctx, os.Stdout, "auto")
+func (g *generalGraph) Compile(ctx context.Context, envName string, pub string, progressMode string) (*llb.Definition, error) {
+	w, err := compileui.New(ctx, os.Stdout, progressMode)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create compileui")
 	}
diff --git a/pkg/lang/ir/v1/compile.go b/pkg/lang/ir/v1/compile.go
index 0219f0281..e3c5264eb 100644
--- a/pkg/lang/ir/v1/compile.go
+++ b/pkg/lang/ir/v1/compile.go
@@ -102,8 +102,8 @@ func (g generalGraph) GetRuntimeCommands() map[string]string {
 	return g.RuntimeCommands
 }
 
-func (g *generalGraph) Compile(ctx context.Context, envName string, pub string) (*llb.Definition, error) {
-	w, err := compileui.New(ctx, os.Stdout, "auto")
+func (g *generalGraph) Compile(ctx context.Context, envName string, pub string, progressMode string) (*llb.Definition, error) {
+	w, err := compileui.New(ctx, os.Stdout, progressMode)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create compileui")
 	}
diff --git a/pkg/progress/compileui/display.go b/pkg/progress/compileui/display.go
index 990bb6e9d..66dbe31f5 100644
--- a/pkg/progress/compileui/display.go
+++ b/pkg/progress/compileui/display.go
@@ -26,6 +26,7 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/tensorchord/envd/pkg/editor/vscode"
+	progressmode "github.com/tensorchord/envd/pkg/progress/mode"
 )
 
 type Writer interface {
@@ -48,15 +49,15 @@ type generalWriter struct {
 func New(ctx context.Context, out console.File, mode string) (Writer, error) {
 	var c console.Console
 	switch mode {
-	case "auto", "tty", "":
+	case progressmode.AUTO, progressmode.TTY, progressmode.NONE:
 		if cons, err := console.ConsoleFromFile(out); err == nil {
 			c = cons
 		} else {
-			if mode == "tty" {
+			if mode == progressmode.TTY {
 				return nil, errors.Wrap(err, "failed to get console")
 			}
 		}
-	case "plain":
+	case progressmode.PLAIN:
 	default:
 		return nil, errors.Errorf("invalid progress mode %s", mode)
 	}
diff --git a/pkg/progress/mode/mode.go b/pkg/progress/mode/mode.go
new file mode 100644
index 000000000..aa0cbb0f3
--- /dev/null
+++ b/pkg/progress/mode/mode.go
@@ -0,0 +1,22 @@
+// Copyright 2023 The envd Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package progressmode
+
+const (
+	AUTO  = "auto"
+	TTY   = "tty"
+	PLAIN = "plain"
+	NONE  = ""
+)
diff --git a/pkg/progress/progresswriter/printer.go b/pkg/progress/progresswriter/printer.go
index e60e1ab21..c4165dfaf 100644
--- a/pkg/progress/progresswriter/printer.go
+++ b/pkg/progress/progresswriter/printer.go
@@ -23,6 +23,7 @@ import (
 	"github.com/containerd/console"
 	"github.com/moby/buildkit/client"
 
+	progressmode "github.com/tensorchord/envd/pkg/progress/mode"
 	"github.com/tensorchord/envd/pkg/progress/progressui"
 )
 
@@ -82,21 +83,21 @@ func NewPrinter(ctx context.Context, out console.File, mode string) (Writer, err
 		done:   doneCh,
 	}
 
-	if v := os.Getenv("BUILDKIT_PROGRESS"); v != "" && mode == "auto" {
+	if v := os.Getenv("BUILDKIT_PROGRESS"); v != "" && mode == progressmode.AUTO {
 		mode = v
 	}
 
 	var c console.Console
 	switch mode {
-	case "auto", "tty", "":
+	case progressmode.AUTO, progressmode.TTY, progressmode.NONE:
 		if cons, err := console.ConsoleFromFile(out); err == nil {
 			c = cons
 		} else {
-			if mode == "tty" {
+			if mode == progressmode.TTY {
 				return nil, errors.Wrap(err, "failed to get console")
 			}
 		}
-	case "plain":
+	case progressmode.PLAIN:
 	default:
 		return nil, errors.Errorf("invalid progress mode %s", mode)
 	}


Test patch:
diff --git a/pkg/builder/builder_test.go b/pkg/builder/builder_test.go
index 13587ff75..40dc1f707 100644
--- a/pkg/builder/builder_test.go
+++ b/pkg/builder/builder_test.go
@@ -31,6 +31,7 @@ import (
 	v0 "github.com/tensorchord/envd/pkg/lang/ir/v0"
 	"github.com/tensorchord/envd/pkg/progress/compileui"
 	compileuimock "github.com/tensorchord/envd/pkg/progress/compileui/mock"
+	progressmode "github.com/tensorchord/envd/pkg/progress/mode"
 	"github.com/tensorchord/envd/pkg/progress/progresswriter"
 	sshconfig "github.com/tensorchord/envd/pkg/ssh/config"
 )
@@ -56,7 +57,7 @@ var _ = Describe("Builder", func() {
 					Options: Options{
 						ManifestFilePath: manifestFilePath,
 						ConfigFilePath:   configFilePath,
-						ProgressMode:     "plain",
+						ProgressMode:     progressmode.PLAIN,
 						Tag:              tag,
 						BuildFuncName:    "build",
 						PubKeyPath:       pub,