diff --git a/internal/acceptance/workflows/using_kiln.feature b/internal/acceptance/workflows/using_kiln.feature index 77de23282..1df2fbb36 100644 --- a/internal/acceptance/workflows/using_kiln.feature +++ b/internal/acceptance/workflows/using_kiln.feature @@ -31,17 +31,18 @@ Feature: As a developer, I want the Kiln CLI to be usable And stderr contains substring: flag provided but not defined Examples: - | command | - | bake | - | re-bake | - | fetch | - | find-release-version | - | find-stemcell-version | - | glaze | - | publish | - | release-notes | - | sync-with-local | - | update-release | - | update-stemcell | - | upload-release | - | validate | \ No newline at end of file + | command | + | bake | + | re-bake | + | fetch | + | find-release-version | + | find-stemcell-version | + | glaze | + | new | + | publish | + | release-notes | + | sync-with-local | + | update-release | + | update-stemcell | + | upload-release | + | validate | \ No newline at end of file diff --git a/internal/commands/new.go b/internal/commands/new.go new file mode 100644 index 000000000..7c6814374 --- /dev/null +++ b/internal/commands/new.go @@ -0,0 +1,45 @@ +package commands + +import ( + "github.com/pivotal-cf/jhanda" + + "github.com/pivotal-cf/kiln/pkg/bake" + "github.com/pivotal-cf/kiln/pkg/cargo" +) + +type New struct { + Options struct { + Dir string `long:"directory" description:"path to directory where source should be written" default:"."` + Tile string `long:"tile" description:"tile path" required:"true"` + Slug string `long:"product-slug" description:"a TanzuNet product slug"` + KilnfileTemplate string `long:"kilnfile-template" description:"a Kilnfile template sets up some reasonable Kilnfile defaults one of [artifactory, bosh.io]" default:"bosh.io"` + } +} + +func (n *New) Execute(args []string) error { + k, err := n.Setup(args) + if err != nil { + return err + } + k.Slug = n.Options.Slug + return bake.New(n.Options.Dir, n.Options.Tile, k) +} + +func (n *New) Setup(args []string) (cargo.Kilnfile, error) { + if _, err := jhanda.Parse(&n.Options, args); err != nil { + return cargo.Kilnfile{}, err + } + k, err := cargo.KilnfileTemplate(n.Options.KilnfileTemplate) + if err != nil { + return cargo.Kilnfile{}, err + } + return k, nil +} + +func (n *New) Usage() jhanda.Usage { + return jhanda.Usage{ + Description: "generate tile source from a tile", + ShortDescription: "generate source", + Flags: n.Options, + } +} diff --git a/internal/commands/new_test.go b/internal/commands/new_test.go new file mode 100644 index 000000000..99ecc708c --- /dev/null +++ b/internal/commands/new_test.go @@ -0,0 +1,83 @@ +package commands + +import ( + "github.com/pivotal-cf/kiln/pkg/bake" + "log" + "os" + "path/filepath" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/pivotal-cf/kiln/pkg/cargo" +) + +var _ = FDescribe("new", func() { + var ( + directory string + n *New + + args []string + ) + + BeforeEach(func() { + var err error + directory, err = os.MkdirTemp("", "kiln-new-*") + if err != nil { + log.Fatal(err) + } + n = new(New) + }) + + When("the tile flag is not set", func() { + It("returns an error", func() { + _, err := n.Setup(nil) + Expect(err).To(MatchError(ContainSubstring("--tile"))) + }) + }) + + When("only required arguments are set", func() { + BeforeEach(func() { + args = []string{ + "--tile", filepath.FromSlash("testdata/tile-0.1.2.pivotal"), + } + }) + It("does not error out", func() { + _, err := n.Setup(args) + Expect(err).NotTo(HaveOccurred()) + }) + It("sets some release sources", func() { + k, _ := n.Setup(args) + Expect(k.ReleaseSources).NotTo(BeEmpty()) + }) + It("sets the current working directory", func() { + _, _ = n.Setup(args) + Expect(n.Options.Dir).To(Equal(".")) + }) + It("does not set the product slug", func() { + _, _ = n.Setup(args) + Expect(n.Options.Slug).To(BeEmpty()) + }) + It("sset the bosh.io release source kilnfile template", func() { + _, _ = n.Setup(args) + Expect(n.Options.KilnfileTemplate).To(Equal(cargo.BOSHReleaseTarballSourceTypeBOSHIO)) + }) + }) + + When("generating source", func() { + BeforeEach(func() { + args = []string{ + "--tile", filepath.FromSlash("testdata/tile-0.1.3.pivotal"), + "--directory", directory, + } + }) + It("generates tile source", func() { + Expect(n.Execute(args)).To(Succeed()) + + Expect(filepath.Join(directory, bake.DefaultFilepathKilnfile)).To(BeAnExistingFile()) + Expect(filepath.Join(directory, bake.DefaultFilepathKilnfileLock)).To(BeAnExistingFile()) + Expect(filepath.Join(directory, bake.DefaultFilepathBaseYML)).To(BeAnExistingFile()) + Expect(filepath.Join(directory, bake.DefaultFilepathIconImage)).To(BeAnExistingFile()) + }) + }) +}) diff --git a/internal/commands/testdata/tile-0.1.3.pivotal b/internal/commands/testdata/tile-0.1.3.pivotal new file mode 100644 index 000000000..5d75019e3 Binary files /dev/null and b/internal/commands/testdata/tile-0.1.3.pivotal differ diff --git a/main.go b/main.go index 52e9d7303..4e063c6fe 100644 --- a/main.go +++ b/main.go @@ -81,6 +81,8 @@ func main() { commandSet["bake"] = bakeCommand commandSet["re-bake"] = commands.NewReBake(bakeCommand) + commandSet["new"] = new(commands.New) + commandSet["test"] = commands.NewTileTest() commandSet["help"] = commands.NewHelp(os.Stdout, globalFlagsUsage, commandSet) commandSet["version"] = commands.NewVersion(outLogger, version) diff --git a/pkg/bake/new.go b/pkg/bake/new.go index 2c6f20f51..734960997 100644 --- a/pkg/bake/new.go +++ b/pkg/bake/new.go @@ -104,11 +104,18 @@ func newKilnfiles(outputDirectory string, spec cargo.Kilnfile, releaseTarballs [ FloatAlways: false, }) } - kilnfileLock, err := yaml.Marshal(lock) + lockBuf, err := yaml.Marshal(lock) if err != nil { return err } - return os.WriteFile(filepath.Join(outputDirectory, DefaultFilepathKilnfileLock), kilnfileLock, fileMode) + specBuf, err := yaml.Marshal(spec) + if err != nil { + return err + } + return errors.Join( + os.WriteFile(filepath.Join(outputDirectory, DefaultFilepathKilnfileLock), lockBuf, fileMode), + os.WriteFile(filepath.Join(outputDirectory, DefaultFilepathKilnfile), specBuf, fileMode), + ) } func newFromProductTemplate(outputDirectory string, productTemplate []byte) (*yaml.Node, error) { @@ -164,7 +171,7 @@ func extractReleases(outputDirectory string, productTemplate *yaml.Node, dir fs. } var releasesList []string for _, tarball := range tarballs { - releasesList = append(releasesList, fmt.Sprintf("{{ release %q }}", tarball.Manifest.Name)) + releasesList = append(releasesList, fmt.Sprintf("$( release %q )", tarball.Manifest.Name)) } return tarballs, releasesNode.Encode(&releasesList) } @@ -195,6 +202,6 @@ func writeIconPNG(outputDirectory string, productTemplate *yaml.Node) error { if err != nil { return fmt.Errorf("failed to decode icon_image: %w", err) } - iconImageNode.Value = `{{ icon }}` + iconImageNode.Value = `$( icon )` return os.WriteFile(filepath.Join(outputDirectory, DefaultFilepathIconImage), iconImage, fileMode) } diff --git a/pkg/cargo/kilnfile.go b/pkg/cargo/kilnfile.go index 4622543e2..484f1b788 100644 --- a/pkg/cargo/kilnfile.go +++ b/pkg/cargo/kilnfile.go @@ -371,3 +371,47 @@ type BakeConfiguration struct { EmbedPaths []string `yaml:"embed_paths,omitempty" json:"embed_paths,omitempty"` VariableFiles []string `yaml:"variable_files,omitempty" json:"variable_files,omitempty"` } + +func KilnfileTemplate(name string) (Kilnfile, error) { + const ( + artifactory = BOSHReleaseTarballSourceTypeArtifactory + boshIO = BOSHReleaseTarballSourceTypeBOSHIO + ) + switch name { + default: + return Kilnfile{}, fmt.Errorf("unknown Kilnfile template please use one of %s", strings.Join([]string{ + artifactory, boshIO, + }, ", ")) + case artifactory: + return Kilnfile{ + ReleaseSources: []ReleaseSourceConfig{ + { + Type: BOSHReleaseTarballSourceTypeArtifactory, + ID: "compiled-releases", + Publishable: true, + ArtifactoryHost: `{{ variable "artifactory_host" }}`, + Repo: `{{ variable "artifactory_repository" }}`, + Username: `{{ variable "artifactory_username" }}`, + Password: `{{ variable "artifactory_password" }}`, + PathTemplate: `compiled-releases/{{.Name}}/{{.Name}}-{{.Version}}-{{.StemcellOS}}-{{.StemcellVersion}}.tgz`, + }, + { + Type: BOSHReleaseTarballSourceTypeArtifactory, + ID: "ingested-releases", + Publishable: true, + ArtifactoryHost: `{{ variable "artifactory_host" }}`, + Repo: `{{ variable "artifactory_repository" }}`, + Username: `{{ variable "artifactory_username" }}`, + Password: `{{ variable "artifactory_password" }}`, + PathTemplate: `bosh-releases/{{.Name}}/{{.Name}}-{{.Version}}.tgz`, + }, + }, + }, nil + case boshIO: + return Kilnfile{ + ReleaseSources: []ReleaseSourceConfig{ + {Type: BOSHReleaseTarballSourceTypeBOSHIO}, + }, + }, nil + } +} diff --git a/pkg/cargo/kilnfile_test.go b/pkg/cargo/kilnfile_test.go index 94d12940f..b8dfe4501 100644 --- a/pkg/cargo/kilnfile_test.go +++ b/pkg/cargo/kilnfile_test.go @@ -361,3 +361,20 @@ func Test_deGlazeBOSHReleaseTarballSpecification(t *testing.T) { }) } } + +func TestKilnfileTemplate(t *testing.T) { + for _, name := range []string{ + "artifactory", + "bosh.io", + } { + t.Run(name, func(t *testing.T) { + k, err := KilnfileTemplate(name) + assert.NoError(t, err) + assert.NotEmpty(t, k.ReleaseSources) + }) + } + t.Run("banana", func(t *testing.T) { + _, err := KilnfileTemplate("banana") + assert.Error(t, err) + }) +}