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

Close #259: Customize gradle-wrapper.properties #262

Merged
merged 2 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ The buildpack optionally accepts the following bindings:
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `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: `gradle-wrapper`

| Secret | Description |
|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `gradle-wrapper.properties` | If present, the values of the properties file override the default ones found at <APPLICATION_ROOT>/gradle/wrapper/gradle-wrapper.properties which is [picked up by the gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html#customizing_wrapper). |


### Type: `dependency-mapping`

| Key | Value | Description |
Expand Down
32 changes: 32 additions & 0 deletions gradle/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
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 {
b.Logger.Debug("binding of type gradle successfully detected, configuring layer")
gradlePropertiesPath, ok := binding.SecretFilePath("gradle.properties")
if ok {
gradlePropertiesFile, err := os.Open(gradlePropertiesPath)
Expand All @@ -149,6 +150,37 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
result.Layers = append(result.Layers, PropertiesFile{
binding,
gradleHome,
"gradle.properties",
"gradle-properties",
b.Logger,
})
}
}

gradleWrapperHome := filepath.Join("gradle", "wrapper")
if binding, ok, err := bindings.ResolveOne(context.Platform.Bindings, bindings.OfType("gradle-wrapper")); err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to resolve binding\n%w", err)
} else if ok {
b.Logger.Debug("binding of type gradle-wrapper successfully detected, configuring layer")
gradleWrapperPropertiesPath, ok := binding.SecretFilePath("gradle-wrapper.properties")
if ok {
gradleWrapperPropertiesFile, err := os.Open(gradleWrapperPropertiesPath)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to open gradle-wrapper.properties\n%w", err)
}

hasher := sha256.New()
if _, err := io.Copy(hasher, gradleWrapperPropertiesFile); err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to hash gradle-wrapper.properties\n%w", err)
}
md["gradle-wrapper-properties-sha256"] = hex.EncodeToString(hasher.Sum(nil))

result.Layers = append(result.Layers, PropertiesFile{
binding,
gradleWrapperHome,
"gradle-wrapper.properties",
"gradle-wrapper-properties",
b.Logger,
})
}
}
Expand Down
80 changes: 67 additions & 13 deletions gradle/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package gradle_test

import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -48,13 +47,13 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
it.Before(func() {
var err error

ctx.Application.Path, err = ioutil.TempDir("", "build-application")
ctx.Application.Path, err = os.MkdirTemp("", "build-application")
Expect(err).NotTo(HaveOccurred())

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

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

gradlewFilepath = filepath.Join(ctx.Application.Path, "gradlew")
Expand All @@ -72,7 +71,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
})

it("does not contribute distribution if wrapper exists", func() {
Expect(ioutil.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
ctx.StackID = "test-stack-id"

result, err := gradleBuild.Build(ctx)
Expand All @@ -85,7 +84,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
})

it("makes sure that gradlew is executable", func() {
Expect(ioutil.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
ctx.StackID = "test-stack-id"

_, err := gradleBuild.Build(ctx)
Expand Down Expand Up @@ -182,7 +181,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
})

it("sets the settings path", func() {
Expect(ioutil.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())

result, err := gradleBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -203,7 +202,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
})

it("sets the settings path", func() {
Expect(ioutil.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())

result, err := gradleBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -223,7 +222,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
Expect(os.Unsetenv(("BP_GRADLE_BUILD_ARGUMENTS"))).To(Succeed())
})
it("sets some build arguments", func() {
Expect(ioutil.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())

result, err := gradleBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -245,7 +244,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
Expect(os.Unsetenv(("BP_GRADLE_ADDITIONAL_BUILD_ARGUMENTS"))).To(Succeed())
})
it("sets some build and additional build arguments", func() {
Expect(ioutil.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())

result, err := gradleBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -262,9 +261,9 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
it.Before(func() {
var err error
ctx.StackID = "test-stack-id"
ctx.Platform.Path, err = ioutil.TempDir("", "gradle-test-platform")
ctx.Platform.Path, err = os.MkdirTemp("", "gradle-test-platform")
Expect(err).NotTo(HaveOccurred())
Expect(ioutil.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
bindingPath = filepath.Join(ctx.Platform.Path, "bindings", "some-gradle")
ctx.Platform.Bindings = libcnb.Bindings{
{
Expand All @@ -277,7 +276,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
gradlePropertiesPath, ok := ctx.Platform.Bindings[0].SecretFilePath("gradle.properties")
Expect(os.MkdirAll(filepath.Dir(gradlePropertiesPath), 0777)).To(Succeed())
Expect(ok).To(BeTrue())
Expect(ioutil.WriteFile(
Expect(os.WriteFile(
gradlePropertiesPath,
[]byte("gradle-properties-content"),
0644,
Expand Down Expand Up @@ -309,6 +308,61 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
Expect(mdMap["gradle-properties-sha256"]).To(Equal(expected))
})
})

context("gradle wrapper properties binding exists", func() {
var bindingPath string

it.Before(func() {
var err error
ctx.StackID = "test-stack-id"
ctx.Platform.Path, err = os.MkdirTemp("", "gradle-test-platform")
Expect(err).NotTo(HaveOccurred())
Expect(os.WriteFile(gradlewFilepath, []byte{}, 0644)).To(Succeed())
bindingPath = filepath.Join(ctx.Platform.Path, "bindings", "some-gradle")
ctx.Platform.Bindings = libcnb.Bindings{
{
Name: "some-gradle",
Type: "gradle-wrapper",
Secret: map[string]string{"gradle-wrapper.properties": "gradle-wrapper-properties-content"},
Path: bindingPath,
},
}
gradlePropertiesPath, ok := ctx.Platform.Bindings[0].SecretFilePath("gradle-wrapper.properties")
Expect(os.MkdirAll(filepath.Dir(gradlePropertiesPath), 0777)).To(Succeed())
Expect(ok).To(BeTrue())
Expect(os.WriteFile(
gradlePropertiesPath,
[]byte("gradle-wrapper-properties-content"),
0644,
)).To(Succeed())
})

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

it("contributes bound gradle-wrapper.properties", func() {
result, err := gradleBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())

Expect(result.Layers).To(HaveLen(3))
Expect(result.Layers[1].Name()).To(Equal("gradle-wrapper-properties"))
Expect(result.Layers[1])
})

it("adds the hash of gradle-wrapper.properties to the layer metadata", func() {
result, err := gradleBuild.Build(ctx)
Expect(err).NotTo(HaveOccurred())

md := result.Layers[2].(libbs.Application).LayerContributor.ExpectedMetadata
mdMap, ok := md.(map[string]interface{})
Expect(ok).To(BeTrue())
// expected: sha256 of the string "gradle-wrapper-properties-content"
expected := "8d98502ceb9504c887b12cfba9427c5338d133a2f10613cb0137695ca09c7ddc"
Expect(mdMap["gradle-wrapper-properties-sha256"]).To(Equal(expected))
})
})

}

type FakeApplicationFactory struct{}
Expand Down
50 changes: 37 additions & 13 deletions gradle/gradle_properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,65 @@ package gradle

import (
"fmt"
"github.com/magiconair/properties"
"github.com/paketo-buildpacks/libpak/bard"
"os"
"path/filepath"

"github.com/buildpacks/libcnb"
)

type PropertiesFile struct {
Binding libcnb.Binding
GradleHome string
Binding libcnb.Binding
GradlePropertiesHome string
GradlePropertiesFileName string
GradlePropertiesName string
Logger bard.Logger
}

func (p PropertiesFile) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
path, ok := p.Binding.SecretFilePath("gradle.properties")
path, ok := p.Binding.SecretFilePath(p.GradlePropertiesFileName)
if !ok {
return libcnb.Layer{}, nil
}

gradlePropertiesPath := filepath.Join(p.GradleHome, "gradle.properties")
if err := os.Symlink(path, gradlePropertiesPath); os.IsExist(err) {
err = os.Remove(gradlePropertiesPath)
originalPropertiesFilePath := filepath.Join(p.GradlePropertiesHome, p.GradlePropertiesFileName)
if p.GradlePropertiesName == "gradle-properties" {
p.Logger.Debug("symlinking gradle-properties bound file")
gradlePropertiesPath := originalPropertiesFilePath
if err := os.Symlink(path, gradlePropertiesPath); os.IsExist(err) {
err = os.Remove(gradlePropertiesPath)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to remove old symlink for %s\n%w", p.GradlePropertiesFileName, err)
}

err = os.Symlink(path, gradlePropertiesPath)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to create symlink for %s on retry\n%w", p.GradlePropertiesFileName, err)
}
} else if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to symlink bound %s\n%w", p.GradlePropertiesFileName, err)
}
} else if p.GradlePropertiesName == "gradle-wrapper-properties" {
file, err := os.ReadFile(path)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to remove old symlink for gradle.properties\n%w", err)
return libcnb.Layer{}, fmt.Errorf("unable to read bound gradle-wrapper.properties file at %s\n%w", path, err)
}

err = os.Symlink(path, gradlePropertiesPath)
p.Logger.Debugf("applying these bound gradle-wrapper-properties to default one: \n%s\n", string(file))
mergedProperties := properties.MustLoadFiles([]string{originalPropertiesFilePath, path}, properties.UTF8, true)
propertiesFile, err := os.Create(originalPropertiesFilePath)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to create/update original gradle-wrapper.properties file at %s\n%w", originalPropertiesFilePath, err)
}
_, err = mergedProperties.Write(propertiesFile, properties.UTF8)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to create symlink for gradle.properties on retry\n%w", err)
return libcnb.Layer{}, fmt.Errorf("unable to merge gradle-wrapper.properties files.\n%w", err)
}
} else if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to symlink bound gradle.properties\n%w", err)
}

return layer, nil
}

func (p PropertiesFile) Name() string {
return "gradle-properties"
return p.GradlePropertiesName
}
Loading