Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add optional [bundle-name] for porter create [bundle-name] #2892

Merged
merged 13 commits into from
Sep 6, 2023
10 changes: 8 additions & 2 deletions cmd/porter/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,16 @@ func buildBundleCommands(p *porter.Porter) *cobra.Command {

func buildBundleCreateCommand(p *porter.Porter) *cobra.Command {
return &cobra.Command{
Use: "create",
Use: "create [bundle-name]",
Short: "Create a bundle",
Long: "Create a bundle. This generates a porter bundle in the current directory.",
Long: "Create a bundle. This command creates a new porter bundle with the specified bundle-name, in the directory with the specified bundle-name." +
" The directory will be created if it doesn't already exist. If no bundle-name is provided, the bundle will be created in current directory and the bundle name will be 'porter-hello'.",
Args: cobra.MaximumNArgs(1), // Expect at most one argument for the bundle name
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
ludfjig marked this conversation as resolved.
Show resolved Hide resolved
bundleName := args[0]
return p.CreateInDir(bundleName)
}
return p.Create()
},
}
Expand Down
4 changes: 2 additions & 2 deletions docs/content/references/cli/bundles_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ Create a bundle

### Synopsis

Create a bundle. This generates a porter bundle in the current directory.
Create a bundle. This generates a porter bundle in the directory with the specified name or in the current directory if no name is provided.

```
porter bundles create [flags]
porter bundles create [bundle-name] [flags]
```

### Options
Expand Down
4 changes: 2 additions & 2 deletions docs/content/references/cli/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ Create a bundle

### Synopsis

Create a bundle. This generates a porter bundle in the current directory.
Create a bundle. This generates a porter bundle in the directory with the specified name or in the current directory if no name is provided.

```
porter create [flags]
porter create [bundle-name] [flags]
```

### Options
Expand Down
55 changes: 48 additions & 7 deletions pkg/porter/create.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,81 @@
package porter

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"get.porter.sh/porter/pkg"
"get.porter.sh/porter/pkg/config"
)

// Create creates a new bundle configuration in the current directory
func (p *Porter) Create() error {
fmt.Fprintln(p.Out, "creating porter configuration in the current directory")
destinationDir := "."
schristoff marked this conversation as resolved.
Show resolved Hide resolved

err := p.CopyTemplate(p.Templates.GetManifest, config.Name)
if err := p.CopyTemplate(p.Templates.GetManifest, filepath.Join(destinationDir, config.Name)); err != nil {
return err
}
return p.copyAllTemplatesExceptPorterYaml(destinationDir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return p.copyAllTemplatesExceptPorterYaml(destinationDir)
// pass in current directory
return p.copyAllTemplatesExceptPorterYaml(".")

}

// CreateInDir creates a new bundle configuration in the specified directory. The directory will be created if it
// doesn't already exist. For example, if dir is "foo/bar/baz", the directory structure "foo/bar/baz" will be created.
// The bundle name will be set to the "base" of the given directory, which is "baz" in the example above.
func (p *Porter) CreateInDir(dir string) error {
bundleName := filepath.Base(dir)

// Create dirs if they don't exist
_, err := os.Stat(dir)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
err = os.MkdirAll(dir, os.ModePerm)
}
if err != nil {
return fmt.Errorf("failed to create directory for bundle: %w", err)
}
}

// create porter.yaml, using base of given dir as the bundle name
err = p.CopyTemplate(func() ([]byte, error) {
content, err := p.Templates.GetManifest()
if err != nil {
return nil, err
}
content = []byte(strings.ReplaceAll(string(content), "porter-hello", bundleName))
return content, nil
}, filepath.Join(dir, config.Name))
if err != nil {
return err
}

err = p.CopyTemplate(p.Templates.GetManifestHelpers, "helpers.sh")
return p.copyAllTemplatesExceptPorterYaml(dir)
}

func (p *Porter) copyAllTemplatesExceptPorterYaml(destinationDir string) error {
err := p.CopyTemplate(p.Templates.GetManifestHelpers, filepath.Join(destinationDir, "helpers.sh"))
if err != nil {
return err
}

err = p.CopyTemplate(p.Templates.GetReadme, "README.md")
err = p.CopyTemplate(p.Templates.GetReadme, filepath.Join(destinationDir, "README.md"))
if err != nil {
return err
}

err = p.CopyTemplate(p.Templates.GetDockerfileTemplate, "template.Dockerfile")
err = p.CopyTemplate(p.Templates.GetDockerfileTemplate, filepath.Join(destinationDir, "template.Dockerfile"))
if err != nil {
return err
}

err = p.CopyTemplate(p.Templates.GetDockerignore, ".dockerignore")
err = p.CopyTemplate(p.Templates.GetDockerignore, filepath.Join(destinationDir, ".dockerignore"))
if err != nil {
return err
}

return p.CopyTemplate(p.Templates.GetGitignore, ".gitignore")
return p.CopyTemplate(p.Templates.GetGitignore, filepath.Join(destinationDir, ".gitignore"))
}

func (p *Porter) CopyTemplate(getTemplate func() ([]byte, error), dest string) error {
Expand All @@ -51,6 +89,9 @@ func (p *Porter) CopyTemplate(getTemplate func() ([]byte, error), dest string) e
mode = pkg.FileModeExecutable
}

if _, err := os.Stat(dest); err == nil {
fmt.Fprintf(p.Err, "WARNING: File %q already exists. Overwriting.\n", dest)
}
err = p.FileSystem.WriteFile(dest, tmpl, mode)
if err != nil {
return fmt.Errorf("failed to write template to %s: %w", dest, err)
Expand Down
89 changes: 87 additions & 2 deletions pkg/porter/create_test.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
package porter

import (
"path/filepath"
"testing"

"get.porter.sh/porter/pkg"
"get.porter.sh/porter/pkg/manifest"
"get.porter.sh/porter/pkg/yaml"
"get.porter.sh/porter/tests"
"github.com/stretchr/testify/require"
)

func TestCreate(t *testing.T) {
func TestCreateInWorkingDirectory(t *testing.T) {
p := NewTestPorter(t)
defer p.Close()

err := p.Create()
require.NoError(t, err)

// Verify that files are present in the root directory
configFileStats, err := p.FileSystem.Stat("porter.yaml")
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, "porter.yaml", pkg.FileModeWritable, configFileStats.Mode())

// Verify that helpers is present and executable
helperFileStats, err := p.FileSystem.Stat("helpers.sh")
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, "helpers.sh", pkg.FileModeExecutable, helperFileStats.Mode())
Expand All @@ -39,5 +42,87 @@ func TestCreate(t *testing.T) {
dockerignoreStats, err := p.FileSystem.Stat(".dockerignore")
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, ".dockerignore", pkg.FileModeWritable, dockerignoreStats.Mode())
}

// tests to ensure behavior similarity with helm create
func TestCreateWithBundleName(t *testing.T) {
bundleName := "mybundle"

p := NewTestPorter(t)
err := p.CreateInDir(bundleName)
require.NoError(t, err)

// Verify that files are present in the "mybundle" directory
configFileStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "porter.yaml"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "porter.yaml"), pkg.FileModeWritable, configFileStats.Mode())

helperFileStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "helpers.sh"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "helpers.sh"), pkg.FileModeExecutable, helperFileStats.Mode())

dockerfileStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "template.Dockerfile"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "template.Dockerfile"), pkg.FileModeWritable, dockerfileStats.Mode())

readmeStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "README.md"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "README.md"), pkg.FileModeWritable, readmeStats.Mode())

gitignoreStats, err := p.FileSystem.Stat(filepath.Join(bundleName, ".gitignore"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, ".gitignore"), pkg.FileModeWritable, gitignoreStats.Mode())

dockerignoreStats, err := p.FileSystem.Stat(filepath.Join(bundleName, ".dockerignore"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, ".dockerignore"), pkg.FileModeWritable, dockerignoreStats.Mode())

// verify "name" inside porter.yaml is set to "mybundle"
porterYaml := &manifest.Manifest{}
data, err := p.FileSystem.ReadFile(filepath.Join(bundleName, "porter.yaml"))
require.NoError(t, err)
require.NoError(t, yaml.Unmarshal(data, &porterYaml))
require.True(t, porterYaml.Name == bundleName)
}

// make sure bundlename is not the entire file structure, just the "base"
func TestCreateNestedBundleName(t *testing.T) {
dir := "foo/bar/bar"
bundleName := "mybundle"

p := NewTestPorter(t)
err := p.CreateInDir(filepath.Join(dir, bundleName))
require.NoError(t, err)

// Verify that files are present in the "mybundle" directory
configFileStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "porter.yaml"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "porter.yaml"), pkg.FileModeWritable, configFileStats.Mode())

helperFileStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "helpers.sh"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "helpers.sh"), pkg.FileModeExecutable, helperFileStats.Mode())

dockerfileStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "template.Dockerfile"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "template.Dockerfile"), pkg.FileModeWritable, dockerfileStats.Mode())

readmeStats, err := p.FileSystem.Stat(filepath.Join(bundleName, "README.md"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, "README.md"), pkg.FileModeWritable, readmeStats.Mode())

gitignoreStats, err := p.FileSystem.Stat(filepath.Join(bundleName, ".gitignore"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, ".gitignore"), pkg.FileModeWritable, gitignoreStats.Mode())

dockerignoreStats, err := p.FileSystem.Stat(filepath.Join(bundleName, ".dockerignore"))
require.NoError(t, err)
tests.AssertFilePermissionsEqual(t, filepath.Join(bundleName, ".dockerignore"), pkg.FileModeWritable, dockerignoreStats.Mode())

// verify "name" inside porter.yaml is set to "mybundle"
porterYaml := &manifest.Manifest{}
data, err := p.FileSystem.ReadFile(filepath.Join(bundleName, "porter.yaml"))
require.NoError(t, err)
require.NoError(t, yaml.Unmarshal(data, &porterYaml))
require.True(t, porterYaml.Name == bundleName)
}
Loading