Combining multiple jenkins shared libraries - jenkins

Is it possible to combine multiple shared Jenkins libraries?
E.g. I have a common shared library: my-shared-library (git repository with a maven project) defined on a jenkins folder that contains some jobs. Each
job that runs inside that folder can use that shared library in the Jenkinsfile with:
#Library("my-shared-library") _
import com.pipelines.Pipeline
new Pipeline().build()
Now I would like to create another shared library: my-specialized-shared-library that contains a few specialized pipelines (in another git repository also as a maven project).
Pipelines (groovy classes, scripts etc) in my-specialized-shared-library should be able to use/import classes, pipelines etc from:
my-shared-library is that possible and if so what are the recommended approach?

In Manage Jenkins > Configure System I defined 2 different Global Pipeline Libraries from different URLs.
GlobalLibrary-1
GlobalLibrary-2
The .jenkinsfile:
#Library('GlobalLibrary-1#some_branch') l1 //you can write here _ (underscore) or any other string.
#Library('GlobalLibrary-2#any_branch') l2
import com.lib1.Class1; // import from GlobalLibrary-1 (Jenkins automatically finds com.lib.Class1 inside GlobalLibrary-1)
import com.prodcode.Class2; // import from GlobalLibrary-2 (Jenkins automatically finds com.prodcode.Class2 inside GlobalLibrary-2)
node {
new Class1();
new Class2();
}
I can even use imports inside Class2.groovy to get classes from GlobalLibrary-1.

Related

How to deal with Jenkins error "Library ***expected to contain at least one of src or vars directories"

Working on my 6th or 7th Jenkins script now - and I already noticed they share a bit of code (essentially just the same groovy subroutines over and over again). I wouldn't like to continue with that and rather learn some best practices.
It seems that "Shared Libraries" are the thing to do. (Or is there a better way when you just want to share groovy code, not script steps etc.?)
Those scripts are part of a larger repo (that contains the source of the entire project, including the other scripts), stored in a subfolder Jenkins/Library with this structure:
Jenkins/Library
+- vars
| common_code.groovy
There is only a vars folder, no src. The documentation said
For Shared Libraries which only define Global Variables (vars/), or a Jenkinsfile which only needs a Global Variable, the annotation pattern #Library('my-shared-library') _ may be useful for keeping code concise. In essence, instead of annotating an unnecessary import statement, the symbol _ is annotated.
so I concluded that I wouldn't need a src folder and can do with vars alone.
The library is made available via "Configure Jenkins" > "Global Pipeline Libraries" with SourcePath set to "/Jenkins/Library/" and is brought in with the statement #Library('{name}') _ as first line of the script.
However, when attempting to use the library, I get the error shown in the subject.
What's the problem? (I already searched around and found this instance of the problem, but that doesn't seem to fit for my issue - unless I misunderstood something.)
To specify a name of the library you should set the same name in your jenkins settings:
Name.
An identifier you pick for this library, to be used in the #Library
annotation. An environment variable library.THIS_NAME.version will
also be set to the version loaded for a build (whether that comes from
the Default version here, or from an annotation after the #
separator).
Your '{name}' parameter inside of #Library() means you should add a library with the same name. Because it's not a variable like "${name}" which is not a built in variable and undefined.
If you wish to set up your library with the same name as your jenkins pipleine you could use env.JOB_NAME variable, or check the all environment and pre-defined variables:
println env.getEnvironment()
Or check job parameters only:
println params
Now step-by-step instructions:
Create your library, for example from Git SCM as shown on the screenshot.
Put your library code to the project, e.g: <project_root_folder>/vars/common_code.groovy. You don't need your additional path Jenkins/Library. Also you have named your file in 'snake case' style, which is not usual for groovy:
The vars directory hosts scripts that define global variables
accessible from Pipeline. The basename of each *.groovy file should be
a Groovy (~ Java) identifier, conventionally camelCased.
So your file in 'camel case' should looks CommonCode.groovy.
Write your library code:
// vars/commonCode.groovy
// Define your method
def call() {
// do some stuff
return 'Some message'
}
Write your pipeline. Example of scripted pipeline:
#!/usr/bin/env groovy
// yourPipeline.groovy file in your project
#Library('jenkins-shared-library') _
// Get the message from the method in your library
def messageText = commonCode() as String
println messageText
If you wish to define some global variables this answer also may help you.
PS: Using 'vars' folder allows you to load everything from your vars folder once at the same time. If you wish to load dynamically use import from src folder.

How to import from one level above the current package in groovy - elaboration

I have a very similar issue to what is described here:
How to import from one level above the current package in groovy
I don't want to move SharedState to an action/pkg subdirectory. What I want to do is to maintain the following structure:
groovy / action / TestDataHelper
groovy / action2 / TestDataHelper2
groovy / action3 / SharedState
and then gain access to SharedState from both TestDataHelper and TestDataHelper2.
I cannot do that anyhow using package, which I tend to understand is impossible by default. I could use classpath shenanigans instead, but need some guidance on that first.
Thanks for any kind of feedback.
The answer is to use additional class path in the Jenkins job seed job - advanced section in the Build. The path needs to be matching mother directory of the project.
/home/user/mother_dir
The groovy script can be anywhere under /home/user/mother_dir, in my case it's under /home/user/mother_dir/other_dir/script.groovy
Then, I put the classes I would like to import within a package in a /home/user/mother_dir/package_dir. In the groovy script mapped with the job I import package.* and have access to all the necessary classes from the package_dir.

Jenkins : Change the name of JenkinsFile

I'm using Pipeline Plugin under Jenkins
My job is basically using a file called "jenkinsFile" to get the divers steps to run.
-> My purpose is how to let the job use a different file name :
examples:
myJenkinsFile
build_JenkinsFile
deploy_JenkinsFile
buildSteps
...
Since it seems that "JenkinsFile" is a conventional format ,
is there any ways to change it if it's not verry clean ??
Suggestions ??
On the project section of the configuration page you just have to click Add > Pipeline Jenkins and then you can choose the custom name that jenkins will look for the pipeline.
If you want also a better level of customization you can also use Remote File Plugin, which allows you to put your pipeline in a repository and make it work with multiple repositories/branch (and of course you can still customize the name of the file)

No suitable ClassLoader found for grab when using #GrabConfig

I'm attempting to write a global function script that uses groovy.sql.SQL.
When adding the annotation #GrabConfig(systemClassLoader=true) I get an exception when using the global function in Jenkinsfile.
Here is the exception:
hudson.remoting.ProxyException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during conversion: No suitable ClassLoader found for grab
Here is my code:
#GrabResolver(name='nexus', root='http://internal.repo.com')
#GrabConfig(systemClassLoader=true)
#Grab('com.microsoft.sqlserver:sqljdbc4:4.0')
import groovy.sql.Sql
import com.microsoft.sqlserver.jdbc.SQLServerDriver
def call(name) {
echo "Hello world, ${name}"
Sql.newInstance("jdbc:sqlserver://ipaddress/dbname", "username","password", "com.microsoft.sqlserver.jdbc.SQLServerDriver")
// sql.execute "select count(*) from TableName"
}
Ensure that the "Use groovy sandbox" checkbox is unticked (it's below the pipeline script text box).
As explained here, Pipeline "scripts" are not simple Groovy scripts, they are heavily transformed before running, some parts on master, some parts on slaves, with their state (variable values) serialized and passed to the next step. As such, not every Groovy feature is supported.
I'm not sure about #Grab support. It is discussed in JENKINS-26192 (which is declared as resolved, so maybe it works now).
Extract from a very interesting comment:
If you need to perform some complex or expensive tasks with
unrestricted Groovy physically running on a slave, it may be simplest
and most effective to simply write that code in a *.groovy file in
your workspace (for example, in an SCM checkout) and then use tool and
sh/bat to run Groovy as an external process; or even put this stuff
into a Gradle script, Groovy Maven plugin execution, etc. The workflow
script itself should be limited to simple and extremely lightweight
logical operations focused on orchestrating the overall flow of
control and interacting with other Jenkins features—slave allocation,
user input, and the like.
In short, if you can move that custom part that needs SQL to an external script and execute that in a separate process (called from your Pipeline script), that should work. But doing this in the Pipeline script itself is more complicated.

Jenkins Plugin: create a new job programmatically

How to create a new Jenkins job within a plugin?
I have a Jenkins plugin that listens to a message queue and, when a message arrives, fires a new event to create a new job (or start a run).
I'm looking for something like:
Job myJob = new Job(...);
I know I can use REST API or CLI but since I'm in the plugin I'd use java internal solution.
Use Job DSL Plugin.
From the plugin page:
Jenkins is a wonderful system for managing builds, and people love using its UI to configure jobs. Unfortunately, as the number of jobs grows, maintaining them becomes tedious, and the paradigm of using a UI falls apart. Additionally, the common pattern in this situation is to copy jobs to create new ones, these "children" have a habit of diverging from their original "template" and consequently it becomes difficult to maintain consistency between these jobs.
The Jenkins job-dsl-plugin attempts to solve this problem by allowing jobs to be defined with the absolute minimum necessary in a programmatic form, with the help of templates that are synced with the generated jobs. The goal is for your project to be able to define all the jobs they want to be related to their project, declaring their intent for the jobs, leaving the common stuff up to a template that were defined earlier or hidden behind the DSL.
You can create a new hudson/jenkins job by simply doing:
FreeStyleProject proj = Hudson.getInstance().createProject(FreeStyleProject.class, NAMEOFJOB);
If you want to be able to handle updates (and you already have the config.xml):
import hudson.model.AbstractItem
import javax.xml.transform.stream.StreamSource
import jenkins.model.Jenkins
final jenkins = Jenkins.getInstance()
final itemName = 'name-of-job-to-be-created-or-updated'
final configXml = new FileInputStream('/path/to/config.xml')
final item = jenkins.getItemByFullName(itemName, AbstractItem.class)
if (item != null) {
item.updateByXml(new StreamSource(configXml))
} else {
jenkins.createProjectFromXML(itemName, configXml)
}
Make sure though you have the core .jar file before doing this though.

Resources