mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
* Update vendoring for coloring to add NO_COLOR * Updates odo init output / adds logo <!-- Thank you for opening a PR! Here are some things you need to know before submitting: 1. Please read our developer guideline: https://github.com/redhat-developer/odo/wiki/Dev:-odo-Dev-Guidelines 2. Label this PR accordingly with the '/kind' line 3. Ensure you have written and ran the appropriate tests: https://github.com/redhat-developer/odo/wiki/Dev:-Writing-and-running-tests 4. Read how we approve and LGTM each PR: https://github.com/redhat-developer/odo/wiki/Pull-Requests:-Review-guideline Documentation: If you are pushing a change to documentation, please read: https://github.com/redhat-developer/odo/wiki/Documentation:-Contributing --> **What type of PR is this:** <!-- Add one of the following kinds: /kind bug /kind cleanup /kind tests /kind documentation Feel free to use other [labels](https://github.com/redhat-developer/odo/labels) as needed. However one of the above labels must be present or the PR will not be reviewed. This instruction is for reviewers as well. --> /kind feature **What does this PR do / why we need it:** We have the logo appearing in `odo dev` and `odo deploy` Those 3 commands are the most used with `odo`, and the ones that are most used. All three should have similar and consistent output that contains: * What the component is doing (first line) * What's detected (in odo init, it's the files, in odo deploy and dev it's the component name) * odo version that is being used / outputted. ```sh $ odo init __ / \__ Initializing a new component \__/ \ Files: No source code detected, a starter project will be created in the current directory / \__/ odo version: v2.5.0 \__/ Interactive mode enabled, please answer the following questions: ? Select language: javascript ? Select project type: Next.js ✓ Downloading devfile "nodejs-nextjs" from registry "DefaultDevfileRegistry" [450ms] ? Which starter project do you want to use? nodejs-nextjs-starter ? Enter component name: my-nodejs-nextjs-app ✓ Downloading starter project "nodejs-nextjs-starter" [516ms] Your new component 'my-nodejs-nextjs-app' is ready in the current directory. To start editing your component, use 'odo dev' and open this folder in your favorite IDE. Changes will be directly reflected on the cluster. ``` **Which issue(s) this PR fixes:** <!-- Specifying the issue will automatically close it when this PR is merged --> N/A **PR acceptance criteria:** - [X] Unit test - [X] Integration test - [X] Documentation **How to test changes / Special notes to the reviewer:** N/A Signed-off-by: Charlie Drage <charlie@charliedrage.com> * Update based on reviews Signed-off-by: Charlie Drage <charlie@charliedrage.com> * Update based on review Signed-off-by: Charlie Drage <charlie@charliedrage.com>
123 lines
3.8 KiB
Go
123 lines
3.8 KiB
Go
//go:build linux || darwin || dragonfly || solaris || openbsd || netbsd || freebsd
|
|
// +build linux darwin dragonfly solaris openbsd netbsd freebsd
|
|
|
|
package helper
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
|
|
"github.com/Netflix/go-expect"
|
|
"github.com/hinshun/vt10x"
|
|
"github.com/kr/pty"
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
// InteractiveContext represents the context of an interactive command to be run.
|
|
type InteractiveContext struct {
|
|
|
|
//Command represents the original command ran
|
|
Command []string
|
|
|
|
// console is the internal interface used by the interactive command
|
|
console *expect.Console
|
|
|
|
// buffer is the internal bytes buffer containing the console output.
|
|
// Its content will get updated as long as there are interactions with the console, like sending lines or
|
|
// expecting lines.
|
|
buffer *bytes.Buffer
|
|
|
|
// A function yto call to stop the process
|
|
StopCommand func()
|
|
}
|
|
|
|
// Tester represents the function that contains all steps to test the given interactive command.
|
|
// The InteractiveContext argument needs to be passed to the various helper.SendLine and helper.ExpectString methods.
|
|
type Tester func(InteractiveContext)
|
|
|
|
// RunInteractive runs the command in interactive mode and returns the output, and error.
|
|
// It takes command as array of strings, and a function `tester` that contains steps to run the test as an argument.
|
|
// The command is executed as a separate process, the environment of which is controlled via the `env` argument.
|
|
// The initial value of the sub-process environment is a copy of the environment of the current process.
|
|
// If `env` is not `nil`, it will be appended to the end of the sub-process environment.
|
|
// If there are duplicate environment keys, only the last value in the slice for each duplicate key is used.
|
|
func RunInteractive(command []string, env []string, tester Tester) (string, error) {
|
|
|
|
fmt.Fprintln(GinkgoWriter, "running command", command, "with env", env)
|
|
|
|
ptm, pts, err := pty.Open()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
term := vt10x.New(vt10x.WithWriter(pts))
|
|
|
|
// Set to 1 MINUTE timeout, since the command may take a while to start
|
|
c, err := expect.NewConsole(expect.WithStdin(ptm), expect.WithStdout(term), expect.WithCloser(pts, ptm), expect.WithDefaultTimeout(3*time.Minute))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
// execute the command
|
|
cmd := exec.Command(command[0], command[1:]...)
|
|
// setup stdin, stdout and stderr
|
|
cmd.Stdin = c.Tty()
|
|
cmd.Stdout = c.Tty()
|
|
cmd.Stderr = c.Tty()
|
|
if env != nil {
|
|
cmd.Env = append(os.Environ(), env...)
|
|
}
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
ctx := InteractiveContext{
|
|
Command: command,
|
|
console: c,
|
|
buffer: buf,
|
|
StopCommand: func() {
|
|
_ = cmd.Process.Kill()
|
|
},
|
|
}
|
|
tester(ctx)
|
|
|
|
err = cmd.Wait()
|
|
|
|
// Close the slave end of the pty, and read the remaining bytes from the master end.
|
|
c.Tty().Close()
|
|
|
|
return buf.String(), err
|
|
}
|
|
|
|
// expectDescriptionSupplier returns a function intended to be used as description supplier
|
|
// when checking errors do not occur in ExpectString and SendLine.
|
|
// Note that the function returned is evaluated lazily, only in case an error occurs.
|
|
func expectDescriptionSupplier(ctx InteractiveContext, line string) func() string {
|
|
return func() string {
|
|
return fmt.Sprintf("error while sending or expecting line: \"%s\"\n"+
|
|
"=== output of command '%+q' read so far ===\n%v\n======================",
|
|
line,
|
|
ctx.Command,
|
|
ctx.buffer)
|
|
}
|
|
}
|
|
|
|
func SendLine(ctx InteractiveContext, line string) {
|
|
_, err := ctx.console.SendLine(line)
|
|
Expect(err).ShouldNot(HaveOccurred(), expectDescriptionSupplier(ctx, line))
|
|
}
|
|
|
|
func ExpectString(ctx InteractiveContext, line string) {
|
|
res, err := ctx.console.ExpectString(line)
|
|
fmt.Fprint(ctx.buffer, res)
|
|
Expect(err).ShouldNot(HaveOccurred(), expectDescriptionSupplier(ctx, line))
|
|
}
|