From bdde6ddb855d6d53ab0a48fcc282fd5ba7c1f8f8 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Tue, 13 Apr 2021 15:56:37 +0200 Subject: [PATCH 1/4] [#59] Add simple gradle and gradle wrapper in docker support --- .../cloudogu/ces/cesbuildlib/Gradle.groovy | 33 ++++++++++++++++++ .../ces/cesbuildlib/GradleInDockerBase.groovy | 34 +++++++++++++++++++ .../cesbuildlib/GradleWrapperInDocker.groovy | 23 +++++++++++++ .../ces/cesbuildlib/GradleMock.groovy | 18 ++++++++++ .../ces/cesbuildlib/GradleTest.groovy | 24 +++++++++++++ .../GradleWrapperInDockerTest.groovy | 20 +++++++++++ 6 files changed, 152 insertions(+) create mode 100644 src/com/cloudogu/ces/cesbuildlib/Gradle.groovy create mode 100644 src/com/cloudogu/ces/cesbuildlib/GradleInDockerBase.groovy create mode 100644 src/com/cloudogu/ces/cesbuildlib/GradleWrapperInDocker.groovy create mode 100644 test/com/cloudogu/ces/cesbuildlib/GradleMock.groovy create mode 100644 test/com/cloudogu/ces/cesbuildlib/GradleTest.groovy create mode 100644 test/com/cloudogu/ces/cesbuildlib/GradleWrapperInDockerTest.groovy diff --git a/src/com/cloudogu/ces/cesbuildlib/Gradle.groovy b/src/com/cloudogu/ces/cesbuildlib/Gradle.groovy new file mode 100644 index 0000000..430b1ae --- /dev/null +++ b/src/com/cloudogu/ces/cesbuildlib/Gradle.groovy @@ -0,0 +1,33 @@ +package com.cloudogu.ces.cesbuildlib + +abstract class Gradle implements Serializable { + protected script + + Gradle(script) { + this.script = script + } + + def call(String args, boolean printStdOut = true) { + gradle(args, printStdOut) + } + + /** + * @param printStdOut - returns output of gradle as String instead of printing to console + */ + protected abstract def gradle(String args, boolean printStdOut = true) + + def gradlew(String args, boolean printStdOut) { + sh("./gradlew -q "+ args, printStdOut) + } + + void sh(String command, boolean printStdOut) { + script.echo "executing sh: ${command}, return Stdout: ${printStdOut}" + if (printStdOut) { + // -V : strongly recommended in CI, will display the JDK and Maven versions in use. + // Don't use this with sh(returnStdout: true ..) ! + script.sh "${command} -V" + } else { + new Sh(script).returnStdOut command + } + } +} diff --git a/src/com/cloudogu/ces/cesbuildlib/GradleInDockerBase.groovy b/src/com/cloudogu/ces/cesbuildlib/GradleInDockerBase.groovy new file mode 100644 index 0000000..c14ae38 --- /dev/null +++ b/src/com/cloudogu/ces/cesbuildlib/GradleInDockerBase.groovy @@ -0,0 +1,34 @@ +package com.cloudogu.ces.cesbuildlib +/** + * Common tools for all GradleInDocker classes + */ +abstract class GradleInDockerBase extends Gradle { + + /** Setting this to {@code true} allows the Gradle build to access the docker host, i.e. to start other containers.*/ + boolean enableDockerHost = false + + Docker docker + + GradleInDockerBase(script) { + super(script) + this.docker = new Docker(script) + } + + @Override + def gradle(String args, boolean printStdOut = true) { + call ({ args }, printStdOut) + } + + abstract def call(Closure closure, boolean printStdOut); + + protected void inDocker(String imageId, Closure closure) { + docker.image(imageId) + // Mount user and set HOME, which results in the workspace being user.home. Otherwise '?' might be the user.home. + .mountJenkinsUser(true) + .mountDockerSocket(enableDockerHost) + .inside("") { + closure.call() + } + } + +} diff --git a/src/com/cloudogu/ces/cesbuildlib/GradleWrapperInDocker.groovy b/src/com/cloudogu/ces/cesbuildlib/GradleWrapperInDocker.groovy new file mode 100644 index 0000000..2da91de --- /dev/null +++ b/src/com/cloudogu/ces/cesbuildlib/GradleWrapperInDocker.groovy @@ -0,0 +1,23 @@ +package com.cloudogu.ces.cesbuildlib + +/** + * Run gradle using a Gradle Wrapper from the local repository within a Docker Container. + * The image of the container can be used to specified a JDK. + */ +class GradleWrapperInDocker extends GradleInDockerBase { + /** The docker image to use, e.g. {@code adoptopenjdk/openjdk11:jdk-11.0.1.13-alpine} **/ + private String imageId + + @SuppressWarnings("GrDeprecatedAPIUsage") // GradleWrapper will become protected constructor that is no longer deprecated + GradleWrapperInDocker(script, String imageId) { + super(script) + this.imageId = imageId + } + + @Override + def call(Closure closure, boolean printStdOut) { + inDocker(imageId) { + gradlew(closure.call(), printStdOut) + } + } +} diff --git a/test/com/cloudogu/ces/cesbuildlib/GradleMock.groovy b/test/com/cloudogu/ces/cesbuildlib/GradleMock.groovy new file mode 100644 index 0000000..39aea0f --- /dev/null +++ b/test/com/cloudogu/ces/cesbuildlib/GradleMock.groovy @@ -0,0 +1,18 @@ +package com.cloudogu.ces.cesbuildlib + +class GradleMock extends Gradle { + String args + + GradleMock(scriptMock) { + super(scriptMock) + } + + def gradle(String args, boolean printStdOut) { + this.args = args + return args + } + + static Docker setupDockerMock(GradleInDockerBase gradle) { + gradle.docker = DockerMock.create() + } +} diff --git a/test/com/cloudogu/ces/cesbuildlib/GradleTest.groovy b/test/com/cloudogu/ces/cesbuildlib/GradleTest.groovy new file mode 100644 index 0000000..01319fa --- /dev/null +++ b/test/com/cloudogu/ces/cesbuildlib/GradleTest.groovy @@ -0,0 +1,24 @@ +package com.cloudogu.ces.cesbuildlib + + +import org.junit.After +import org.junit.Test + +import static org.junit.Assert.assertEquals + +class GradleTest { + def scriptMock = new ScriptMock() + def gradle = new GradleMock(scriptMock) + + @After + void tearDown() throws Exception { + // always reset metaClass after messing with it to prevent changes from leaking to other tests + Gradle.metaClass = null + } + + @Test + void testCall() throws Exception { + def result = gradle "test" + assertEquals("test", result) + } +} diff --git a/test/com/cloudogu/ces/cesbuildlib/GradleWrapperInDockerTest.groovy b/test/com/cloudogu/ces/cesbuildlib/GradleWrapperInDockerTest.groovy new file mode 100644 index 0000000..2fa5781 --- /dev/null +++ b/test/com/cloudogu/ces/cesbuildlib/GradleWrapperInDockerTest.groovy @@ -0,0 +1,20 @@ +package com.cloudogu.ces.cesbuildlib + +import org.junit.Test +import static org.junit.Assert.assertEquals + +import static org.mockito.Mockito.verify + +class GradleWrapperInDockerTest { + def scriptMock = new ScriptMock() + + @Test + void gradleInDocker() { + def gradle = new GradleWrapperInDocker(scriptMock, 'adoptopenjdk/openjdk11:jdk-11.0.1.13-alpine') + Docker docker = GradleMock.setupDockerMock(gradle) + gradle 'clean install' + + assert scriptMock.actualShStringArgs[0].trim().contains('clean install') + verify(docker).image('adoptopenjdk/openjdk11:jdk-11.0.1.13-alpine') + } +} From c4f7fd1d4a9c27978b6967e43f082dd504c1653a Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Tue, 13 Apr 2021 16:28:20 +0200 Subject: [PATCH 2/4] [#59] Remove argument unknown to gradle --- src/com/cloudogu/ces/cesbuildlib/Gradle.groovy | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/Gradle.groovy b/src/com/cloudogu/ces/cesbuildlib/Gradle.groovy index 430b1ae..b8535ce 100644 --- a/src/com/cloudogu/ces/cesbuildlib/Gradle.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/Gradle.groovy @@ -23,9 +23,7 @@ abstract class Gradle implements Serializable { void sh(String command, boolean printStdOut) { script.echo "executing sh: ${command}, return Stdout: ${printStdOut}" if (printStdOut) { - // -V : strongly recommended in CI, will display the JDK and Maven versions in use. - // Don't use this with sh(returnStdout: true ..) ! - script.sh "${command} -V" + script.sh "${command}" } else { new Sh(script).returnStdOut command } From 8d31bf4df87e72cd97da22aba9ece88bee8e3319 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Thu, 15 Apr 2021 12:25:31 +0200 Subject: [PATCH 3/4] [#59] Update README.md --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 7917ba2..3f8d582 100644 --- a/README.md +++ b/README.md @@ -441,6 +441,27 @@ Available from both local Maven and Maven in Docker. See [Maven](src/com/cloudogu/ces/cesbuildlib/MavenInDocker.groovy) +# Gradle + +## Gradle in Docker + +Run gradle in a docker container. This can be helpful, when + +* constant ports are bound during the build that cause port conflicts in concurrent builds. For example, when running + integration tests, unit tests that use infrastructure that binds to ports or +* one maven repo per builds is required For example when concurrent builds of multi module project install the same + snapshot versions. + +Example: +```groovy +String gradleDockerImage = 'openjdk:11.0.10-jdk' +Gradle gradlew = new GradleWrapperInDocker(this, gradleDockerImage) + +stage('Build') { + gradlew "clean build" +} +``` + # Git An extension to the `git` step, that provides an API for some commonly used git commands and utilities. From 9e67df88b7c9775ad7aa869a6b724adbfe01a176 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Thu, 15 Apr 2021 12:30:14 +0200 Subject: [PATCH 4/4] [#59] Update README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3f8d582..db3a497 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ Jenkins Pipeline Shared library, that contains additional features for Git, Mave - [Deploying sites](#deploying-sites) - [Passing additional arguments](#passing-additional-arguments) - [Maven Utilities](#maven-utilities) +- [Gradle](#gradle) + - [Gradle Wrapper in Docker](#gradle-wrapper-in-docker) - [Git](#git) - [Credentials](#credentials) - [Git Utilities](#git-utilities) @@ -443,14 +445,12 @@ See [Maven](src/com/cloudogu/ces/cesbuildlib/MavenInDocker.groovy) # Gradle -## Gradle in Docker +## Gradle Wrapper in Docker -Run gradle in a docker container. This can be helpful, when +It's also possible to use a GradleWrapper in a Docker Container. Here, the Docker container is responsible for +providing the JDK. -* constant ports are bound during the build that cause port conflicts in concurrent builds. For example, when running - integration tests, unit tests that use infrastructure that binds to ports or -* one maven repo per builds is required For example when concurrent builds of multi module project install the same - snapshot versions. +See [GradleWrapperInDocker](src/com/cloudogu/ces/cesbuildlib/GradleWrapperInDocker.groovy) Example: ```groovy @@ -462,6 +462,9 @@ stage('Build') { } ``` +Since Oracle's announcement of shorter free JDK support, plenty of JDK images have appeared on public container image +registries, where `adoptopenjdk` is just one option. The choice is yours. + # Git An extension to the `git` step, that provides an API for some commonly used git commands and utilities.