Skip to content

Commit

Permalink
Merge pull request #330 from dgageot/improve-suggestions
Browse files Browse the repository at this point in the history
Improve stack/builders suggestions
  • Loading branch information
jromero authored Oct 7, 2019
2 parents 880b8a5 + a633381 commit 59b0595
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 106 deletions.
1 change: 0 additions & 1 deletion acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,6 @@ func testAcceptance(t *testing.T, when spec.G, it spec.S, builder, runImageMirro
if err != nil {
t.Fatalf("suggest-stacks command failed: %s: %s", output, err)
}
h.AssertContains(t, string(output), "Stacks maintained by the Cloud Native Buildpacks project:")
h.AssertContains(t, string(output), "Stacks maintained by the community:")
})
})
Expand Down
90 changes: 0 additions & 90 deletions commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@ package commands
import (
"context"
"fmt"
"math/rand"
"os"
"os/signal"
"syscall"
"text/tabwriter"

"github.com/spf13/cobra"

"github.com/buildpack/pack"
"github.com/buildpack/pack/config"
"github.com/buildpack/pack/logging"
"github.com/buildpack/pack/style"
)

//go:generate mockgen -package mocks -destination mocks/pack_client.go github.com/buildpack/pack/commands PackClient
Expand All @@ -26,21 +23,6 @@ type PackClient interface {
Build(context.Context, pack.BuildOptions) error
}

type suggestedBuilder struct {
name string
image string
}

var suggestedBuilders = [][]suggestedBuilder{
{
{"Cloud Foundry", "cloudfoundry/cnb:bionic"},
{"Cloud Foundry", "cloudfoundry/cnb:cflinuxfs3"},
},
{
{"Heroku", "heroku/buildpacks:18"},
},
}

func AddHelpFlag(cmd *cobra.Command, commandName string) {
cmd.Flags().BoolP("help", "h", false, fmt.Sprintf("Help for '%s'", commandName))
}
Expand Down Expand Up @@ -84,75 +66,3 @@ func getMirrors(config config.Config) map[string][]string {
}
return mirrors
}

func suggestSettingBuilder(logger logging.Logger, client PackClient) {
logger.Info("Please select a default builder with:")
logger.Info("")
logger.Info("\tpack set-default-builder <builder image>")
logger.Info("")
suggestBuilders(logger, client)
}

func suggestBuilders(logger logging.Logger, client PackClient) {
logger.Info("Suggested builders:")
tw := tabwriter.NewWriter(logger.Writer(), 10, 10, 5, ' ', tabwriter.TabIndent)
for _, i := range rand.Perm(len(suggestedBuilders)) {
builders := suggestedBuilders[i]
for _, builder := range builders {
_, _ = tw.Write([]byte(fmt.Sprintf("\t%s:\t%s\t%s\t\n", builder.name, style.Symbol(builder.image), getBuilderDescription(builder.image, client))))
}
}
_, _ = tw.Write([]byte("\n"))
_ = tw.Flush()

logging.Tip(logger, "Learn more about a specific builder with:\n")
logger.Info("\tpack inspect-builder [builder image]")
}

var defaultBuilderDescriptions = map[string]string{
"cloudfoundry/cnb:bionic": "Small base image with Java & Node.js",
"cloudfoundry/cnb:cflinuxfs3": "Larger base image with Java, Node.js & Python",
"heroku/buildpacks:18": "heroku-18 base image with buildpacks for Ruby, Java, Node.js, Python, Golang, & PHP",
}

func getBuilderDescription(builderName string, client PackClient) string {
desc := ""
info, err := client.InspectBuilder(builderName, false)
if err == nil {
desc = info.Description
}

if desc == "" {
defaultDesc, ok := defaultBuilderDescriptions[builderName]
if ok {
desc = defaultDesc
}
}

return desc
}

func suggestStacks(log logging.Logger) {
log.Info(`
Stacks maintained by the Cloud Native Buildpacks project:
Stack ID: io.buildpacks.stacks.bionic
Description: Minimal Ubuntu 18.04 stack
Maintainer: Cloud Native Buildpacks
Build Image: cnbs/build:bionic
Run Image: cnbs/run:bionic
Stacks maintained by the community:
Stack ID: heroku-18
Description: The official Heroku stack based on Ubuntu 18.04
Maintainer: Heroku
Build Image: heroku/pack:18-build
Run Image: heroku/pack:18
Stack ID: org.cloudfoundry.stacks.cflinuxfs3
Description: The official Cloud Foundry stack based on Ubuntu 18.04
Maintainer: Cloud Foundry
Build Image: cloudfoundry/build:full-cnb
Run Image: cloudfoundry/run:full-cnb`)
}
81 changes: 78 additions & 3 deletions commands/suggest_builders.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,97 @@
package commands

import (
"fmt"
"sort"
"sync"
"text/tabwriter"

"github.com/spf13/cobra"

"github.com/buildpack/pack/logging"
"github.com/buildpack/pack/style"
)

type suggestedBuilder struct {
Name string
Image string
DefaultDescription string
}

var suggestedBuilders = []suggestedBuilder{
{
Name: "Cloud Foundry",
Image: "cloudfoundry/cnb:bionic",
DefaultDescription: "Small base image with Java & Node.js",
},
{
Name: "Cloud Foundry",
Image: "cloudfoundry/cnb:cflinuxfs3",
DefaultDescription: "Larger base image with Java, Node.js & Python",
},
{
Name: "Heroku",
Image: "heroku/buildpacks:18",
DefaultDescription: "heroku-18 base image with buildpacks for Ruby, Java, Node.js, Python, Golang, & PHP",
},
}

func SuggestBuilders(logger logging.Logger, client PackClient) *cobra.Command {
cmd := &cobra.Command{
Use: "suggest-builders",
Short: "Display list of recommended builders",
Args: cobra.NoArgs,
RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
Run: func(*cobra.Command, []string) {
suggestBuilders(logger, client)
return nil
}),
},
}

AddHelpFlag(cmd, "suggest-builders")
return cmd
}

func suggestSettingBuilder(logger logging.Logger, client PackClient) {
logger.Info("Please select a default builder with:")
logger.Info("")
logger.Info("\tpack set-default-builder <builder image>")
logger.Info("")
suggestBuilders(logger, client)
}

func suggestBuilders(logger logging.Logger, client PackClient) {
sort.Slice(suggestedBuilders, func(i, j int) bool { return suggestedBuilders[i].Image < suggestedBuilders[j].Image })

logger.Info("Suggested builders:")

// Fetch descriptions concurrently.
descriptions := make([]string, len(suggestedBuilders))

var wg sync.WaitGroup
for i, builder := range suggestedBuilders {
wg.Add(1)

go func(i int, builder suggestedBuilder) {
descriptions[i] = getBuilderDescription(builder, client)
wg.Done()
}(i, builder)
}
wg.Wait()

tw := tabwriter.NewWriter(logger.Writer(), 10, 10, 5, ' ', tabwriter.TabIndent)
for i, builder := range suggestedBuilders {
fmt.Fprintf(tw, "\t%s:\t%s\t%s\t\n", builder.Name, style.Symbol(builder.Image), descriptions[i])
}
fmt.Fprintln(tw)

logging.Tip(logger, "Learn more about a specific builder with:\n")
logger.Info("\tpack inspect-builder [builder image]")
}

func getBuilderDescription(builder suggestedBuilder, client PackClient) string {
info, err := client.InspectBuilder(builder.Image, false)
if err == nil && info.Description != "" {
return info.Description
}

return builder.DefaultDescription
}
64 changes: 61 additions & 3 deletions commands/suggest_stacks.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,80 @@
package commands

import (
"html/template"
"sort"

"github.com/spf13/cobra"

"github.com/buildpack/pack/logging"
)

type suggestedStack struct {
ID string
Description string
Maintainer string
BuildImage string
RunImage string
}

var suggestedStacks = []suggestedStack{
{
ID: "heroku-18",
Description: "The official Heroku stack based on Ubuntu 18.04",
Maintainer: "Heroku",
BuildImage: "heroku/pack:18-build",
RunImage: "heroku/pack:18",
},
{
ID: "io.buildpacks.stacks.bionic",
Description: "A minimal Cloud Foundry stack based on Ubuntu 18.04",
Maintainer: "Cloud Foundry",
BuildImage: "cloudfoundry/build:base-cnb",
RunImage: "cloudfoundry/run:base-cnb",
},
{
ID: "org.cloudfoundry.stacks.cflinuxfs3",
Description: "A large Cloud Foundry stack based on Ubuntu 18.04",
Maintainer: "Cloud Foundry",
BuildImage: "cloudfoundry/build:full-cnb",
RunImage: "cloudfoundry/run:full-cnb",
},
{
ID: "org.cloudfoundry.stacks.tiny",
Description: "A tiny Cloud Foundry stack based on Ubuntu 18.04, similar to distroless",
Maintainer: "Cloud Foundry",
BuildImage: "cloudfoundry/build:tiny-cnb",
RunImage: "cloudfoundry/run:tiny-cnb",
},
}

func SuggestStacks(logger logging.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "suggest-stacks",
Short: "Display list of recommended stacks",
Args: cobra.NoArgs,
RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
Run: func(*cobra.Command, []string) {
suggestStacks(logger)
return nil
}),
},
}

AddHelpFlag(cmd, "suggest-stacks")
return cmd
}

func suggestStacks(log logging.Logger) {
sort.Slice(suggestedStacks, func(i, j int) bool { return suggestedStacks[i].ID < suggestedStacks[j].ID })

tmpl := template.Must(template.New("").Parse(`
Stacks maintained by the community:
{{- range . }}
Stack ID: {{ .ID }}
Description: {{ .Description }}
Maintainer: {{ .Maintainer }}
Build Image: {{ .BuildImage }}
Run Image: {{ .RunImage }}
{{- end }}
`))
tmpl.Execute(log.Writer(), suggestedStacks)
}
22 changes: 13 additions & 9 deletions commands/suggest_stacks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@ func testSuggestStacksCommand(t *testing.T, when spec.G, it spec.S) {
command.SetArgs([]string{})
h.AssertNil(t, command.Execute())
h.AssertEq(t, outBuf.String(), `
Stacks maintained by the Cloud Native Buildpacks project:
Stack ID: io.buildpacks.stacks.bionic
Description: Minimal Ubuntu 18.04 stack
Maintainer: Cloud Native Buildpacks
Build Image: cnbs/build:bionic
Run Image: cnbs/run:bionic
Stacks maintained by the community:
Stack ID: heroku-18
Expand All @@ -48,11 +40,23 @@ Stacks maintained by the community:
Build Image: heroku/pack:18-build
Run Image: heroku/pack:18
Stack ID: io.buildpacks.stacks.bionic
Description: A minimal Cloud Foundry stack based on Ubuntu 18.04
Maintainer: Cloud Foundry
Build Image: cloudfoundry/build:base-cnb
Run Image: cloudfoundry/run:base-cnb
Stack ID: org.cloudfoundry.stacks.cflinuxfs3
Description: The official Cloud Foundry stack based on Ubuntu 18.04
Description: A large Cloud Foundry stack based on Ubuntu 18.04
Maintainer: Cloud Foundry
Build Image: cloudfoundry/build:full-cnb
Run Image: cloudfoundry/run:full-cnb
Stack ID: org.cloudfoundry.stacks.tiny
Description: A tiny Cloud Foundry stack based on Ubuntu 18.04, similar to distroless
Maintainer: Cloud Foundry
Build Image: cloudfoundry/build:tiny-cnb
Run Image: cloudfoundry/run:tiny-cnb
`)
})
})
Expand Down

0 comments on commit 59b0595

Please sign in to comment.