diff --git a/cli/go.mod b/cli/go.mod index cfa1ede113..d1aadd2084 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -4,7 +4,10 @@ go 1.19 require ( github.com/99designs/keyring v1.2.2 + github.com/mattn/go-isatty v0.0.14 + github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a github.com/muesli/mango-cobra v1.2.0 + github.com/muesli/reflow v0.3.0 github.com/muesli/roff v0.1.0 github.com/spf13/cobra v1.6.1 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d @@ -22,7 +25,6 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/mattn/go-colorable v0.1.9 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/mtibben/percent v0.2.1 // indirect diff --git a/cli/go.sum b/cli/go.sum index dc97287db2..2b514ea6e9 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -56,6 +56,7 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= @@ -63,12 +64,16 @@ github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4= +github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI= github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4= github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg= github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA= github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg= github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -78,6 +83,7 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -106,7 +112,6 @@ go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAV golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 95224b24e2..69a4539a67 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -263,10 +263,10 @@ var secretsSetCmd = &cobra.Command{ } // Print secret operations - headers := []string{"SECRET NAME", "SECRET VALUE", "STATUS"} - rows := [][]string{} + headers := [...]string{"SECRET NAME", "SECRET VALUE", "STATUS"} + rows := [][3]string{} for _, secretOperation := range secretOperations { - rows = append(rows, []string{secretOperation.SecretKey, secretOperation.SecretValue, secretOperation.SecretOperation}) + rows = append(rows, [...]string{secretOperation.SecretKey, secretOperation.SecretValue, secretOperation.SecretOperation}) } visualize.Table(headers, rows) diff --git a/cli/packages/visualize/secrets.go b/cli/packages/visualize/secrets.go index e9ac5d2979..7be41020db 100644 --- a/cli/packages/visualize/secrets.go +++ b/cli/packages/visualize/secrets.go @@ -3,12 +3,12 @@ package visualize import "github.com/Infisical/infisical-merge/packages/models" func PrintAllSecretDetails(secrets []models.SingleEnvironmentVariable) { - rows := [][]string{} + rows := [][3]string{} for _, secret := range secrets { - rows = append(rows, []string{secret.Key, secret.Value, secret.Type}) + rows = append(rows, [...]string{secret.Key, secret.Value, secret.Type}) } - headers := []string{"SECRET NAME", "SECRET VALUE", "SECRET TYPE"} + headers := [...]string{"SECRET NAME", "SECRET VALUE", "SECRET TYPE"} Table(headers, rows) } diff --git a/cli/packages/visualize/visualize.go b/cli/packages/visualize/visualize.go index 5658197eed..8599f24056 100644 --- a/cli/packages/visualize/visualize.go +++ b/cli/packages/visualize/visualize.go @@ -2,8 +2,14 @@ package visualize import ( "os" + "strings" "github.com/jedib0t/go-pretty/table" + "github.com/mattn/go-isatty" + "github.com/muesli/ansi" + "github.com/muesli/reflow/truncate" + log "github.com/sirupsen/logrus" + "golang.org/x/term" ) type TableOptions struct { @@ -16,8 +22,35 @@ type TableOptions struct { // } // } +const ( + // combined width of the table borders and padding + borderWidths = 10 + // char to indicate that a string has been truncated + ellipsis = "…" +) + // Given headers and rows, this function will print out a table -func Table(headers []string, rows [][]string) { +func Table(headers [3]string, rows [][3]string) { + // if we're not in a terminal or cygwin terminal, don't truncate the secret value + shouldTruncate := isatty.IsTerminal(os.Stdout.Fd()) + + // This will return an error if we're not in a terminal or + // if the terminal is a cygwin terminal like Git Bash. + width, _, err := term.GetSize(int(os.Stdout.Fd())) + if err != nil { + if shouldTruncate { + log.Errorf("error getting terminal size: %s", err) + } else { + log.Debug(err) + } + } + + longestSecretName, longestSecretType := getLongestValues(append(rows, headers)) + availableWidth := width - longestSecretName - longestSecretType - borderWidths + if availableWidth < 0 { + availableWidth = 0 + } + t := table.NewWriter() t.SetOutputMirror(os.Stdout) t.SetStyle(table.StyleLight) @@ -35,7 +68,11 @@ func Table(headers []string, rows [][]string) { t.AppendHeader(tableHeaders) for _, row := range rows { tableRow := table.Row{} - for _, val := range row { + for i, val := range row { + // only truncate the first column (secret value) + if i == 1 && stringWidth(val) > availableWidth && shouldTruncate { + val = truncate.StringWithTail(val, uint(availableWidth), ellipsis) + } tableRow = append(tableRow, val) } t.AppendRow(tableRow) @@ -43,3 +80,28 @@ func Table(headers []string, rows [][]string) { t.Render() } + +// getLongestValues returns the length of the longest secret name and type from all rows (including the header). +func getLongestValues(rows [][3]string) (longestSecretName, longestSecretType int) { + for _, row := range rows { + if len(row[0]) > longestSecretName { + longestSecretName = stringWidth(row[0]) + } + if len(row[2]) > longestSecretType { + longestSecretType = stringWidth(row[2]) + } + } + return +} + +// stringWidth returns the width of a string. +// ANSI escape sequences are ignored and double-width characters are handled correctly. +func stringWidth(str string) (width int) { + for _, l := range strings.Split(str, "\n") { + w := ansi.PrintableRuneWidth(l) + if w > width { + width = w + } + } + return width +}