Skip to content

Commit

Permalink
Factor install out of Build
Browse files Browse the repository at this point in the history
Provide install functionality that can be re-used by Build.
  • Loading branch information
AidanDelaney committed Aug 12, 2022
1 parent 040ecfa commit 3cee658
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 48 deletions.
6 changes: 6 additions & 0 deletions buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ api = "0.7"
default = "false"
description = "use maven daemon"
name = "BP_MAVEN_DAEMON_ENABLED"

[[metadata.configurations]]
build = true
default = "false"
description = "distribute maven binary"
name = "BP_MAVEN_COMMAND"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:apache:maven:3.8.6:*:*:*:*:*:*:*"]
Expand Down
131 changes: 83 additions & 48 deletions maven/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ const (
RunBuild = "RunBuild"
)

var (
artifacts = map[string]string{
"mvn": "maven",
"mvnd": "mvnd",
}
)

type Build struct {
Logger bard.Logger
ApplicationFactory ApplicationFactory
Expand All @@ -53,26 +60,39 @@ type ApplicationFactory interface {
cache libbs.Cache, command string, bom *libcnb.BOM, applicationPath string, bomScanner sbom.SBOMScanner) (libbs.Application, error)
}

func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
b.Logger.Title(context.Buildpack)
result := libcnb.NewBuildResult()

cr, err := libpak.NewConfigurationResolver(context.Buildpack, &b.Logger)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to create configuration resolver\n%w", err)
}

func install(b Build, context libcnb.BuildContext, artifact string) (string, libcnb.LayerContributor, libcnb.BOMEntry, error) {
dr, err := libpak.NewDependencyResolver(context)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency resolver\n%w", err)
return "", nil, libcnb.BOMEntry{}, fmt.Errorf("unable to create dependency resolver\n%w", err)
}

dc, err := libpak.NewDependencyCache(context)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency cache\n%w", err)
return "", nil, libcnb.BOMEntry{}, fmt.Errorf("unable to create dependency cache\n%w", err)
}
dc.Logger = b.Logger

dep, err := dr.Resolve(artifact, "")
if err != nil {
return "", nil, libcnb.BOMEntry{}, fmt.Errorf("unable to find dependency\n%w", err)
}

if artifact == "maven" {
dist, be := NewDistribution(dep, dc)
dist.Logger = b.Logger
command := filepath.Join(context.Layers.Path, dist.Name(), "bin", "mvn")
return command, dist, be, nil
}
dist, be := NewDistribution(dep, dc)
dist.Logger = b.Logger
command := filepath.Join(context.Layers.Path, dist.Name(), "bin", "mvnd")
return command, dist, be, nil
}

func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
b.Logger.Title(context.Buildpack)
result := libcnb.NewBuildResult()

pr := libpak.PlanEntryResolver{
Plan: context.Plan,
}
Expand All @@ -84,45 +104,24 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
}
}

bpMavenCommand, _ := cr.Resolve(BpMavenCommand)
command := ""
if cr.ResolveBool("BP_MAVEN_DAEMON_ENABLED") || bpMavenCommand == "mvnd" {
dep, err := dr.Resolve("mvnd", "")
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err)
}

dist, be := NewMvndDistribution(dep, dc)
dist.Logger = b.Logger
result.Layers = append(result.Layers, dist)
result.BOM.Entries = append(result.BOM.Entries, be)

command = filepath.Join(context.Layers.Path, dist.Name(), "bin", "mvnd")
} else {
command = filepath.Join(context.Application.Path, "mvnw")
if _, err := os.Stat(command); os.IsNotExist(err) || bpMavenCommand == "mvn" {
dep, err := dr.Resolve("maven", "")
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err)
}

dist, be := NewDistribution(dep, dc)
dist.Logger = b.Logger
result.Layers = append(result.Layers, dist)
result.BOM.Entries = append(result.BOM.Entries, be)
cr, err := libpak.NewConfigurationResolver(context.Buildpack, &b.Logger)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to create configuration resolver\n%w", err)
}

command = filepath.Join(context.Layers.Path, dist.Name(), "bin", "mvn")
} else if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to stat %s\n%w", command, err)
} else {
if err := os.Chmod(command, 0755); err != nil {
b.Logger.Bodyf("WARNING: unable to chmod %s:\n%s", command, err)
}
bpMavenCommand, _ := cr.Resolve(BpMavenCommand)
// no install requested and no build requested
if bpMavenCommand == "" && runBuild == false {
return libcnb.BuildResult{}, nil
}

if err = b.CleanMvnWrapper(command); err != nil {
b.Logger.Bodyf("WARNING: unable to clean mvnw file: %s\n%s", command, err)
}
if bpMavenCommand == "mvn" || bpMavenCommand == "mvnd" {
cmd, layer, bomEntry, err := install(b, context, artifacts[bpMavenCommand])
if cmd == "" {
return libcnb.BuildResult{}, fmt.Errorf("unable to install dependency\n%w", err)
}
result.Layers = append(result.Layers, layer)
result.BOM.Entries = append(result.BOM.Entries, bomEntry)
}

u, err := user.Current()
Expand All @@ -132,9 +131,42 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {

c := libbs.Cache{Path: filepath.Join(u.HomeDir, ".m2")}
c.Logger = b.Logger
result.Layers = append(result.Layers, c)

if runBuild {
command := ""
if cr.ResolveBool("BP_MAVEN_DAEMON_ENABLED") && bpMavenCommand != "mvnd" {
cmd, layer, bomEntry, err := install(b, context, "mvnd")
if err != nil {
return libcnb.BuildResult{}, err
}
result.Layers = append(result.Layers, layer)
result.BOM.Entries = append(result.BOM.Entries, bomEntry)
command = cmd
} else {
command = filepath.Join(context.Application.Path, "mvnw")
if _, err := os.Stat(command); os.IsNotExist(err) && bpMavenCommand != "mvn" {
cmd, layer, bomEntry, err := install(b, context, "maven")
if err != nil {
return libcnb.BuildResult{}, err
}
result.Layers = append(result.Layers, layer)
result.BOM.Entries = append(result.BOM.Entries, bomEntry)
command = cmd
} else if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to stat %s\n%w", command, err)
} else {
if err := os.Chmod(command, 0755); err != nil {
b.Logger.Bodyf("WARNING: unable to chmod %s:\n%s", command, err)
}

if err = b.CleanMvnWrapper(command); err != nil {
b.Logger.Bodyf("WARNING: unable to clean mvnw file: %s\n%s", command, err)
}
}
}

result.Layers = append(result.Layers, c)

args, err := libbs.ResolveArguments("BP_MAVEN_BUILD_ARGUMENTS", cr)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to resolve build arguments\n%w", err)
Expand Down Expand Up @@ -185,7 +217,10 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {

a.Logger = b.Logger
result.Layers = append(result.Layers, a)
} else {
result.Layers = append(result.Layers, c)
}

return result, nil
}

Expand Down
95 changes: 95 additions & 0 deletions maven/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,12 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
},
}
ctx.Plan.Entries = append(ctx.Plan.Entries, entry)
os.Setenv("BP_MAVEN_COMMAND", "mvn")
})

it.After(func() {
os.Unsetenv("BP_MAVEN_COMMAND")
_, ctx.Plan.Entries = ctx.Plan.Entries[len(ctx.Plan.Entries)-1], ctx.Plan.Entries[:len(ctx.Plan.Entries)-1]
})

it("contributes distribution for API 0.7+", func() {
Expand Down Expand Up @@ -509,6 +515,95 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
Expect(result.BOM.Entries[0].Launch).To(BeFalse())
})
})

context("distribute binary build with another", func() {
it.Before(func() {
entry := libcnb.BuildpackPlanEntry{
Name: maven.PlanEntryMaven,
Metadata: map[string]interface{}{
maven.RunBuild: true,
},
}
ctx.Plan.Entries = append(ctx.Plan.Entries, entry)
os.Setenv("BP_MAVEN_COMMAND", "mvn")
os.Setenv("BP_MAVEN_DAEMON_ENABLED", "1")
})

it.After(func() {
os.Unsetenv("BP_MAVEN_COMMAND")
os.Unsetenv("BP_MAVEN_DAEMON_ENABLED")
_, ctx.Plan.Entries = ctx.Plan.Entries[len(ctx.Plan.Entries)-1], ctx.Plan.Entries[:len(ctx.Plan.Entries)-1]
})

it("contributes distribution for API 0.7+", func() {
ctx.Buildpack.Metadata["dependencies"] = []map[string]interface{}{
{
"id": "maven",
"version": "1.1.1",
"stacks": []interface{}{"test-stack-id"},
"cpes": []string{"cpe:2.3:a:apache:maven:3.8.3:*:*:*:*:*:*:*"},
"purl": "pkg:generic/[email protected]",
},
{
"id": "mvnd",
"version": "1.1.1",
"stacks": []interface{}{"test-stack-id"},
},
}
ctx.StackID = "test-stack-id"

result, err := mavenBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())

Expect(result.Layers).To(HaveLen(4))
Expect(result.Layers[0].Name()).To(Equal("maven"))
Expect(result.Layers[1].Name()).To(Equal("mvnd"))
Expect(result.Layers[2].Name()).To(Equal("cache"))
Expect(result.Layers[3].Name()).To(Equal("application"))

Expect(result.BOM.Entries).To(HaveLen(2))
Expect(result.BOM.Entries[0].Name).To(Equal("maven"))
Expect(result.BOM.Entries[0].Build).To(BeTrue())
Expect(result.BOM.Entries[0].Launch).To(BeFalse())
Expect(result.BOM.Entries[1].Name).To(Equal("mvnd"))
Expect(result.BOM.Entries[1].Build).To(BeTrue())
Expect(result.BOM.Entries[1].Launch).To(BeFalse())
})

it("contributes distribution for API <=0.6", func() {
ctx.Buildpack.Metadata["dependencies"] = []map[string]interface{}{
{
"id": "maven",
"version": "1.1.1",
"stacks": []interface{}{"test-stack-id"},
},
{
"id": "mvnd",
"version": "1.1.1",
"stacks": []interface{}{"test-stack-id"},
},
}
ctx.StackID = "test-stack-id"
ctx.Buildpack.API = "0.6"

result, err := mavenBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())

Expect(result.Layers).To(HaveLen(4))
Expect(result.Layers[0].Name()).To(Equal("maven"))
Expect(result.Layers[1].Name()).To(Equal("mvnd"))
Expect(result.Layers[2].Name()).To(Equal("cache"))
Expect(result.Layers[3].Name()).To(Equal("application"))

Expect(result.BOM.Entries).To(HaveLen(2))
Expect(result.BOM.Entries[0].Name).To(Equal("maven"))
Expect(result.BOM.Entries[0].Build).To(BeTrue())
Expect(result.BOM.Entries[0].Launch).To(BeFalse())
Expect(result.BOM.Entries[1].Name).To(Equal("mvnd"))
Expect(result.BOM.Entries[1].Build).To(BeTrue())
Expect(result.BOM.Entries[1].Launch).To(BeFalse())
})
})
}

type FakeApplicationFactory struct{}
Expand Down

0 comments on commit 3cee658

Please sign in to comment.