hi I am trying to understand issue where given if (version_exists != '' && env.force_build == true) is evaluating to true even if env.force_build is set to false
pipeline {
agent { label 'master' }
parameters {
string(defaultValue: 'DEV', description: '', name: 'ENV', trim: true)
string(defaultValue: 'sys', description: '', name: 'platform_type', trim: true)
string(defaultValue: 'yyy', description: '', name: 'dev_app_host', trim: true)
string(defaultValue: 'xxx.dv.local', description: '', name: 'dev_xbar_host', trim: true)
string(defaultValue: '1.0.0.23', description: '', name: 'VERSION', trim: true)
booleanParam(defaultValue: false, description: 'force build if possible', name: 'force_build')
}
environment {
git_credential_id = '1234'
app_name = 'application'
app_module_name = 'dvmt_event_processor'
app_git_url = 'ssh://git#oooo:7999/data/application.git'
dna_common_git_url = 'ssh://git#oooo:7999/data/dna-common.git'
ansible_git_url = 'ssh://git#oooo:7999/data/operations-ansible.git'
pip_port = '9700'
}
stages {
stage('checkout from SCM') {
.....
}
stage('build') {
steps {
script {
// version_exists == dvmt_event_processor-1.0.0.23-py2.py3-none-any.whl
// force_build == false
def version_exists = ''
version_exists = sh(script: "ssh -o StrictHostKeyChecking=no ansible#pypi server ls /var/pypi/packages/dev/ | grep ${env.app_module_name} | grep ${env.VERSION}" , returnStdout: true)
if (version_exists != '' && env.force_build == true) {
......
// evaluating to true no matter what
}
}
else if (version_exists != '' && env.force_build == false ) {
echo "version already exists, just deploying"
......
}
else {
echo "starting full build"
.........
}
}
}
}
stage('Test') {
steps {
echo 'Testing...'
.............
}
}
}
}
jenkins console o/p
dvmt_event_processor-1.0.0.23-py2.py3-none-any.whl
[Pipeline] echo
false
[Pipeline] echo
starting full build
env.force_build prints like a boolean, but it is a string.
"false" == true
// → false
"false" == false
// → false
Always debug problems like this with the proper tools. Use .inspect()
or even .dump() to get insights, what kind of data you have at hand:
"false".inspect()
// → 'false'
"false".dump()
// → <java.lang.String#5cb1923 value=[102, 97, 108, 115, 101] coder=0 hash=97196323>
Note the quotes around false with the .inspect() example.
To convert a string to boolean with groovy, you can use
String.toBoolean():
"false".toBoolean() == true
// → false
"false".toBoolean() == false
// → true
Related
I am receiving the below error when trying to compile the device_handler code for the Sylvania Smart+ Plug. The code comes from https://images-na.ssl-images-amazon.com/images/I/71PrgM-PamL.pdf
The error:
Org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: script_dth_metadata_0631e407_ffd8_4ceb_b49a_877fd47635df: 94: expecting ''', found '\r' # line 94, column 55. nalResult.value == "on" ? '{{ ^ 1 error
Line 94:
def descriptionText = finalResult.value == "on" ? '{{
metadata {
definition (name: "SYLVANIA Smart Plug", namespace: "ledvanceDH", author:
"Ledvance") {
capability "Actuator"
capability "Switch"
capability "Power Meter"
capability "Configuration"
capability "Refresh"
capability "Sensor"
capability "Health Check"
fingerprint profileId: "C05E", inClusters:
"1000,0000,0003,0004,0005,0006,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM",
model: "Plug 01", deviceJoinName: "SYLVANIA Smart Plug"
fingerprint profileId: "0104", inClusters:
"0000,0003,0004,0005,0006,0B05,FC01,FC08", outClusters: "0003,0019", manufacturer:
"LEDVACE", model: "PLUG", deviceJoinName: "SYLVANIA Smart Plug"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
preferences {
section {
image(name: 'educationalcontent', multiple: true, images: [
"http://cdn.devicegse.smartthings.com/Outlet/US/OutletUS1.jpg",
"http://cdn.devicegse.smartthings.com/Outlet/US/OutletUS2.jpg"
])
}
}
// UI tile definitions
tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4,
canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label: 'On', action: "switch.off",
icon: "st.Appliances.appliances17", backgroundColor: "#79b821", nextState: "turningOff"
attributeState "off", label: 'Off', action: "switch.on",
icon: "st.Appliances.appliances17", backgroundColor: "#565C51", nextState: "turningOn"
attributeState "turningOn", label: 'Turning On', action:
"switch.off", icon: "st.Appliances.appliances17", backgroundColor: "#60903A", nextState:
"turningOff"
attributeState "turningOff", label: 'Turning Off', action:
"switch.on", icon: "st.Appliances.appliances17", backgroundColor: "#CACACA", nextState:
"turningOn"
}
tileAttribute ("power", key: "SECONDARY_CONTROL") {
attributeState "power", label:'${currentValue} W'
}
}
standardTile("refresh", "device.power", inactiveLabel: false, decoration:
"flat", width: 2, height: 2) {
state "default", label:'', action:"refresh.refresh",
icon:"st.secondary.refresh"
}
main "switch"
details(["switch","refresh"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
log.debug "description is $description"
def finalResult = zigbee.getKnownDescription(description)
def event = [:]
//TODO: Remove this after getKnownDescription can parse it automatically
if (!finalResult && description!="updated")
finalResult =
getPowerDescription(zigbee.parseDescriptionAsMap(description))
if (finalResult) {
log.info "final result = $finalResult"
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
event = null
}
else if (finalResult.type == "power") {
def powerValue = (finalResult.value as Integer)/10
event = createEvent(name: "power", value: powerValue,
descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable:
true)
/*
Dividing by 10 as the Divisor is 10000 and unit is kW for
the device. AttrId: 0302 and 0300. Simplifying to 10
power level is an integer. The exact power level with
correct units needs to be handled in the device type
to account for the different Divisor value (AttrId: 0302)
and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
*/
}
else {
def descriptionText = finalResult.value == "on" ? '{{
device.displayName }} is On' : '{{ device.displayName }} is Off'
event = createEvent(name: finalResult.type, value:
finalResult.value, descriptionText: descriptionText, translatable: true)
}
}
else {
def cluster = zigbee.parse(description)
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07){
if (cluster.data[0] == 0x00) {
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
event = createEvent(name: "checkInterval", value: 60 * 12,
displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
else {
log.warn "ON/OFF REPORTING CONFIG FAILED- error
code:${cluster.data[0]}"
event = null
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug "${cluster}"
}
}
return event
}
def off() {
zigbee.off()
}
def on() {
zigbee.on()
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.onOffRefresh()
}
def refresh() {
zigbee.onOffRefresh() + zigbee.electricMeasurementPowerRefresh()
}
def configure() {
// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time)
// enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false,
data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no
activity
refresh() + zigbee.onOffConfig(0, 300) + powerConfig()
}
//power config for devices with min reporting interval as 1 seconds and reporting
interval if no activity as 10min (600s)
//min change in value is 01
def powerConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04
{${device.zigbeeId}} {}", "delay 2000",
"zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}",
//The send-me-a-report is custom to the attribute type for CentraLite
"delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
]
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
//TODO: Remove this after getKnownDescription can parse it automatically
def getPowerDescription(descMap) {
def powerValue = "undefined"
if (descMap.cluster == "0B04") {
if (descMap.attrId == "050b") {
if(descMap.value!="ffff")
powerValue = zigbee.convertHexToInt(descMap.value)
}
}
else if (descMap.clusterId == "0B04") {
if(descMap.command=="07"){
return [type: "update", value : "power (0B04) capability configured
successfully"]
}
}
if (powerValue != "undefined"){
return [type: "power", value : powerValue]
}
else {
return [:]
}
}
I should have thought of it sooner... The problem is with the copy/paste. The long lines were broken by a "return". I removed the returns and the script compiled.
Latest Jenkins used.
edit: pastebin of full Java exception - https://pastebin.com/zZDNj18E
Goal: loop through all nodes, check for offline, email alert for each offline node.
(tried emailext alerts as well, could not use "offline")
Failed: My jenkinsfile runs perfectly with no email.
With email in the for loop or separately defined in a function, the job crashes after the first email is sent.
[Pipeline] End of Pipeline an exception which occurred: in field hudson.model.Slave.launcher in object hudson.slaves.DumbSlave#ae938e61 .... and many more
My jenkinsfile:
pipeline {
agent{
label 'master'
}
options {
// Enable timestamps in log
timestamps()
skipDefaultCheckout()
timeout(time: 4, unit: 'MINUTES')
}
stages {
stage('Monitor') {
steps{
script{
def offlineSlaves = []
for (aSlave in hudson.model.Hudson.instance.slaves) {
def thisSlave = aSlave.name
echo 'Name: ' + thisSlave + ' is being checked.'
if ( aSlave.getComputer().isOffline().toString() == 'true') {
slaveState = 'OFFLINE'
echo 'Name: ' + thisSlave + ' is ' + slaveState + ' !'
emailext (
mimeType: 'text/html',
body: "${env.JOB_NAME} found an OFFLINE node: ${name} ",
subject: "Jenkins ERROR: Build Node ${name} is OFFLINE " ,
to: 'jfisher#xxx')
}
}
}
}
}
}
post {
failure {
emailext (
body: 'Monitor Nodes Jenkins Job failed !',
presendScript: '$DEFAULT_PRESEND_SCRIPT',
recipientProviders: [requestor(),culprits()],
subject: 'Monitor Nodes Jenkins Failed',
to: 'jfisher#intouchhealth.com')
}
}
}
The problem with this code is the getComputer() part. In the pipeline you should only use Serializable and the SlaveComputer returned from getComputer() isn't.
https://javadoc.jenkins.io/hudson/slaves/SlaveComputer.html
What you should do is move this part to a function annotated with NonCPS
#NonCPS
def shallTrigger() {
for (aSlave in hudson.model.Hudson.instance.slaves) {
def thisSlave = aSlave.name
echo 'Name: ' + thisSlave + ' is being checked.'
if ( aSlave.getComputer().isOffline().toString() == 'true') {
slaveState = 'OFFLINE'
echo 'Name: ' + thisSlave + ' is ' + slaveState + ' !'
emailext (
mimeType: 'text/html',
body: "${env.JOB_NAME} found an OFFLINE node: ${name} ",
subject: "Jenkins ERROR: Build Node ${name} is OFFLINE " ,
to: 'jfisher#xxx')
}
}
}
I have the following pipeline:
pipeline {
agent any
stages {
... building stuff...
stage('push to develop'){
when {
branch 'develop'
}
steps {
ftpPublisher paramPublish: [ parameterName: "" ], alwaysPublishFromMaster: true, masterNodeName: master, continueOnError: false, failOnError: false, publishers: [
[configName: 'cp-front', usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true, transfers: [
[asciiMode: false, cleanRemote: false, excludes: '', flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '**/*']
]]
]
}
}
}
}
Unfortunately, this throws:
groovy.lang.MissingPropertyException: No such property: master for
class: groovy.lang.Binding at
groovy.lang.Binding.getVariable(Binding.java:63) at
org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:264)
at org.kohsuke.groovy.sandbox.impl.Checker$6.call(Checker.java:288)
at
org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:292)
at
org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:268)
at
org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:268)
at
com.cloudbees.groovy.cps.sandbox.SandboxInvoker.getProperty(SandboxInvoker.java:29)
at
com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:20)
at WorkflowScript.run(WorkflowScript:22)
Which gives me about 0 idea what is going on. Any pointers?
master (an object, property) is not equal to 'master' - which is an instance of String. Maybe you made a simple mistake?
I wrote some functions to help me writing Jenkins pipelines.
The following functions, are responsible to returning a shell command output:
def gen_uuid(){
randomUUID() as String
}
def sh_out(cmd){
String uuid = gen_uuid()
sh """( ${cmd} )> ${uuid}"""
String out = readFile(uuid).trim()
sh "set +x ; rm ${uuid}"
return out
}
That shown, here's another function:
Map get_started_by(){
withCredentials([ // Use Jenkins credentials ID of artifactory
[$class: 'UsernamePasswordMultiBinding', credentialsId: '0b8d591a-f4ce-XXXX-XXXX-faecb504d3d0', usernameVariable: 'J_USER', passwordVariable: 'J_PASS'],
]){
List startedBy = sh_out("""
set +x; curl -u ${J_USER}:${J_PASS} '${env.BUILD_URL}api/json' 2>/dev/null | \
python -mjson.tool | \
awk -F'"' '/(userId|userName)/{print \$4}'
""").split(/(\n)/)
return [
userId: startedBy[0],
userName: startedBy[1]
]
}
}
Which returns the userId and userName of the user who issue the job run.
Then, my problem is in this function:
def run_in_stage_func(String stage_name, Closure command, String sendTo){
String started_by = get_started_by()
String ulink = "<#${started_by['userId']}>"
String jlink = "(<${env.BUILD_URL}|Open>)"
println "============================================================"
stage (stage_name) {
try {
command()
if (currentBuild.result == 'FAILURE') {
error "Build failed, see log for further details."
}
println "============================================================"
} catch (Exception ex) {
def except = "${ex}"
slackSend channel: channel, color: 'danger', teamDomain: null, token: null,
message: " :${ulink} *Failed to build ${env.JOB_NAME}*! :x: ${jlink} (<!here|here>)"
echo "Pipeline failed at stage: ${stage_name}"
throw ex
}
}
}
When I run the job, I get the following error:
groovy.lang.MissingPropertyException: No such property: userId for class: java.lang.String
What could be the reason that the line ' String ulink = "<#${started_by['userId']}>" ' - is not working as intended?
You cast the result of get_started_by() to String explicitly. To fix it, change your code to
def started_by = get_started_by()
or
Map started_by = get_started_by()
Ok, Below you find my opus.. (lol not really) but still this is my attempt to take a mess and automate it. Those who are willing, any advice would be appreciated.
The project all lives in one git repo. very much like https://github.com/jmvanryn/multibranchPipeline
some of my questions are..
could I be doing some of this better? how do I use Global variables?
you can see I'm defining some things twice. why does GIT pull twice?
is there a way to get the cobertura report plugin to work?
env.BRANCH_NAME gives me "feature/FeatureName" is there a
DSL/Groovy way to trim it to just FeatureName?
node {
wrap([$class: 'BuildUser']) {
def user = env.BUILD_USER_ID
}
env.JAVA_HOME = tool 'JDK1.7.0.21'
def mvnHome = tool 'M3'
def nodeJS = tool 'NodeJSv5.6.0'
env.MAVEN_OPTS = "-Xmx1024m -XX:MaxPermSize=512m"
env.PATH = "${mvnHome}/bin:${nodeJS}/bin:${env.JAVA_HOME}/bin:${env.PATH}"
//checkout scm
sh 'git rev-parse --verify HEAD > hash.txt'
env.GIT_COMMIT = readFile('hash.txt')
env.GIT_COMMIT = env.GIT_COMMIT.trim()
setVersion();
stage("Build Core")
hipchatSend color: 'GREEN', notify: true, room: '1654572'
BuildIt ("sharedLib1")
BuildIt ("sharedLib2")
stage("Build Modules")
parallel "first-ui": {
BuildIt ("first-ui")
}, "second-ui": {
BuildIt ("second-ui")
}, "first-ws": {
BuildIt ("first-ws")
}, "second-ws": {
BuildIt ("second-ws")
}
stage("Archive files")
echo("================================== Archive Stage ==================================")
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.war', fingerprint: true])
//step([$class: 'ArtifactArchiver', artifacts: 'CONFIG/*.*', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
step([$class: 'hipchat', room: '1654572', startNotification: false, notifySuccess: true, notifyAborted: false, notifyNotBuil: false, notifyUnstable: false, notifyFailure: true, notifyBackToNormal: false])
stage("Notification")
step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: 'jvanryn#liaison-intl.com', sendToIndividuals: true])
}
def BuildIt(module) {
sh "echo '================================== Build Stage : ${module} ==================================';\
cd ${module};\
mvn --batch-mode -V -U -e clean deploy -U -DskipITs sonar:sonar -Dsonar.branch=${env.BRANCH_NAME} -Dsurefire.useFile=false"
}
def setVersion () {
def JobCode = env.BRANCH_NAME
def JobName = env.JOB_NAME
def BuildURL = env.BUILD_URLD
def BuildNum = env.BUILD_NUMBER
def RevNum = env.GIT_COMMIT
def WrkSpce = pwd()
echo "Running Build for: "+JobCode
// update version to a unique version
def script=WrkSpce+"/Tools/PomVersionUpdater.sh "+JobCode+"-"+RevNum+"-"+BuildNum
sh script
}
First, you can create a .mvn folder with jvm.config and maven.config to store all those xmx parameters and others, so everyone is using the same build settings.
You also need a try/catch block to send some notification if the build fails.
Other than that it looks ok, could be better but it works