Add Jenkins Shared Libraries with classes reference in a Jenkinsfile dynamically - jenkins

I have constructed a Shared Library in which I want to have class definitions, below is the example shared lib code.
package api
class api implements Serializable {
private final Map data
private final DSL steps
api(Map data, DSL steps) {
this.data = data
this.steps = steps
}
}
I have placed this class definition in a shared library git repo, with the path api/src/api/api.groovy. I am importing the shared library using the following step at the top of my jenkinsfile, outside any node or pipeline directive:
library identifier: 'api#master', retriever: modernSCM(
scm: [
$class: 'GitSCMSource',
credentialsId: '<my-credentials-id>',
remote: '<my-git-remote>',
traits: [gitBranchDiscovery()]
],
libraryPath: 'api/'
)
import api.*
When I use the above class from the shared library it's throwing an exception saying that the class is not found.
WorkflowScript: 75: unable to resolve class api
# line 75, column 29.
api foo = new api(env.data, steps)
^
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:309)
at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:981)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:626)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:575)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:142)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:562)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:514)
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:336)
at hudson.model.ResourceController.execute(ResourceController.java:107)
at hudson.model.Executor.run(Executor.java:449)
Finished: FAILURE
As i read in the doccument that loading library dynamically does not import classes directly to the pipeline's classpath.
So need a little help to get this class into my job execution.
Note: I have tested it and it's working when adding the global libraries under jenkins global settings, however i want this library setting to be dynamic from jenkins file.

Related

aws_cdk events rule target for cdk pipelines fails

below error pops when I try to target a CDK pipeline using events targets.
jsii.errors.JavaScriptError:
Error: Resolution error: Supplied properties not correct for "CfnRuleProps"
targets: element 0: supplied properties not correct for "TargetProperty"
arn: required but missing.
code is below
from aws_cdk import (
core,
aws_codecommit as codecommit,
aws_codepipeline as codepipeline,
aws_events as events,
aws_events_targets as targets
)
from aws_cdk import pipelines
from aws_cdk.pipelines import CodePipeline, CodePipelineSource, ShellStep
class BootStrappingStack(core.Stack):
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs
repo = codecommit.Repository(
self, 'Repository',
repository_name='Repository'
)
source_artifact = codepipeline.Artifact()
cloud_assembly_artifact = codepipeline.Artifact()
pipeline = CodePipeline(self, 'Pipeline',
synth=ShellStep("Synth",
input=CodePipelineSource.code_commit(
repository=repo,
branch='master'),
commands=[
'pip install -r requirements',
'npm install -g aws-cdk',
'cdk synth']
)
)
rule = events.Rule(self, 'TriggerPipeline',
schedule=events.Schedule.expression('rate(1 hour)')
)
rule.add_target(targets.CodePipeline(pipeline))
The documentation for aws_cdk.events_targets works with codepipeline construct, however doesn't work as documented for cdk pipelines.
This needs to be addressed in the documentation, once I get to know what's the fix. Please help.
As mentioned by #Otavio, you need to use the codepipeline.IPipeline.
You can use the pipeline property from CDK CodePipeline construct but in order to use that first you need to construct the pipeline using build_pipeline() method:
pipeline = CodePipeline(self, 'Pipeline',
synth=ShellStep("Synth",
input=CodePipelineSource.code_commit(
repository=repo,
branch='master'),
commands=[
'pip install -r requirements',
'npm install -g aws-cdk',
'cdk synth']
)
)
# You need to construct the pipeline before passing it as a target in rule
pipeline.build_pipeline()
rule = events.Rule(self, 'TriggerPipeline',
schedule=events.Schedule.expression('rate(1 hour)')
)
# Using the pipeline property from CDK Codepipeline
rule.add_target(targets.CodePipeline(pipeline.pipeline))
The problem is that targets.CodePipeline receives a codepipeline.IPipeline as a parameter. But what you are using instead is a pipelines.CodePipeline, which is a different thing. CodePipeline is more abstract construct, built on top of the codepipeline module.
You can try this:
const pipeline = new CodePipeline(self, 'Pipeline' ....
Then:
rule.addTarget(new targets.CodePipeline(pipeline))

Specifying a GitHub project URL in a Jenkins Multibranch pipeline

I'm trying to specify the GithubProjectProperty in a Jenkins Multibranch pipeline. I've been unsuccessful attempting to set an entry in the option block to control this value.
The pipeline syntax snippet generator suggests:
properties([
$class: 'GithubProjectProperty',
displayName: '',
projectUrlStr: 'https://myServer/myOrg/myRepo'
])
None of the following appear to work:
1) Try to put the properties directly in the options block
options {
// Set the URL for the GitHub project option
properties([
$class: 'GithubProjectProperty',
displayName: '',
projectUrlStr: 'https://myServer/myOrg/myRepo'
])
}
ERROR: The ‘properties’ section has been renamed as of version 0.8. Use ‘options’ instead
2) Remove the properties keyword but leave the option in the options block
options {
// Set the URL for the GitHub project option
[
$class: 'GithubProjectProperty',
displayName: '',
projectUrlStr: 'https://myServer/myOrg/myRepo'
]
}
ERROR: Options cannot be defined as maps
3) Treat the GitHubProjectProperty as if it can be instantiated (like office365ConnectorWebhooks)
options {
// Set the URL for the GitHub project option
GithubProjectProperty('https://myServer/myOrg/myRepo')
}
ERROR: Invalid option type "GithubProjectProperty". Valid option types: [authorizationMatrix, buildDiscarder, catchError, checkoutToSubdirectory, disableConcurrentBuilds, disableResume, durabilityHint, newContainerPerStage, office365ConnectorWebhooks, overrideIndexTriggers, parallelsAlwaysFailFast, preserveStashes, quietPeriod, rateLimitBuilds, retry, script, skipDefaultCheckout, skipStagesAfterUnstable, timeout, waitUntil, warnError, withContext, withCredentials, withEnv, ws]
4) Treat the GitHubProjectProperty as if it can be instantiated but inside a script block (because script is supposed to be valid according to attempt #3)
options {
script {
// Set the URL for the GitHub project option
GithubProjectProperty('https://myServer/myOrg/myRepo')
}
}
ERROR: Options definitions cannot have blocks
The office-365-connector-plugin is a working plugin that is supported in the options block of a Jenkinsfile. I compared its code with the github-plugin source on GitHub and noticed the following line:
#Extension
public static final class DescriptorImpl extends JobPropertyDescriptor {
The code is lacking a #Symbol directive that the office365ConnectorWebhooks appears to provide in its code:
#Extension
#Symbol("office365ConnectorWebhooks")
public final class WebhookJobPropertyDescriptor extends JobPropertyDescriptor {
Is there some special syntax to use to add the GitHub URL to a multibranch pipeline or does that plugin just not support managing it through a Jenkinsfile?
The ability to specify options in a pipeline using a Jenkinsfile requires a Symbol. There is a proposed fix in the Jenkins github-plugin that adds the necessary Symbol directive but it is not currently part of the plugin as of version 1.30.0.
See: https://issues.jenkins-ci.org/browse/JENKINS-62339
It's possible for a developer to build their own updated plugin in the meantime by updating the following file:
src/main/java/com/coravy/hudson/plugins/github/GithubProjectProperty.java in the source found at: https://github.com/jenkinsci/github-plugin
Add the Symbol:
import org.jenkinsci.Symbol;
...
#Extension
#Symbol("githubProjectProperty")
public static final class DescriptorImpl extends JobPropertyDescriptor {
...
And for good measure, make sure the code specifies the correct function signature for newInstance:
#Override
public JobProperty<?> newInstance(#Nonnull StaplerRequest req, JSONObject formData)
throws Descriptor.FormException {
The updated plugin may be installed by a Jenkins admin using the Advanced option in the Plugin Manager to upload an .hpi file from outside the central plugin repository
I had the same problem and was able to solve it with:
options{
githubProjectProperty(displayName: '', projectUrlStr: 'your_project_url')
}

#Grab defined in shared-library is not working while referencing library in another java pipeline project

We have created a pipeline shared library using Groovy and referenced that in one of java project. Build is getting success for shared library while running individual build, but when we reference the shared library and run the jenkins pipeline build of java project, it is getting failed with the below error.
org.jenkinsci.plugins.workflow.cps.CpsCompilationErrorsException: startup failed:
file:/var/lib/jenkins/jobs/Pipeline%20Test/jobs/pipeline-test/branches/develop/builds/10/libs/c3-shared-library/src/com/test/c3/http/JenkinsHttpClient.groovy: 6: unable to resolve class jodd.http.HttpRequest
# line 6, column 1.
#Grab("org.jodd:jodd-http:5.1.0")
c3-shared-library - JenkinsHttpClient.groovy:
#! /usr/bin/groovy
package com.test.c3.http
import com.cloudbees.groovy.cps.NonCPS
import groovy.json.JsonBuilder
#Grab("org.jodd:jodd-http:5.1.0")
import jodd.http.HttpRequest
import jodd.http.HttpResponse
class JenkinsHttpClient implements Serializable {
private String userAgent
JenkinsHttpClient(String userAgent = "Jenkins"){
this.userAgent = userAgent
}
Herewith attached jenkins configuration. Please check and provide your valuable inputs.
Shared Library Pipeline build.grade:
group "com.test.c3"
version "1.0.0-develop-SNAPSHOT"
apply plugin: 'maven'
apply plugin: 'groovy'
repositories {
mavenCentral()
}
compileGroovy {
groovyOptions.configurationScript = file("gradle/config.groovy")
}
sourceSets {
groovy {
main.groovy.srcDirs += 'src/'
main.groovy.srcDirs += 'vars/'
test.groovy.srcDirs += 'tests/'
}
}
dependencies {
compile "org.apache.ivy:ivy:2.5.0"
compile 'org.codehaus.groovy:groovy-all:2.5.9'
compile 'org.jodd:jodd-http:5.1.0'
compile 'com.cloudbees:groovy-cps:1.31'
testImplementation group: 'junit', name: 'junit', version: '4.4'
}

Trigger Jenkins Job from shared library

This is what I have in my shared library file
build job: 'Job Name',
parameters:
[
string(name: 'ENVIRONMENT', value: 'sit'),
string(name: 'param1', value: 'value1' )
]
It is failing with below error :
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: build.call() is applicable for argument types: (java.util.LinkedHashMap) values: [[job:**********, parameters:[#string(name=ENVIRONMENT,value=sit), ...]]]
Possible solutions: call(java.lang.Object, java.lang.Object, java.lang.Object), wait(), any(), wait(long), main([Ljava.lang.String;), any(groovy.lang.Closure)
Any help here?
Library classes cannot directly call steps such as sh or git. They can however implement methods, outside of the scope of an enclosing class, which in turn invoke Pipeline steps, for example:
// src/org/foo/Zot.groovy
package org.foo;
def checkOutFrom(repo) {
git url: "git#github.com:jenkinsci/${repo}"
}
return this
Which can then be called from a Scripted Pipeline:
def z = new org.foo.Zot()
z.checkOutFrom(repo)
This approach has limitations; for example, it prevents the declaration of a superclass.
Alternately, a set of steps can be passed explicitly using this to a library class, in a constructor, or just one method:
package org.foo
class Utilities implements Serializable {
def steps
Utilities(steps) {this.steps = steps}
def mvn(args) {
steps.sh "${steps.tool 'Maven'}/bin/mvn -o ${args}"
}
}
When saving state on classes, such as above, the class must implement the Serializable interface. This ensures that a Pipeline using the class, as seen in the example below, can properly suspend and resume in Jenkins.
#Library('utils') import org.foo.Utilities
def utils = new Utilities(this)
node {
utils.mvn 'clean package'
}
If the library needs to access global variables, such as env, those should be explicitly passed into the library classes, or methods, in a similar manner.
Instead of passing numerous variables from the Scripted Pipeline into a library,
package org.foo
class Utilities {
static def mvn(script, args) {
script.sh "${script.tool 'Maven'}/bin/mvn -s ${script.env.HOME}/jenkins.xml -o ${args}"
}
}
The above example shows the script being passed in to one static method, invoked from a Scripted Pipeline as follows:
#Library('utils') import static org.foo.Utilities.*
node {
mvn this, 'clean package'
}
For more info see jenkins shared library documentation: https://jenkins.io/doc/book/pipeline/shared-libraries/
Try adding propagate and wait like below:
build job: 'Job Name', parameters: [ string(name: 'ENVIRONMENT', value: 'sit'), string(name: 'param1', value: 'value1' ) ],propagate: true, wait: true
Ok. So I figured out the problem.
One of the shared file name was build.groovy which was causing conflicts with build pipeline step. Renamed the file and that fixed the issue.

multiple jenkins shared libraries with the same function

I am trying to import multiple jenkins libraries in a Jenkinsfile, but I've come across the issue of "what if both libraries share a function?" For example, if I have library A and library B, and both of those libraries have function helloWorld, how would I properly differentiate those two functions in the Jenkinsfile?
Let's assume I am importing the libraries like this:
#!groovy
import ...
import ...
import ...
library identifier: 'A#master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: '<the github link for A>'])
library identifier: 'B#master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: '<the github link for B>'])
// rest of the jenkinsfile
How would I be able to use the helloWorld function from both libraries? Is there a way to call A.helloWorld and B.helloWorld in this Jenkinsfile?
edit: helloWorld in this example would be from the vars folder. I'd like to call the same function even when it exists in both libraries' vars folder.
According to Jenkins Shared Libraries documentation, in the §Loading Libraries Dynamically section you can find you can assign a loaded library into a variable, then you can use the variable as a qualifier:
def A = library identifier: 'A#master', retriever: modernSCM(
[$class: 'GitSCMSource', remote: '<the github link for A>']
)
def B = library identifier: 'B#master', retriever: modernSCM(
[$class: 'GitSCMSource', remote: '<the github link for B>']
)
A.helloWorld()
B.helloWorld()

Resources