Build configured to run tasks in the following order war->bootRepackage->ear.
Relevant gradle build configuration:
war {
............
}
bootRepackage {
withJarTask war
}
dependencies {
deploy files(bootRepackage)
}
ear {
deploymentDescriptor {
displayName = project.name
webModule(war.archiveName, '/service')
}
}
The issue happens in such configuration due to circular dependencies between bootRepackage and EAR tasks.
The next code snippet registers archive task deps as default dependencies:
TaskDependency runtimeProjectDependencyJarTasks = runtimeConfiguration
.getTaskDependencyFromProjectDependency(true, JavaPlugin.JAR_TASK_NAME);
task.dependsOn(
project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION)
.getAllArtifacts().getBuildDependencies(),
runtimeProjectDependencyJarTasks);
The above code causes bootRepackage to depend on the next tasks:
bootRepackage task deps [task 'distZip', task ':distTar', task ':ear', task ':war']
Any ideas on how to fix this apart of using separate builds\change task dependency graph?
Which version of Boot are you using? I think this problem may be caused by the application plugin which Boot's plugin applies for you in 1.2.x. 1.3.0 no longer applies the application plugin so if you're using 1.2.x, please try with 1.3.0.M5.
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.5.RELEASE")
So, yes it's 1.2.5.RELESE.
Will check in few mins 1.3.0.M5 (how stable is it btw?)
Same issue with 1.3.0.M5
here is snippet from ready tasks graph:
bootRepackage task deps [task ':ear', task ':findMainClass', task ':war']
still ear included by default.
Ear dependency comes from:
project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION)
.getAllArtifacts().getBuildDependencies();
Can you share a complete build.grade that reproduces the problem please?
Sure. It's multi project build.
-project
- projecta
- projectb
project build.gradle (parent):
buildscript {
repositories {
mavenCentral()
maven { url 'http://repo.spring.io/plugins-release' }
maven { url artifactoryVirtualRemoteRepos }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.0.M5")
classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.7'
classpath "io.spring.gradle:dependency-management-plugin:0.5.2.RELEASE"
classpath 'com.sourcemuse.gradle.plugin:gradle-mongo-plugin:0.8.0'
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:3.1.1"
}
}
subprojects {
apply from: '../common.gradle'
apply plugin: 'idea'
apply plugin: 'eclipse'
apply plugin: 'mongo'
apply plugin: 'propdeps-idea'
apply plugin: 'propdeps-eclipse'
apply plugin: 'maven-publish'
apply plugin: "io.spring.dependency-management"
apply plugin: 'com.jfrog.artifactory'
ext {
versionNumberString = project.version.toString()
}
idea {
module {
downloadJavadoc = true
}
}
test {
include "**/unit/**"
}
task integrationTest(type: Test) {
environment "MONGO_DB_HOST", "127.0.0.1"
include "**/integration/**"
}
mongo {
port 27017
logging 'console'
storageLocation "build/mongodb"
logFilePath "build/embedded-mongo.log"
}
repositories {
mavenCentral()
}
sourceCompatibility = globalSourceCompatibility
targetCompatibility = globalTargetCompatibility
dependencyManagement {
dependencies {
/** common **/
dependency 'org.apache.commons:commons-lang3:3.4'
dependency 'commons-io:commons-io:2.4'
dependency 'org.apache.commons:commons-collections4:4.0'
dependency 'com.google.guava:guava:18.0'
dependency 'junit:junit:4.12'
dependency 'org.springframework.ldap:spring-ldap-core:2.0.4.RELEASE'
dependency 'org.springframework.retry:spring-retry:1.1.2.RELEASE'
/** client deps **/
dependency 'org.springframework:spring-webmvc:4.1.7.RELEASE'
dependency 'org.springframework:spring-web:4.1.7.RELEASE'
dependency 'org.springframework.retry:spring-retry:1.1.2.RELEASE'
dependency 'org.apache.httpcomponents:httpclient:4.5'
dependency 'com.fasterxml.jackson.core:jackson-databind:2.4.6'
dependency 'com.fasterxml.jackson.core:jackson-core:2.4.6'
dependency 'com.fasterxml.jackson.core:jackson-annotations:2.4.6'
dependency 'org.aspectj:aspectjweaver:1.8.6'
}
}
dependencies {
compile 'org.apache.commons:commons-lang3'
compile 'commons-io:commons-io'
compile 'com.google.guava:guava'
compile 'org.apache.commons:commons-collections4'
compile 'org.springframework.retry:spring-retry'
testCompile 'junit:junit'
}
task wrapper(type: Wrapper) {
gradleVersion = globalGradleWrapperVersion
}
artifactory {
clientConfig.setIncludeEnvVars(true)
contextUrl = artifactoryContextUrl
publish {
repository {
repoKey = 'libs-release-local'
username = "${artifactoryDeployerUser}"
password = "${artifactoryDeployerPassword}"
}
}
resolve {
repoKey = artifactoryVirtualReleaseRepos
}
}
}
projecta build gradle (this one fails to run sequence war->bootRepackage->ear)
plugins {
id "nebula.os-package" version "2.2.6"
}
apply plugin: 'war'
apply plugin: 'spring-boot'
apply plugin: 'ear'
apply plugin: 'rpm'
war {
dependsOn createBuildInfoFile
baseName = globalProjectBaseName
version = versionNumberString
from(buildDir) {
include "build-info.properties"
into("WEB-INF/classes")
}
}
bootRepackage {
withJarTask war
}
dependencies {
deploy files(bootRepackage)
}
ear {
baseName = globalProjectBaseName
version = versionNumberString
deploymentDescriptor {
displayName = project.name
webModule(war.archiveName, '/service')
}
configurations.archives {
exclude 'application.yml'
}
};
tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}")
}
task sourceJar(type: Jar) {
from sourceSets.main.allJava
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-data-rest'
compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
compile 'org.springframework.boot:spring-boot-starter-logging'
compile 'com.fasterxml.jackson.datatype:jackson-datatype-joda'
compile 'org.springframework.ldap:spring-ldap-core'
/** must be providedRuntime for production builds! **/
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
testCompile('org.springframework.boot:spring-boot-starter-test')
}
publishing {
publications {
mavenJar(MavenPublication) {
from components.java
}
mavenWar(MavenPublication) {
from components.web
}
mavenEar(MavenPublication) {
artifacts = [ear.archivePath, ear]
}
}
}
artifactoryPublish {
publications('mavenJar', 'mavenWar', 'mavenEar')
publishArtifacts = true
properties = ['groupId': globalProjectBaseGroup, 'artifactId': globalProjectBaseName, 'version': versionNumberString]
publishPom = true
publishIvy = true
}
projectb build gradle (jar library)
apply plugin: 'java'
jar {
baseName = globalProjectBaseName
version = versionNumberString
}
dependencies {
compile 'org.springframework:spring-webmvc'
compile 'org.springframework:spring-web'
compile 'org.springframework.retry:spring-retry'
compile 'org.apache.httpcomponents:httpclient'
compile 'com.fasterxml.jackson.core:jackson-databind'
compile 'com.fasterxml.jackson.core:jackson-core'
compile 'com.fasterxml.jackson.core:jackson-annotations'
compile 'org.aspectj:aspectjweaver'
}
jar {
baseName = project.name
version = versionNumberString
manifest {
attributes 'Implementation-Title': project.name.tokenize('-').collect { it.capitalize() }.join(' '),
'Implementation-Version': version,
'Build-time': buildTimestamp
}
}
publishing {
publications {
mavenJar(MavenPublication) {
from components.java
}
}
}
artifactoryPublish {
skip = false
publications('mavenJar')
publishArtifacts = true
properties = ['groupId': globalProjectBaseGroup, 'artifactId': globalProjectBaseName, 'version': versionNumberString]
publishPom = true
}
I can send those project zipped if you want to check it out in action (should be easier to determine where issue is).
gradle clean buld

Something that I can just unzip/clone and run would certainly help. I can't see where common.gradle is coming from at the moment, for example.
I'm also a bit confused by a few things:
here it is https://github.com/clajder/bootRepackage-ear.git
>gradle clean build
I know that issue can be fixed easily moving ear packaging into another prj that depends on war. If there are no other workarounds I will go with this approach.
thanks
Thanks. That's helped a lot. The problem is that the bootRepackage task is configured to depend on all of the tasks that contribute artifacts to the project's archives configuration:
project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION)
.getAllArtifacts().getBuildDependencies()
That creates a cycle as the one artifact is the ear file. The creation of the ear file depends on bootRepackage as you've configured it to contain the repackaged war file:
dependencies {
deploy files(bootRepackage)
}
There's no need for bootRepackage to depend on the ear task so that's something that we can improve. In the meantime, you could package the original war file in the ear and keep the repackaged version separate to use for testing. To do that, you just need to change the dependencies of the deploy configuration:
dependencies {
deploy files(war)
}
This will work of course - as it packs original war into ear removing circular dependency.
Btw I tried to move ear packaging into a separate project that depends on :war project - it doesn't work as well as there is no registered outgoing bootRepackage artifact. Are there any ways to make ear packaging project to depend on bootRepackage output?
the next sctipt snippet picks up war archive configuration and doesn't see bootrepackage one:
dependencies {
deploy project(path:':projecta', configuration: 'archives')
}
ear {
baseName = globalProjectBaseName
version = versionNumberString
deploymentDescriptor {
displayName = project.name
webModule(project(':projecta').war.archiveName, '/service')
}
configurations.archives {
exclude 'application.yml'
exclude '**/*.tar'
exclude '**/*.zip'
}
};
how to reference for packaging bootRepackage task output?
Perhaps you could add the output of the bootRepackage task to a new repackagedArchives configuration and then depend on that?
There is no way to define new configuration + define outgoing artifacts for the task which depends on bootRepackage.
It's weird...but the same cyclic dependency appears again
Ah, yes. That makes sense actually. It doesn't break the cycle, just adds an extra step to it. Ok, sounds like we need to improve the plugin to reduce the tasks that it depends upon. As I said above, there's no need for bootRepackage to depend on the ear task so we should fix that.
Potentially makes sense to give freedom to select on what tasks bootRepackage task depends on.
For all fighting this, snippet from my workaround:
//if you need to do sth like this:
project.artifacts {
myConf myTaskDependantOnBootRepackage
}
//you can remove then bootRepackage dependencies to Archives like this:
project.tasks.matching {it.name == "bootRepackage"}.each {
Set<Object> deps = it.taskDependencies.values
def toBeeRemoved = deps.find {
it.class.name.startsWith("org.gradle.api.internal.artifacts.DefaultPublishArtifactSet")
}
deps.removeAll(toBeeRemoved)
}