Improve the error message for pulumi new when the template doesn't exist (#1823)

And offer distance-based suggestions.
This commit is contained in:
Justin Van Patten 2018-09-04 08:40:59 -07:00 committed by GitHub
parent ddd83fafc4
commit 5586f4ecad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 1 deletions

8
Gopkg.lock generated
View file

@ -362,6 +362,12 @@
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[[projects]]
branch = "master"
name = "github.com/texttheater/golang-levenshtein"
packages = ["levenshtein"]
revision = "d188e65d659ef53fcdb0691c12f1bba64928b649"
[[projects]]
name = "github.com/uber/jaeger-client-go"
packages = [
@ -578,6 +584,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "71208477393825ddcf9e60bd0a6855758c05f49ec560fd9e5ba6336592a18da0"
inputs-digest = "d6db93501077426a275193a3d77e69cc4fe68fd80603907438fcb0503d7817a0"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -23,6 +23,7 @@ import (
"runtime"
"strings"
"github.com/texttheater/golang-levenshtein/levenshtein"
git "gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
@ -216,6 +217,15 @@ func retrievePulumiTemplates(templateName string, offline bool) (TemplateReposit
subDir := templateDir
if templateName != "" {
subDir = filepath.Join(subDir, templateName)
// Provide a nicer error message when the template can't be found (dir doesn't exist).
_, err := os.Stat(subDir)
if err != nil {
if os.IsNotExist(err) {
return TemplateRepository{}, newTemplateNotFoundError(templateDir, templateName)
}
contract.IgnoreError(err)
}
}
return TemplateRepository{
@ -472,6 +482,40 @@ func newExistingFilesError(existing []string) error {
return errors.New(message)
}
// newTemplateNotFoundError returns an error for when the template doesn't exist,
// offering distance-based suggestions in the error message.
func newTemplateNotFoundError(templateDir string, templateName string) error {
message := fmt.Sprintf("template '%s' not found", templateName)
// Attempt to read the directory to offer suggestions.
infos, err := ioutil.ReadDir(templateDir)
if err != nil {
contract.IgnoreError(err)
return errors.New(message)
}
// Get suggestions based on levenshtein distance.
suggestions := []string{}
const minDistance = 2
op := levenshtein.DefaultOptions
for _, info := range infos {
distance := levenshtein.DistanceForStrings([]rune(templateName), []rune(info.Name()), op)
if distance <= minDistance {
suggestions = append(suggestions, info.Name())
}
}
// Build-up error message with suggestions.
if len(suggestions) > 0 {
message = message + "\n\nDid you mean this?\n"
for _, suggestion := range suggestions {
message = message + fmt.Sprintf("\t%s\n", suggestion)
}
}
return errors.New(message)
}
// transform returns a new string with ${PROJECT} and ${DESCRIPTION} replaced by
// the value of projectName and projectDescription.
func transform(content string, projectName string, projectDescription string) string {