Skip to content

Commit

Permalink
Merge pull request #1533 from dgageot/ui-mode
Browse files Browse the repository at this point in the history
Experimental UI mode for `skaffold dev`
  • Loading branch information
dgageot authored Feb 5, 2019
2 parents 96ac37e + dcfb2e3 commit 09ee966
Show file tree
Hide file tree
Showing 146 changed files with 27,230 additions and 18 deletions.
48 changes: 47 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

110 changes: 106 additions & 4 deletions cmd/skaffold/app/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ package cmd
import (
"context"
"io"
"strings"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner"
"github.com/pkg/errors"
"github.com/rivo/tview"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
Expand All @@ -34,7 +38,7 @@ func NewCmdDev(out io.Writer) *cobra.Command {
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
opts.Command = "dev"
return dev(out)
return dev(out, opts.ExperimentalGUI)
},
}
AddRunDevFlags(cmd)
Expand All @@ -45,13 +49,17 @@ func NewCmdDev(out io.Writer) *cobra.Command {
cmd.Flags().IntVarP(&opts.WatchPollInterval, "watch-poll-interval", "i", 1000, "Interval (in ms) between two checks for file changes")
cmd.Flags().BoolVar(&opts.PortForward, "port-forward", true, "Port-forward exposed container ports within pods")
cmd.Flags().StringArrayVarP(&opts.CustomLabels, "label", "l", nil, "Add custom labels to deployed objects. Set multiple times for multiple labels")
cmd.Flags().BoolVar(&opts.ExperimentalGUI, "experimental-gui", false, "Experimental Graphical User Interface")

return cmd
}

func dev(out io.Writer) error {
func dev(out io.Writer, ui bool) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
catchCtrlC(cancel)
if !ui {
catchCtrlC(cancel)
}

cleanup := func() {}
if opts.Cleanup {
Expand All @@ -60,6 +68,25 @@ func dev(out io.Writer) error {
}()
}

var (
app *tview.Application
output *config.Output
)
if ui {
app, output = createApp()
defer app.Stop()

go func() {
app.Run()
cancel()
}()
} else {
output = &config.Output{
Main: out,
Logs: out,
}
}

for {
select {
case <-ctx.Done():
Expand All @@ -70,7 +97,7 @@ func dev(out io.Writer) error {
return errors.Wrap(err, "creating runner")
}

err = r.Dev(ctx, out, config.Build.Artifacts)
err = r.Dev(ctx, output, config.Build.Artifacts)
if r.HasDeployed() {
cleanup = func() {
if err := r.Cleanup(context.Background(), out); err != nil {
Expand All @@ -86,3 +113,78 @@ func dev(out io.Writer) error {
}
}
}

func createApp() (*tview.Application, *config.Output) {
app := tview.NewApplication()

mainView := tview.NewTextView()
mainView.
SetChangedFunc(func() {
app.Draw()
}).
SetDynamicColors(true).
SetBorder(true).
SetTitle("Build")

logsView := tview.NewTextView()
logsView.
SetChangedFunc(func() {
app.Draw()
}).
SetDynamicColors(true).
SetBorder(true).
SetTitle("Logs")

grid := tview.NewGrid()
grid.
SetRows(0, 0).
SetColumns(0).
SetBorders(false).
AddItem(mainView, 0, 0, 1, 1, 0, 0, false).
AddItem(logsView, 1, 0, 1, 1, 0, 0, false)

app.
SetRoot(grid, true).
SetFocus(grid)

output := &config.Output{
Main: color.ColoredWriter{Writer: ansiWriter(mainView)},
Logs: color.ColoredWriter{Writer: ansiWriter(logsView)},
}

return app, output
}

func ansiWriter(writer io.Writer) io.Writer {
return &ansi{
Writer: writer,
replacer: strings.NewReplacer(
"\033[31m", "[maroon]",
"\033[32m", "[green]",
"\033[33m", "[olive]",
"\033[34m", "[navy]",
"\033[35m", "[purple]",
"\033[36m", "[teal]",
"\033[37m", "[silver]",

"\033[91m", "[red]",
"\033[92m", "[lime]",
"\033[93m", "[yellow]",
"\033[94m", "[blue]",
"\033[95m", "[fuchsia]",
"\033[96m", "[aqua]",
"\033[97m", "[white]",

"\033[0m", "",
),
}
}

type ansi struct {
io.Writer
replacer *strings.Replacer
}

func (a *ansi) Write(text []byte) (int, error) {
return a.replacer.WriteString(a.Writer, string(text))
}
2 changes: 2 additions & 0 deletions docs/content/en/docs/references/cli/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ Usage:
Flags:
--cleanup Delete deployments after dev mode is interrupted (default true)
-d, --default-repo string Default repository value (overrides global config)
--experimental-gui Experimental Graphical User Interface
-f, --filename string Filename or URL to the pipeline file (default "skaffold.yaml")
-l, --label stringArray Add custom labels to deployed objects. Set multiple times for multiple labels
-n, --namespace string Run deployments in the specified namespace
Expand All @@ -228,6 +229,7 @@ Env vars:

* `SKAFFOLD_CLEANUP` (same as --cleanup)
* `SKAFFOLD_DEFAULT_REPO` (same as --default-repo)
* `SKAFFOLD_EXPERIMENTAL_GUI` (same as --experimental-gui)
* `SKAFFOLD_FILENAME` (same as --filename)
* `SKAFFOLD_LABEL` (same as --label)
* `SKAFFOLD_NAMESPACE` (same as --namespace)
Expand Down
10 changes: 10 additions & 0 deletions pkg/skaffold/color/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ var (
Purple = Color(35)
// Cyan can format text to be displayed to the terminal in cyan, using ANSI escape codes.
Cyan = Color(36)
// White can format text to be displayed to the terminal in white, using ANSI escape codes.
White = Color(37)
// None uses ANSI escape codes to reset all formatting.
None = Color(0)

Expand Down Expand Up @@ -99,12 +101,20 @@ type ColoredWriteCloser struct {
io.WriteCloser
}

// ColoredWriter forces printing with colors to an io.Writer.
type ColoredWriter struct {
io.Writer
}

// This implementation comes from logrus (https://github.com/sirupsen/logrus/blob/master/terminal_check_notappengine.go),
// unfortunately logrus doesn't expose a public interface we can use to call it.
func isTerminal(w io.Writer) bool {
if _, ok := w.(ColoredWriteCloser); ok {
return true
}
if _, ok := w.(ColoredWriter); ok {
return true
}

switch v := w.(type) {
case *os.File:
Expand Down
8 changes: 8 additions & 0 deletions pkg/skaffold/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@ limitations under the License.
package config

import (
"io"
"strings"
)

// Output defines which zones on the screen to print to
type Output struct {
Main io.Writer
Logs io.Writer
}

// SkaffoldOptions are options that are set by command line arguments not included
// in the config file itself
type SkaffoldOptions struct {
Expand All @@ -30,6 +37,7 @@ type SkaffoldOptions struct {
TailDev bool
PortForward bool
SkipTests bool
ExperimentalGUI bool
Profiles []string
CustomTag string
Namespace string
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/kubernetes/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func (a *LogAggregator) streamRequest(ctx context.Context, headerColor color.Col
if _, err := headerColor.Fprintf(a.output, "%s ", header); err != nil {
return errors.Wrap(err, "writing pod prefix header to out")
}
if _, err := fmt.Fprint(a.output, string(line)); err != nil {
if _, err := color.White.Fprint(a.output, string(line)); err != nil {
return errors.Wrap(err, "writing pod log to out")
}
}
Expand Down
Loading

0 comments on commit 09ee966

Please sign in to comment.