mirror of
https://gitea.elkins.co/Networking/ccl.git
synced 2025-03-09 12:41:40 -05:00
Add colorized output for ls subcommand
Initiative is turning into a little hackathon. Trying to beautify ls output a little since the number of configured containers on my main server is growing to a large number (currently 44). text/tabwriter can't handle SGR codes correctly. Investigations led me to the "ansiterm" library. I'm not crazy about the API really, but it handles all the corners I'm concerned about.
This commit is contained in:
parent
40357ba9b8
commit
070394fae1
70
cmd/ls.go
70
cmd/ls.go
@ -26,9 +26,11 @@ package cmd
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"text/tabwriter"
|
||||
"slices"
|
||||
|
||||
"gitea.elkins.co/Networking/ccl/internal/pkg/config"
|
||||
"gitea.elkins.co/Networking/ccl/internal/pkg/container"
|
||||
"github.com/juju/ansiterm"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -37,6 +39,8 @@ var (
|
||||
lsCountRunning bool
|
||||
lsCountNotRunning bool
|
||||
lsJsonFormat bool
|
||||
lsSortRuntime bool
|
||||
lsNoColor bool
|
||||
)
|
||||
|
||||
type lsContainerObj struct {
|
||||
@ -103,6 +107,18 @@ ccl ls squid`,
|
||||
}
|
||||
}
|
||||
|
||||
if lsSortRuntime {
|
||||
slices.SortFunc(conts, func(a, b *container.Container) int {
|
||||
if a.RunningTime().Seconds() > b.RunningTime().Seconds() {
|
||||
return -1
|
||||
}
|
||||
if a.RunningTime().Seconds() < b.RunningTime().Seconds() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
}
|
||||
|
||||
if lsJsonFormat {
|
||||
out := make([]interface{}, 0)
|
||||
for _, c := range conts {
|
||||
@ -148,44 +164,57 @@ ccl ls squid`,
|
||||
return
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
|
||||
tw := ansiterm.NewTabWriter(w, 0, 0, 2, ' ', 0)
|
||||
defcolor := ansiterm.Foreground(ansiterm.Default)
|
||||
red := ansiterm.Foreground(ansiterm.Red)
|
||||
yellow := ansiterm.Foreground(ansiterm.Yellow)
|
||||
bold := ansiterm.Styles(ansiterm.Bold)
|
||||
ital := ansiterm.Styles(ansiterm.Italic)
|
||||
defer tw.Flush()
|
||||
|
||||
if conn != nil {
|
||||
titlemsg := "CATEGORY\tGROUP\tNAME\tIMAGE\tCREATED\t RUNNING\t CPU%\t MEM%\t\n"
|
||||
block := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n"
|
||||
fmt.Fprint(tw, titlemsg)
|
||||
bold.Fprint(tw, "CATEGORY\tGROUP\tNAME\tIMAGE\tCREATED\t RUNNING\t CPU%\t MEM%\t\n")
|
||||
for _, c := range conts {
|
||||
data := make([]any, 0, 8)
|
||||
data = append(data, c.Category, fmt.Sprintf("%5d", c.StartGroup), c.Name, c.Image)
|
||||
defcolor.Fprintf(tw, "%s\t%5d\t", c.Category, c.StartGroup)
|
||||
ital.Fprintf(tw, "%s\t", c.Name)
|
||||
defcolor.Fprintf(tw, "%s\t", c.Image)
|
||||
if c.IsCreated() {
|
||||
data = append(data, " ✓")
|
||||
defcolor.Fprint(tw, " ✓\t")
|
||||
} else {
|
||||
data = append(data, "")
|
||||
red.Fprint(tw, " ✗\t")
|
||||
}
|
||||
if c.IsRunning() {
|
||||
raw := int64(c.RunningTime().Seconds())
|
||||
seconds := raw % 60
|
||||
minutes := (raw / 60) % 60
|
||||
hours := (raw / 60) / 60
|
||||
disp := fmt.Sprintf("%3d:%02d:%02d", hours, minutes, seconds)
|
||||
data = append(data, disp)
|
||||
defcolor.Fprintf(tw, "%3d:%02d:%02d\t", hours, minutes, seconds)
|
||||
} else {
|
||||
data = append(data, "")
|
||||
red.Fprint(tw, " ✗\t")
|
||||
}
|
||||
if stats := c.GetStats(); c.IsRunning() && stats != nil {
|
||||
data = append(data, fmt.Sprintf("%5.1f", stats.CPU), fmt.Sprintf("%5.1f", stats.MemPerc))
|
||||
for _, st := range []float64{stats.CPU, stats.MemPerc} {
|
||||
hi := defcolor
|
||||
if st > 5.0 {
|
||||
hi = yellow
|
||||
}
|
||||
if st > 20.0 {
|
||||
hi = red
|
||||
}
|
||||
hi.Fprintf(tw, "%5.1f\t", st)
|
||||
}
|
||||
} else {
|
||||
data = append(data, "", "")
|
||||
red.Fprint(tw, " -\t -\t")
|
||||
}
|
||||
fmt.Fprintf(tw, block, data...)
|
||||
fmt.Fprint(tw, "\n")
|
||||
}
|
||||
} else {
|
||||
titlemsg := "CATEGORY\tGROUP\tNAME\tIMAGE"
|
||||
fmt.Fprintf(tw, "%s\n", titlemsg)
|
||||
titlemsg := "CATEGORY\tGROUP\tNAME\tIMAGE\n"
|
||||
bold.Fprint(tw, titlemsg)
|
||||
|
||||
for _, c := range conts {
|
||||
data := []interface{}{c.Category, c.StartGroup, c.Name, c.Image}
|
||||
fmt.Fprintf(tw, "%s\t%5d\t%s\t%s\n", data...)
|
||||
defcolor.Fprintf(tw, "%s\t%5d\t%s\t%s\n", data...)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -196,6 +225,11 @@ func init() {
|
||||
lsCmd.Flags().BoolVarP(&lsCountRunning, "running", "R", false, "Return only the count of running items")
|
||||
lsCmd.Flags().BoolVarP(&lsCountNotRunning, "not-running", "N", false, "Return only the count of stopped/failed items")
|
||||
lsCmd.Flags().BoolVarP(&lsJsonFormat, "json", "J", false, "Output results as a json array")
|
||||
lsCmd.Flags().BoolVarP(&lsSortRuntime, "sort-runtime", "T", false, "Sort by running time (descending)")
|
||||
lsCmd.Flags().BoolVarP(&lsNoColor, "no-color", "K", false, "Suppress ansi color sequences in output")
|
||||
lsCmd.MarkFlagsMutuallyExclusive("count", "running", "not-running")
|
||||
lsCmd.MarkFlagsMutuallyExclusive("sort-runtime", "json")
|
||||
lsCmd.MarkFlagsMutuallyExclusive("sort-runtime", "running")
|
||||
lsCmd.MarkFlagsMutuallyExclusive("sort-runtime", "not-running")
|
||||
rootCmd.AddCommand(lsCmd)
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -8,6 +8,7 @@ require (
|
||||
github.com/containers/common v0.57.1-0.20240222191224-9b0d0a77eb2e
|
||||
github.com/containers/podman/v4 v4.5.0-rc1.0.20240207150443-caee76ed57c9
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/juju/ansiterm v1.0.0
|
||||
github.com/miekg/dns v1.1.58
|
||||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
@ -84,8 +85,11 @@ require (
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20240221225126-96f124060393 // indirect
|
||||
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
|
16
go.sum
16
go.sum
@ -568,6 +568,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/ansiterm v1.0.0 h1:gmMvnZRq7JZJx6jkfSq9/+2LMrVEwGwt7UR6G+lmDEg=
|
||||
github.com/juju/ansiterm v1.0.0/go.mod h1:PyXUpnI3olx3bsPcHt98FGPX/KCFZ1Fi+hw1XLI6384=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
@ -601,6 +603,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/letsencrypt/boulder v0.0.0-20240221225126-96f124060393 h1:MLDVKJgZgs43ITIeWJkdDGJZ9mrETJCXX4pMmvKh+k4=
|
||||
github.com/letsencrypt/boulder v0.0.0-20240221225126-96f124060393/go.mod h1:NTo9eSR9oPoLqZmiH0dzr2WNa1UplMjQnNhPLhqn2vk=
|
||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@ -612,7 +616,15 @@ github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYt
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.10/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
@ -1115,6 +1127,7 @@ golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1147,6 +1160,7 @@ golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -1155,10 +1169,12 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
Loading…
x
Reference in New Issue
Block a user