how to run vars/script.groovy from a Jenkins shared library - jenkins-pipeline

I'm trying to get information from a groovy script located under vars called from a shared library Jenkins class but gets an error. some info:
Need - global configuration file. similar to Manage Jenkins -> Configure System -> Environment variables
Goal - to be able to get global values inside shared library without sending parameters from Jenkinsfile.
My solution - I tried to use "vars/script.groovy" which works for Jenkinsfile but not inside shared library.
file structure
.
├── src
│   └── org
│   └── jenkins
│ └──shared_library.groovy
│── vars
│ └── globals.groovy
│
│── jenkinsfile.groovy
vars/globals.groovy
def my_global() {
return 'my_global_name'
}
shared_library class
package src.org.jenkins
class shared_library implements Serializable {
private steps
shared_library(steps) {
this.steps = steps
}
def some_func(){
println globals.my_global
}
jenkinsfile
#Library 'shared_library'
import org.jenkins.shared_library
my_shared_library = new shared_library(steps)
node(){
stage('my_first_stage'){
println globals.my_global
}
stage('my_second_stage'){
println shared_library.some_func()
}
}
so, I can see the value for the first print in pipeline, but for the second I get:
No such property: globals for class: src.org.jenkins.shared_library

You need to use the steps object as well to access the globals:
def some_func(){
println steps.globals.my_global()
}
Taking your example that would become
shared_library class
package src.org.jenkins
class shared_library implements Serializable {
private steps
shared_library(steps) {
this.steps = steps
}
def some_func(){
println steps.globals.my_global()
}
Edit: Just saw your Jenkinsfile has a typo as well. Need to use the shared library object instead of the class in 'my_second_stage':
Jenkinsfile
#Library('shared_library')
import org.jenkins.shared_library
my_shared_library = new shared_library(steps)
node(){
stage('my_first_stage'){
println globals.my_global()
}
stage('my_second_stage'){
println my_shared_library.some_func()
}
}

If you are comfortable with defining the value in .properties or .json files you can use 'resource' folder
sharedlibrary/resource/global.properties
In your pipeline script or var/script.groovy
Use the libraryResource method
globalPropertyContent = libraryResource 'global.properties'
access the property values like:
globalPropertyContent.PROJECT_NAME

Related

Get rootPoject.name, or reference to settings.gradle, of included builds

I'm using includeBuild to include modules from my own library in settings.gradle:
rootProject.name = "MyApp"
include ':app'
includeBuild '/usr/local/library/Android/Event'
includeBuild '/usr/local/library/Android/Location'
includeBuild '/usr/local/library/Android/Widget'
I know I can iterate these later with:
gradle.includedBuilds.each{ includeBuild ->
println includeBuild.name
}
However, that prints:
Event
Location
Widget
Is there a simple way to get the rootProject.names that I have defined in each of those individual library projects' settings.gradle files?
I know I can do the following to give alternative names:
includeBuild('/usr/local/library/Android/Event', {name = 'com.example.android.event'})
includeBuild('/usr/local/library/Android/Location', {name = 'com.example.android.location'})
includeBuild('/usr/local/library/Android/Widget', {name = 'com.example.android.widget'})
... but that is redundant and cumbersome when I've already defined those as rootProject.name is their respective settings.gradle.
Rather, I'm looking for something akin to:
gradle.includedBuilds.each{ includeBuild ->
println includeBuild.rootProject.name
}
For instance, I know about includeBuild.projectDir. Can I somehow get a (parsed) reference to the settings.gradle file in that directory?
I've managed to solve it using org.gradle.tooling.GradleConnector:
import org.gradle.tooling.GradleConnector
import org.gradle.tooling.ProjectConnection
import org.gradle.tooling.model.GradleProject
def getIncludedProjectNamesMap(Project project) {
def projectNamesMap = new HashMap<String, String>()
project.gradle.includedBuilds.each { includedBuild ->
ProjectConnection connection = GradleConnector.newConnector()
.forProjectDirectory(includedBuild.projectDir)
.connect()
GradleProject includedProject = connection.getModel(GradleProject.class);
def name = includedProject.getName();
connection.close();
projectNamesMap.put includedBuild.name, name;
}
return projectNamesMap
}
println getIncludedProjectNamesMap(project)
... which prints:
{Event=com.example.android.event, Location=com.example.android.location, Widget=com.example.android.widget}
... but that appears to be rather slow, probably due to all the connections it need to make. It does the job for now, but I'm still looking for alternative approaches, if available.

Jenkins Groovy Pipeline Get (Windows)User Folder Per Node

I have a distributed Jenkins build and the user under which the jenkins process runs on the slaves is not necessarily static, so I need a mechanism to get the user per node.
I am trying something like
#!/usr/bin/env groovy
class TestSettings {
public static String NuGetPackagesPath = "${env.USERPROFILE}\\.nuget\\packages"
}
node("master"){
println env.USERPROFILE // works as expected
println TestSettings.NuGetPackagesPath // throws exception
}
node("build"){
println env.USERPROFILE // works as expected
println TestSettings.NuGetPackagesPath // throws exception
}
env doesn't work in the static property, because the property is already initialized before you enter the node closure. So env just isn't available yet.
I see two ways around this:
Turn the property into a function and pass the env variable as parameter.
Make it a non-static function and pass env to the class constructor.
I would propably go with the latter as it will be easier to use when you have many test settings.
class TestSettings {
public static String getNuGetPackagesPath( def env ) { "${env.USERPROFILE}\\.nuget\\packages" }
}
class TestSettings2 {
def env = null
TestSettings2( def env ) {
this.env = env
}
public String getNuGetPackagesPath() { "${env.USERPROFILE}\\.nuget\\packages" }
}
node("master"){
println env.USERPROFILE
println TestSettings.getNuGetPackagesPath( env )
def testSettings = new TestSettings2( env )
// Note that we can use the method like a property!
println testSettings.nuGetPackagesPath
}

How to access extra properties defined in build.gradle in a Groovy class?

I am defining an extra property in build.gradle:
ext {
GRGIT_TARGET_REPO = ...
}
I have a groovy class under buildSrc/src/main/groovy
class Utils {
def test() {
println GRGIT_TARGET_REPO
}
}
I have another gradle file which can access GRGIT_TARGET_REPO just fine. But if it calls the function in the class:
Utils utils = new Utils()
utils.test()
On calling the above function, I get the following error:
No such property: GRGIT_TARGET_REPO for class: Utils
Is there a way to access project/extra properties in Groovy classes?
I believe you would need to send the gradle project object into your Utils class to accomplish what you want. In other words:
class Utils {
def project
def test() {
println(project.ext.GRGIT_TARGET_REPO)
}
}
and
def utils = new Utils(project: project)
utils.test()
in your build.gradle file.
Every gradle build file has a project instance as its delegate which means that you can call all methods in the project class directly from the build file code. The example here is that the above project access calls the getProject method on the project object.
For extra properties, there is an "extra properties" section in the above groovy/javadoc for the Project object.

In a custom task in my buildSrc folder: How do I determine the file path of the subproject it is called in?

If I define a custom gradle task in buildSrc: How do I find out the relative path to the project from which the task is called?
In my buildSrc folder, I have a custom task that creates a Enum our of my message.properties file:
open class GenerateEnumTask : DefaultTask() {
#get:Input
open var inputFolder: String = "src/main/resources"
#get:Input
open val targetFilePath: String = "src/generated/kotlin/MessageCode.kt"
#get:OutputFile
val enumFile = File(targetFilePath)
#TaskAction
fun generateEnum() {
...
}
#Internal
override fun getDescription() = "This task uses downloaded property files and creates an enum kotlin file"
}
I then want to make sure the enum is generated before code compilation.
So I put this in the subproject "core", where I need the Enum.
build.gradle.kts:
tasks {
val generateEnumTask by registering(GenerateEnumTask::class)
withType<KotlinCompile> {
kotlinOptions.jvmTarget = Versions.jvmTarget
dependsOn(generateEnumTask)
dependsOn(formatKotlin)
doFirst{
println("compile kotlin in core project")
}
}
}
This does indeed work if I run gradle compileKotlin directly from the subfolder of the core project.
However, if I run the same command from the root project, the code searches for a src folder in the root directory.
Ah, the answer was simple: DefaultTask inherits from AbstractTask, which has a reference to the project that the task was called in (getProject)
This works nicely:
open var targetFolder: String = this.project.file("src/main/resources").absolutePath

Gradle how to change version number in source code

Java code:
public static String VERSION = "version_number";
Gradle build.gradle
version = '1.0'
How to set the version in java code from grade? The version must be in source code.
Is there a convenient way? A not-so-nice way:
copy the java file to another location, e.g. build/changed-source
change the version in the source, by replacing token
add the build/changed-source in main source set.
I'd do similar to Michael Easter but with these differences
Store generated sources separately from main sources (src/main/java and $buildDir/generated/java). This has the added benefit of not needing custom gitignore
Generate in a subdirectory of $buildDir so that clean task will delete the generated sources
Use a separate task for code generation with proper up-to-date & skip support
Use Copy.expand(Map) to do the token replacement
Since its directory based, everything in src/template/java will have tokens replaced. You can easily add more templates in future
src/template/java/com/foo/BuildInfo.java
package com.foo;
public class BuildInfo {
public static String getVersion() {
return "${version}";
}
}
build.gradle
task generateJava(type:Copy) {
def templateContext = [version: project.version]
inputs.properties templateContext // for gradle up-to-date check
from 'src/template/java'
into "$buildDir/generated/java"
expand templateContext
}
sourceSets.main.java.srcDir "$buildDir/generated/java" // add the extra source dir
compileJava.dependsOn generateJava // wire the generateJava task into the DAG
One method is to similar to your not-so-nice way, but slightly easier. Consider a file in templates/BuildInfo.java:
package __PACKAGE;
public class BuildInfo {
private static final String version = "__VERSION";
private static final String buildTimestamp = "__BUILD_TIMESTAMP";
public String toString() {
return "version : " + version + "\n" +
"build timestamp : " + buildTimestamp + "\n";
}
}
This file can then be "stamped" with information as first thing in the compileJava task and written to src/main/java/your/package/BuildInfo.java:
def targetPackage = 'net/codetojoy/util'
def targetPackageJava = 'net.codetojoy.util'
def appVersion = project.appVersion // from gradle.properties
def buildTimeStamp = new Date().toString()
compileJava {
doFirst {
ant.mkdir(dir: "${projectDir}/src/main/java/${targetPackage}")
def newBuildInfo = new File("${projectDir}/src/main/java/${targetPackage}/BuildInfo.java")
def templateBuildInfo = new File("${projectDir}/templates/TemplateBuildInfo.java")
newBuildInfo.withWriter { def writer ->
templateBuildInfo.eachLine { def line ->
def newLine = line.replace("__PACKAGE", targetPackageJava)
.replace("__VERSION", appVersion)
.replace("__BUILD_TIMESTAMP", buildTimeStamp)
writer.write(newLine + "\n");
}
}
}
}
A working example is provided here. Everything would be stored in source-control except the src/main/java/your/package/BuildInfo.java file. Note the version would be stored in gradle.properties.

Resources