Closed stefanlack closed 5 years ago
Suggested fix. Modify bootJar Task, so that jar file is generated in default gradle build/libs directory, copy jar file from this location to destianation directory docker and rename it to app.jar:
bootJar {
doLast {
println "Copy jar file to docker directory...file build/libs/$bootJar.archiveName"
copy {
from "build/libs/$bootJar.archiveName"
into "$buildDir/../docker"
rename(bootJar.archiveName,"app.jar")
}
}
}
uploadArchives {
repositories{
mavenDeployer {
repository(url: "${nexus_url}/repository/${nexus_folder}/") {
authentication(userName: "${nexus_user}", password: "${nexus_pw}")
}
pom.artifactId = 'be-spring-boot'
pom.groupId = 'org.opendevstack.tstsla03'
pom.version="${System.getenv("TAGVERSION")}" // we will get a TAGVERSION from environment
}
}
}
Modification in Jenkinsfile:
def stageBuild(def context) {
def javaOpts = "-Xmx512m"
def gradleTestOpts = "-Xmx128m"
def springBootEnv = context.environment
if (springBootEnv.contains('-dev')) {
springBootEnv = 'dev'
}
stage('Build') {
withEnv(["TAGVERSION=${context.tagversion}", "NEXUS_HOST=${context.nexusHost}", "NEXUS_USERNAME=${context.nexusUsername}", "NEXUS_PASSWORD=${context.nexusPassword}", "JAVA_OPTS=${javaOpts}","GRADLE_TEST_OPTS=${gradleTestOpts}","ENVIRONMENT=${springBootEnv}"]) {
def status = sh(script: "./gradlew clean build --stacktrace --no-daemon", returnStatus: true)
junit 'build/test-results/test/*.xml'
if (status != 0) {
error "Build failed!"
} else {
def deployStatus = sh(script: "./gradlew uploadArchives --stacktrace --no-daemon", returnStatus: true)
if (deployStatus != 0) {
error "Deployment to nexus failed!"
}
}
}
}
}
Hi @stefanlack I think the build stage can be simplifyed by just adding uploadArchives
in first call to gradlew
after build
. It would look like ./gradlew clean build uploadArchives
.
However and apart from that, it looks to me not necessary to upload the jar file to nexus for these reasons:
uploadArchives
task from gradle file at all, not sure... cheers@stitakis good points. Eighter fix broken upload artifacts or remove upload to nexus should be done.
So my suggestion:
gradlew clean build
separated from upload stepgradlew uploadArchives
from stage stageBuild
stageUploadToNexus
to JenkinsfileAny thoughts regarding my suggestion?
Yes, but your assumption is only right, if docker image is the exhange format. That is not always the case. We develop on ODS, but our customers still require WAR and JAR files to be delivered.
On the other hand it has an operational problem. If you only have the jar file in the docker image and the underlying image (e.g. openjdk) has a security issue, you cannot simply "rebuild" the image and redeploy it on production, but you have to go through the complete process for your app.
@rattermeyer You are right that one needs to rebuild if the underlying image changes. Though certainly not ideal, I think it is worth the trade-off. In the beginning, we had upload to Nexus, and download from there into the Docker image and it was a complicated, brittle setup.
In the end, a quickstarter can never cover all use cases. Right now, a quickstarter means "deployment on OpenShift", and if you need sth. else you can modify the generated files.
That said, I think it would be nice to show how to upload to Nexus instead of deploying the Docker image. So maybe a disabled/commented stage in the Jenkinsfile
that shows how to upload could be a compromise.
Agree on the optional part. But what was the brittle part in the setup? Have done it for year (upload to nexus, download as part of the image build)
@all for uploading maven artifacts the current uploadArtifacts
does not provide the proper functionality. It just uploads a fat jar without pom. I have a working example that I could share. To fix that we could opt for A. a quickstarter for java libs, B. code to uncomment in gradle build file and jenkinsfile. C. document what changes are required. Your thoughs?
@rattermeyer You need matching paths in both upload / download, and you need to have Nexus credentials during the container build. There is really no need for that if you see Jenkins as the thing that builds artefacts, and then just wrap what is build in the container image. Plus, it feels a bit awkward to have a JAR built, and instead of putting it into the image, you put it into Nexus only to pull it out into the image.
What is the reason of having the JAR in Nexus? Only to not having to rebuild in case there is a security fix in the underlying image? Other than that I don't see the point really as the image is what you are shipping ...
thats fine. lets leave it per default as a copy to docker image and optionally having the option to upload to nexus. As fas as I understand Stefans proposal thats exactly what he does.
@rattermeyer : yes, that was my opinion and that is implemented by my suggestion.
@stitakis : if you may add a comment with a hint for better uploadArtifacts
task implementation: your welcome. Thanks in advance.
@stefanlack there are some changes to add to build.gradle
and Jenkinsfile
:
build.gradle
:
plugins {
...
id 'maven-publish'
}
jar
:
jar {
enabled = true
}
replace ùploadArchives` for:
publishing {
repositories {
maven {
def releasesRepoUrl = "${nexus_url}/repository/candidates"
def snapshotsRepoUrl ="${nexus_url}/repository/snapshots"
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username = "${nexus_user}"
password = "${nexus_pw}"
}
}
}
publications {
maven(MavenPublication) {
artifactId = 'be-java-lib'
groupId = 'com.bix-digital.sebio'
version = "${version}"
from components.java
}
}
}
Please note the different repo urls and the url resolution based in version name.
Jenkinsfile
:
build
stage to the ./gradlew
command the parameter -P branchName=${context.gitBranch}
def stagePublishLibToNexus(def context) {
def javaOpts = "-Xmx512m"
stage('Publish Library to Nexus') {
withEnv(["TAGVERSION=${context.tagversion}", "NEXUS_HOST=${context.nexusHost}", "NEXUS_USERNAME=${context.nexusUsername}", "NEXUS_PASSWORD=${context.nexusPassword}", "JAVA_OPTS=${javaOpts}"]) {
sh(script: "ls -lart ./build/libs")
def status = sh(script: "./gradlew publish -P branchName=${context.gitBranch} --stacktrace --no-daemon", returnStatus: true)
junit 'build/test-results/test/*.xml'
if (status != 0) {
error "Build failed!"
}
}
}
}
Note: this implementation will change the version of the artifact by removing the suffx SNAPSHOT when the branch is being merged to master. This allow the development team to do library releases
Task bootJar on generated gradle project is modified and writes jar archive to _docker/app.jar':
Task uploadArchives is invalid, because it expects the jar file in build/libs/be-spring-boot..jar.