From 32ee8811cb4561bc797cf61285a7c8385e06c200 Mon Sep 17 00:00:00 2001 From: Sergey Sorokin Date: Sun, 4 Apr 2021 01:27:38 +0300 Subject: [PATCH] 2.x --- .gitignore | 1 + Makefile | 5 + README.md | 42 ++++---- bash/aliases.sh | 15 +++ bin/docker-compose-ps | 173 --------------------------------- bin/docker-images | 144 --------------------------- bin/docker-ps | 189 ------------------------------------ color/color.go | 85 ++++++++++++++++ console/constants.go | 10 ++ console/parse-docker-cmd.go | 48 +++++++++ console/usage.go | 14 +++ create/factories.go | 29 ++++++ go.mod | 3 + go.sum | 0 main.go | 34 +++++++ stdin/stdin.go | 34 +++++++ stdout/constants.go | 6 ++ stdout/contracts.go | 5 + stdout/docker-compose-ps.go | 64 ++++++++++++ stdout/docker-images.go | 81 ++++++++++++++++ stdout/docker-ps.go | 102 +++++++++++++++++++ stdout/first.go | 25 +++++ stdout/stdout.go | 12 +++ utils/exit.go | 13 +++ utils/utils.go | 41 ++++++++ 25 files changed, 645 insertions(+), 530 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 bash/aliases.sh delete mode 100755 bin/docker-compose-ps delete mode 100755 bin/docker-images delete mode 100755 bin/docker-ps create mode 100644 color/color.go create mode 100644 console/constants.go create mode 100644 console/parse-docker-cmd.go create mode 100644 console/usage.go create mode 100644 create/factories.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 stdin/stdin.go create mode 100644 stdout/constants.go create mode 100644 stdout/contracts.go create mode 100644 stdout/docker-compose-ps.go create mode 100644 stdout/docker-images.go create mode 100644 stdout/docker-ps.go create mode 100644 stdout/first.go create mode 100644 stdout/stdout.go create mode 100644 utils/exit.go create mode 100644 utils/utils.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad47f76 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +.PHONY: build + +build: + GOOS=darwin GOARCH=amd64 go build -o bin/dco-darwin-amd64 && \ + GOOS=linux GOARCH=amd64 go build -o bin/dco-linux-amd64 \ No newline at end of file diff --git a/README.md b/README.md index d5d49b4..d431609 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,26 @@ -# 🐳 Docker Color Output +# 🐳 Docker Color Output 2.x -This package allows you to run Docker commands and get color output. - -## Requirements - -- **php** >= 7.0 +This package allows you to colorize the docker output. ## ⚡️ Installation -Clone the repo and add the following lines to `~/.bash_aliases` (`~/.bashrc`, `~/.zshrc`) file. +Download the required binary file for your operating system from the [releases page](../../releases/latest). -```bash -DOCKER_COLOR_OUTPUT_PATH='/absolute-path-to-cloned-repo' -alias di="$DOCKER_COLOR_OUTPUT_PATH/bin/docker-images" -alias dps="$DOCKER_COLOR_OUTPUT_PATH/bin/docker-ps" -alias dcps="$DOCKER_COLOR_OUTPUT_PATH/bin/docker-compose-ps" -``` +### Aliases -**Note:** change `DOCKER_COLOR_OUTPUT_PATH` to your absolute path where you cloned the repository. +For convenience, you can use the aliases from the [bash/aliases.sh](bash/aliases.sh) file. Update +the `DOCKER_COLOR_OUTPUT_PATH` variable and run the `source bash/aliases.sh` command. ## 💥 Usage -You can also pass all arguments as you pass to the command. - ### 💡 Docker images ```bash -# The 'docker images' command will be called. -di +di # alias +``` + +```bash +docker images | dco ``` ![docker images](https://user-images.githubusercontent.com/5787193/93581956-7ae7f580-f9aa-11ea-8f81-d6922e1ca892.png) @@ -35,13 +28,11 @@ di ### 💡 Docker ps ```bash -# The 'docker ps' command will be called. -dps +dps [-a] # alias ``` ```bash -# The 'docker ps -a' command will be called. -dps -a +docker ps [-a] | dco ``` ![docker ps](https://user-images.githubusercontent.com/5787193/93581144-69521e00-f9a9-11ea-86bb-c23d7879c689.png) @@ -49,8 +40,11 @@ dps -a ### 💡 Docker compose ```bash -# The 'docker-compose ps' command will be called. -dcps +dcps # alias +``` + +```bash +docker-compose ps | dco ``` ![docker-compose ps](https://user-images.githubusercontent.com/5787193/93630916-7267dd00-f9f3-11ea-9521-e69152fa86f1.png) diff --git a/bash/aliases.sh b/bash/aliases.sh new file mode 100644 index 0000000..ac04b74 --- /dev/null +++ b/bash/aliases.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +DOCKER_COLOR_OUTPUT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)/bin/dco-darwin-amd64" + +di() { + docker images $@ | $DOCKER_COLOR_OUTPUT_PATH +} + +dps() { + docker ps $@ | $DOCKER_COLOR_OUTPUT_PATH +} + +dcps() { + docker-compose ps $@ | $DOCKER_COLOR_OUTPUT_PATH +} \ No newline at end of file diff --git a/bin/docker-compose-ps b/bin/docker-compose-ps deleted file mode 100755 index 5697720..0000000 --- a/bin/docker-compose-ps +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env php - '0;30', - 'dark-gray' => '1;30', - 'blue' => '0;34', - 'light-blue' => '1;34', - 'green' => '0;32', - 'light-green' => '1;32', - 'cyan' => '0;36', - 'light-cyan' => '1;36', - 'red' => '0;31', - 'light-red' => '1;31', - 'purple' => '0;35', - 'light-purple' => '1;35', - 'brown' => '0;33', - 'yellow' => '1;33', - 'light-gray' => '0;37', - 'white' => '1;37' - ]; - - public function fill($string, $foreground = null) - { - if ($this->foreground[$foreground] ?? null) { - $string = "\033[" . $this->foreground[$foreground] . "m" . $string . "\033[0m"; - } - - return $string; - } -} - -class DockerComposePs -{ - private $colors; - private $output = []; - private $rows = []; - private $lengths = []; - - private $minColumnLength = 0; - private $separatorLength = 3; - - public function __construct(Colors $colors) - { - $this->colors = $colors; - } - - protected function exec() - { - $args = implode(' ', array_slice($_SERVER['argv'], 1)); - exec('docker-compose ps ' . $args, $this->output); - if (!$this->output) { - exit(1); - } - } - - protected function parseOutput() - { - foreach ($this->output as $line) { - $row = preg_split('/\s{2,}/', trim($line)); - - // Remove the second line - if (count($row) <= 1) { - continue; - } - - $this->calculateLengths($row); - - $this->rows[] = $row; - } - } - - protected function calculateLengths(array $row) - { - foreach ($row as $key => $col) { - if (!array_key_exists($key, $this->lengths)) { - $this->lengths[$key] = $this->minColumnLength; - } - - $length = strlen($col); - - if (($this->lengths[$key]) < $length) { - $this->lengths[$key] = $length; - } - } - } - - protected function printRow(array $row) - { - foreach ($row as $key => $col) { - $dirtyLength = substr_count($col, "\033[") / 2 * 11; - $rawLength = strlen($col) - $dirtyLength; - $len = $rawLength < $this->lengths[$key] ? $this->lengths[$key] : $rawLength; - $len += $dirtyLength; - echo str_pad($col, $len + $this->separatorLength); - } - echo PHP_EOL; - } - - protected function printHeader() - { - $row = array_map(function ($col) { - return $this->colors->fill(strtoupper($col), 'light-blue'); - }, $this->rows[0]); - - $this->printRow($row); - } - - protected function printContent() - { - foreach ($this->rows as $key => $row) { - if ($key == 0) { - continue; - } - - $exited = $this->contains($row[2], 'Exit'); - - // Name column - if (!$exited) { - $row[0] = $this->colors->fill($row[0], 'white'); - } - - // Command column - $row[1] = $this->colors->fill($row[1], 'dark-gray'); - - // State column - $row[2] = $this->colors->fill($row[2], $exited ? 'red' : 'light-green'); - - // Ports column - if ($row[3] = $row[3] ?? '') { - $arr = []; - foreach (explode(',', $row[3]) as $part) { - $part = trim($part); - if ($this->contains($part, '->')) { - $ports = explode('->', $part); - $arr[] = $this->colors->fill($ports[0], 'light-cyan') . '->' . $ports[1]; - } else { - $arr[] = $part; - } - } - $row[3] = implode(', ', $arr); - } - - $this->printRow($row); - } - } - - protected function contains($haystack, $needle) - { - if (is_array($needle)) { - foreach ($needle as $value) { - if ($this->contains($haystack, $value)) { - return true; - } - } - return false; - } - - return strpos($haystack, $needle) !== false; - } - - public function call() - { - $this->exec(); - $this->parseOutput(); - $this->printHeader(); - $this->printContent(); - } -} - -(new DockerComposePs(new Colors()))->call(); diff --git a/bin/docker-images b/bin/docker-images deleted file mode 100755 index d94d31e..0000000 --- a/bin/docker-images +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env php - '0;30', - 'dark-gray' => '1;30', - 'blue' => '0;34', - 'light-blue' => '1;34', - 'green' => '0;32', - 'light-green' => '1;32', - 'cyan' => '0;36', - 'light-cyan' => '1;36', - 'red' => '0;31', - 'light-red' => '1;31', - 'purple' => '0;35', - 'light-purple' => '1;35', - 'brown' => '0;33', - 'yellow' => '1;33', - 'light-gray' => '0;37', - 'white' => '1;37' - ]; - - public function fill($string, $foreground = null) - { - if ($this->foreground[$foreground] ?? null) { - $string = "\033[" . $this->foreground[$foreground] . "m" . $string . "\033[0m"; - } else { - $string = "\033[0;00m" . $string . "\033[0m"; - } - - return $string; - } -} - -class DockerImages -{ - private $colors; - private $output = []; - private $rows = []; - private $lengths = []; - private $format; - - public function __construct(Colors $colors) - { - $this->colors = $colors; - } - - protected function exec() - { - $args = implode(' ', array_slice($_SERVER['argv'], 1)); - exec('docker images ' . $args, $this->output); - if (!$this->output) { - exit(1); - } - } - - protected function parseOutput() - { - foreach ($this->output as $line) { - $this->rows[] = $row = preg_split('/\s{2,}/', $line); - for ($i = 0; $i < count($row); $i++) { - $length = strlen($row[$i]); - $this->lengths[$i] = $this->lengths[$i] ?? 0; - $this->lengths[$i] = $length > $this->lengths[$i] ? $length : $this->lengths[$i]; - } - } - } - - protected function prepareFormat() - { - foreach ($this->lengths as $length) { - $length += 13; - $this->format .= "%-{$length}.{$length}s "; - } - $this->format .= PHP_EOL; - } - - protected function printHeader() - { - $header = array_map(function ($col) { - return $this->colors->fill($col, 'light-blue'); - }, $this->rows[0]); - - echo sprintf($this->format, ...$header); - } - - protected function printContent() - { - foreach ($this->rows as $key => $row) { - if ($key == 0) { - continue; - } - - // Repository - $row[0] = $this->colors->fill($row[0], $this->contains($row[0], '/') ? 'dark-gray' : 'white'); - - // Tag - $row[1] = $this->colors->fill($row[1], $row[1] == 'latest' ? 'light-green' : null); - - // Image ID - $row[2] = $this->colors->fill($row[2], 'dark-gray'); - - // Created - $color = $this->contains($row[3], ['days', 'weeks']) ? 'green' : null; - $color = $this->contains($row[3], 'months') ? 'brown' : $color; - $color = $this->contains($row[3], 'years') ? 'red' : $color; - $row[3] = $this->colors->fill($row[3], $color); - - // Size - $color = $this->contains($row[4], 'GB') ? 'red' : null; - $color = $this->contains($row[4], 'MB') && intval($row[4]) >= 500 ? 'brown' : $color; - $row[4] = $this->colors->fill($row[4], $color); - - echo sprintf($this->format, ...$row); - } - } - - protected function contains($haystack, $needle) - { - if (is_array($needle)) { - foreach ($needle as $value) { - if ($this->contains($haystack, $value)) { - return true; - } - } - return false; - } - - return strpos($haystack, $needle) !== false; - } - - public function call() - { - $this->exec(); - $this->parseOutput(); - $this->prepareFormat(); - $this->printHeader(); - $this->printContent(); - } -} - -(new DockerImages(new Colors()))->call(); diff --git a/bin/docker-ps b/bin/docker-ps deleted file mode 100755 index 57ebaec..0000000 --- a/bin/docker-ps +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env php - '0;30', - 'dark-gray' => '1;30', - 'blue' => '0;34', - 'light-blue' => '1;34', - 'green' => '0;32', - 'light-green' => '1;32', - 'cyan' => '0;36', - 'light-cyan' => '1;36', - 'red' => '0;31', - 'light-red' => '1;31', - 'purple' => '0;35', - 'light-purple' => '1;35', - 'brown' => '0;33', - 'yellow' => '1;33', - 'light-gray' => '0;37', - 'white' => '1;37' - ]; - - public function fill($string, $foreground = null) - { - if ($this->foreground[$foreground] ?? null) { - $string = "\033[" . $this->foreground[$foreground] . "m" . $string . "\033[0m"; - } - - return $string; - } -} - -class DockerPs -{ - private $colors; - private $output = []; - private $rows = []; - private $lengths = []; - - private $separatorLength = 3; - - public function __construct(Colors $colors) - { - $this->colors = $colors; - } - - protected function exec() - { - $args = implode(' ', array_slice($_SERVER['argv'], 1)); - exec('docker ps ' . $args, $this->output); - if (!$this->output) { - exit(1); - } - } - - protected function parseOutput() - { - foreach ($this->output as $line) { - $row = preg_split('/\s{2,}/', trim($line)); - - // If ports are empty - if (count($row) <= 6) { - $row[6] = $row[5]; - $row[5] = ''; - } - - $this->calculateLengths($row); - - $this->rows[] = $row; - } - } - - protected function calculateLengths(array $row) - { - foreach ($row as $key => $col) { - $this->lengths[$key] = $this->lengths[$key] ?? 0; - - $length = strlen($col); - - if ($this->contains($col, '…')) { - $length -= 2; - } - - if (($this->lengths[$key]) < $length) { - $this->lengths[$key] = $length; - } - } - } - - protected function printRow(array $row) - { - foreach ($row as $key => $col) { - $dirtyLength = substr_count($col, "\033[") / 2 * 11; - $rawLength = strlen($col) - $dirtyLength; - $len = $rawLength < $this->lengths[$key] ? $this->lengths[$key] : $rawLength; - $len += $dirtyLength; - echo str_pad($col, $len + $this->separatorLength); - } - echo PHP_EOL; - } - - protected function printHeader() - { - $row = array_map(function ($col) { - return $this->colors->fill($col, 'light-blue'); - }, $this->rows[0]); - - $this->printRow($row); - } - - protected function printContent() - { - foreach ($this->rows as $key => $row) { - if ($key == 0) { - continue; - } - - // Container column - $row[0] = $this->colors->fill($row[0], 'dark-gray'); - - // Image column - if ($this->contains($row[1], ':')) { - $parts = explode(':', $row[1]); - $row[1] = $this->colors->fill($parts[0], 'yellow'); - $row[1] .= $this->colors->fill(':' . $parts[1], 'light-green'); - } else { - $row[1] = $this->colors->fill($row[1], 'yellow'); - } - - // Command column - $row[2] = $this->colors->fill($row[2], 'dark-gray'); - - // Created column - $color = $this->contains($row[4], ['second', 'minute', 'hour']) ? 'green' : null; - $color = $this->contains($row[4], 'days') ? 'brown' : $color; - $color = $this->contains($row[4], 'months') ? 'red' : $color; - $row[3] = $this->colors->fill($row[3], $color); - - // Status column - $row[4] = $this->colors->fill($row[4], $this->contains($row[4], 'Exited') ? 'red' : 'light-green'); - - // Ports column - if ($row[5]) { - $arr = []; - foreach (explode(',', $row[5]) as $part) { - $part = trim($part); - if ($this->contains($part, '->')) { - $ports = explode('->', $part); - $arr[] = $this->colors->fill($ports[0], 'light-cyan') . '->' . $ports[1]; - } else { - $arr[] = $part; - } - } - $row[5] = implode(', ', $arr); - } - - // Names column - $row[6] = $this->colors->fill($row[6], 'white'); - - $this->printRow($row); - } - } - - protected function contains($haystack, $needle) - { - if (is_array($needle)) { - foreach ($needle as $value) { - if ($this->contains($haystack, $value)) { - return true; - } - } - return false; - } - - return strpos($haystack, $needle) !== false; - } - - public function call() - { - $this->exec(); - $this->parseOutput(); - $this->printHeader(); - $this->printContent(); - } -} - -(new DockerPs(new Colors()))->call(); diff --git a/color/color.go b/color/color.go new file mode 100644 index 0000000..9c01bfc --- /dev/null +++ b/color/color.go @@ -0,0 +1,85 @@ +package color + +const ( + reset = "\033[0m" + black = "\033[0;30m" + darkGray = "\033[1;30m" + red = "\033[0;31m" + lightRed = "\033[1;31m" + green = "\033[0;32m" + lightGreen = "\033[1;32m" + brown = "\033[0;33m" + yellow = "\033[1;33m" + blue = "\033[0;34m" + lightBlue = "\033[1;34m" + purple = "\033[0;35m" + lightPurple = "\033[1;35m" + cyan = "\033[0;36m" + lightCyan = "\033[1;36m" + lightGray = "\033[0;37m" + white = "\033[1;37m" +) + +func Black(value string) string { + return black + value + reset +} + +func DarkGray(value string) string { + return darkGray + value + reset +} + +func Red(value string) string { + return red + value + reset +} + +func LightRed(value string) string { + return lightRed + value + reset +} + +func Green(value string) string { + return green + value + reset +} + +func LightGreen(value string) string { + return lightGreen + value + reset +} + +func Brown(value string) string { + return brown + value + reset +} + +func Yellow(value string) string { + return yellow + value + reset +} + +func Blue(value string) string { + return blue + value + reset +} + +func LightBlue(value string) string { + return lightBlue + value + reset +} + +func Purple(value string) string { + return purple + value + reset +} + +func LightPurple(value string) string { + return lightPurple + value + reset +} + +func Cyan(value string) string { + return cyan + value + reset +} + +func LightCyan(value string) string { + return lightCyan + value + reset +} + +func LightGray(value string) string { + return lightGray + value + reset +} + +func White(value string) string { + return white + value + reset +} diff --git a/console/constants.go b/console/constants.go new file mode 100644 index 0000000..d134aa3 --- /dev/null +++ b/console/constants.go @@ -0,0 +1,10 @@ +package console + +const ( + App = "dco" + Version = "2.0.0" + + DockerPs = "docker ps" + DockerImages = "docker images" + DockerComposePs = "docker-compose ps" +) diff --git a/console/parse-docker-cmd.go b/console/parse-docker-cmd.go new file mode 100644 index 0000000..a900c1c --- /dev/null +++ b/console/parse-docker-cmd.go @@ -0,0 +1,48 @@ +package console + +import ( + "errors" + "github.com/devemio/docker-color-output/utils" +) + +func ParseCmd(lines []string) (string, error) { + if len(lines) == 0 { + return "", errors.New("no first line") + } + + cols := utils.Split(lines[0]) + + if len(cols) < 2 { + return "", errors.New("invalid first line") + } + + cols = cols[:2] + + if equals(cols, []string{"REPOSITORY", "TAG"}) { + return DockerImages, nil + } + + if equals(cols, []string{"CONTAINER ID", "IMAGE"}) { + return DockerPs, nil + } + + if equals(cols, []string{"Name", "Command"}) { + return DockerComposePs, nil + } + + return "", errors.New("invalid cmd") +} + +func equals(a []string, b []string) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} diff --git a/console/usage.go b/console/usage.go new file mode 100644 index 0000000..2a96e13 --- /dev/null +++ b/console/usage.go @@ -0,0 +1,14 @@ +package console + +import ( + "fmt" + "github.com/devemio/docker-color-output/color" +) + +func Usage() { + fmt.Println("💥 Docker color output " + color.Green(Version)) + fmt.Println("⚡️ Usage:") + fmt.Println(" " + color.Green(DockerImages) + " | " + color.Brown(App)) + fmt.Println(" " + color.Green(DockerPs) + " [-a] | " + color.Brown(App)) + fmt.Println(" " + color.Green(DockerComposePs) + " [-a] | " + color.Brown(App)) +} diff --git a/create/factories.go b/create/factories.go new file mode 100644 index 0000000..5fee774 --- /dev/null +++ b/create/factories.go @@ -0,0 +1,29 @@ +package create + +import ( + "github.com/devemio/docker-color-output/console" + "github.com/devemio/docker-color-output/stdout" + "github.com/devemio/docker-color-output/utils" +) + +func Split(cmd string) utils.SplitContract { + switch cmd { + case console.DockerPs: + return stdout.SplitDockerPsLine + default: + return utils.Split + } +} + +func Line(cmd string, line string) stdout.Line { + switch cmd { + case console.DockerImages: + return stdout.CreateDockerImageLine(line) + case console.DockerPs: + return stdout.CreateDockerPsLine(line) + case console.DockerComposePs: + return stdout.CreateDockerComposePsLine(line) + default: + return nil + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..85b1ba8 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/devemio/docker-color-output + +go 1.16 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/main.go b/main.go new file mode 100644 index 0000000..e4f91ee --- /dev/null +++ b/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "github.com/devemio/docker-color-output/console" + "github.com/devemio/docker-color-output/create" + "github.com/devemio/docker-color-output/stdin" + "github.com/devemio/docker-color-output/stdout" + "github.com/devemio/docker-color-output/utils" +) + +func main() { + // Parse lines + lines, _lines := stdin.GetLines() + if _lines != nil { + utils.Exit(_lines, console.Usage) + } + + // Parse cmd + cmd, _cmd := console.ParseCmd(lines) + if _cmd != nil { + utils.Exit(_cmd, console.Usage) + } + + // Get max column lengths + lens := utils.GetMaxLens(lines, create.Split(cmd)) + + // Print first line + stdout.CreateFirstLine(lines[0]).Println(lens) + + // Print lines + for _, line := range lines[1:] { + create.Line(cmd, line).Println(lens) + } +} diff --git a/stdin/stdin.go b/stdin/stdin.go new file mode 100644 index 0000000..c30d7a7 --- /dev/null +++ b/stdin/stdin.go @@ -0,0 +1,34 @@ +package stdin + +import ( + "bufio" + "errors" + "os" + "strings" +) + +func GetLines() ([]string, error) { + fi, err := os.Stdin.Stat() + if err != nil { + return nil, err + } + + if fi.Mode()&os.ModeNamedPipe == 0 && fi.Size() <= 0 { + return nil, errors.New("no stdin") + } + + var lines []string + s := bufio.NewScanner(os.Stdin) + for s.Scan() { + line := s.Text() + if !strings.HasPrefix(line, "-") { + lines = append(lines, s.Text()) + } + } + + if err := s.Err(); err != nil { + return nil, err + } + + return lines, nil +} diff --git a/stdout/constants.go b/stdout/constants.go new file mode 100644 index 0000000..10311c4 --- /dev/null +++ b/stdout/constants.go @@ -0,0 +1,6 @@ +package stdout + +const ( + NonPrintableCharactersLength = 11 + SpaceLength = 2 +) diff --git a/stdout/contracts.go b/stdout/contracts.go new file mode 100644 index 0000000..99e4a89 --- /dev/null +++ b/stdout/contracts.go @@ -0,0 +1,5 @@ +package stdout + +type Line interface { + Println(lens []int) +} diff --git a/stdout/docker-compose-ps.go b/stdout/docker-compose-ps.go new file mode 100644 index 0000000..13c830a --- /dev/null +++ b/stdout/docker-compose-ps.go @@ -0,0 +1,64 @@ +package stdout + +import ( + "fmt" + "github.com/devemio/docker-color-output/color" + "github.com/devemio/docker-color-output/utils" + "strings" +) + +type DockerComposePsLine struct { + name string + command string + state string + ports string +} + +func (line *DockerComposePsLine) Name() string { + if strings.Contains(line.state, "Exit") { + return line.name + } + return color.White(line.name) +} + +func (line *DockerComposePsLine) Command() string { + return color.DarkGray(line.command) +} + +func (line *DockerComposePsLine) State() string { + if strings.Contains(line.state, "Exit") { + return color.Red(line.state) + } + return color.LightGreen(line.state) +} + +func (line *DockerComposePsLine) Ports() string { + var ports []string + for _, port := range strings.Split(line.ports, ",") { + parts := strings.Split(port, "->") + if len(parts) == 2 { + port = color.LightCyan(parts[0]) + "->" + parts[1] + } + ports = append(ports, port) + } + return strings.Join(ports, ", ") +} + +func (line *DockerComposePsLine) Println(lens []int) { + fmt.Println( + Format(line.Name(), lens[0]), + Format(line.Command(), lens[1]), + Format(line.State(), lens[2]), + Format(line.Ports(), lens[3]), + ) +} + +func CreateDockerComposePsLine(line string) Line { + cols := utils.Split(line) + return &DockerComposePsLine{ + name: cols[0], + command: cols[1], + state: cols[2], + ports: cols[3], + } +} diff --git a/stdout/docker-images.go b/stdout/docker-images.go new file mode 100644 index 0000000..7c411d1 --- /dev/null +++ b/stdout/docker-images.go @@ -0,0 +1,81 @@ +package stdout + +import ( + "fmt" + "github.com/devemio/docker-color-output/color" + "github.com/devemio/docker-color-output/utils" + "strings" +) + +type DockerImageLine struct { + repository string + tag string + imageId string + created string + size string +} + +func (line *DockerImageLine) Repository() string { + if strings.Contains(line.repository, "/") { + return color.DarkGray(line.repository) + } + return color.White(line.repository) +} + +func (line *DockerImageLine) Tag() string { + if line.tag == "latest" { + return color.LightGreen(line.tag) + } + return line.tag +} + +func (line *DockerImageLine) ImageId() string { + return color.DarkGray(line.imageId) +} + +func (line *DockerImageLine) Created() string { + if strings.Contains(line.created, "days") { + return color.Green(line.created) + } + if strings.Contains(line.created, "weeks") { + return color.Green(line.created) + } + if strings.Contains(line.created, "months") { + return color.Brown(line.created) + } + if strings.Contains(line.created, "years") { + return color.Red(line.created) + } + return line.created +} + +func (line *DockerImageLine) Size() string { + if strings.Contains(line.size, "GB") { + return color.Red(line.size) + } + if strings.Contains(line.size, "MB") && utils.ParseFloat(line.size) >= 500 { + return color.Brown(line.size) + } + return line.size +} + +func (line *DockerImageLine) Println(lens []int) { + fmt.Println( + Format(line.Repository(), lens[0]), + Format(line.Tag(), lens[1]), + Format(line.ImageId(), lens[2]), + Format(line.Created(), lens[3]), + Format(line.Size(), lens[4]), + ) +} + +func CreateDockerImageLine(line string) Line { + cols := utils.Split(line) + return &DockerImageLine{ + repository: cols[0], + tag: cols[1], + imageId: cols[2], + created: cols[3], + size: cols[4], + } +} diff --git a/stdout/docker-ps.go b/stdout/docker-ps.go new file mode 100644 index 0000000..7e1e1f6 --- /dev/null +++ b/stdout/docker-ps.go @@ -0,0 +1,102 @@ +package stdout + +import ( + "fmt" + "github.com/devemio/docker-color-output/color" + "github.com/devemio/docker-color-output/utils" + "strings" +) + +type DockerPsLine struct { + containerId string + image string + command string + created string + status string + ports string + names string +} + +func (line *DockerPsLine) ContainerId() string { + return color.DarkGray(line.containerId) +} + +func (line *DockerPsLine) Image() string { + parts := strings.Split(line.image, ":") + if len(parts) == 2 { + return color.Yellow(parts[0]) + color.LightGreen(":"+parts[1]) + } + return color.Yellow(line.image) +} + +func (line *DockerPsLine) Command() string { + return color.DarkGray(line.command) +} + +func (line *DockerPsLine) Created() string { + if strings.Contains(line.created, "months") { + return color.Brown(line.created) + } + if strings.Contains(line.created, "years") { + return color.Red(line.created) + } + return color.Green(line.created) +} + +func (line *DockerPsLine) Status() string { + if strings.Contains(line.status, "Exited") { + return color.Red(line.status) + } + return color.LightGreen(line.status) +} + +func (line *DockerPsLine) Ports() string { + var ports []string + for _, port := range strings.Split(line.ports, ",") { + parts := strings.Split(port, "->") + if len(parts) == 2 { + port = color.LightCyan(parts[0]) + "->" + parts[1] + } + ports = append(ports, port) + } + return strings.Join(ports, ", ") +} + +func (line *DockerPsLine) Names() string { + return color.White(line.names) +} + +func (line *DockerPsLine) Println(lens []int) { + fmt.Println( + Format(line.ContainerId(), lens[0]), + Format(line.Image(), lens[1]), + Format(line.Command(), lens[2]), + Format(line.Created(), lens[3]), + Format(line.Status(), lens[4]), + Format(line.Ports(), lens[5]), + Format(line.Names(), lens[6]), + ) +} + +func CreateDockerPsLine(line string) Line { + cols := SplitDockerPsLine(line) + return &DockerPsLine{ + containerId: cols[0], + image: cols[1], + command: cols[2], + created: cols[3], + status: cols[4], + ports: cols[5], + names: cols[6], + } +} + +func SplitDockerPsLine(line string) []string { + cols := utils.Split(line) + // If no ports column + if len(cols) == 6 { + cols = append(cols, cols[5]) + cols[5] = "" + } + return cols +} diff --git a/stdout/first.go b/stdout/first.go new file mode 100644 index 0000000..de3c369 --- /dev/null +++ b/stdout/first.go @@ -0,0 +1,25 @@ +package stdout + +import ( + "fmt" + "github.com/devemio/docker-color-output/color" + "github.com/devemio/docker-color-output/utils" + "strings" +) + +type FirstLine struct { + cols []string +} + +func (line *FirstLine) Println(lens []int) { + for i, v := range line.cols { + fmt.Print(Format(color.LightBlue(strings.ToUpper(v)), lens[i]) + " ") + } + fmt.Println() +} + +func CreateFirstLine(line string) Line { + return &FirstLine{ + cols: utils.Split(line), + } +} diff --git a/stdout/stdout.go b/stdout/stdout.go new file mode 100644 index 0000000..84b04c7 --- /dev/null +++ b/stdout/stdout.go @@ -0,0 +1,12 @@ +package stdout + +import ( + "fmt" + "strconv" + "strings" +) + +func Format(value string, length int) string { + length += NonPrintableCharactersLength * strings.Count(value, "\033[0m") + return fmt.Sprintf("%-"+strconv.Itoa(length+SpaceLength)+"s", value) +} diff --git a/utils/exit.go b/utils/exit.go new file mode 100644 index 0000000..120ed69 --- /dev/null +++ b/utils/exit.go @@ -0,0 +1,13 @@ +package utils + +import ( + "fmt" + "github.com/devemio/docker-color-output/color" + "os" +) + +func Exit(err error, usage func()) { + fmt.Println(color.LightRed("💡 Error: " + err.Error())) + usage() + os.Exit(1) +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..21d8106 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,41 @@ +package utils + +import ( + "regexp" + "strconv" + "strings" + "unicode" +) + +type SplitContract func(line string) []string + +func Split(line string) []string { + cols := regexp.MustCompile("\\s{2,}").Split(line, -1) + if cols[0] == "" { + return cols[1:] + } + return cols +} + +func GetMaxLens(lines []string, split SplitContract) []int { + lens := make([]int, len(split(lines[0]))) + for _, line := range lines { + for i, v := range split(line) { + length := len(v) + if strings.Contains(v, "…") { + length -= 2 + } + if length > lens[i] { + lens[i] = length + } + } + } + return lens +} + +func ParseFloat(value string) float64 { + res, _ := strconv.ParseFloat(strings.TrimFunc(value, func(r rune) bool { + return !unicode.IsNumber(r) + }), 64) + return res +}