Skip to content

Commit

Permalink
Merge pull request #58 from paketo-buildpacks/cmd-cleanup
Browse files Browse the repository at this point in the history
Moves carton package in from libpak
  • Loading branch information
dmikusa authored Oct 30, 2024
2 parents e7fae51 + 780a4cd commit b0ca61d
Show file tree
Hide file tree
Showing 32 changed files with 3,082 additions and 410 deletions.
71 changes: 71 additions & 0 deletions carton/build_image_dependency.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package carton

import (
"fmt"
"os"
"regexp"

"github.com/paketo-buildpacks/libpak/v2/log"
"github.com/paketo-buildpacks/libpak/v2/utils"
)

const (
ImageDependencyPattern = `(?m)(.*build-image[\s]+=[\s]+"[^"]+:)[^"]+(".*)`
ImageDependencySubstitution = "${1}%s${2}"
)

type BuildImageDependency struct {
BuilderPath string
Version string
}

func (i BuildImageDependency) Update(options ...Option) {
config := Config{
exitHandler: utils.NewExitHandler(),
}

for _, option := range options {
config = option(config)
}

logger := log.NewPaketoLogger(os.Stdout)
_, _ = fmt.Fprintf(logger.TitleWriter(), "\n%s\n", log.FormatIdentity("Build Image", i.Version))

c, err := os.ReadFile(i.BuilderPath)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to read %s\n%w", i.BuilderPath, err))
return
}

r := regexp.MustCompile(ImageDependencyPattern)

if !r.Match(c) {
config.exitHandler.Error(fmt.Errorf("unable to match '%s'", r.String()))
return
}

s := fmt.Sprintf(ImageDependencySubstitution, i.Version)
c = r.ReplaceAll(c, []byte(s))

// #nosec G306 - permissions need to be 644 on the builder
if err := os.WriteFile(i.BuilderPath, c, 0644); err != nil {
config.exitHandler.Error(fmt.Errorf("unable to write %s\n%w", i.BuilderPath, err))
return
}
}
74 changes: 74 additions & 0 deletions carton/build_image_dependency_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package carton_test

import (
"os"
"testing"

"github.com/buildpacks/libcnb/v2/mocks"
. "github.com/onsi/gomega"
"github.com/sclevine/spec"
"github.com/stretchr/testify/mock"

"github.com/paketo-buildpacks/libpak-tools/carton"
)

func testBuildImageDependency(t *testing.T, _ spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

exitHandler *mocks.ExitHandler
path string
)

it.Before(func() {
var err error

exitHandler = &mocks.ExitHandler{}
exitHandler.On("Error", mock.Anything)

f, err := os.CreateTemp("", "carton-image-dependency")
Expect(err).NotTo(HaveOccurred())

_, err = f.WriteString(`test-prologue
build-image = "image-name:test-version-1"
test-epilogue
`)
Expect(err).To(Succeed())
Expect(f.Close()).To(Succeed())
path = f.Name()
})

it.After(func() {
Expect(os.RemoveAll(path)).To(Succeed())
})

it("updates dependency", func() {
d := carton.BuildImageDependency{
BuilderPath: path,
Version: "test-version-2",
}

d.Update(carton.WithExitHandler(exitHandler))

Expect(os.ReadFile(path)).To(Equal([]byte(`test-prologue
build-image = "image-name:test-version-2"
test-epilogue
`)))
})
}
195 changes: 195 additions & 0 deletions carton/buildmodule_dependency.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package carton

import (
"bytes"
"fmt"
"os"
"regexp"

"github.com/BurntSushi/toml"

"github.com/paketo-buildpacks/libpak/v2/log"
"github.com/paketo-buildpacks/libpak/v2/utils"
)

const (
BuildModuleDependencyPattern = `(?m)([\s]*.*id[\s]+=[\s]+"%s"\n.*\n[\s]*version[\s]+=[\s]+")%s("\n[\s]*uri[\s]+=[\s]+").*("\n[\s]*sha256[\s]+=[\s]+").*(".*)`
BuildModuleDependencySubstitution = "${1}%s${2}%s${3}%s${4}"
)

type BuildModuleDependency struct {
BuildModulePath string
ID string
SHA256 string
URI string
Version string
VersionPattern string
CPE string
CPEPattern string
PURL string
PURLPattern string
}

func (b BuildModuleDependency) Update(options ...Option) {
config := Config{
exitHandler: utils.NewExitHandler(),
}

for _, option := range options {
config = option(config)
}

logger := log.NewPaketoLogger(os.Stdout)
_, _ = fmt.Fprintf(logger.TitleWriter(), "\n%s\n", log.FormatIdentity(b.ID, b.VersionPattern))
logger.Headerf("Version: %s", b.Version)
logger.Headerf("PURL: %s", b.PURL)
logger.Headerf("CPEs: %s", b.CPE)
logger.Headerf("URI: %s", b.URI)
logger.Headerf("SHA256: %s", b.SHA256)

versionExp, err := regexp.Compile(b.VersionPattern)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to compile version regex %s\n%w", b.VersionPattern, err))
return
}

cpeExp, err := regexp.Compile(b.CPEPattern)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to compile cpe regex %s\n%w", b.CPEPattern, err))
return
}

purlExp, err := regexp.Compile(b.PURLPattern)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to compile cpe regex %s\n%w", b.PURLPattern, err))
return
}

c, err := os.ReadFile(b.BuildModulePath)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to read %s\n%w", b.BuildModulePath, err))
return
}

// save any leading comments, this is to preserve license headers
// inline comments will be lost
comments := []byte{}
for i, line := range bytes.SplitAfter(c, []byte("\n")) {
if bytes.HasPrefix(line, []byte("#")) || (i > 0 && len(bytes.TrimSpace(line)) == 0) {
comments = append(comments, line...)
} else {
break // stop on first comment
}
}

md := make(map[string]interface{})
if err := toml.Unmarshal(c, &md); err != nil {
config.exitHandler.Error(fmt.Errorf("unable to decode md%s\n%w", b.BuildModulePath, err))
return
}

metadataUnwrapped, found := md["metadata"]
if !found {
config.exitHandler.Error(fmt.Errorf("unable to find metadata block"))
return
}

metadata, ok := metadataUnwrapped.(map[string]interface{})
if !ok {
config.exitHandler.Error(fmt.Errorf("unable to cast metadata"))
return
}

dependenciesUnwrapped, found := metadata["dependencies"]
if !found {
config.exitHandler.Error(fmt.Errorf("unable to find dependencies block"))
return
}

dependencies, ok := dependenciesUnwrapped.([]map[string]interface{})
if !ok {
config.exitHandler.Error(fmt.Errorf("unable to cast dependencies"))
return
}

for _, dep := range dependencies {
depIDUnwrapped, found := dep["id"]
if !found {
continue
}
depID, ok := depIDUnwrapped.(string)
if !ok {
continue
}

if depID == b.ID {
depVersionUnwrapped, found := dep["version"]
if !found {
continue
}

depVersion, ok := depVersionUnwrapped.(string)
if !ok {
continue
}
if versionExp.MatchString(depVersion) {
dep["version"] = b.Version
dep["uri"] = b.URI
dep["sha256"] = b.SHA256

purlUnwrapped, found := dep["purl"]
if found {
purl, ok := purlUnwrapped.(string)
if ok {
dep["purl"] = purlExp.ReplaceAllString(purl, b.PURL)
}
}

cpesUnwrapped, found := dep["cpes"]
if found {
cpes, ok := cpesUnwrapped.([]interface{})
if ok {
for i := 0; i < len(cpes); i++ {
cpe, ok := cpes[i].(string)
if !ok {
continue
}

cpes[i] = cpeExp.ReplaceAllString(cpe, b.CPE)
}
}
}
}
}
}

c, err = utils.Marshal(md)
if err != nil {
config.exitHandler.Error(fmt.Errorf("unable to encode md %s\n%w", b.BuildModulePath, err))
return
}

c = append(comments, c...)

// #nosec G306 - permissions need to be 644 on the build module
if err := os.WriteFile(b.BuildModulePath, c, 0644); err != nil {
config.exitHandler.Error(fmt.Errorf("unable to write %s\n%w", b.BuildModulePath, err))
return
}
}
Loading

0 comments on commit b0ca61d

Please sign in to comment.