diff --git a/go.mod b/go.mod index b24b7e109..d763df7c4 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,6 @@ require ( github.com/containerd/containerd v1.6.18 // indirect github.com/containerd/typeurl v1.0.2 // indirect github.com/cppforlife/go-semi-semantic v0.0.0-20160921010311-576b6af77ae4 // indirect - github.com/crhntr/bijection v0.0.0-20230628013949-46b5c800bc70 // indirect github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/distribution v2.8.1+incompatible // indirect diff --git a/internal/acceptance/bake/bake_test.go b/internal/acceptance/bake/bake_test.go index c971b9646..b520ee306 100644 --- a/internal/acceptance/bake/bake_test.go +++ b/internal/acceptance/bake/bake_test.go @@ -18,11 +18,17 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/pivotal-cf-experimental/gomegamatchers" + + "github.com/pivotal-cf/kiln/internal/builder" +) + +const ( + buildVersion = "0.0.0-dev.0+acceptance" ) var ( - pathToMain string - buildVersion string + pathToMain string + tileSourceRevision string ) func TestAcceptance(t *testing.T) { @@ -32,12 +38,12 @@ func TestAcceptance(t *testing.T) { } var _ = BeforeSuite(func() { - buildVersion = fmt.Sprintf("v0.0.0-dev.%d", time.Now().Unix()) - var err error pathToMain, err = gexec.Build("github.com/pivotal-cf/kiln", "--ldflags", fmt.Sprintf("-X main.version=%s", buildVersion)) Expect(err).NotTo(HaveOccurred()) + tileSourceRevision, err = builder.GitMetadataSHA(".", true) + Expect(err).NotTo(HaveOccurred()) }) var _ = AfterSuite(func() { @@ -146,7 +152,7 @@ var _ = Describe("bake command", func() { metadataContents, err := io.ReadAll(file) Expect(err).NotTo(HaveOccurred()) - renderedYAML := fmt.Sprintf(expectedMetadata, diegoSHA1, cfSHA1) + renderedYAML := fmt.Sprintf(expectedMetadata, diegoSHA1, cfSHA1, tileSourceRevision) Expect(metadataContents).To(HelpfullyMatchYAML(renderedYAML)) archivedMigration1, err := bakedTile.Open("migrations/v1/201603041539_custom_buildpacks.js") @@ -217,7 +223,7 @@ var _ = Describe("bake command", func() { metadataContents, err := io.ReadAll(file) Expect(err).NotTo(HaveOccurred()) - renderedYAML := fmt.Sprintf(expectedMetadataWithMultipleStemcells, cfSHA1) + renderedYAML := fmt.Sprintf(expectedMetadataWithMultipleStemcells, cfSHA1, tileSourceRevision) Expect(metadataContents).To(HelpfullyMatchYAML(renderedYAML)) }) }) @@ -260,7 +266,7 @@ var _ = Describe("bake command", func() { metadataContents, err := io.ReadAll(file) Expect(err).NotTo(HaveOccurred()) - renderedYAML := fmt.Sprintf(expectedMetadataWithStemcellTarball, cfSHA1) + renderedYAML := fmt.Sprintf(expectedMetadataWithStemcellTarball, cfSHA1, tileSourceRevision) Expect(metadataContents).To(HelpfullyMatchYAML(renderedYAML)) }) }) @@ -347,7 +353,7 @@ var _ = Describe("bake command", func() { metadataContents, err := io.ReadAll(file) Expect(err).NotTo(HaveOccurred()) - renderedYAML := fmt.Sprintf(expectedMetadata, diegoSHA1, cfSHA1) + renderedYAML := fmt.Sprintf(expectedMetadata, diegoSHA1, cfSHA1, tileSourceRevision) Expect(metadataContents).To(HelpfullyMatchYAML(renderedYAML)) archivedMigration1, err := bakedTile.Open("migrations/v1/201603041539_custom_buildpacks.js") @@ -454,7 +460,7 @@ var _ = Describe("bake command", func() { // intervals added to make tests pass. it is taking too long locally Eventually(session, time.Second*10).Should(gexec.Exit(0)) - renderedYAML := fmt.Sprintf(expectedMetadata, diegoSHA1, cfSHA1) + renderedYAML := fmt.Sprintf(expectedMetadata, diegoSHA1, cfSHA1, tileSourceRevision) Eventually(session.Out.Contents).Should(HelpfullyMatchYAML(renderedYAML)) }) }) @@ -696,7 +702,7 @@ var _ = Describe("bake command", func() { metadataContents, err := io.ReadAll(file) Expect(err).NotTo(HaveOccurred()) - renderedYAML := fmt.Sprintf(expectedMetadataWithStemcellCriteria, diegoSHA1, cfSHA1) + renderedYAML := fmt.Sprintf(expectedMetadataWithStemcellCriteria, diegoSHA1, cfSHA1, tileSourceRevision) Expect(metadataContents).To(HelpfullyMatchYAML(renderedYAML)) Eventually(session.Err).Should(gbytes.Say("Reading release manifests")) @@ -867,6 +873,9 @@ some_runtime_configs: version: some-addon-version serial: false selected_value: "235" +kiln_metadata: + kiln_version: 0.0.0-dev.0+acceptance + metadata_git_sha: %s ` var expectedMetadataWithStemcellCriteria = `--- @@ -889,6 +898,9 @@ stemcell_criteria: version: 250.21 requires_cpi: false enable_patch_security_updates: true +kiln_metadata: + kiln_version: 0.0.0-dev.0+acceptance + metadata_git_sha: %s ` var expectedMetadataWithMultipleStemcells = `--- @@ -908,6 +920,9 @@ stemcell_criteria: additional_stemcells_criteria: - os: windows version: "2019.4" +kiln_metadata: + kiln_version: 0.0.0-dev.0+acceptance + metadata_git_sha: %s ` var expectedMetadataWithStemcellTarball = `--- @@ -924,4 +939,7 @@ some_releases: stemcell_criteria: os: ubuntu-trusty version: "3215.4" +kiln_metadata: + kiln_version: 0.0.0-dev.0+acceptance + metadata_git_sha: %s ` diff --git a/internal/acceptance/bake/fixtures/metadata-with-multiple-stemcells.yml b/internal/acceptance/bake/fixtures/metadata-with-multiple-stemcells.yml index 044f5054b..126767ef3 100644 --- a/internal/acceptance/bake/fixtures/metadata-with-multiple-stemcells.yml +++ b/internal/acceptance/bake/fixtures/metadata-with-multiple-stemcells.yml @@ -9,3 +9,4 @@ label: Pivotal Elastic Runtime stemcell_criteria: $( stemcell "ubuntu-trusty" ) additional_stemcells_criteria: - $( stemcell "windows" ) +kiln_metadata: {} \ No newline at end of file diff --git a/internal/acceptance/bake/fixtures/metadata-with-stemcell-criteria.yml b/internal/acceptance/bake/fixtures/metadata-with-stemcell-criteria.yml index d537604d4..4e9133cbf 100644 --- a/internal/acceptance/bake/fixtures/metadata-with-stemcell-criteria.yml +++ b/internal/acceptance/bake/fixtures/metadata-with-stemcell-criteria.yml @@ -12,3 +12,4 @@ stemcell_criteria: version: 250.21 requires_cpi: false enable_patch_security_updates: true +kiln_metadata: {} \ No newline at end of file diff --git a/internal/acceptance/bake/fixtures/metadata-with-stemcell-tarball.yml b/internal/acceptance/bake/fixtures/metadata-with-stemcell-tarball.yml index cd41fcf1f..484b7d88a 100644 --- a/internal/acceptance/bake/fixtures/metadata-with-stemcell-tarball.yml +++ b/internal/acceptance/bake/fixtures/metadata-with-stemcell-tarball.yml @@ -7,3 +7,4 @@ icon_img: $( icon ) product_version: $( version ) label: Pivotal Elastic Runtime stemcell_criteria: $( stemcell ) +kiln_metadata: {} \ No newline at end of file diff --git a/internal/builder/interpolator.go b/internal/builder/interpolator.go index bff5ebe65..6abf53832 100644 --- a/internal/builder/interpolator.go +++ b/internal/builder/interpolator.go @@ -10,6 +10,8 @@ import ( "strings" "text/template" + "github.com/pivotal-cf/kiln/pkg/proofing" + yamlConverter "github.com/ghodss/yaml" "gopkg.in/yaml.v2" ) @@ -35,6 +37,7 @@ type Interpolator struct{} type InterpolateInput struct { Version string + KilnVersion string BOSHVariables map[string]any Variables map[string]any ReleaseManifests map[string]any @@ -47,7 +50,7 @@ type InterpolateInput struct { PropertyBlueprints map[string]any RuntimeConfigs map[string]any StubReleases bool - MetadataGitSHA func() (string, error) + MetadataGitSHA string } func NewInterpolator() Interpolator { @@ -65,7 +68,10 @@ func (i Interpolator) Interpolate(input InterpolateInput, name string, templateY return nil, err // un-tested } - return prettyMetadata, nil + return setKilnMetadata(prettyMetadata, KilnMetadata{ + KilnVersion: input.KilnVersion, + MetadataGitSHA: input.MetadataGitSHA, + }) } func (i Interpolator) functions(input InterpolateInput) template.FuncMap { @@ -124,11 +130,11 @@ func (i Interpolator) functions(input InterpolateInput) template.FuncMap { if !ok { if input.StubReleases { - val = map[string]any{ - "name": name, - "version": "UNKNOWN", - "file": fmt.Sprintf("%s-UNKNOWN.tgz", name), - "sha1": "dead8e1ea5e00dead8e1ea5ed00ead8e1ea5e000", + val = proofing.Release{ + Name: name, + Version: "UNKNOWN", + File: fmt.Sprintf("%s-UNKNOWN.tgz", name), + SHA1: "dead8e1ea5e00dead8e1ea5ed00ead8e1ea5e000", } } else { return "", fmt.Errorf("could not find release with name '%s'", name) @@ -171,9 +177,7 @@ func (i Interpolator) functions(input InterpolateInput) template.FuncMap { if !ok { switch key { case MetadataGitSHAVariable: - if input.MetadataGitSHA != nil { - return input.MetadataGitSHA() - } + return input.MetadataGitSHA, nil case BuildVersionVariable: return versionFunc() } diff --git a/internal/builder/interpolator_test.go b/internal/builder/interpolator_test.go index bca7b7b25..cfea3fba7 100644 --- a/internal/builder/interpolator_test.go +++ b/internal/builder/interpolator_test.go @@ -5,6 +5,8 @@ import ( "io" "testing" + "github.com/pivotal-cf/kiln/pkg/proofing" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/pivotal-cf-experimental/gomegamatchers" @@ -59,7 +61,7 @@ selected_value: $( release "some-release" | select "version" ) "some-variable": "some-value", }, ReleaseManifests: map[string]any{ - "some-release": builder.ReleaseManifest{ + "some-release": proofing.Release{ Name: "some-release", Version: "1.2.3", File: "some-release-1.2.3.tgz", @@ -224,7 +226,7 @@ some_form_types: BeforeEach(func() { input = builder.InterpolateInput{ ReleaseManifests: map[string]any{ - "some-release": builder.ReleaseManifest{ + "some-release": proofing.Release{ Name: "some-release", Version: "1.2.3", File: "some-release-1.2.3.tgz", @@ -289,7 +291,7 @@ stemcell_criteria: $( stemcell )` input = builder.InterpolateInput{ ReleaseManifests: map[string]any{ - "some-release": builder.ReleaseManifest{ + "some-release": proofing.Release{ Name: "some-release", Version: "1.2.3", File: "some-release-1.2.3.tgz", @@ -329,7 +331,7 @@ some_runtime_configs: input = builder.InterpolateInput{ ReleaseManifests: map[string]any{ - "some-release": builder.ReleaseManifest{ + "some-release": proofing.Release{ Name: "some-release", Version: "1.2.3", File: "some-release-1.2.3.tgz", diff --git a/internal/builder/kiln_metadata.go b/internal/builder/kiln_metadata.go new file mode 100644 index 000000000..bf37eb39b --- /dev/null +++ b/internal/builder/kiln_metadata.go @@ -0,0 +1,54 @@ +package builder + +import ( + "bytes" + "fmt" + + "github.com/crhntr/yamlutil/yamlnode" + "gopkg.in/yaml.v3" +) + +type KilnMetadata struct { + MetadataGitSHA string `yaml:"metadata_git_sha"` + KilnVersion string `yaml:"kiln_version"` +} + +func setKilnMetadata(in []byte, kilnMetadata KilnMetadata) ([]byte, error) { + var productTemplate yaml.Node + err := yaml.Unmarshal(in, &productTemplate) + if err != nil { + return nil, fmt.Errorf("failed to parse product template: %w", err) + } + + _, hasMetadataVersionKey := yamlnode.LookupKey(&productTemplate, "metadata_version") + if !hasMetadataVersionKey { + return in, nil + } + + kilnMetadataValueNode, fieldExists := yamlnode.LookupKey(&productTemplate, "kiln_metadata") + if fieldExists { + fmt.Println(`WARNING: the metadata field "kiln_metadata" is owned by Kiln. You are setting it in your "base.yml". You should not set it anymore.`) + if err := kilnMetadataValueNode.Encode(kilnMetadata); err != nil { + return nil, err + } + } else { + var productTemplatePartial yaml.Node + if err := productTemplatePartial.Encode(struct { + KilnMetadata KilnMetadata `yaml:"kiln_metadata"` + }{ + KilnMetadata: kilnMetadata, + }); err != nil { + return nil, fmt.Errorf("failed to encode kiln_metadata: %w", err) + } + productTemplate.Content[0].Content = append(productTemplate.Content[0].Content, productTemplatePartial.Content...) + } + + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.SetIndent(2) + err = enc.Encode(productTemplate.Content[0]) + if err != nil { + return nil, fmt.Errorf("failed to encode product template: %w", err) + } + return buf.Bytes(), nil +} diff --git a/internal/builder/kiln_metadata_test.go b/internal/builder/kiln_metadata_test.go new file mode 100644 index 000000000..7a8e305c8 --- /dev/null +++ b/internal/builder/kiln_metadata_test.go @@ -0,0 +1,31 @@ +package builder + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_setKilnMetadata(t *testing.T) { + for _, tt := range []struct{ Name string }{ + {Name: "append_kiln_metadata"}, + {Name: "replace_kiln_metadata"}, + } { + t.Run(tt.Name, func(t *testing.T) { + inputMetadataYML, err := os.ReadFile(filepath.Join("testdata", tt.Name, "input_metadata.yml")) + require.NoError(t, err) + outputMetadataYML, err := os.ReadFile(filepath.Join("testdata", tt.Name, "output_metadata.yml")) + require.NoError(t, err) + + result, err := setKilnMetadata(inputMetadataYML, KilnMetadata{ + MetadataGitSHA: "some-commit-sha", + KilnVersion: "some-kiln-version", + }) + require.NoError(t, err) + assert.Equal(t, string(outputMetadataYML), string(result)) + }) + } +} diff --git a/internal/builder/metadata_git_sha.go b/internal/builder/metadata_git_sha.go index f924ec2d2..56c5b3ba5 100644 --- a/internal/builder/metadata_git_sha.go +++ b/internal/builder/metadata_git_sha.go @@ -10,34 +10,38 @@ import ( const dirtyStateSHAValue = "DEVELOPMENT" -func GitMetadataSHA(repositoryDirectory string, isDev bool) func() (string, error) { - var cache string - return func() (s string, err error) { - if cache != "" { - return cache, nil - } - if _, err := exec.LookPath("git"); err != nil { - return "", fmt.Errorf("could not calculate %q: %w", MetadataGitSHAVariable, err) - } - gitStatus := exec.Command("git", "status", "--porcelain") - gitStatus.Dir = repositoryDirectory - err = gitStatus.Run() - if err != nil { - if gitStatus.ProcessState.ExitCode() == 1 && isDev { - _, _ = fmt.Fprintf(os.Stderr, "WARNING: git working directory has un-commited changes: the variable %q has has development only value %q", MetadataGitSHAVariable, dirtyStateSHAValue) - return "DEVELOPMENT", nil - } - return "", fmt.Errorf("failed to run `%s %s`: %w", gitStatus.Path, strings.Join(gitStatus.Args, " "), err) - } - var out bytes.Buffer - gitRevParseHead := exec.Command("git", "rev-parse", "HEAD") - gitRevParseHead.Dir = repositoryDirectory - gitRevParseHead.Stdout = &out - err = gitRevParseHead.Run() - if err != nil { - return "", fmt.Errorf("failed to get HEAD revision hash: %w", err) +func GitMetadataSHA(repositoryDirectory string, isDev bool) (string, error) { + if err := ensureGitExecutableIsFound(); err != nil { + return "", err + } + gitStatus := exec.Command("git", "status", "--porcelain") + gitStatus.Dir = repositoryDirectory + err := gitStatus.Run() + if err != nil { + if gitStatus.ProcessState.ExitCode() == 1 && isDev { + _, _ = fmt.Fprintf(os.Stderr, "WARNING: git working directory has un-commited changes: the variable %q has has development only value %q", MetadataGitSHAVariable, dirtyStateSHAValue) + return dirtyStateSHAValue, nil } - cache = strings.TrimSpace(out.String()) - return cache, nil + return "", fmt.Errorf("failed to run `%s %s`: %w", gitStatus.Path, strings.Join(gitStatus.Args, " "), err) + } + return gitHeadRevision(repositoryDirectory) +} + +func gitHeadRevision(repositoryDirectory string) (string, error) { + var out bytes.Buffer + gitRevParseHead := exec.Command("git", "rev-parse", "HEAD") + gitRevParseHead.Dir = repositoryDirectory + gitRevParseHead.Stdout = &out + err := gitRevParseHead.Run() + if err != nil { + return "", fmt.Errorf("failed to get HEAD revision hash: %w", err) + } + return strings.TrimSpace(out.String()), nil +} + +func ensureGitExecutableIsFound() error { + if _, err := exec.LookPath("git"); err != nil { + return fmt.Errorf("could not calculate %q: %w", MetadataGitSHAVariable, err) } + return nil } diff --git a/internal/builder/release_manifest_reader.go b/internal/builder/release_manifest_reader.go index 3c212d74b..ae647d03a 100644 --- a/internal/builder/release_manifest_reader.go +++ b/internal/builder/release_manifest_reader.go @@ -1,80 +1,32 @@ package builder import ( - "crypto/sha1" - "encoding/hex" - "fmt" - "io" "path/filepath" - "github.com/pivotal-cf/kiln/pkg/cargo" + "github.com/pivotal-cf/kiln/pkg/proofing" - "github.com/go-git/go-billy/v5" - "github.com/go-git/go-billy/v5/osfs" + "github.com/pivotal-cf/kiln/pkg/cargo" ) -type ReleaseManifest struct { - Name string - Version string - File string - SHA1 string - StemcellOS string `yaml:"-"` - StemcellVersion string `yaml:"-"` -} - -type ReleaseManifestReader struct { - fs billy.Filesystem -} - -func NewReleaseManifestReader(fs billy.Filesystem) ReleaseManifestReader { - return ReleaseManifestReader{fs: fs} -} - -func (r ReleaseManifestReader) Read(releaseTarball string) (Part, error) { - if r.fs == nil { - r.fs = osfs.New("") - } +type ReleaseManifestReader struct{} - file, err := r.fs.Open(releaseTarball) - if err != nil { - return Part{}, err - } - defer closeAndIgnoreError(file) +func NewReleaseManifestReader() (_ ReleaseManifestReader) { return } - inputReleaseManifest, err := cargo.ReadProductTemplatePartFromBOSHReleaseTarball(file) +func (r ReleaseManifestReader) Read(releaseTarballFilepath string) (Part, error) { + releaseTarball, err := cargo.OpenBOSHReleaseTarball(releaseTarballFilepath) if err != nil { return Part{}, err } - stemcellOS, stemcellVersion, stemcellOK := inputReleaseManifest.Stemcell() - if !stemcellOK && len(inputReleaseManifest.CompiledPackages) > 0 { - return Part{}, fmt.Errorf("%s/%s has invalid stemcell: %q", inputReleaseManifest.Name, inputReleaseManifest.Version, inputReleaseManifest.CompiledPackages[0].Stemcell) - } - - outputReleaseManifest := ReleaseManifest{ - Name: inputReleaseManifest.Name, - Version: inputReleaseManifest.Version, - File: filepath.Base(releaseTarball), - StemcellOS: stemcellOS, - StemcellVersion: stemcellVersion, - } - - _, err = file.Seek(0, 0) - if err != nil { - return Part{}, err // NOTE: cannot replicate this error scenario in a test - } - - hash := sha1.New() - _, err = io.Copy(hash, file) - if err != nil { - return Part{}, err // NOTE: cannot replicate this error scenario in a test - } - - outputReleaseManifest.SHA1 = hex.EncodeToString(hash.Sum(nil)) - return Part{ - File: releaseTarball, - Name: inputReleaseManifest.Name, - Metadata: outputReleaseManifest, + File: releaseTarballFilepath, + Name: releaseTarball.Manifest.Name, + Metadata: proofing.Release{ + Name: releaseTarball.Manifest.Name, + Version: releaseTarball.Manifest.Version, + File: filepath.Base(releaseTarballFilepath), + SHA1: releaseTarball.SHA1, + CommitHash: releaseTarball.Manifest.CommitHash, + }, }, nil } diff --git a/internal/builder/release_manifest_reader_test.go b/internal/builder/release_manifest_reader_test.go index 48c8193d9..51d264a80 100644 --- a/internal/builder/release_manifest_reader_test.go +++ b/internal/builder/release_manifest_reader_test.go @@ -1,137 +1,62 @@ package builder_test import ( - "archive/tar" - "bufio" - "bytes" - "compress/gzip" - "crypto/sha1" - "encoding/hex" - "io" - "os" "path/filepath" - "time" - - "github.com/go-git/go-billy/v5/osfs" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/pivotal-cf/kiln/pkg/proofing" "github.com/pivotal-cf/kiln/internal/builder" ) -func createReleaseTarball(releaseMetadata string) (*os.File, string) { - tarball, err := os.CreateTemp("", "kiln") - Expect(err).NotTo(HaveOccurred()) - - gw := gzip.NewWriter(tarball) - tw := tar.NewWriter(gw) - - releaseManifest := bytes.NewBuffer([]byte(releaseMetadata)) - - header := &tar.Header{ - Name: "./release.MF", - Size: int64(releaseManifest.Len()), - Mode: int64(0o644), - ModTime: time.Now(), - } - - err = tw.WriteHeader(header) - Expect(err).NotTo(HaveOccurred()) - - _, err = io.Copy(tw, releaseManifest) - Expect(err).NotTo(HaveOccurred()) - - err = tw.Close() - Expect(err).NotTo(HaveOccurred()) - - err = gw.Close() - Expect(err).NotTo(HaveOccurred()) - - err = tarball.Close() - Expect(err).NotTo(HaveOccurred()) - - var file *os.File - file, err = os.Open(tarball.Name()) - Expect(err).NotTo(HaveOccurred()) - - hash := sha1.New() - _, err = io.Copy(hash, file) - Expect(err).NotTo(HaveOccurred()) - - releaseSHA1 := hex.EncodeToString(hash.Sum(nil)) - - err = file.Close() - Expect(err).NotTo(HaveOccurred()) - - return tarball, releaseSHA1 -} - var _ = Describe("ReleaseManifestReader", func() { var ( - reader builder.ReleaseManifestReader - releaseSHA1 string - tarball *os.File - err error + reader builder.ReleaseManifestReader + err error + + nonPreCompiledTarballPath = filepath.Join("testdata", "bpm-1.1.21.tgz") + compiledTarballPath = filepath.Join("testdata", "bpm-1.1.21-ubuntu-xenial-621.463.tgz") ) BeforeEach(func() { - reader = builder.NewReleaseManifestReader(osfs.New("")) - tarball, releaseSHA1 = createReleaseTarball(` -name: release -version: 1.2.3 -compiled_packages: -- name: some-package - stemcell: ubuntu-xenial/170.25 -`) - }) - - AfterEach(func() { - err = os.Remove(tarball.Name()) - Expect(err).NotTo(HaveOccurred()) + reader = builder.NewReleaseManifestReader() }) Describe("Read", func() { It("extracts the release manifest information from the tarball", func() { var releaseManifest builder.Part - releaseManifest, err = reader.Read(tarball.Name()) + releaseManifest, err = reader.Read(compiledTarballPath) Expect(err).NotTo(HaveOccurred()) Expect(releaseManifest).To(Equal(builder.Part{ - File: tarball.Name(), - Name: "release", - Metadata: builder.ReleaseManifest{ - Name: "release", - Version: "1.2.3", - File: filepath.Base(tarball.Name()), - SHA1: releaseSHA1, - StemcellOS: "ubuntu-xenial", - StemcellVersion: "170.25", + File: compiledTarballPath, + Name: "bpm", + Metadata: proofing.Release{ + Name: "bpm", + Version: "1.1.21", + File: filepath.Base(compiledTarballPath), + + SHA1: "be5b1710f33128f6c864eae1d97effddb94dd3ac", + CommitHash: "fd88358", }, })) }) Context("when the release is not pre-compiled", func() { - BeforeEach(func() { - tarball, releaseSHA1 = createReleaseTarball(` -name: release -version: 1.2.3 -`) - }) - It("extracts the release manifest information from the tarball", func() { var releaseManifest builder.Part - releaseManifest, err = reader.Read(tarball.Name()) + releaseManifest, err = reader.Read(nonPreCompiledTarballPath) Expect(err).NotTo(HaveOccurred()) Expect(releaseManifest).To(Equal(builder.Part{ - File: tarball.Name(), - Name: "release", - Metadata: builder.ReleaseManifest{ - Name: "release", - Version: "1.2.3", - File: filepath.Base(tarball.Name()), - SHA1: releaseSHA1, - StemcellOS: "", - StemcellVersion: "", + File: nonPreCompiledTarballPath, + Name: "bpm", + Metadata: proofing.Release{ + Name: "bpm", + Version: "1.1.21", + File: filepath.Base(nonPreCompiledTarballPath), + + SHA1: "519b78f2f3333a7b9c000bbef325e12a2f36996d", + CommitHash: "fd88358", }, })) }) @@ -144,159 +69,6 @@ version: 1.2.3 Expect(err).To(MatchError(ContainSubstring("no such file"))) }) }) - - Context("when the input is not a valid gzip", func() { - It("returns an error", func() { - tarball, err = os.OpenFile(tarball.Name(), os.O_RDWR, 0o666) - Expect(err).NotTo(HaveOccurred()) - - _, err = tarball.WriteAt([]byte{}, 10) - Expect(err).NotTo(HaveOccurred()) - - err = tarball.Close() - Expect(err).NotTo(HaveOccurred()) - - var contents []byte - contents, err = os.ReadFile(tarball.Name()) - Expect(err).NotTo(HaveOccurred()) - - By("corrupting the gzip header contents", func() { - contents[0] = 0 - err = os.WriteFile(tarball.Name(), contents, 0o666) - Expect(err).NotTo(HaveOccurred()) - }) - - _, err = reader.Read(tarball.Name()) - Expect(err).To(MatchError("gzip: invalid header")) - }) - }) - - Context("when the header file is corrupt", func() { - It("returns an error", func() { - tarball, err = os.Create(tarball.Name()) - Expect(err).NotTo(HaveOccurred()) - - gw := gzip.NewWriter(tarball) - tw := tar.NewWriter(gw) - - Expect(tw.Close()).NotTo(HaveOccurred()) - Expect(gw.Close()).NotTo(HaveOccurred()) - - _, err = reader.Read(tarball.Name()) - Expect(err).To(MatchError("failed to find release.MF in tarball")) - }) - }) - - Context("when there is no release.MF", func() { - It("returns an error", func() { - tarball, err = os.Create(tarball.Name()) - Expect(err).NotTo(HaveOccurred()) - - gw := gzip.NewWriter(tarball) - tw := tar.NewWriter(gw) - - releaseManifest := bytes.NewBuffer([]byte(`--- -name: release -version: 1.2.3 -`)) - - header := &tar.Header{ - Name: "./someotherfile.MF", - Size: int64(releaseManifest.Len()), - Mode: int64(0o644), - ModTime: time.Now(), - } - - err = tw.WriteHeader(header) - Expect(err).NotTo(HaveOccurred()) - - _, err = io.Copy(tw, releaseManifest) - Expect(err).NotTo(HaveOccurred()) - - err = tw.Close() - Expect(err).NotTo(HaveOccurred()) - - err = gw.Close() - Expect(err).NotTo(HaveOccurred()) - - _, err = reader.Read(tarball.Name()) - Expect(err).To(MatchError("failed to find release.MF in tarball")) - }) - }) - - Context("when the tarball is corrupt", func() { - It("returns an error", func() { - tarball, err = os.Create(tarball.Name()) - Expect(err).NotTo(HaveOccurred()) - - gw := gzip.NewWriter(tarball) - tw := bufio.NewWriter(gw) - - _, err = tw.WriteString("I am a banana!") - Expect(err).NotTo(HaveOccurred()) - - err = tw.Flush() - Expect(err).NotTo(HaveOccurred()) - - err = gw.Close() - Expect(err).NotTo(HaveOccurred()) - - _, err = reader.Read(tarball.Name()) - Expect(err).To(MatchError("unexpected EOF")) - }) - }) - - Context("when the release manifest is not YAML", func() { - It("returns an error", func() { - tarball, err = os.Create(tarball.Name()) - Expect(err).NotTo(HaveOccurred()) - - gw := gzip.NewWriter(tarball) - tw := tar.NewWriter(gw) - - releaseManifest := bytes.NewBuffer([]byte(`%%%%%`)) - - header := &tar.Header{ - Name: "./release.MF", - Size: int64(releaseManifest.Len()), - Mode: int64(0o644), - ModTime: time.Now(), - } - - err = tw.WriteHeader(header) - Expect(err).NotTo(HaveOccurred()) - - _, err = io.Copy(tw, releaseManifest) - Expect(err).NotTo(HaveOccurred()) - - err = tw.Close() - Expect(err).NotTo(HaveOccurred()) - - err = gw.Close() - Expect(err).NotTo(HaveOccurred()) - - _, err = reader.Read(tarball.Name()) - Expect(err).To(MatchError("yaml: could not find expected directive name")) - }) - }) - }) - - Context("when the release has a malformed stemcell string", func() { - BeforeEach(func() { - tarball, releaseSHA1 = createReleaseTarball(` -name: release -version: 1.2.3 -compiled_packages: -- name: some-package - stemcell: invalid -`) - }) - - It("extracts the release manifest information from the tarball", func() { - _, err := reader.Read(tarball.Name()) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("invalid")) - }) }) }) }) diff --git a/internal/builder/testdata/append_kiln_metadata/input_metadata.yml b/internal/builder/testdata/append_kiln_metadata/input_metadata.yml new file mode 100644 index 000000000..4d32b4daa --- /dev/null +++ b/internal/builder/testdata/append_kiln_metadata/input_metadata.yml @@ -0,0 +1,83 @@ +description: Serves a "Hello World" server. +icon_image: some-image +form_types: [] +job_types: + - description: HTTP Server + dynamic_ip: 0 + instance_definition: + configurable: true + constraints: + max: 1 + min: 0 + default: 1 + label: Instances + name: instances + type: integer + label: Server + max_in_flight: 1 + name: hello-server + resource_definitions: + - configurable: true + constraints: + min: 1024 + default: 1024 + label: RAM + name: ram + type: integer + - configurable: true + constraints: + min: 2000 + default: 4000 + label: Ephemeral Disk + name: ephemeral_disk + type: integer + - configurable: false + constraints: + min: 2000 + default: 4000 + label: Persistent Disk + name: persistent_disk + type: integer + - configurable: true + constraints: + min: 1 + default: 1 + label: CPU + name: cpu + type: integer + resource_label: Server + single_az_only: true + static_ip: 1 + templates: + - manifest: | + job-properties: + port: 8080 + name: hello-server + release: hello-release + - manifest: {} + name: bpm + release: bpm +label: Hello +metadata_version: 2.7.0 +minimum_version_for_upgrade: 0.1.0 +name: hello +product_version: 0.1.2 +property_blueprints: [] +provides_product_versions: + - name: hello + version: 0.1.2 +rank: 90 +releases: + - file: hello-release-v0.1.4-ubuntu-xenial-621.256.tgz + name: hello-release + sha1: c471ac6371eb8fc24508b14d9a49a44f9a5ef98c + version: v0.1.4 + - file: bpm-1.1.18-ubuntu-xenial-621.256.tgz + name: bpm + sha1: 476c516e0644564838c025b165560bb24102fe6f + version: 1.1.18 +runtime_configs: [] +serial: false +stemcell_criteria: + os: ubuntu-xenial + version: "621.256" diff --git a/internal/builder/testdata/append_kiln_metadata/output_metadata.yml b/internal/builder/testdata/append_kiln_metadata/output_metadata.yml new file mode 100644 index 000000000..f029ed4e3 --- /dev/null +++ b/internal/builder/testdata/append_kiln_metadata/output_metadata.yml @@ -0,0 +1,86 @@ +description: Serves a "Hello World" server. +icon_image: some-image +form_types: [] +job_types: + - description: HTTP Server + dynamic_ip: 0 + instance_definition: + configurable: true + constraints: + max: 1 + min: 0 + default: 1 + label: Instances + name: instances + type: integer + label: Server + max_in_flight: 1 + name: hello-server + resource_definitions: + - configurable: true + constraints: + min: 1024 + default: 1024 + label: RAM + name: ram + type: integer + - configurable: true + constraints: + min: 2000 + default: 4000 + label: Ephemeral Disk + name: ephemeral_disk + type: integer + - configurable: false + constraints: + min: 2000 + default: 4000 + label: Persistent Disk + name: persistent_disk + type: integer + - configurable: true + constraints: + min: 1 + default: 1 + label: CPU + name: cpu + type: integer + resource_label: Server + single_az_only: true + static_ip: 1 + templates: + - manifest: | + job-properties: + port: 8080 + name: hello-server + release: hello-release + - manifest: {} + name: bpm + release: bpm +label: Hello +metadata_version: 2.7.0 +minimum_version_for_upgrade: 0.1.0 +name: hello +product_version: 0.1.2 +property_blueprints: [] +provides_product_versions: + - name: hello + version: 0.1.2 +rank: 90 +releases: + - file: hello-release-v0.1.4-ubuntu-xenial-621.256.tgz + name: hello-release + sha1: c471ac6371eb8fc24508b14d9a49a44f9a5ef98c + version: v0.1.4 + - file: bpm-1.1.18-ubuntu-xenial-621.256.tgz + name: bpm + sha1: 476c516e0644564838c025b165560bb24102fe6f + version: 1.1.18 +runtime_configs: [] +serial: false +stemcell_criteria: + os: ubuntu-xenial + version: "621.256" +kiln_metadata: + metadata_git_sha: some-commit-sha + kiln_version: some-kiln-version diff --git a/internal/builder/testdata/bpm-1.1.21-ubuntu-xenial-621.463.tgz b/internal/builder/testdata/bpm-1.1.21-ubuntu-xenial-621.463.tgz new file mode 100644 index 000000000..34266e764 Binary files /dev/null and b/internal/builder/testdata/bpm-1.1.21-ubuntu-xenial-621.463.tgz differ diff --git a/internal/builder/testdata/bpm-1.1.21.tgz b/internal/builder/testdata/bpm-1.1.21.tgz new file mode 100644 index 000000000..40095d7ee Binary files /dev/null and b/internal/builder/testdata/bpm-1.1.21.tgz differ diff --git a/internal/builder/testdata/replace_kiln_metadata/input_metadata.yml b/internal/builder/testdata/replace_kiln_metadata/input_metadata.yml new file mode 100644 index 000000000..b95a3c76b --- /dev/null +++ b/internal/builder/testdata/replace_kiln_metadata/input_metadata.yml @@ -0,0 +1,86 @@ +description: Serves a "Hello World" server. +icon_image: some-image +form_types: [] +job_types: + - description: HTTP Server + dynamic_ip: 0 + instance_definition: + configurable: true + constraints: + max: 1 + min: 0 + default: 1 + label: Instances + name: instances + type: integer + label: Server + max_in_flight: 1 + name: hello-server + resource_definitions: + - configurable: true + constraints: + min: 1024 + default: 1024 + label: RAM + name: ram + type: integer + - configurable: true + constraints: + min: 2000 + default: 4000 + label: Ephemeral Disk + name: ephemeral_disk + type: integer + - configurable: false + constraints: + min: 2000 + default: 4000 + label: Persistent Disk + name: persistent_disk + type: integer + - configurable: true + constraints: + min: 1 + default: 1 + label: CPU + name: cpu + type: integer + resource_label: Server + single_az_only: true + static_ip: 1 + templates: + - manifest: | + job-properties: + port: 8080 + name: hello-server + release: hello-release + - manifest: {} + name: bpm + release: bpm +kiln_metadata: + metadata_git_sha: peach + kiln_version: orange +label: Hello +metadata_version: 2.7.0 +minimum_version_for_upgrade: 0.1.0 +name: hello +product_version: 0.1.2 +property_blueprints: [] +provides_product_versions: + - name: hello + version: 0.1.2 +rank: 90 +releases: + - file: hello-release-v0.1.4-ubuntu-xenial-621.256.tgz + name: hello-release + sha1: c471ac6371eb8fc24508b14d9a49a44f9a5ef98c + version: v0.1.4 + - file: bpm-1.1.18-ubuntu-xenial-621.256.tgz + name: bpm + sha1: 476c516e0644564838c025b165560bb24102fe6f + version: 1.1.18 +runtime_configs: [] +serial: false +stemcell_criteria: + os: ubuntu-xenial + version: "621.256" diff --git a/internal/builder/testdata/replace_kiln_metadata/output_metadata.yml b/internal/builder/testdata/replace_kiln_metadata/output_metadata.yml new file mode 100644 index 000000000..3deb47241 --- /dev/null +++ b/internal/builder/testdata/replace_kiln_metadata/output_metadata.yml @@ -0,0 +1,86 @@ +description: Serves a "Hello World" server. +icon_image: some-image +form_types: [] +job_types: + - description: HTTP Server + dynamic_ip: 0 + instance_definition: + configurable: true + constraints: + max: 1 + min: 0 + default: 1 + label: Instances + name: instances + type: integer + label: Server + max_in_flight: 1 + name: hello-server + resource_definitions: + - configurable: true + constraints: + min: 1024 + default: 1024 + label: RAM + name: ram + type: integer + - configurable: true + constraints: + min: 2000 + default: 4000 + label: Ephemeral Disk + name: ephemeral_disk + type: integer + - configurable: false + constraints: + min: 2000 + default: 4000 + label: Persistent Disk + name: persistent_disk + type: integer + - configurable: true + constraints: + min: 1 + default: 1 + label: CPU + name: cpu + type: integer + resource_label: Server + single_az_only: true + static_ip: 1 + templates: + - manifest: | + job-properties: + port: 8080 + name: hello-server + release: hello-release + - manifest: {} + name: bpm + release: bpm +kiln_metadata: + metadata_git_sha: some-commit-sha + kiln_version: some-kiln-version +label: Hello +metadata_version: 2.7.0 +minimum_version_for_upgrade: 0.1.0 +name: hello +product_version: 0.1.2 +property_blueprints: [] +provides_product_versions: + - name: hello + version: 0.1.2 +rank: 90 +releases: + - file: hello-release-v0.1.4-ubuntu-xenial-621.256.tgz + name: hello-release + sha1: c471ac6371eb8fc24508b14d9a49a44f9a5ef98c + version: v0.1.4 + - file: bpm-1.1.18-ubuntu-xenial-621.256.tgz + name: bpm + sha1: 476c516e0644564838c025b165560bb24102fe6f + version: 1.1.18 +runtime_configs: [] +serial: false +stemcell_criteria: + os: ubuntu-xenial + version: "621.256" diff --git a/internal/commands/bake.go b/internal/commands/bake.go index 26f7197a4..88dd7ebdd 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -141,6 +141,8 @@ type Bake struct { stemcell stemcellService releases fromDirectories + KilnVersion string + boshVariables, forms, instanceGroups, @@ -454,7 +456,13 @@ func (b Bake) Execute(args []string) error { return fmt.Errorf("failed to read metadata: %s", err) } + gitMetadataSHA, err := builder.GitMetadataSHA(filepath.Dir(b.Options.Kilnfile), b.Options.MetadataOnly || b.Options.StubReleases) + if err != nil { + return fmt.Errorf("failed to read metadata: %s", err) + } + input := builder.InterpolateInput{ + KilnVersion: b.KilnVersion, Version: b.Options.Version, Variables: templateVariables, BOSHVariables: boshVariables, @@ -468,7 +476,7 @@ func (b Bake) Execute(args []string) error { PropertyBlueprints: propertyBlueprints, RuntimeConfigs: runtimeConfigs, StubReleases: b.Options.StubReleases, - MetadataGitSHA: builder.GitMetadataSHA(filepath.Dir(b.Options.Kilnfile), b.Options.MetadataOnly || b.Options.StubReleases), + MetadataGitSHA: gitMetadataSHA, } interpolatedMetadata, err := b.interpolator.Interpolate(input, b.Options.Metadata, metadata) if err != nil { diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index 03e433cf5..78a888ac4 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -9,11 +9,14 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/pivotal-cf-experimental/gomegamatchers" + "github.com/pivotal-cf/jhanda" + "gopkg.in/yaml.v2" + "github.com/pivotal-cf/kiln/internal/builder" "github.com/pivotal-cf/kiln/internal/commands" "github.com/pivotal-cf/kiln/internal/commands/fakes" - "gopkg.in/yaml.v2" + "github.com/pivotal-cf/kiln/pkg/proofing" ) var _ = Describe("Bake", func() { @@ -101,12 +104,12 @@ var _ = Describe("Bake", func() { }, nil) fakeReleasesService.FromDirectoriesReturns(map[string]any{ - "some-release-1": builder.ReleaseManifest{ + "some-release-1": proofing.Release{ Name: "some-release-1", Version: "1.2.3", File: "release1.tgz", }, - "some-release-2": builder.ReleaseManifest{ + "some-release-2": proofing.Release{ Name: "some-release-2", Version: "2.3.4", File: "release2.tar.gz", @@ -258,11 +261,14 @@ var _ = Describe("Bake", func() { Expect(fakeInterpolator.InterpolateCallCount()).To(Equal(1)) + metadataGitSHA, err := builder.GitMetadataSHA(".", true) + Expect(err).NotTo(HaveOccurred()) + input, interpolateName, metadata := fakeInterpolator.InterpolateArgsForCall(0) - Expect(input.MetadataGitSHA).NotTo(BeNil()) - input.MetadataGitSHA = nil // func pointers are not comparable unless they are both nil + Expect(input.MetadataGitSHA).NotTo(BeEmpty()) Expect(input).To(Equal(builder.InterpolateInput{ - Version: "1.2.3", + MetadataGitSHA: metadataGitSHA, + Version: "1.2.3", BOSHVariables: map[string]any{ "some-secret": builder.Metadata{ "name": "some-secret", @@ -274,12 +280,12 @@ var _ = Describe("Bake", func() { "some-variable": "some-variable-value", }, ReleaseManifests: map[string]any{ - "some-release-1": builder.ReleaseManifest{ + "some-release-1": proofing.Release{ Name: "some-release-1", Version: "1.2.3", File: "release1.tgz", }, - "some-release-2": builder.ReleaseManifest{ + "some-release-2": proofing.Release{ Name: "some-release-2", Version: "2.3.4", File: "release2.tar.gz", diff --git a/internal/commands/generate_osm_manifest_test.go b/internal/commands/generate_osm_manifest_test.go index 7852f73d2..cbc779709 100644 --- a/internal/commands/generate_osm_manifest_test.go +++ b/internal/commands/generate_osm_manifest_test.go @@ -29,7 +29,7 @@ func TestOSM_Execute(t *testing.T) { tmp := t.TempDir() kfp := filepath.Join(tmp, "Kilnfile") - writeYAML(t, kfp, cargo.Kilnfile{ + writeYAMLHelper(t, kfp, cargo.Kilnfile{ Releases: []cargo.BOSHReleaseTarballSpecification{ { Name: "banana", @@ -42,7 +42,7 @@ func TestOSM_Execute(t *testing.T) { }) klp := filepath.Join(tmp, "Kilnfile.lock") - writeYAML(t, klp, cargo.KilnfileLock{ + writeYAMLHelper(t, klp, cargo.KilnfileLock{ Releases: []cargo.BOSHReleaseTarballLock{ {Name: "banana", Version: "1.2.3"}, }, @@ -80,7 +80,7 @@ func TestOSM_Execute(t *testing.T) { tmp := t.TempDir() kfp := filepath.Join(tmp, "Kilnfile") - writeYAML(t, kfp, cargo.Kilnfile{ + writeYAMLHelper(t, kfp, cargo.Kilnfile{ Releases: []cargo.BOSHReleaseTarballSpecification{ { Name: "banana", @@ -97,7 +97,7 @@ func TestOSM_Execute(t *testing.T) { }) klp := filepath.Join(tmp, "Kilnfile.lock") - writeYAML(t, klp, cargo.KilnfileLock{ + writeYAMLHelper(t, klp, cargo.KilnfileLock{ Releases: []cargo.BOSHReleaseTarballLock{ {Name: "banana", Version: "1.2.3"}, {Name: "apple", Version: "1.2.4"}, @@ -134,7 +134,7 @@ func TestOSM_Execute(t *testing.T) { tmp := t.TempDir() kfp := filepath.Join(tmp, "Kilnfile") - writeYAML(t, kfp, cargo.Kilnfile{ + writeYAMLHelper(t, kfp, cargo.Kilnfile{ Releases: []cargo.BOSHReleaseTarballSpecification{ { Name: "lemon-offline-buildpack", @@ -150,7 +150,7 @@ func TestOSM_Execute(t *testing.T) { }) klp := filepath.Join(tmp, "Kilnfile.lock") - writeYAML(t, klp, cargo.KilnfileLock{ + writeYAMLHelper(t, klp, cargo.KilnfileLock{ Releases: []cargo.BOSHReleaseTarballLock{ {Name: "lemon-offline-buildpack", Version: "1.2.3"}, {Name: "banana", Version: "1.2.3"}, @@ -334,7 +334,7 @@ func TestOSM_Execute(t *testing.T) { }) } -func writeYAML(t *testing.T, path string, data any) { +func writeYAMLHelper(t *testing.T, path string, data any) { t.Helper() buf, err := yaml.Marshal(data) if err != nil { @@ -352,3 +352,11 @@ func writeYAML(t *testing.T, path string, data any) { t.Fatal(err) } } + +func writeYAML(path string, data any) error { + buf, err := yaml.Marshal(data) + if err != nil { + return err + } + return os.WriteFile(path, buf, 0o666) +} diff --git a/internal/commands/release_notes_test.go b/internal/commands/release_notes_test.go index 4d2fac8c0..c431ac38e 100644 --- a/internal/commands/release_notes_test.go +++ b/internal/commands/release_notes_test.go @@ -134,7 +134,7 @@ func TestReleaseNotes_Execute(t *testing.T) { please.Expect(issuesQuery.IssueIDs).To(Equal([]string{"54000", "54321"})) please.Expect(issuesQuery.IssueLabels).To(Equal([]string{"tropical", "20000"})) - //t.Log(out.String()) + // t.Log(out.String()) please.Expect(out.String()).To(Equal(releaseNotesExpectedOutput)) }) } diff --git a/internal/commands/testdata/bpm-1.1.21-ubuntu-xenial-621.463.tgz b/internal/commands/testdata/bpm-1.1.21-ubuntu-xenial-621.463.tgz new file mode 100644 index 000000000..34266e764 Binary files /dev/null and b/internal/commands/testdata/bpm-1.1.21-ubuntu-xenial-621.463.tgz differ diff --git a/internal/commands/testdata/bpm-1.1.21.tgz b/internal/commands/testdata/bpm-1.1.21.tgz new file mode 100644 index 000000000..40095d7ee Binary files /dev/null and b/internal/commands/testdata/bpm-1.1.21.tgz differ diff --git a/internal/commands/upload_release.go b/internal/commands/upload_release.go index 27912143e..46ce54e4f 100644 --- a/internal/commands/upload_release.go +++ b/internal/commands/upload_release.go @@ -3,13 +3,13 @@ package commands import ( "fmt" "log" + "os" "github.com/Masterminds/semver/v3" "github.com/go-git/go-billy/v5" "github.com/pivotal-cf/jhanda" - "github.com/pivotal-cf/kiln/internal/builder" "github.com/pivotal-cf/kiln/internal/commands/flags" "github.com/pivotal-cf/kiln/internal/component" "github.com/pivotal-cf/kiln/pkg/cargo" @@ -32,7 +32,7 @@ type UploadRelease struct { type ReleaseUploaderFinder func(cargo.Kilnfile, string) (component.ReleaseUploader, error) func (command UploadRelease) Execute(args []string) error { - _, err := flags.LoadFlagsWithDefaults(&command.Options, args, command.FS.Stat) + _, err := flags.LoadFlagsWithDefaults(&command.Options, args, os.Stat) if err != nil { return err } @@ -47,45 +47,38 @@ func (command UploadRelease) Execute(args []string) error { return fmt.Errorf("error finding release source: %w", err) } - file, err := command.FS.Open(command.Options.LocalPath) - if err != nil { - return fmt.Errorf("could not open release: %w", err) - } - - manifestReader := builder.NewReleaseManifestReader(command.FS) - part, err := manifestReader.Read(command.Options.LocalPath) + releaseTarball, err := cargo.OpenBOSHReleaseTarball(command.Options.LocalPath) if err != nil { return fmt.Errorf("error reading the release manifest: %w", err) } - manifest := part.Metadata.(builder.ReleaseManifest) - if manifest.StemcellOS != "" { - return fmt.Errorf("cannot upload compiled release %q - only uncompiled releases are allowed", command.Options.LocalPath) - } - - version, err := semver.NewVersion(manifest.Version) + version, err := semver.NewVersion(releaseTarball.Manifest.Version) if err != nil { - return fmt.Errorf("error parsing release version %q - release version is not valid semver", manifest.Version) + return fmt.Errorf("error parsing release version %q: release version is not valid semver: %w", releaseTarball.Manifest.Version, err) } if version.Prerelease() != "" { - return fmt.Errorf("cannot upload development release %q - only finalized releases are allowed", manifest.Version) + return fmt.Errorf("cannot upload development release %q - only finalized releases are allowed", releaseTarball.Manifest.Version) } - requirement := cargo.BOSHReleaseTarballSpecification{Name: manifest.Name, Version: manifest.Version} + requirement := cargo.BOSHReleaseTarballSpecification{Name: releaseTarball.Manifest.Name, Version: releaseTarball.Manifest.Version} _, err = releaseUploader.GetMatchedRelease(requirement) - if err != nil { if !component.IsErrNotFound(err) { return fmt.Errorf("couldn't query release source: %w", err) } } else { return fmt.Errorf("a release with name %q and version %q already exists on %s", - manifest.Name, manifest.Version, command.Options.UploadTargetID) + releaseTarball.Manifest.Name, releaseTarball.Manifest.Version, command.Options.UploadTargetID) } + file, err := os.Open(releaseTarball.FilePath) + if err != nil { + return err + } + defer closeAndIgnoreError(file) _, err = releaseUploader.UploadRelease(cargo.BOSHReleaseTarballSpecification{ - Name: manifest.Name, - Version: manifest.Version, + Name: releaseTarball.Manifest.Name, + Version: releaseTarball.Manifest.Version, }, file) if err != nil { return fmt.Errorf("error uploading the release: %w", err) diff --git a/internal/commands/upload_release_test.go b/internal/commands/upload_release_test.go index be16a1bd7..89d92574d 100644 --- a/internal/commands/upload_release_test.go +++ b/internal/commands/upload_release_test.go @@ -1,14 +1,10 @@ package commands_test import ( - "crypto/sha1" "errors" - "fmt" - "io" "log" - - "github.com/go-git/go-billy/v5" - "github.com/go-git/go-billy/v5/memfs" + "os" + "path/filepath" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -17,24 +13,29 @@ import ( commandsFakes "github.com/pivotal-cf/kiln/internal/commands/fakes" "github.com/pivotal-cf/kiln/internal/component" "github.com/pivotal-cf/kiln/internal/component/fakes" - testHelpers "github.com/pivotal-cf/kiln/internal/test-helpers" "github.com/pivotal-cf/kiln/pkg/cargo" ) var _ = Describe("UploadRelease", func() { Context("Execute", func() { var ( - fs billy.Filesystem releaseUploaderFinder *commandsFakes.ReleaseUploaderFinder releaseUploader *fakes.ReleaseUploader uploadRelease commands.UploadRelease - expectedReleaseSHA string + tileDirectory string ) BeforeEach(func() { - fs = memfs.New() + var err error + tileDirectory, err = os.MkdirTemp("", "") + if err != nil { + log.Fatal(err) + } + + Expect(writeYAML(filepath.Join(tileDirectory, "Kilnfile"), cargo.Kilnfile{})).NotTo(HaveOccurred()) + Expect(writeYAML(filepath.Join(tileDirectory, "Kilnfile.lock"), cargo.KilnfileLock{})).NotTo(HaveOccurred()) releaseUploader = new(fakes.ReleaseUploader) releaseUploader.GetMatchedReleaseReturns(cargo.BOSHReleaseTarballLock{}, component.ErrNotFound) @@ -42,23 +43,16 @@ var _ = Describe("UploadRelease", func() { releaseUploaderFinder.Returns(releaseUploader, nil) uploadRelease = commands.UploadRelease{ - FS: fs, Logger: log.New(GinkgoWriter, "", 0), ReleaseUploaderFinder: releaseUploaderFinder.Spy, } - - Expect(fsWriteYAML(fs, "Kilnfile", cargo.Kilnfile{})).NotTo(HaveOccurred()) - Expect(fsWriteYAML(fs, "Kilnfile.lock", cargo.KilnfileLock{})).NotTo(HaveOccurred()) - - var err error - expectedReleaseSHA, err = testHelpers.WriteReleaseTarball("banana-release.tgz", "banana", "1.2.3", fs) - Expect(err).NotTo(HaveOccurred()) }) When("it receives a correct tarball path", func() { It("uploads the tarball to the release source", func() { err := uploadRelease.Execute([]string{ - "--local-path", "banana-release.tgz", + "--kilnfile", filepath.Join(tileDirectory, "Kilnfile"), + "--local-path", filepath.Join("testdata", "bpm-1.1.21.tgz"), "--upload-target-id", "orange-bucket", }) @@ -66,16 +60,14 @@ var _ = Describe("UploadRelease", func() { Expect(releaseUploader.UploadReleaseCallCount()).To(Equal(1)) - spec, file := releaseUploader.UploadReleaseArgsForCall(0) - Expect(spec.Name).To(Equal("banana")) - Expect(spec.Version).To(Equal("1.2.3")) + spec, f := releaseUploader.UploadReleaseArgsForCall(0) + Expect(spec.Name).To(Equal("bpm")) + Expect(spec.Version).To(Equal("1.1.21")) - hash := sha1.New() - _, err = io.Copy(hash, file) - Expect(err).NotTo(HaveOccurred()) + file, ok := f.(*os.File) + Expect(ok).To(BeTrue()) - releaseSHA := fmt.Sprintf("%x", hash.Sum(nil)) - Expect(releaseSHA).To(Equal(expectedReleaseSHA)) + Expect(file.Name()).To(Equal(filepath.Join("testdata", "bpm-1.1.21.tgz"))) }) When("the release already exists on the release source", func() { @@ -89,7 +81,8 @@ var _ = Describe("UploadRelease", func() { It("errors and does not upload", func() { err := uploadRelease.Execute([]string{ - "--local-path", "banana-release.tgz", + "--kilnfile", filepath.Join(tileDirectory, "Kilnfile"), + "--local-path", filepath.Join("testdata", "bpm-1.1.21.tgz"), "--upload-target-id", "orange-bucket", }) Expect(err).To(MatchError(ContainSubstring("already exists"))) @@ -97,94 +90,25 @@ var _ = Describe("UploadRelease", func() { Expect(releaseUploader.GetMatchedReleaseCallCount()).To(Equal(1)) requirement := releaseUploader.GetMatchedReleaseArgsForCall(0) - Expect(requirement).To(Equal(cargo.BOSHReleaseTarballSpecification{Name: "banana", Version: "1.2.3"})) - - Expect(releaseUploader.UploadReleaseCallCount()).To(Equal(0)) - }) - }) - - When("the release tarball is compiled", func() { - BeforeEach(func() { - _, err := testHelpers.WriteTarballWithFile("banana-release.tgz", "release.MF", ` -name: banana -version: 1.2.3 -compiled_packages: -- stemcell: plan9/42 -`, fs) - Expect(err).NotTo(HaveOccurred()) - }) - - It("errors and does not upload", func() { - err := uploadRelease.Execute([]string{ - "--local-path", "banana-release.tgz", - "--upload-target-id", "orange-bucket", - }) - Expect(err).To(MatchError(ContainSubstring("compiled release"))) - Expect(releaseUploader.UploadReleaseCallCount()).To(Equal(0)) - }) - }) - - When("the release version is not a finalized release", func() { - var err error - devReleases := []struct { - tarballName string - version string - }{ - {"banana-rc.tgz", "1.2.3-rc.100"}, - {"banana-build.tgz", "1.2.3-build.56"}, - {"banana-dev.tgz", "1.2.3-dev.14784"}, - {"banana-alpha.tgz", "1.2.3-alpha.1"}, - } - - BeforeEach(func() { - for _, rel := range devReleases { - _, err = testHelpers.WriteReleaseTarball(rel.tarballName, "banana", rel.version, fs) - Expect(err).NotTo(HaveOccurred()) - } - }) - - It("errors with a descriptive message", func() { - for _, rel := range devReleases { - err := uploadRelease.Execute([]string{ - "--local-path", rel.tarballName, - "--upload-target-id", "orange-bucket", - }) - Expect(err).To(MatchError(ContainSubstring("only finalized releases are allowed"))) - } - - Expect(releaseUploader.UploadReleaseCallCount()).To(Equal(0)) - }) - }) - - When("the release version is malformed", func() { - BeforeEach(func() { - _, err := testHelpers.WriteReleaseTarball("banana-malformed.tgz", "banana", "v1_2_garbage", fs) - Expect(err).NotTo(HaveOccurred()) - }) + Expect(requirement).To(Equal(cargo.BOSHReleaseTarballSpecification{Name: "bpm", Version: "1.1.21"})) - It("errors with a descriptive message", func() { - err := uploadRelease.Execute([]string{ - "--local-path", "banana-malformed.tgz", - "--upload-target-id", "orange-bucket", - }) - Expect(err).To(MatchError(ContainSubstring("release version is not valid semver"))) Expect(releaseUploader.UploadReleaseCallCount()).To(Equal(0)) }) }) }) When("the release tarball is invalid", func() { + var invalidFilePath string BeforeEach(func() { - f, err := fs.Create("invalid-release.tgz") - _, _ = f.Write([]byte("invalid")) - defer closeAndIgnoreError(f) - + invalidFilePath = filepath.Join(tileDirectory, "invalid-release.tgz") + err := os.WriteFile(invalidFilePath, []byte("invalid"), 0o600) Expect(err).NotTo(HaveOccurred()) }) It("errors", func() { err := uploadRelease.Execute([]string{ - "--local-path", "invalid-release.tgz", + "--kilnfile", filepath.Join(tileDirectory, "Kilnfile"), + "--local-path", invalidFilePath, "--upload-target-id", "orange-bucket", }) Expect(err).To(MatchError(ContainSubstring("error reading the release manifest"))) @@ -199,6 +123,7 @@ compiled_packages: It("returns the error", func() { err := uploadRelease.Execute([]string{ + "--kilnfile", filepath.Join(tileDirectory, "Kilnfile"), "--local-path", "banana-release.tgz", "--upload-target-id", "orange-bucket", }) @@ -215,7 +140,8 @@ compiled_packages: It("returns an error", func() { err := uploadRelease.Execute([]string{ - "--local-path", "banana-release.tgz", + "--kilnfile", filepath.Join(tileDirectory, "Kilnfile"), + "--local-path", filepath.Join("testdata", "bpm-1.1.21.tgz"), "--upload-target-id", "orange-bucket", }) Expect(err).To(HaveOccurred()) @@ -224,6 +150,7 @@ compiled_packages: It("doesn't upload anything", func() { _ = uploadRelease.Execute([]string{ + "--kilnfile", filepath.Join(tileDirectory, "Kilnfile"), "--local-path", "banana-release.tgz", "--upload-target-id", "orange-bucket", }) @@ -238,7 +165,8 @@ compiled_packages: It("returns an error", func() { err := uploadRelease.Execute([]string{ - "--local-path", "banana-release.tgz", + "--kilnfile", filepath.Join(tileDirectory, "Kilnfile"), + "--local-path", filepath.Join("testdata", "bpm-1.1.21.tgz"), "--upload-target-id", "orange-bucket", }) Expect(err).To(HaveOccurred()) diff --git a/internal/component/local_release_directory.go b/internal/component/local_release_directory.go index 3f8e33863..85e8a4b26 100644 --- a/internal/component/local_release_directory.go +++ b/internal/component/local_release_directory.go @@ -7,46 +7,52 @@ import ( "io" "log" "os" + "path/filepath" "sort" "github.com/go-git/go-billy/v5" - "github.com/go-git/go-billy/v5/osfs" - - "github.com/pivotal-cf/kiln/internal/baking" - "github.com/pivotal-cf/kiln/internal/builder" "github.com/pivotal-cf/kiln/pkg/cargo" ) type LocalReleaseDirectory struct { - logger *log.Logger - releasesService baking.ReleasesService + logger *log.Logger } -func NewLocalReleaseDirectory(logger *log.Logger, releasesService baking.ReleasesService) LocalReleaseDirectory { +func NewLocalReleaseDirectory(logger *log.Logger) LocalReleaseDirectory { return LocalReleaseDirectory{ - logger: logger, - releasesService: releasesService, + logger: logger, } } func (l LocalReleaseDirectory) GetLocalReleases(releasesDir string) ([]Local, error) { - var outputReleases []Local - - rawReleases, err := l.releasesService.ReleasesInDirectory(releasesDir) + if _, err := os.Stat(releasesDir); err != nil { + return nil, fmt.Errorf("could not find releases in %s: %w", releasesDir, err) + } + releaseFilePaths, err := filepath.Glob(filepath.Join(releasesDir, "*.tgz")) if err != nil { return nil, err } - for _, rel := range rawReleases { - rm := rel.Metadata.(builder.ReleaseManifest) - lock := cargo.BOSHReleaseTarballLock{Name: rm.Name, Version: rm.Version, StemcellOS: rm.StemcellOS, StemcellVersion: rm.StemcellVersion} - - lock.SHA1, err = CalculateSum(rel.File, osfs.New("")) + var outputReleases []Local + for _, releaseFilepath := range releaseFilePaths { + releaseTarball, err := cargo.OpenBOSHReleaseTarball(releaseFilepath) if err != nil { - return nil, fmt.Errorf("couldn't calculate SHA1 sum of %q: %w", rel.File, err) // untested + return nil, err + } + + lock := cargo.BOSHReleaseTarballLock{ + Name: releaseTarball.Manifest.Name, + Version: releaseTarball.Manifest.Version, + SHA1: releaseTarball.SHA1, + } + + stemcellOS, stemcellVersion, ok := releaseTarball.Manifest.Stemcell() + if ok { + lock.StemcellOS = stemcellOS + lock.StemcellVersion = stemcellVersion } - outputReleases = append(outputReleases, Local{Lock: lock, LocalPath: rel.File}) + outputReleases = append(outputReleases, Local{Lock: lock, LocalPath: releaseFilepath}) } return outputReleases, nil } diff --git a/internal/component/local_release_directory_test.go b/internal/component/local_release_directory_test.go index 4ed796bf3..6d7444a19 100644 --- a/internal/component/local_release_directory_test.go +++ b/internal/component/local_release_directory_test.go @@ -6,14 +6,11 @@ import ( "os" "path/filepath" - "github.com/go-git/go-billy/v5/osfs" "github.com/onsi/gomega/gbytes" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/pivotal-cf/kiln/internal/baking" - "github.com/pivotal-cf/kiln/internal/builder" "github.com/pivotal-cf/kiln/internal/component" "github.com/pivotal-cf/kiln/pkg/cargo" ) @@ -39,10 +36,7 @@ var _ = Describe("LocalReleaseDirectory", func() { logBuf = gbytes.NewBuffer() fakeLogger = log.New(logBuf, "", 0) - releaseManifestReader := builder.NewReleaseManifestReader(osfs.New("")) - releasesService := baking.NewReleasesService(fakeLogger, releaseManifestReader) - - localReleaseDirectory = component.NewLocalReleaseDirectory(fakeLogger, releasesService) + localReleaseDirectory = component.NewLocalReleaseDirectory(fakeLogger) }) AfterEach(func() { diff --git a/internal/gh/uri_test.go b/internal/gh/uri_test.go index 6fa4e02c1..1ee5be28e 100644 --- a/internal/gh/uri_test.go +++ b/internal/gh/uri_test.go @@ -1,10 +1,11 @@ package gh_test import ( + "testing" + "github.com/pivotal-cf/kiln/internal/gh" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_RepositoryOwnerAndNameFromPath(t *testing.T) { diff --git a/internal/test/container.go b/internal/test/container.go index 2d4be70f5..82708d1fe 100644 --- a/internal/test/container.go +++ b/internal/test/container.go @@ -9,6 +9,15 @@ import ( "encoding/json" "errors" "fmt" + "io" + "log" + "net" + "os" + "os/signal" + "path" + "path/filepath" + "strings" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" @@ -20,14 +29,6 @@ import ( "github.com/moby/buildkit/session/sshforward/sshprovider" specV1 "github.com/opencontainers/image-spec/specs-go/v1" "golang.org/x/sync/errgroup" - "io" - "log" - "net" - "os" - "os/signal" - "path" - "path/filepath" - "strings" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" diff --git a/main.go b/main.go index d4397fbe0..81c7663e8 100644 --- a/main.go +++ b/main.go @@ -55,10 +55,10 @@ func main() { fs := osfs.New("") - releaseManifestReader := builder.NewReleaseManifestReader(fs) + releaseManifestReader := builder.NewReleaseManifestReader() releasesService := baking.NewReleasesService(errLogger, releaseManifestReader) pivnetService := new(pivnet.Service) - localReleaseDirectory := component.NewLocalReleaseDirectory(outLogger, releasesService) + localReleaseDirectory := component.NewLocalReleaseDirectory(outLogger) mrsProvider := commands.MultiReleaseSourceProvider(func(kilnfile cargo.Kilnfile, allowOnlyPublishable bool) component.MultiReleaseSource { repo := component.NewReleaseSourceRepo(kilnfile, outLogger) return repo.Filter(allowOnlyPublishable) @@ -75,7 +75,10 @@ func main() { commandSet := jhanda.CommandSet{} fetch := commands.NewFetch(outLogger, mrsProvider, localReleaseDirectory) commandSet["fetch"] = fetch - commandSet["bake"] = commands.NewBake(fs, releasesService, outLogger, errLogger, fetch) + + bakeCommand := commands.NewBake(fs, releasesService, outLogger, errLogger, fetch) + bakeCommand.KilnVersion = version + commandSet["bake"] = bakeCommand commandSet["test"] = commands.NewTileTest() commandSet["help"] = commands.NewHelp(os.Stdout, globalFlagsUsage, commandSet) diff --git a/pkg/cargo/bosh_release.go b/pkg/cargo/bosh_release.go index f42eedfb1..333b5e6ce 100644 --- a/pkg/cargo/bosh_release.go +++ b/pkg/cargo/bosh_release.go @@ -7,19 +7,17 @@ import ( "crypto/sha1" "encoding/hex" "fmt" - "hash" "io" "io/fs" "os" "path" "strings" - "github.com/pivotal-cf/kiln/pkg/tile" - "golang.org/x/exp/slices" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" "github.com/pivotal-cf/kiln/pkg/proofing" + "github.com/pivotal-cf/kiln/pkg/tile" ) func ReadBOSHReleaseFromFile(tilePath, releaseName, releaseVersion string, releaseTarball io.Writer) (proofing.Release, error) { @@ -121,35 +119,40 @@ type BOSHReleaseTarball struct { FilePath string } -func ReadBOSHReleaseManifestsFromTarballs(dir fs.FS, tarballPaths ...string) ([]BOSHReleaseTarball, error) { +func OpenBOSHReleaseManifestsFromTarballs(tarballPaths ...string) ([]BOSHReleaseTarball, error) { results := make([]BOSHReleaseTarball, 0, len(tarballPaths)) for _, tarballPath := range tarballPaths { - mf, err := openAndProcessFile(dir, tarballPath, ReadProductTemplatePartFromBOSHReleaseTarball) - if err != nil { - return nil, err - } - sha1Checksum, err := openAndProcessFile(dir, tarballPath, calculateChecksum(sha1.New())) + tb, err := OpenBOSHReleaseTarball(tarballPath) if err != nil { return nil, err } - - results = append(results, BOSHReleaseTarball{ - Manifest: mf, - SHA1: sha1Checksum, - FilePath: tarballPath, - }) + results = append(results, tb) } return slices.Clip(results), nil } -func openAndProcessFile[T any](dir fs.FS, fileName string, process func(io.Reader) (T, error)) (T, error) { - file, err := dir.Open(fileName) +func OpenBOSHReleaseTarball(tarballPath string) (BOSHReleaseTarball, error) { + file, err := os.Open(tarballPath) if err != nil { - var zero T - return zero, err + return BOSHReleaseTarball{}, err } defer closeAndIgnoreError(file) - return process(file) + return ReadBOSHReleaseTarball(tarballPath, file) +} + +func ReadBOSHReleaseTarball(tarballPath string, r io.Reader) (BOSHReleaseTarball, error) { + sum := sha1.New() + r = io.TeeReader(r, sum) + m, err := ReadProductTemplatePartFromBOSHReleaseTarball(r) + if err != nil { + return BOSHReleaseTarball{}, err + } + _, err = io.ReadAll(r) + return BOSHReleaseTarball{ + Manifest: m, + SHA1: hex.EncodeToString(sum.Sum(nil)), + FilePath: tarballPath, + }, err } func ReadProductTemplatePartFromBOSHReleaseTarball(r io.Reader) (BOSHReleaseManifest, error) { @@ -185,13 +188,3 @@ func ReadProductTemplatePartFromBOSHReleaseTarball(r io.Reader) (BOSHReleaseMani } return BOSHReleaseManifest{}, fmt.Errorf("failed to find release.MF in tarball") } - -func calculateChecksum(h hash.Hash) func(r io.Reader) (string, error) { - return func(r io.Reader) (string, error) { - _, err := io.Copy(h, r) - if err != nil { - return "", err - } - return hex.EncodeToString(h.Sum(nil)), nil - } -} diff --git a/pkg/cargo/bosh_release_test.go b/pkg/cargo/bosh_release_test.go index a88b44051..9e91ee64f 100644 --- a/pkg/cargo/bosh_release_test.go +++ b/pkg/cargo/bosh_release_test.go @@ -36,13 +36,13 @@ func TestReadReleaseFromFile(t *testing.T) { } func TestReadBOSHReleaseManifestsFromTarballs(t *testing.T) { - boshReleases, err := cargo.ReadBOSHReleaseManifestsFromTarballs(os.DirFS("testdata"), "bpm-1.1.21-ubuntu-xenial-621.463.tgz", "bpm-1.1.21.tgz") + boshReleases, err := cargo.OpenBOSHReleaseManifestsFromTarballs(filepath.Join("testdata", "bpm-1.1.21-ubuntu-xenial-621.463.tgz"), filepath.Join("testdata", "bpm-1.1.21.tgz")) require.NoError(t, err) require.Len(t, boshReleases, 2) assert.Equal(t, "be5b1710f33128f6c864eae1d97effddb94dd3ac", boshReleases[0].SHA1) assert.Equal(t, "519b78f2f3333a7b9c000bbef325e12a2f36996d", boshReleases[1].SHA1) - assert.Equal(t, "bpm-1.1.21-ubuntu-xenial-621.463.tgz", boshReleases[0].FilePath) - assert.Equal(t, "bpm-1.1.21.tgz", boshReleases[1].FilePath) + assert.Equal(t, filepath.Join("testdata", "bpm-1.1.21-ubuntu-xenial-621.463.tgz"), boshReleases[0].FilePath) + assert.Equal(t, filepath.Join("testdata", "bpm-1.1.21.tgz"), boshReleases[1].FilePath) } func TestReadProductTemplatePartFromBOSHReleaseTarball(t *testing.T) { diff --git a/pkg/cargo/kilnfile.go b/pkg/cargo/kilnfile.go index d6cd347df..841c595a4 100644 --- a/pkg/cargo/kilnfile.go +++ b/pkg/cargo/kilnfile.go @@ -3,9 +3,10 @@ package cargo import ( "errors" "fmt" - "gopkg.in/yaml.v3" "strings" + "gopkg.in/yaml.v3" + "github.com/Masterminds/semver/v3" boshdir "github.com/cloudfoundry/bosh-cli/director" "github.com/crhntr/bijection" diff --git a/pkg/proofing/release.go b/pkg/proofing/release.go index d3f5b462b..6e4e1ac17 100644 --- a/pkg/proofing/release.go +++ b/pkg/proofing/release.go @@ -10,7 +10,8 @@ type Release struct { Version string `yaml:"version"` File string `yaml:"file"` - SHA1 string `yaml:"sha1"` // NOTE: this only exists because of kiln + SHA1 string `yaml:"sha1,omitempty"` // NOTE: this only exists because of kiln + CommitHash string `yaml:"commit_hash,omitempty"` // read from release.MF yaml commit_sha field // TODO: validations: https://github.com/pivotal-cf/installation/blob/039a2ef3f751ef5915c425da8150a29af4b764dd/web/app/models/persistence/metadata/release.rb#L8-L15 }