Added colors on all column headers

* Refactored colors: use content colors (title, header, error...) instead of color directly
* Added dependency on github.com/juju/ansiterm which a drop-in replaceable library for tabwriter but compatible with colors

Signed-off-by: Silvin Lubecki <silvin.lubecki@docker.com>
This commit is contained in:
Silvin Lubecki 2020-10-15 12:19:10 +02:00
Родитель cf84ea5f62
Коммит f6367493c0
13 изменённых файлов: 119 добавлений и 71 удалений

Просмотреть файл

@ -23,12 +23,12 @@ import (
"os/signal"
"syscall"
"github.com/cli/cli/utils"
"github.com/docker/cli/cli/command"
cliflags "github.com/docker/cli/cli/flags"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/commands"
"github.com/docker/hub-tool/internal/hub"
)
@ -54,7 +54,7 @@ func main() {
hubClient, err := hub.NewClient(authResolver, hub.WithContext(ctx))
if err != nil {
if hub.IsAuthenticationError(err) {
fmt.Println(utils.Red(`You need to be logged in to Docker Hub to use this tool.
fmt.Println(color.Error(`You need to be logged in to Docker Hub to use this tool.
Please login to Docker Hub using the "docker login" command.`))
os.Exit(1)
}

2
go.mod
Просмотреть файл

@ -20,7 +20,9 @@ require (
github.com/gofrs/uuid v3.3.0+incompatible // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/jinzhu/gorm v1.9.16 // indirect
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a
github.com/lib/pq v1.8.0 // indirect
github.com/lunixbochs/vtclean v1.0.0 // indirect
github.com/mattn/go-sqlite3 v1.14.3 // indirect
github.com/miekg/pkcs11 v1.0.3 // indirect
github.com/opencontainers/image-spec v1.0.1

4
go.sum
Просмотреть файл

@ -448,6 +448,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
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 v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
@ -480,6 +482,8 @@ github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
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 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
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=

36
internal/color/color.go Normal file
Просмотреть файл

@ -0,0 +1,36 @@
/*
Copyright 2020 Docker Hub Tool authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package color
import "github.com/cli/cli/utils"
var (
// Title color should be used for any important title
Title = utils.Green
// Header color should be used for all the listing column headers
Header = utils.Blue
// Key color should be used for all key title content
Key = utils.Blue
// Info color should be used when we prompt an info
Info = utils.Blue
// Warn color should be used when we warn the user
Warn = utils.Yellow
// Error color should be used when something bad happened
Error = utils.Red
// Emphasise color should be used with important content
Emphasise = utils.Green
)

Просмотреть файл

@ -19,15 +19,15 @@ package account
import (
"fmt"
"io"
"text/tabwriter"
"time"
"github.com/cli/cli/utils"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/go-units"
"github.com/juju/ansiterm"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/format"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
@ -74,31 +74,31 @@ func runInfo(streams command.Streams, hubClient *hub.Client, opts infoOptions) e
func printAccount(out io.Writer, value interface{}) error {
account := value.(account)
// print user info
w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, utils.Blue("Username:")+"\t%s\n", account.User.UserName)
fmt.Fprintf(w, utils.Blue("Full name:")+"\t%s\n", account.User.FullName)
fmt.Fprintf(w, utils.Blue("Company:")+"\t%s\n", account.User.Company)
fmt.Fprintf(w, utils.Blue("Location:")+"\t%s\n", account.User.Location)
fmt.Fprintf(w, utils.Blue("Joined:")+"\t%s ago\n", units.HumanDuration(time.Since(account.User.Joined)))
fmt.Fprintf(w, utils.Blue("Plan:")+"\t%s\n", utils.Green(account.Plan.Name))
w := ansiterm.NewTabWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, color.Key("Username:")+"\t%s\n", account.User.UserName)
fmt.Fprintf(w, color.Key("Full name:")+"\t%s\n", account.User.FullName)
fmt.Fprintf(w, color.Key("Company:")+"\t%s\n", account.User.Company)
fmt.Fprintf(w, color.Key("Location:")+"\t%s\n", account.User.Location)
fmt.Fprintf(w, color.Key("Joined:")+"\t%s ago\n", units.HumanDuration(time.Since(account.User.Joined)))
fmt.Fprintf(w, color.Key("Plan:")+"\t%s\n", color.Emphasise(account.Plan.Name))
if err := w.Flush(); err != nil {
return err
}
// print plan info
w = tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, utils.Blue("Limits:")+"\n")
fmt.Fprintf(w, utils.Blue("%sSeats:")+"\t%v\n", pfx, account.Plan.Limits.Seats)
fmt.Fprintf(w, utils.Blue("%sPrivate repositories:")+"\t%v\n", pfx, account.Plan.Limits.PrivateRepos)
fmt.Fprintf(w, utils.Blue("%sParallel builds:")+"\t%v\n", pfx, account.Plan.Limits.ParallelBuilds)
fmt.Fprintf(w, utils.Blue("%sCollaborators:")+"\t%v\n", pfx, getLimit(account.Plan.Limits.Collaborators))
fmt.Fprintf(w, utils.Blue("%sTeams:")+"\t%v\n", pfx, getLimit(account.Plan.Limits.Teams))
w = ansiterm.NewTabWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, color.Key("Limits:")+"\n")
fmt.Fprintf(w, color.Key("%sSeats:")+"\t%v\n", pfx, account.Plan.Limits.Seats)
fmt.Fprintf(w, color.Key("%sPrivate repositories:")+"\t%v\n", pfx, account.Plan.Limits.PrivateRepos)
fmt.Fprintf(w, color.Key("%sParallel builds:")+"\t%v\n", pfx, account.Plan.Limits.ParallelBuilds)
fmt.Fprintf(w, color.Key("%sCollaborators:")+"\t%v\n", pfx, getLimit(account.Plan.Limits.Collaborators))
fmt.Fprintf(w, color.Key("%sTeams:")+"\t%v\n", pfx, getLimit(account.Plan.Limits.Teams))
return w.Flush()
}
func getLimit(number int) string {
if number == 9999 {
return utils.Green("unlimited")
return color.Emphasise("unlimited")
}
return fmt.Sprintf("%v", number)
}

Просмотреть файл

@ -20,12 +20,13 @@ import (
"fmt"
"io"
"strings"
"text/tabwriter"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/juju/ansiterm"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/format"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
@ -82,12 +83,12 @@ func runList(streams command.Streams, hubClient *hub.Client, opts listOptions) e
func printOrganizations(out io.Writer, values interface{}) error {
organizations := values.([]hub.Organization)
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
w := ansiterm.NewTabWriter(out, 20, 1, 3, ' ', 0)
var headers []string
for _, column := range defaultColumns {
headers = append(headers, column.header)
}
fmt.Fprintln(w, strings.Join(headers, "\t"))
fmt.Fprintln(w, color.Header(strings.Join(headers, "\t")))
for _, organization := range organizations {
var values []string

Просмотреть файл

@ -20,12 +20,13 @@ import (
"fmt"
"io"
"strings"
"text/tabwriter"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/juju/ansiterm"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/format"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
@ -79,12 +80,12 @@ func runMembers(streams command.Streams, hubClient *hub.Client, opts memberOptio
func printMembers(out io.Writer, values interface{}) error {
members := values.([]hub.Member)
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
w := ansiterm.NewTabWriter(out, 20, 1, 3, ' ', 0)
var headers []string
for _, column := range memberColumns {
headers = append(headers, column.header)
}
fmt.Fprintln(w, strings.Join(headers, "\t"))
fmt.Fprintln(w, color.Header(strings.Join(headers, "\t")))
for _, member := range members {
var values []string

Просмотреть файл

@ -20,12 +20,13 @@ import (
"fmt"
"io"
"strings"
"text/tabwriter"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/juju/ansiterm"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/format"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
@ -80,12 +81,12 @@ func runTeams(streams command.Streams, hubClient *hub.Client, opts teamsOptions,
func printTeams(out io.Writer, values interface{}) error {
teams := values.([]hub.Team)
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
w := ansiterm.NewTabWriter(out, 20, 1, 3, ' ', 0)
var headers []string
for _, column := range teamsColumns {
headers = append(headers, column.header)
}
fmt.Fprintln(w, strings.Join(headers, "\t"))
fmt.Fprintln(w, color.Header(strings.Join(headers, "\t")))
for _, team := range teams {
var values []string

Просмотреть файл

@ -20,14 +20,15 @@ import (
"fmt"
"io"
"strings"
"text/tabwriter"
"time"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/go-units"
"github.com/juju/ansiterm"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/format"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
@ -102,12 +103,12 @@ func runList(streams command.Streams, hubClient *hub.Client, opts listOptions, a
func printRepositories(out io.Writer, values interface{}) error {
repositories := values.([]hub.Repository)
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
w := ansiterm.NewTabWriter(out, 20, 1, 3, ' ', 0)
var headers []string
for _, column := range defaultColumns {
headers = append(headers, column.header)
}
fmt.Fprintln(w, strings.Join(headers, "\t"))
fmt.Fprintln(w, color.Header(strings.Join(headers, "\t")))
for _, repository := range repositories {
var values []string

Просмотреть файл

@ -26,6 +26,7 @@ import (
"github.com/docker/distribution/reference"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
)
@ -67,7 +68,7 @@ func runRm(streams command.Streams, hubClient *hub.Client, opts rmOptions, repos
}
if !opts.force {
fmt.Fprintln(streams.Out(), "Please type the name of your repository to confirm deletion:", namedRef.Name())
fmt.Fprintln(streams.Out(), color.Warn("Please type the name of your repository to confirm deletion:"), namedRef.Name())
reader := bufio.NewReader(streams.In())
input, _ := reader.ReadString('\n')
input = strings.ToLower(strings.TrimSpace(input))

Просмотреть файл

@ -22,10 +22,8 @@ import (
"fmt"
"io"
"strings"
"text/tabwriter"
"time"
"github.com/cli/cli/utils"
"github.com/containerd/containerd/images"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli/cli"
@ -34,9 +32,11 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/go-units"
"github.com/juju/ansiterm"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
)
@ -177,14 +177,14 @@ func printImage(out io.Writer, image *Image) error {
}
func printManifest(out io.Writer, image *Image) error {
w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
w := ansiterm.NewTabWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, utils.Green("Manifest:")+"\n")
fmt.Fprintf(w, utils.Blue("%sName:")+"\t%s\n", pfx, image.Name)
fmt.Fprintf(w, utils.Blue("%sMediaType:")+"\t%s\n", pfx, image.Descriptor.MediaType)
fmt.Fprintf(w, utils.Blue("%sDigest:")+"\t%s\n", pfx, image.Descriptor.Digest)
fmt.Fprintf(w, color.Title("Manifest:")+"\n")
fmt.Fprintf(w, color.Key("%sName:")+"\t%s\n", pfx, image.Name)
fmt.Fprintf(w, color.Key("%sMediaType:")+"\t%s\n", pfx, image.Descriptor.MediaType)
fmt.Fprintf(w, color.Key("%sDigest:")+"\t%s\n", pfx, image.Descriptor.Digest)
if image.Descriptor.Platform != nil {
fmt.Fprintf(w, utils.Blue("%sPlatform:")+"\t%s\n", pfx, getPlatform(image.Descriptor.Platform))
fmt.Fprintf(w, color.Key("%sPlatform:")+"\t%s\n", pfx, getPlatform(image.Descriptor.Platform))
}
if len(image.Manifest.Annotations) > 0 {
printAnnotations(w, image.Manifest.Annotations)
@ -192,59 +192,59 @@ func printManifest(out io.Writer, image *Image) error {
printAnnotations(w, image.Descriptor.Annotations)
}
if image.Config.Architecture != "" {
fmt.Fprintf(w, utils.Blue("%sOs/Arch:")+"\t%s/%s\n", pfx, image.Config.OS, image.Config.Architecture)
fmt.Fprintf(w, color.Key("%sOs/Arch:")+"\t%s/%s\n", pfx, image.Config.OS, image.Config.Architecture)
}
if image.Config.Author != "" {
fmt.Fprintf(w, utils.Blue("%sAuthor:")+"\t%s\n", pfx, image.Config.Author)
fmt.Fprintf(w, color.Key("%sAuthor:")+"\t%s\n", pfx, image.Config.Author)
}
if image.Config.Created != nil {
fmt.Fprintf(w, utils.Blue("%sCreated:")+"\t%s ago\n", pfx, units.HumanDuration(time.Since(*image.Config.Created)))
fmt.Fprintf(w, color.Key("%sCreated:")+"\t%s ago\n", pfx, units.HumanDuration(time.Since(*image.Config.Created)))
}
fmt.Fprintf(w, "\n")
return w.Flush()
}
func printConfig(out io.Writer, image *Image) error {
w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, utils.Green("Config:")+"\n")
fmt.Fprintf(w, utils.Blue("%sMediaType:")+"\t%s\n", pfx, image.Manifest.Config.MediaType)
fmt.Fprintf(w, utils.Blue("%sSize:")+"\t%v\n", pfx, units.HumanSize(float64(image.Manifest.Config.Size)))
fmt.Fprintf(w, utils.Blue("%sDigest:")+"\t%s\n", pfx, image.Manifest.Config.Digest)
w := ansiterm.NewTabWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, color.Title("Config:")+"\n")
fmt.Fprintf(w, color.Key("%sMediaType:")+"\t%s\n", pfx, image.Manifest.Config.MediaType)
fmt.Fprintf(w, color.Key("%sSize:")+"\t%v\n", pfx, units.HumanSize(float64(image.Manifest.Config.Size)))
fmt.Fprintf(w, color.Key("%sDigest:")+"\t%s\n", pfx, image.Manifest.Config.Digest)
if len(image.Config.Config.Cmd) > 0 {
fmt.Fprintf(w, utils.Blue("%sCommand:")+"\t%q\n", pfx, strings.TrimPrefix(strings.Join(image.Config.Config.Cmd, " "), "/bin/sh -c "))
fmt.Fprintf(w, color.Key("%sCommand:")+"\t%q\n", pfx, strings.TrimPrefix(strings.Join(image.Config.Config.Cmd, " "), "/bin/sh -c "))
}
if len(image.Config.Config.Entrypoint) > 0 {
fmt.Fprintf(w, utils.Blue("%sEntrypoint:")+"\t%q\n", pfx, strings.Join(image.Config.Config.Entrypoint, " "))
fmt.Fprintf(w, color.Key("%sEntrypoint:")+"\t%q\n", pfx, strings.Join(image.Config.Config.Entrypoint, " "))
}
if image.Config.Config.User != "" {
fmt.Fprintf(w, utils.Blue("%sUser:")+"\t%s\n", pfx, image.Config.Config.User)
fmt.Fprintf(w, color.Key("%sUser:")+"\t%s\n", pfx, image.Config.Config.User)
}
if len(image.Config.Config.ExposedPorts) > 0 {
fmt.Fprintf(w, utils.Blue("%sExposed ports:")+"\t%s\n", pfx, getExposedPorts(image.Config.Config.ExposedPorts))
fmt.Fprintf(w, color.Key("%sExposed ports:")+"\t%s\n", pfx, getExposedPorts(image.Config.Config.ExposedPorts))
}
if len(image.Config.Config.Env) > 0 {
fmt.Fprintf(w, utils.Blue("%sEnvironment:")+"\n", pfx)
fmt.Fprintf(w, color.Key("%sEnvironment:")+"\n", pfx)
for _, env := range image.Config.Config.Env {
fmt.Fprintf(w, "%s%s%s\n", pfx, pfx, env)
}
}
if len(image.Config.Config.Volumes) > 0 {
fmt.Fprintf(w, utils.Blue("%sVolumes:")+"\n", pfx)
fmt.Fprintf(w, color.Key("%sVolumes:")+"\n", pfx)
for volume := range image.Config.Config.Volumes {
fmt.Fprintf(w, "%s%s%s\n", pfx, pfx, volume)
}
}
if image.Config.Config.WorkingDir != "" {
fmt.Fprintf(w, utils.Blue("%sWorking Directory:")+"\t%q\n", pfx, image.Config.Config.WorkingDir)
fmt.Fprintf(w, color.Key("%sWorking Directory:")+"\t%q\n", pfx, image.Config.Config.WorkingDir)
}
if len(image.Config.Config.Labels) > 0 {
fmt.Fprintf(w, utils.Blue("%sLabels:")+"\n", pfx)
fmt.Fprintf(w, color.Key("%sLabels:")+"\n", pfx)
for k, v := range image.Config.Config.Labels {
fmt.Fprintf(w, "%s%s%s=%q\n", pfx, pfx, k, v)
}
}
if image.Config.Config.StopSignal != "" {
fmt.Fprintf(w, utils.Blue("%sStop signal:")+"\t%s\n", pfx, image.Config.Config.StopSignal)
fmt.Fprintf(w, color.Key("%sStop signal:")+"\t%s\n", pfx, image.Config.Config.StopSignal)
}
fmt.Fprintf(w, "\n")
@ -253,19 +253,19 @@ func printConfig(out io.Writer, image *Image) error {
func printLayers(out io.Writer, image *Image) error {
history := filterEmptyLayers(image.Config.History)
w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintln(w, utils.Green("Layers:"))
w := ansiterm.NewTabWriter(out, 0, 0, 1, ' ', 0)
fmt.Fprintln(w, color.Title("Layers:"))
for i, layer := range image.Manifest.Layers {
if i != 0 {
fmt.Fprintln(w)
}
fmt.Fprintf(w, utils.Blue("%sMediaType:")+"\t%s\n", pfx, layer.MediaType)
fmt.Fprintf(w, utils.Blue("%sSize:")+"\t%v\n", pfx, units.HumanSize(float64(layer.Size)))
fmt.Fprintf(w, utils.Blue("%sDigest:")+"\t%s\n", pfx, layer.Digest)
fmt.Fprintf(w, color.Key("%sMediaType:")+"\t%s\n", pfx, layer.MediaType)
fmt.Fprintf(w, color.Key("%sSize:")+"\t%v\n", pfx, units.HumanSize(float64(layer.Size)))
fmt.Fprintf(w, color.Key("%sDigest:")+"\t%s\n", pfx, layer.Digest)
if len(image.Manifest.Layers) == len(history) {
fmt.Fprintf(w, utils.Blue("%sCommand:")+"\t%s\n", pfx, cleanCreatedBy(history[i].CreatedBy))
fmt.Fprintf(w, color.Key("%sCommand:")+"\t%s\n", pfx, cleanCreatedBy(history[i].CreatedBy))
if history[i].Created != nil {
fmt.Fprintf(w, utils.Blue("%sCreated:")+"\t%s ago\n", pfx, units.HumanDuration(time.Since(*history[i].Created)))
fmt.Fprintf(w, color.Key("%sCreated:")+"\t%s ago\n", pfx, units.HumanDuration(time.Since(*history[i].Created)))
}
}
}
@ -311,7 +311,7 @@ func getExposedPorts(configPorts map[string]struct{}) string {
}
func printAnnotations(w io.Writer, annotations map[string]string) {
fmt.Fprintf(w, utils.Blue("%sAnnotations:")+"\n", pfx)
fmt.Fprintf(w, color.Key("%sAnnotations:")+"\n", pfx)
for k, v := range annotations {
fmt.Fprintf(w, "%s%s%s:\t%s\n", pfx, pfx, k, v)
}

Просмотреть файл

@ -20,15 +20,15 @@ import (
"fmt"
"io"
"strings"
"text/tabwriter"
"time"
"github.com/cli/cli/utils"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/go-units"
"github.com/juju/ansiterm"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/format"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
@ -169,12 +169,12 @@ func runList(streams command.Streams, hubClient *hub.Client, opts listOptions, r
func printTags(out io.Writer, values interface{}) error {
tags := values.([]hub.Tag)
w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
w := ansiterm.NewTabWriter(out, 20, 1, 3, ' ', 0)
var headers []string
for _, column := range defaultColumns {
headers = append(headers, column.header)
}
fmt.Fprintln(w, strings.Join(headers, "\t"))
fmt.Fprintln(w, color.Header(strings.Join(headers, "\t")))
for _, tag := range tags {
var values []string
for _, column := range defaultColumns {
@ -235,6 +235,6 @@ func promptCallToAction(out io.Writer, client accountInfo) error {
return nil
}
_, err = fmt.Fprint(out, utils.Blue(callToAction))
_, err = fmt.Fprint(out, color.Info(callToAction))
return err
}

Просмотреть файл

@ -26,6 +26,7 @@ import (
"github.com/docker/distribution/reference"
"github.com/spf13/cobra"
"github.com/docker/hub-tool/internal/color"
"github.com/docker/hub-tool/internal/hub"
"github.com/docker/hub-tool/internal/metrics"
)
@ -67,7 +68,7 @@ func runRm(streams command.Streams, hubClient *hub.Client, opts rmOptions, image
}
if !opts.force {
fmt.Fprintln(streams.Out(), "Please type the name of your repository to confirm deletion:", namedTaggedRef.Name())
fmt.Fprintln(streams.Out(), color.Warn("Please type the name of your repository to confirm deletion:"), namedTaggedRef.Name())
reader := bufio.NewReader(streams.In())
input, _ := reader.ReadString('\n')
input = strings.ToLower(strings.TrimSpace(input))