Parsing pom.xml file within a Jenkins pipeline - maven

I am trying to parse my pom.xml in jenkins pipeline plugin. I intend to modify it and save it back.
My problem is that it gives me "unclassified field java.lang.String version"
My code is as follows:
#NonCPS
groovy.util.Node getPom(path) {
new XmlParser().parseText(readFile(path)).version
}
node {
groovy.util.Node pomNode = getPom("pom.xml")
println pomNode
}
Similar issue has been discussed here: Parsing an XML file within a Jenkins pipeline

Why not use (you need pipeline utility steps plugin for this):
pom = readMavenPom file: 'pom.xml'
Now you have access to all data in the pom (as a Model).

Related

Jenkins shared library; Refer to static resource (xml) file when executing maven commands

We are currently in the process of setting up a Jenkins shared library to be used for several Java projects. Our file vars/utils.groovy contains the following (simplified) snippet:
def mvn(String mvnCommand) {
// withCredentials(userVarEtc=USER, pwVarEtc=PASS, etc, etc) {
sh "mvn ${mvnCommand} -Duser${USER} -Dpass${PASS} --settings maven_settings.xml"
// }
}
Although above snippet works fine, it requires all Java projects to keep the exact same maven_settings.xml template file in the root of the project. To prevent duplication we would like to add the maven_settings.xml file in the shared library and refer to its shared library path in the groovy method above. Would anyone know how to do this?
I can think of two possible solutions.
Solution 1
Keep the file maven_settings.xml in the resources directory.
shared-library-repository
+--resources/maven_settings.xml
+--vars/utils.groovy
In your shared library method, read this file and write to workspace before calling the maven commands.
def mvn(String mvnCommand) {
def myMavenSettings = libraryResource 'maven_settings.xml'
writeFile file: 'maven_settings.xml', text: myMavenSettings
withCredentials(userVarEtc=USER, pwVarEtc=PASS, etc, etc) {
sh "mvn ${mvnCommand} -Duser${USER} -Dpass${PASS} --settings $WORKSPACE/maven_settings.xml"
}
}
Solution 2
Install the Config File Provider plugin.
Go to Manage Jenkins > Managed files > Add a new Config.
Click Maven settings.xml and then Submit.
Type the name and paste the content in the designated fields.
Rename or copy the file ID and then click Save.
In you shared library method, invoke the plugin step to run maven commands.
def mvn(String mvnCommand) {
configFileProvider([configFile(fileId: '<file_ID>', variable: 'MAVEN_SETTINGS')]) {
withCredentials(userVarEtc=USER, pwVarEtc=PASS, etc, etc) {
sh "mvn ${mvnCommand} -Duser${USER} -Dpass${PASS} --settings $MAVEN_SETTINGS"
}
}
}

List all properties contributed by BOM to gradle 5

Is there a way to list all properties contributed by a given bom to gradle using gradlew/gradle
Suppose I have the following build script
dependencies {
//*** bill of materials
springBom platform("org.springframework.boot:spring-boot-dependencies:2.1.2.RELEASE")
}
I would like to list all properties that are available as part of the bom, how can I do that?
I know it contributes a property called micrometer.version because the source says so
Ref: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-dependencies/pom.xml
io.spring.dependency-managementplugin makes all of the properties from imported BOMs available for use in your Gradle build.
So, basically, you can write your custom gradle task to print all of them.
tasks.register("spring-boot-properties") {
group = 'Introspection'
description = 'Print properties from all BOMs'
doLast {
println dependencyManagement.importedProperties
}
}
Then just execute the task: ./gradlew spring-boot-properties
Maven BOM support in Gradle does not expose that information. The properties are effectively inlined when parsing the POM hierarchy and thus no longer available in the dependency metadata format of Gradle.
As commented in the other answer, using the Spring dependency-management-plugin gives you access to these values.

Sonar scanner report file with taskId and analysisId

What I have for JAVA
I am using Jenkins as my CI/CD server and I created a Jenkinsfile for my JAVA project and for the scanning and quality I am using the maven sonar plugin. The mvn sonar:sonar command generate a file at target/sonar/report-task.txt. The file contains information related with the scanning process and using that information I am able to call the SonarQube REST API with the taskId generated and then I am able to call the REST API using analysisId and decide if the pipeline is broken based on the quality conditions.
What I want for Javascript (any other type of project)
I am trying to do something similar for a Javascript project but this time and using the sonar-scanner from command line but I realized that there is not file generated as report-task.txt ( I believe this file is only generated by maven sonar-plugin). So I will like to know if there is a way to generate that kind of information.
I really need the taskId value in order to do dynamically calls to SonarQube REST API once the scanner process has started.
Since you're using a Jenkinsfile there's no need to do this manually. From the docs
node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
withSonarQubeEnv('My SonarQube Server') {
sh 'mvn clean package sonar:sonar'
} // SonarQube taskId is automatically attached to the pipeline context
}
}
// No need to occupy a node
stage("Quality Gate"){
timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout
def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}
If you're not using Maven to build and analyze, then just sub-in the correct commands as appropriate.
The file contains information related with the scanning process is:
.scannerwork/report-task.txt

Resolve a dependency dynamically inside a gradle task

I am trying to build a gradle plugin, which does the following:
As part of one its tasks, it creates a new configuration
It adds a DefaultExternalModuleDependency to this configuration - more specifically, it constructs a dependency to the application server zip file (available on Nexus). This information can be overridden by the invoking project as well.
Tries to resolve this newly added dependency and then unpacks the file to a local folder
All of this was working well when I had the details hard coded in a build file, but it looks like adding dependencies as part of a task are not treated the same way as having that information available at the parsing time.
So my question is, how do I get the project to reload the configurations / dependencies?
The code looks like the following:
#TaskAction
void installAppserver() {
Dependency dependency = new DefaultExternalModuleDependency(group,name,version)
Configuration configuration = project.configurations.detachedConfiguration(dependency)
configuration.setTransitive(false)
configuration.files.each { file ->
if (file.isFile() && file.name.endsWith('.zip')) {
println 'Attempting to unzip: ' + file + ' into folder: ' + appServerFolder
new Copy().from(project.zipTree(file)).into(appServerFolder).execute()
}
}
}
The problem is that the actual artifacts are not getting resolved!
A task can't configure the build model (that's what plugins do). It's fine to create and resolve a detached configuration in a task. If this doesn't work, there is likely a problem with the task's code, or the dependency it tries to resolve. Note that dependencies can only be resolved if the correct repository(s) are defined.
Instead of new DetaultExternalModuleDependency() (which is an internal class), project.dependencies.create() should be used. Instead of new Copy().execute() (Task#execute must not be called from user code), project.copy should be used.

How to expand property references in jar resources?

I'm using Gradle to build a jar containing an xml file in META-INF. This file has a row like
<property name="databasePlatform" value="${sqlDialect}" />
to allow for different SQL databases for different environments. I want to tell gradle to expand ${sqlDialect} from the project properties.
I tried this:
jar {
expand project.properties
}
but it fails with a GroovyRuntimeException that seems to me like the Jar task attempts to expand properties in .class files as well. So then I tried
jar {
from(sourceSets.main.resources) {
expand project.properties
}
}
which does not throw the above exception, but instead results in all resources being copied twice - once with property expansion and once without. I managed to work around this with
jar {
eachFile {
if(it.relativePath.segments[0] in ['META-INF']) {
expand project.properties
}
}
}
which does what I want, since in my use case I only need to expand properties of files in the META-INF directory. But this feels like a pretty ugly hack, is there a better way to do this?
I stumbled across this post in a thread about a different but closely related issue. Turns out you want to configure the processResources task, not the jar task:
processResources {
expand project.properties
}
For some reason, though, I did have to clean once before Gradle noticed the change.
In addition to #emil-lundberg 's excellent solution, I'd limit the resource processing to just the desired target file:
build.gradle
processResources {
filesMatching("**/applicationContext.xml") {
expand(project: project)
}
}
An additional note: if the ${...} parentheses are causing "Could not resolve placeholder" errors, you can alternatively use <%=...%>. N.B. tested with a *.properties file, not sure how this would work for an XML file.
I've had similar problems migrating from maven to gradle build. And so far the simplest/easiest solution was to simply do the filtering yourself such as:
processResources {
def buildProps = new Properties()
buildProps.load(file('build.properties').newReader())
filter { String line ->
line.findAll(/\$\{([a-z,A-Z,0-9,\.]+)\}/).each {
def key = it.replace("\${", "").replace("}", "")
if (buildProps[key] != null)
{
line = line.replace(it, buildProps[key])
}
}
line
}
}
This will load all the properties from the specified properties file and filter all the "${some.property.here}" type placeholders. Fully supports dot-separated properties in the *.properties file.
As an added bonus, it doesn't clash with $someVar type placeholders like expand() does. Also, if the placeholder could not be matched with a property, it's left untouched, thus reducing the possibility of property clashes from different sources.
here is what worked for me (Gradle 4.0.1) in a multi-module project:
in /webshared/build.gradle:
import org.apache.tools.ant.filters.*
afterEvaluate {
configure(allProcessResourcesTasks()) {
filter(ReplaceTokens,
tokens: [myAppVersion: MY_APP_VERSION])
}
}
def allProcessResourcesTasks() {
sourceSets*.processResourcesTaskName.collect {
tasks[it]
}
}
and my MY_APP_VERSION variable is defined in top-level build.gradle file:
ext {
// application release version.
// it is used in the ZIP file name and is shown in "About" dialog.
MY_APP_VERSION = "1.0.0-SNAPSHOT"
}
and my resource file is in /webshared/src/main/resources/version.properties :
# Do NOT set application version here, set it in "build.gradle" file
# This file is transformed/populated during the Gradle build.
version=#myAppVersion#
I took your first attempt and created a test project. I put a pom file from a jenkins plugin in ./src/main/resources/META-INF/. I assume it is a good enough xml example. I replaced the artifactId line to look like the following:
<artifactId>${artifactId}</artifactId>
My build.gradle:
apply plugin: 'java'
jar {
expand project.properties
}
When I ran gradle jar for the first time it exploded because I forgot to define a value for the property. My second attempt succeeded with the following commandline:
gradle jar -PartifactId=WhoCares
For testing purposes I just defined the property using -P. I'm not sure how you are trying to define your property, but perhaps that is the missing piece. Without seeing the stacktrace of your exception it's hard to know for sure, but the above example worked perfectly for me and seems to solve your problem.

Resources