Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow users to provide a custom gradle.properties file #60

Merged
merged 7 commits into from
Apr 28, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ The buildpack will do the following:
## Bindings
The buildpack optionally accepts the following bindings:

### Type: `gradle`
|Secret | Description
|-----|--------------
|`gradle.properties` | If present, the contents of the file are copied to `$GRADLE_USER_HOME/gradle.properties` which is [picked up by gradle and merged](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties) when it runs.

### Type: `dependency-mapping`
|Key | Value | Description
|----------------------|---------|------------
Expand Down
1 change: 1 addition & 0 deletions cmd/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func main() {
gradle.Build{
ApplicationFactory: libbs.NewApplicationFactory(),
Logger: bard.NewLogger(os.Stdout),
HomeDirectoryResolver: gradle.OSHomeDirectoryResolver{},
},
)
}
74 changes: 70 additions & 4 deletions gradle/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
package gradle

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/paketo-buildpacks/libpak/bindings"
"io"
"io/ioutil"
"os"
"os/user"
"path/filepath"
Expand All @@ -31,13 +36,29 @@ import (
type Build struct {
Logger bard.Logger
ApplicationFactory ApplicationFactory
HomeDirectoryResolver HomeDirectoryResolver
}

type ApplicationFactory interface {
NewApplication(additionalMetadata map[string]interface{}, arguments []string, artifactResolver libbs.ArtifactResolver,
cache libbs.Cache, command string, bom *libcnb.BOM, applicationPath string) (libbs.Application, error)
}

type HomeDirectoryResolver interface {
Location() (string, error)
}

type OSHomeDirectoryResolver struct {}

func (p OSHomeDirectoryResolver) Location() (string, error) {
u, err := user.Current()
if err != nil {
return "", fmt.Errorf("unable to determine user home directory\n%w", err)
}

return u.HomeDir, nil
}

func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
b.Logger.Title(context.Buildpack)
result := libcnb.NewBuildResult()
Expand Down Expand Up @@ -79,12 +100,13 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
}
}

u, err := user.Current()
homeDir, err := b.HomeDirectoryResolver.Location()
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to determine user home directory\n%w", err)
return libcnb.BuildResult{}, fmt.Errorf("home directory resolution failure\n%w", err)
}
gradleHome := filepath.Join(homeDir, ".gradle")

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

Expand All @@ -93,6 +115,16 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
return libcnb.BuildResult{}, fmt.Errorf("unable to resolve build arguments\n%w", err)
}

md := map[string]interface{}{}
if binding, ok, err := bindings.ResolveOne(context.Platform.Bindings, bindings.OfType("gradle")); err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to resolve binding\n%w", err)
} else if ok {
err = handleGradleSettings(binding, gradleHome, md)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to process maven settings from binding\n%w", err)
}
}

art := libbs.ArtifactResolver{
ArtifactConfigurationKey: "BP_GRADLE_BUILT_ARTIFACT",
ConfigurationResolver: cr,
Expand All @@ -101,7 +133,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
}

a, err := b.ApplicationFactory.NewApplication(
map[string]interface{}{},
md,
args,
art,
c,
Expand All @@ -117,3 +149,37 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {

return result, nil
}

func handleGradleSettings(binding libcnb.Binding, gradleHome string, md map[string]interface{}) error {
path, ok := binding.SecretFilePath("gradle.properties")
if !ok {
return nil
}

gradleProperties, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("error reading gradle.properties\n%w", err)
}

err = os.MkdirAll(gradleHome, 0755)
if err != nil {
return fmt.Errorf("cannot make gradle home\n%w", err)
}

err = ioutil.WriteFile(filepath.Join(gradleHome, "gradle.properties"), gradleProperties, 0644)
if err != nil {
return fmt.Errorf("error writing gradle.properties\n%w", err)
}

hasher := sha256.New()
propertiesFile, err := os.Open(path)
if err != nil {
return fmt.Errorf("unable to open gradle.properties\n%w", err)
}
if _, err := io.Copy(hasher, propertiesFile); err != nil {
return fmt.Errorf("error hashing gradle.properties\n%w", err)
}

md["gradle-properties-sha256"] = hex.EncodeToString(hasher.Sum(nil))
return nil
}
96 changes: 92 additions & 4 deletions gradle/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package gradle_test

import (
"github.com/paketo-buildpacks/libpak"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -37,6 +38,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {

ctx libcnb.BuildContext
gradleBuild gradle.Build
homeDir string
)

it.Before(func() {
Expand All @@ -45,17 +47,31 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
ctx.Application.Path, err = ioutil.TempDir("", "build-application")
Expect(err).NotTo(HaveOccurred())

ctx.Buildpack.Metadata = map[string]interface{}{
"configurations": []map[string]interface{}{
{"name": "BP_GRADLE_BUILD_ARGUMENTS", "default": "test-argument"},
},
}

ctx.Layers.Path, err = ioutil.TempDir("", "build-layers")
Expect(err).NotTo(HaveOccurred())

homeDir, err = ioutil.TempDir("", "home-dir")
Expect(err).NotTo(HaveOccurred())

gradleBuild = gradle.Build{
ApplicationFactory: &FakeApplicationFactory{},
HomeDirectoryResolver: FakeHomeDirectoryResolver{path: homeDir},
}
})

it.After(func() {
Expect(os.RemoveAll(ctx.Application.Path)).To(Succeed())
Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed())

homeDir,err := gradleBuild.HomeDirectoryResolver.Location()
Expect(err).ToNot(HaveOccurred())
Expect(os.RemoveAll(homeDir)).To(Succeed())
})

it("does not contribute distribution if wrapper exists", func() {
Expand Down Expand Up @@ -101,18 +117,90 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
Expect(result.BOM.Entries[0].Build).To(BeTrue())
Expect(result.BOM.Entries[0].Launch).To(BeFalse())
})


context("gradle properties bindings exists", func() {
var result libcnb.BuildResult

it.Before(func() {
var err error
ctx.StackID = "test-stack-id"
ctx.Platform.Path, err = ioutil.TempDir("", "gradle-test-platform")
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "gradlew"), []byte{}, 0644)).To(Succeed())
ctx.Platform.Bindings = libcnb.Bindings{
{
Name: "some-gradle",
Type: "gradle",
Secret: map[string]string{"gradle.properties": "gradle-settings-content"},
Path: filepath.Join(ctx.Platform.Path, "bindings", "some-gradle"), // TODO: is this what get's hashed?
},
}
gradleSettingsPath, ok := ctx.Platform.Bindings[0].SecretFilePath("gradle.properties")
Expect(os.MkdirAll(filepath.Dir(gradleSettingsPath), 0777)).To(Succeed())
Expect(ok).To(BeTrue())
Expect(ioutil.WriteFile(
gradleSettingsPath,
[]byte("gradle-settings-content"),
0644,
)).To(Succeed())

result, err = gradleBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())
Expect(result.Layers).To(HaveLen(2))
})

it.After(func() {
Expect(os.RemoveAll(ctx.Platform.Path)).To(Succeed())
})

it("provides gradle.properties under $GRADLE_USER_HOME", func() {
gradlePropertiesPath := filepath.Join(homeDir, ".gradle", "gradle.properties")
Expect(gradlePropertiesPath).To(BeARegularFile())

data, err := ioutil.ReadFile(gradlePropertiesPath)
Expect(err).ToNot(HaveOccurred())
Expect(string(data)).To(Equal("gradle-settings-content"))
})

it("adds the hash of gradle.properties to the layer metadata", func() {
md := result.Layers[1].(libbs.Application).LayerContributor.ExpectedMetadata
mdMap, ok := md.(map[string]interface{})
Expect(ok).To(BeTrue())
// expected: sha256 of the string "gradle-settings-content"
expected := "e6fdb059bdd9e59cec36afd5fb39c1e5b3c83694253b61c359701b4097520da4"
Expect(mdMap["gradle-properties-sha256"]).To(Equal(expected))
})
})
}

type FakeApplicationFactory struct{}

func (f *FakeApplicationFactory) NewApplication(
_ map[string]interface{},
_ []string,
additionalMetdata map[string]interface{},
argugments []string,
_ libbs.ArtifactResolver,
_ libbs.Cache,
cache libbs.Cache,
command string,
_ *libcnb.BOM,
_ string,
) (libbs.Application, error) {
return libbs.Application{Command: command}, nil
contributor := libpak.NewLayerContributor(
"Compiled Application",
additionalMetdata,
libcnb.LayerTypes{Cache: true},
)
return libbs.Application{
LayerContributor: contributor,
Arguments: argugments,
Command: command,
Cache: cache,
}, nil
}

type FakeHomeDirectoryResolver struct{
path string
}

func (f FakeHomeDirectoryResolver) Location() (string, error) {
return f.path, nil
}