I'm creating my own Jenkins shared library to reuse some code between pipelines. I know how to create steps (really easy indeed) but I have a problem when I want to make accessible my "utils class" in the jenkinsfile (or WorkflowScript as Jenkins say in logs). I call "Util class" a groovy class located in folder src/my/domain/... that have many methods, for example, i want to create a log class like this:
# src/my/domain/group
package my.domain.group
class Console implements Serializable {
def log(msg) {
spit(msg, '\033[34m')
}
def error(msg) {
spit(msg, '\033[31m')
}
def warning(msg){
spit(msg, '\033[33m')
}
def success(msg){
spit(msg, '\033[32m')
}
private def spit(msg, color){
ansiColor('xterm') {
echo color + msg + '\033[0m'
}
}
}
And then I try to instantiate in a file in /vars/library.groovy so can be accessed from the Jenkinsfile with some code as for example:
node("mynode"){
console.log("Hello World!")
}
The idea is to avoid to instantiate the console in all the script and make it automatically, I have tried several things like
# vars/library.groovy
import my.domain.group.Console
import groovy.transform.Field
@Field String console = new Console()
or
# vars/library.groovy
import my.domain.group.Console
def load(){
this.console = new Console()
// And also
console = new Console()
}
But when trying to access from jenkins file I always get the error: groovy.lang.MissingPropertyException: No such property: console for class: WorkflowScript
I understand that this means that the instance is not at the right scope so cannot be directly seen by the script but how can I do that?
Thanks!
Looks like you are using your vars
script incorrectly inside your Jenkinsfile, but I will try to solve some confusions :)
Groovy scripts defined in the vars
directory are called by their file name as "custom" step in your Jenkinsfile. Assuming your script name stays library.groovy
, your Jenkinsfile should look like this
node ("mynode") {
library("Hello World!")
}
or alternatively (since the parentheses are optional)
node ("mynode") {
library "Hello World!"
}
Additionally vars
scripts should only contain a single function named call
and necessary imports. Since you want to pass your log message as a string, the call
function additionally needs a parameter. So your library.groovy
script in vars
could look like this
# vars/library.groovy
import my.domain.group.Console
def call(msg){
Console console = new Console(steps)
console.log(msg)
}
Inside the call
function you can use all basic Jenkins steps (sh
, bat
, etc.) as you could in your Jenkinsfile, plus any Groovy/Java classes imported via import
(like your Console
class).
As you may have noticed, I also added a steps
variable (which does not have to declared or instantiated manually) to the constructor of your Console
class. This is necessary since you cant use any Jenkins steps directly in any of your classes defined in the src
directory. Resulting, the method
private def spit(msg, color){
ansiColor('xterm') {
echo color + msg + '\033[0m'
}
}
would not work, because echo
is a Jenkins step. In order to use echo
inside your class, you can pass the steps
variable to the constructor and store it in another variable inside your class. Then you can call all Jenkins steps like steps.echo "Hello World"
# src/my/domain/group
package my.domain.group
class Console implements Serializable {
def steps
Console(steps) {
this.steps = steps
}
def log(msg) {
spit(msg, '\033[34m')
}
def error(msg) {
spit(msg, '\033[31m')
}
def warning(msg){
spit(msg, '\033[33m')
}
def success(msg){
spit(msg, '\033[32m')
}
private def spit(msg, color){
ansiColor('xterm') {
steps.echo color + msg + '\033[0m'
}
}
}
That should make it work.
You should think about renaming your library.groovy
inside vars
though (for example myLog.groovy
). Then your Jenkinsfile could look like this
node ("mynode") {
myLog "Hello World!"
}
In order to use the other methods of your Console
class, you can create additional scripts inside vars
. Using them could look like this
node ("mynode") {
myLog "Hello World!"
myWarning "Hello World!"
myError "Hello World!"
mySuccess "Hello World!"
}
(im using the my
prefix to avoid name collisions with already existing Jenkins steps. For example, the error
step already exists and calling it in your Jenkinsfile will cancel your build)
For more info about shared libraries check out the Jenkins documentation at https://jenkins.io/doc/book/pipeline/shared-libraries/
You can find a complete list of all basic Jenkins steps at https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/
Getting Jenkins and pipelines to work can be a huge hassle, but in the end it's worth it. I hope this wall of text helps a bit. Good luck and happy coding :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With