Skip to content

Commit

Permalink
Validate the project name when it's entered, fixes #514, fixes #86 (#…
Browse files Browse the repository at this point in the history
…1147)



* Validate also in non-interactive config

* Add tests for valid and invalid project names
  • Loading branch information
rfay committed Oct 10, 2018
1 parent 16d72e4 commit bdfd80d
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 8 deletions.
7 changes: 7 additions & 0 deletions cmd/ddev/cmd/config.go
Expand Up @@ -297,6 +297,13 @@ func handleMainConfigArgs(cmd *cobra.Command, args []string, app *ddevapp.DdevAp
app.WebserverType = webserverTypeArg
}

provider, err := app.GetProvider()
util.CheckErr(err)
err = provider.ValidateField("Name", app.Name)
if err != nil {
return fmt.Errorf("failed to validate configuration: %v", err)
}

err = app.WriteConfig()
if err != nil {
return fmt.Errorf("could not write ddev config file %s: %v", app.ConfigPath, err)
Expand Down
45 changes: 45 additions & 0 deletions cmd/ddev/cmd/config_test.go
Expand Up @@ -158,3 +158,48 @@ func TestConfigSetValues(t *testing.T) {
assert.Equal(uploadDir, app.UploadDir)
assert.Equal(webserverType, app.WebserverType)
}

// TestConfigInvalidProjectname tests to make sure that invalid projectnames
// are not accepted and valid names are accepted.
func TestConfigInvalidProjectname(t *testing.T) {
var err error
assert := asrt.New(t)

// Create a temporary directory and switch to it.
tmpdir := testcommon.CreateTmpDir(t.Name())
defer testcommon.CleanupDir(tmpdir)
defer testcommon.Chdir(tmpdir)()

// Create an existing docroot
docroot := "web"
if err = os.MkdirAll(filepath.Join(tmpdir, docroot), 0755); err != nil {
t.Errorf("Could not create docroot %s in %s", docroot, tmpdir)
}

// Test some valid project names
for _, projName := range []string{"no-spaces-but-hyphens", "UpperAndLower", "should.work.with.dots"} {
args := []string{
"config",
"--projectname", projName,
}

out, err := exec.RunCommand(DdevBin, args)
assert.NoError(err)
assert.NotContains(out, "is not a valid project name")
assert.Contains(out, "You may now run 'ddev start'")
}

// test some invalid project names.
for _, projName := range []string{"with spaces", "with_underscores", "no,commas-will-make-it"} {
args := []string{
"config",
"--projectname", projName,
}

out, err := exec.RunCommand(DdevBin, args)
assert.Error(err)
assert.Contains(out, fmt.Sprintf("%s is not a valid project name", projName))
assert.NotContains(out, "You may now run 'ddev start'")
}

}
9 changes: 3 additions & 6 deletions pkg/ddevapp/config.go
Expand Up @@ -280,7 +280,7 @@ func (app *DdevApp) ValidateConfig() error {
// validate hostname
match := hostRegex.MatchString(app.GetHostname())
if !match {
return fmt.Errorf("%s is not a valid hostname. Please enter a site name in your configuration that will allow for a valid hostname. See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements", app.GetHostname())
return fmt.Errorf("%s is not a valid hostname. Please enter a project name in your configuration that will allow for a valid hostname. See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements", app.GetHostname())
}

// validate apptype
Expand Down Expand Up @@ -451,14 +451,13 @@ func (app *DdevApp) RenderComposeYAML() (string, error) {
return doc.String(), err
}

// Define an application name.
// prompt for a project name.
func (app *DdevApp) promptForName() error {
provider, err := app.GetProvider()
if err != nil {
return err
}

namePrompt := "Project name"
if app.Name == "" {
dir, err := os.Getwd()
// if working directory name is invalid for hostnames, we shouldn't suggest it
Expand All @@ -468,9 +467,7 @@ func (app *DdevApp) promptForName() error {
}
}

namePrompt = fmt.Sprintf("%s (%s)", namePrompt, app.Name)
fmt.Print(namePrompt + ": ")
app.Name = util.GetInput(app.Name)
app.Name = util.Prompt("Project name", app.Name)
return provider.ValidateField("Name", app.Name)
}

Expand Down
18 changes: 17 additions & 1 deletion pkg/ddevapp/config_test.go
Expand Up @@ -183,6 +183,22 @@ func TestConfigCommand(t *testing.T) {
assert.Contains(out, testDir)
assert.Contains(out, fmt.Sprintf("'%s' is not a valid project type", invalidAppType))

// Create an example input buffer that writes an invalid projectname, then a valid-project-name,
// a valid document root,
// a valid app type
input = fmt.Sprintf("invalid_project_name\n%s\ndocroot\n%s", name, testValues[apptypePos])
scanner = bufio.NewScanner(strings.NewReader(input))
util.SetInputScanner(scanner)

restoreOutput = testcommon.CaptureUserOut()
err = app.PromptForConfig()
assert.NoError(err, t)
out = restoreOutput()

// Ensure we have expected vales in output.
assert.Contains(out, testDir)
assert.Contains(out, "invalid_project_name is not a valid project name")

// Ensure values were properly set on the app struct.
assert.Equal(name, app.Name)
assert.Equal(testValues[apptypePos], app.Type)
Expand Down Expand Up @@ -447,7 +463,7 @@ func TestValidate(t *testing.T) {

app.Name = "Invalid!"
err = app.ValidateConfig()
assert.EqualError(err, fmt.Sprintf("%s is not a valid hostname. Please enter a site name in your configuration that will allow for a valid hostname. See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements", app.GetHostname()))
assert.EqualError(err, fmt.Sprintf("%s is not a valid hostname. Please enter a project name in your configuration that will allow for a valid hostname. See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements", app.GetHostname()))

app.Docroot = "testdata"
app.Name = "valid"
Expand Down
13 changes: 12 additions & 1 deletion pkg/ddevapp/providerDefault.go
@@ -1,6 +1,9 @@
package ddevapp

import "os"
import (
"fmt"
"os"
)

// DefaultProvider provides a no-op for the provider plugin interface methods.
type DefaultProvider struct{}
Expand All @@ -12,6 +15,14 @@ func (p *DefaultProvider) Init(app *DdevApp) error {

// ValidateField provides a no-op for the ValidateField operation.
func (p *DefaultProvider) ValidateField(field, value string) error {
if field == "Name" {
// validate project name
match := hostRegex.MatchString(value)
if !match {
return fmt.Errorf("%s is not a valid project name. Please enter a project name in your configuration that will allow for a valid hostname. See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements", value)
}

}
return nil
}

Expand Down
1 change: 1 addition & 0 deletions pkg/exec/exec.go
Expand Up @@ -9,6 +9,7 @@ import (
)

// RunCommand runs a command on the host system.
// returns the stdout of the command and an err
func RunCommand(command string, args []string) (string, error) {
output.UserOut.WithFields(log.Fields{
"Command": command + " " + strings.Join(args[:], " "),
Expand Down

0 comments on commit bdfd80d

Please sign in to comment.