diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile new file mode 100644 index 00000000..a81f7c80 --- /dev/null +++ b/.ci/Jenkinsfile @@ -0,0 +1,18 @@ +#!groovy +node { + deleteDir() // Purge workspace + // Checkout the repo to get the necessary groovy scripts (and place it in the required subdirectory) + dir('MySensors') { + checkout scm + } + def pipeline = load('MySensors/.ci/pipeline.groovy') + + // Invoke the main pipeline + pipeline { + library_root = 'MySensors/' // Location of the MySensors library + repository_root = 'MySensors/' // Location of the repository root + github_organization = 'mysensors' // Name of the GitHub Organization + repository_name = 'MySensors' // Name of the repository on GitHub + nightly_arduino_ide = false // Pick Arduino IDE variant to use + } +} diff --git a/.ci/Jenkinsfile-nightly b/.ci/Jenkinsfile-nightly new file mode 100644 index 00000000..800bf18f --- /dev/null +++ b/.ci/Jenkinsfile-nightly @@ -0,0 +1,18 @@ +#!groovy +node { + deleteDir() // Purge workspace + // Checkout the repo to get the necessary groovy scripts (and place it in the required subdirectory) + dir('MySensors') { + checkout scm + } + def pipeline = load('MySensors/.ci/pipeline.groovy') + + // Invoke the main pipeline + pipeline { + library_root = 'MySensors/' // Location of the MySensors library + repository_root = 'MySensors/' // Location of the repository root + github_organization = 'mysensors' // Name of the GitHub Organization + repository_name = 'MySensors' // Name of the repository on GitHub + nightly_arduino_ide = true // Pick Arduino IDE variant to use + } +} diff --git a/.ci/arduino.groovy b/.ci/arduino.groovy new file mode 100644 index 00000000..9e7eaf18 --- /dev/null +++ b/.ci/arduino.groovy @@ -0,0 +1,351 @@ +#!groovy +def buildArduino(config, String buildFlags, String sketch, String key) { + def root = '/opt/arduino-1.8.2/' + if (config.nightly_arduino_ide) + { + root = '/opt/arduino-nightly/' + } + def esp8266_tools = '/opt/arduino-nightly/hardware/esp8266com/esp8266/tools' + def jenkins_root = '/var/lib/jenkins/' + def builder = root+'arduino-builder' + def standard_args = ' -warnings="all"' //-verbose=true + def builder_specifics = ' -hardware '+root+'hardware -tools '+root+'hardware/tools/avr -tools '+ + root+'tools-builder -tools '+esp8266_tools+' -built-in-libraries '+root+'libraries' + def jenkins_packages = jenkins_root+'.arduino15/packages' + def site_specifics = ' -hardware '+jenkins_packages+' -tools '+jenkins_packages + def repo_specifics = ' -hardware hardware -libraries . ' + def build_cmd = builder+standard_args+builder_specifics+site_specifics+repo_specifics+buildFlags + sh """#!/bin/bash + printf "\\e[1m\\e[32mBuilding \\e[34m${sketch} \\e[0musing \\e[1m\\e[36m${build_cmd}\\e[0m\\n" + ${build_cmd} ${sketch} 2>> compiler_${key}.log""" +} + +def parseWarnings(String key) { + warnings canResolveRelativePaths: false, canRunOnFailed: true, categoriesPattern: '', + defaultEncoding: '', + excludePattern: '''.*/EEPROM\\.h,.*/Dns\\.cpp,.*/socket\\.cpp,.*/util\\.h,.*/Servo\\.cpp, + .*/Adafruit_NeoPixel\\.cpp,.*/UIPEthernet.*,.*/SoftwareSerial\\.cpp, + .*/pins_arduino\\.h,.*/Stream\\.cpp,.*/USBCore\\.cpp,.*/Wire\\.cpp, + .*/hardware/esp8266.*,.*/libraries/SD/.*''', + healthy: '', includePattern: '', messagesPattern: '', + parserConfigurations: [[parserName: 'Arduino/AVR', pattern: 'compiler_'+key+'.log']], + unHealthy: '', unstableNewAll: '0', unstableTotalAll: '0' + sh """#!/bin/bash + echo "Compiler warnings/errors:" + printf "\\e[101m" + cat compiler_${key}.log + printf "\\e[0m" + rm compiler_${key}.log""" +} + +def buildMySensorsMicro(config, sketches, String key) { + def fqbn = '-fqbn MySensors:avr:MysensorsMicro -prefs build.f_cpu=1000000 -prefs build.mcu=atmega328p' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (MySensorsMicro - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && + sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino') { + buildArduino(config, fqbn, sketches[sketch].path, key+'_MySensorsMicro') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (MySensorsMicro - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_MySensorsMicro') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (MySensorsMicro - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (MySensorsMicro - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (MySensorsMicro - '+key+')', 'Pass', '') + } +} + +def buildMySensorsGw(config, sketches, String key) { + def fqbn = '-fqbn MySensors:samd:mysensors_gw_native -prefs build.f_cpu=48000000 -prefs build.mcu=cortex-m0plus' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (MySensorsGW - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/BatteryPoweredSensor/BatteryPoweredSensor.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && + sketches[sketch].path != config.library_root+'examples/GatewaySerialRS485/GatewaySerialRS485.ino' && + sketches[sketch].path != config.library_root+'examples/MotionSensorRS485/MotionSensorRS485.ino') { + buildArduino(config, fqbn, sketches[sketch].path, key+'_MySensorsGw') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (MySensorsGW - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_MySensorsGw') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (MySensorsGW - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (MySensorsGW - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (MySensorsGW - '+key+')', 'Pass', '') + } +} + +def buildArduinoNano(config, sketches, String key) { + def fqbn = '-fqbn arduino:avr:nano -prefs build.f_cpu=16000000 -prefs build.mcu=atmega328p' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Arduino Nano - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && + sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino') { + buildArduino(config, fqbn, sketches[sketch].path, key+'_ArduinoNano') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Nano - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_ArduinoNano') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (Arduino Nano - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Nano - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Arduino Nano - '+key+')', 'Pass', '') + } +} + +def buildArduinoUno(config, sketches, String key) { + def fqbn = '-fqbn arduino:avr:uno -prefs build.f_cpu=16000000 -prefs build.mcu=atmega328p' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Arduino Uno - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && + sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino') { + buildArduino(config, fqbn, sketches[sketch].path, key+'_ArduinoUno') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Uno - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_ArduinoUno') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (Arduino Uno - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Uno - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Arduino Uno - '+key+')', 'Pass', '') + } +} + +def buildArduinoPro(config, sketches, String key) { + def fqbn = '-fqbn arduino:avr:pro -prefs build.f_cpu=8000000 -prefs build.mcu=atmega328p' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Arduino Pro/Mini - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && + sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino') { + buildArduino(config, fqbn, sketches[sketch].path, key+'_ArduinoPro') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Pro/Mini - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_ArduinoPro') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, config, 'ERROR', 'Toll gate (Arduino Pro/Mini - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Pro/Mini - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Arduino Pro/Mini - '+key+')', 'Pass', '') + } +} + +def buildArduinoMega(config, sketches, String key) { + def fqbn = '-fqbn arduino:avr:mega -prefs build.f_cpu=16000000 -prefs build.mcu=atmega2560' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Arduino Mega - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && + sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino') { + buildArduino(config, fqbn, sketches[sketch].path, key+'_ArduinoMega') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Mega - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_ArduinoMega') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, config, 'ERROR', 'Toll gate (Arduino Mega - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Arduino Mega - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Arduino Mega - '+key+')', 'Pass', '') + } +} + +def buildEsp8266(config, sketches, String key) { + def fqbn = '-fqbn esp8266:esp8266:generic -prefs build.f_cpu=80000000 -prefs build.mcu=esp8266' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (ESP8266 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/BatteryPoweredSensor/BatteryPoweredSensor.ino' && + sketches[sketch].path != config.library_root+'examples/CO2Sensor/CO2Sensor.ino' && + sketches[sketch].path != config.library_root+'examples/DustSensorDSM/DustSensorDSM.ino' && + sketches[sketch].path != config.library_root+'examples/GatewaySerialRS485/GatewaySerialRS485.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayW5100/GatewayW5100.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayW5100MQTTClient/GatewayW5100MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/MotionSensorRS485/MotionSensorRS485.ino' && + sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino' && + sketches[sketch].path != config.library_root+'examples/SoilMoistSensor/SoilMoistSensor.ino') { + buildArduino(config, '-prefs build.flash_ld=eagle.flash.512k0.ld -prefs build.flash_freq=40 -prefs build.flash_size=512K '+fqbn, sketches[sketch].path, key+'_Esp8266') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (ESP8266 - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_Esp8266') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (ESP8266 - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (ESP8266 - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (ESP8266 - '+key+')', 'Pass', '') + } +} + +def buildnRF5(config, sketches, String key) { + def fqbn = '-fqbn sandeepmistry:nRF5:Generic_nRF52832 -prefs build.f_cpu=16000000 -prefs build.mcu=cortex-m4' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (nRF5 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + for (sketch = 0; sketch < sketches.size(); sketch++) { + if (sketches[sketch].path != config.library_root+'examples/BatteryPoweredSensor/BatteryPoweredSensor.ino' && + sketches[sketch].path != config.library_root+'examples/CO2Sensor/CO2Sensor.ino' && + sketches[sketch].path != config.library_root+'examples/DustSensorDSM/DustSensorDSM.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && + sketches[sketch].path != config.library_root+'examples/GatewaySerialRS485/GatewaySerialRS485.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayW5100/GatewayW5100.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayW5100MQTTClient/GatewayW5100MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/MotionSensorRS485/MotionSensorRS485.ino' && + sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino') { + buildArduino(config, fqbn, sketches[sketch].path, key+'_nRF5') + } + } + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF5 - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_nRF5') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (nRF5 - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF5 - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (nRF5 - '+key+')', 'Pass', '') + } +} + +def buildnRF52832(config, sketches, String key) { + def fqbn = '-fqbn=MySensors:nRF5:MyBoard_nRF52832:bootcode=none,lfclk=lfxo,reset=notenable -prefs build.f_cpu=16000000 -prefs build.mcu=cortex-m4' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (nRF52832 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + buildArduino(config, fqbn, 'hardware/MySensors/nRF5/libraries/MyNRF5Board/examples/MyNRF5Board/MyNRF5Board.ino', key+'_nRF52832') + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF52832 - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_nRF52832') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (nRF52832 - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF52832 - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (nRF52832 - '+key+')', 'Pass', '') + } +} + +def buildnRF51822(config, sketches, String key) { + def fqbn = '-fqbn=MySensors:nRF5:MyBoard_nRF51822:chip=xxaa,bootcode=none,lfclk=lfxo -prefs build.f_cpu=16000000 -prefs build.mcu=cortex-m0' + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (nRF51822 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/') + try { + buildArduino(config, fqbn, 'hardware/MySensors/nRF5/libraries/MyNRF5Board/examples/MyNRF5Board/MyNRF5Board.ino', key+'_nRF51822') + } catch (ex) { + echo "Build failed with: "+ ex.toString() + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF51822 - '+key+')', 'Build error', '${BUILD_URL}') + throw ex + } finally { + parseWarnings(key+'_nRF51822') + } + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (nRF51822 - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new') + if (config.is_pull_request) { + error 'Termiated due to warnings found' + } + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF51822 - '+key+')', 'Build error', '${BUILD_URL}') + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (nRF51822 - '+key+')', 'Pass', '') + } +} + +return this \ No newline at end of file diff --git a/.ci/doxygen.groovy b/.ci/doxygen.groovy new file mode 100644 index 00000000..00247f79 --- /dev/null +++ b/.ci/doxygen.groovy @@ -0,0 +1,47 @@ +#!groovy +def call(config) { + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Documentation)', 'Generating...', '${BUILD_URL}flowGraphTable/') + sh """#!/bin/bash +x + cd ${config.repository_root} + export PROJECTNUMBER=\$( + if [[ \$(git rev-parse --abbrev-ref HEAD) == "master" ]]; then + git describe --tags ; + else + git rev-parse --short HEAD ; + fi + ) + echo 'WARN_LOGFILE=doxygen.log' >> Doxyfile && doxygen""" + warnings canComputeNew: false, canResolveRelativePaths: false, + defaultEncoding: '', + excludePattern: '''.*/sha204_library\\.h,.*/drivers/Linux/.*,.*/cores/esp8266/.*,hardware/.*''', + failedTotalAll: '', healthy: '', includePattern: '', messagesPattern: '', + parserConfigurations: [[parserName: 'Doxygen', pattern: config.repository_root+'doxygen.log']], + unHealthy: '', unstableTotalAll: '0' + publishHTML([allowMissing: false, alwaysLinkToLastBuild: false, keepAll: true, + reportDir: config.repository_root+'Documentation/html', + reportFiles: 'index.html', reportName: 'Doxygen HTML', reportTitles: '']) + + if (!config.is_pull_request) + { + // Publish docs to API server + if (env.BRANCH_NAME == 'master') { + sh """#!/bin/bash + scp -r ${config.repository_root}Documentation/html docs@direct.openhardware.io""" + } else if (env.BRANCH_NAME == 'development') { + sh """#!/bin/bash + scp -r ${config.repository_root}Documentation/html docs@direct.openhardware.io:beta""" + } + } else { + if (currentBuild.currentResult == 'UNSTABLE') { + config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (Documentation)', 'Warnings found', '${BUILD_URL}warnings16Result/new') + error 'Terminating due to doxygen error' + } else if (currentBuild.currentResult == 'FAILURE') { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Documentation)', 'Error generating documentation', '${BUILD_URL}flowGraphTable/') + error 'Terminating due to doxygen error' + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Documentation)', 'Pass', '${BUILD_URL}Doxygen_HTML/index.html') + } + } +} + +return this \ No newline at end of file diff --git a/.ci/gitler.groovy b/.ci/gitler.groovy new file mode 100644 index 00000000..6def1703 --- /dev/null +++ b/.ci/gitler.groovy @@ -0,0 +1,105 @@ +#!groovy +def call(config) { + config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Gitler)', 'Checking...', '${BUILD_URL}flowGraphTable/') + if (env.CHANGE_TARGET == 'master' && + (env.CHANGE_AUTHOR != 'bblacey' || env.CHANGE_AUTHOR != 'd00616' || + env.CHANGE_AUTHOR != 'fallberg' || env.CHANGE_AUTHOR != 'henrikekblad' || + env.CHANGE_AUTHOR != 'marceloaqno' || env.CHANGE_AUTHOR != 'mfalkvidd' || + env.CHANGE_AUTHOR != 'scalz' || env.CHANGE_AUTHOR != 'tbowmo' || + env.CHANGE_AUTHOR != 'tekka007' || env.CHANGE_AUTHOR != 'user2684' || + env.CHANGE_AUTHOR != 'Yveaux')) + { + config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Gitler)', 'This pull request targets master. That is not permitted!', '') + error "This pull request targets master. That is not permitted!" + } + + dir(config.repository_root) { + step([$class: 'GitChangelogRecorder', config: [configFile: 'git-changelog-settings.json', + createFileTemplateContent: ''' +{{#commits}} +{{{messageTitle}}} +{{/commits}} +''', + createFileTemplateFile: '', createFileUseTemplateContent: true, + createFileUseTemplateFile: false, customIssues: [[link: '', name: '', pattern: '', title: ''], + [link: '', name: '', pattern: '', title: '']], dateFormat: 'YYYY-MM-dd HH:mm:ss', + file: 'subjects.txt', fromReference: env.CHANGE_TARGET, fromType: 'ref', gitHubApi: '', + gitHubApiTokenCredentialsId: '', gitHubIssuePattern: '#([0-9]+)', gitHubToken: '', + gitLabApiTokenCredentialsId: '', gitLabProjectName: '', gitLabServer: '', gitLabToken: '', + ignoreCommitsIfMessageMatches: '^Merge.*', + ignoreCommitsWithoutIssue: false, ignoreTagsIfNameMatches: '', + jiraIssuePattern: '\\b[a-zA-Z]([a-zA-Z]+)-([0-9]+)\\b', jiraPassword: '', jiraServer: '', + jiraUsername: '', jiraUsernamePasswordCredentialsId: '', mediaWikiPassword: '', + mediaWikiTemplateContent: '', mediaWikiTemplateFile: '', mediaWikiTitle: '', mediaWikiUrl: '', + mediaWikiUseTemplateContent: false, mediaWikiUseTemplateFile: false, mediaWikiUsername: '', + noIssueName: 'No issue', readableTagName: '/([^/]+?)$', showSummary: false, + showSummaryTemplateContent: '', showSummaryTemplateFile: '', showSummaryUseTemplateContent: false, + showSummaryUseTemplateFile: false, subDirectory: '', timeZone: 'UTC', + toReference: config.git_sha, toType: 'commit', untaggedName: 'Unreleased', + useConfigFile: false, useFile: true, useGitHub: false, useGitHubApiTokenCredentials: false, + useGitLab: false, useGitLabApiTokenCredentials: false, useIgnoreTagsIfNameMatches: false, + useJira: false, useJiraUsernamePasswordCredentialsId: false, useMediaWiki: false, + useReadableTagName: false, useSubDirectory: false] + ]) + step([$class: 'GitChangelogRecorder', config: [configFile: 'git-changelog-settings.json', + createFileTemplateContent: ''' +{{#commits}} +{{#messageBodyItems}} +{{.}} +{{/messageBodyItems}} +{{/commits}} +''', + createFileTemplateFile: '', createFileUseTemplateContent: true, + createFileUseTemplateFile: false, customIssues: [[link: '', name: '', pattern: '', title: ''], + [link: '', name: '', pattern: '', title: '']], dateFormat: 'YYYY-MM-dd HH:mm:ss', + file: 'bodies.txt', fromReference: env.CHANGE_TARGET, fromType: 'ref', gitHubApi: '', + gitHubApiTokenCredentialsId: '', gitHubIssuePattern: '#([0-9]+)', gitHubToken: '', + gitLabApiTokenCredentialsId: '', gitLabProjectName: '', gitLabServer: '', gitLabToken: '', + ignoreCommitsIfMessageMatches: '^Merge.*', + ignoreCommitsWithoutIssue: false, ignoreTagsIfNameMatches: '', + jiraIssuePattern: '\\b[a-zA-Z]([a-zA-Z]+)-([0-9]+)\\b', jiraPassword: '', jiraServer: '', + jiraUsername: '', jiraUsernamePasswordCredentialsId: '', mediaWikiPassword: '', + mediaWikiTemplateContent: '', mediaWikiTemplateFile: '', mediaWikiTitle: '', mediaWikiUrl: '', + mediaWikiUseTemplateContent: false, mediaWikiUseTemplateFile: false, mediaWikiUsername: '', + noIssueName: 'No issue', readableTagName: '/([^/]+?)$', showSummary: false, + showSummaryTemplateContent: '', showSummaryTemplateFile: '', showSummaryUseTemplateContent: false, + showSummaryUseTemplateFile: false, subDirectory: '', timeZone: 'UTC', + toReference: config.git_sha, toType: 'commit', untaggedName: 'Unreleased', + useConfigFile: false, useFile: true, useGitHub: false, useGitHubApiTokenCredentials: false, + useGitLab: false, useGitLabApiTokenCredentials: false, useIgnoreTagsIfNameMatches: false, + useJira: false, useJiraUsernamePasswordCredentialsId: false, useMediaWiki: false, + useReadableTagName: false, useSubDirectory: false] + ]) + } + + ret = sh(returnStatus: true, + script:"""#!/bin/bash + cd ${config.repository_root}/.ci + ./gitler.sh""") + + if (fileExists(config.repository_root+'restyling.patch')) { + emailext ( + subject: "Job '${env.JOB_NAME} #${env.BUILD_NUMBER} [PR#${env.CHANGE_ID}]' failed due to bad code styling", + body: """
Job '${env.JOB_NAME} [PR#${env.CHANGE_ID} - ${env.CHANGE_TITLE}]' failed because code style does not follow the standards.
+ A patch to rectify the errors is attached. You apply the patch using:+ If you disagree to this, please discuss it here.
+ Yours sincerely, Gitler, on behalf of Jenkins""",
+ mimeType: 'text/html', to: '${env.CHANGE_AUTHOR_EMAIL}',
+ attachLog: false, compressLog: false, attachmentsPattern: config.repository_root+'restyling.patch'
+ )
+ }
+ publishHTML([allowMissing: true, alwaysLinkToLastBuild: false, keepAll: true,
+ reportDir: config.repository_root,
+ reportFiles: 'gitler.html', reportName: 'Gitler report', reportTitles: ''])
+ if (ret == 1) {
+ config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Gitler)', 'Commit(s) does not meet coding standards', '${BUILD_URL}Gitler_report/gitler.html')
+ currentBuild.currentResult == 'FAILURE'
+ echo "Termiated due to Gitler assert" // For BFA
+ error 'Termiated due to Gitler assert'
+ } else {
+ config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Gitler)', 'Pass', '')
+ }
+}
+
+return this
\ No newline at end of file
diff --git a/.ci/gitler.sh b/.ci/gitler.sh
new file mode 100755
index 00000000..5cb3b6b0
--- /dev/null
+++ b/.ci/gitler.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+# Yes, this script can be improved immensly...
+cd ..
+
+result=0
+
+echo "No subjects are of invalid size
" > too_long_subjects.txt
+echo "No subjects with leading lower case characters
" > leading_lowercases.txt
+echo "No subjects with trailing periods
" > trailing_periods.txt
+echo "No body lines are too wide
" > too_long_body_lines.txt
+
+too_long_subjects=`awk 'length > 50' subjects.txt`
+if [ -n "$too_long_subjects" ]; then
+ echo "Commit subjects that are too wide (>50 characters):" > too_long_subjects.txt
+ echo "$too_long_subjects" >> too_long_subjects.txt
+ sed -i -e 's/$/
/' too_long_subjects.txt
+ result=1
+fi
+leading_lowercases=`awk '/^[[:lower:][:punct:]]/' subjects.txt`
+if [ -n "$leading_lowercases" ]; then
+ echo "Commit subjects with leading lowercase characters:" > leading_lowercases.txt
+ echo "$leading_lowercases" >> leading_lowercases.txt
+ sed -i -e 's/$/
/' leading_lowercases.txt
+ result=1
+fi
+trailing_periods=`awk '/(\.)$/' subjects.txt`
+if [ -n "$trailing_periods" ]; then
+ echo "Commit subjects with trailing periods:" > trailing_periods.txt
+ echo "$trailing_periods" >> trailing_periods.txt
+ sed -i -e 's/$/
/' trailing_periods.txt
+ result=1
+fi
+
+too_long_body_lines=`awk 'length > 72' bodies.txt`
+if [ -n "$too_long_body_lines" ]; then
+ echo "Body lines that are too wide (>72 characters):" > too_long_body_lines.txt
+ echo "$too_long_body_lines" >> too_long_body_lines.txt
+ sed -i -e 's/$/
/' too_long_body_lines.txt
+ result=1
+fi
+
+printf "%s" "" > gitler.html
+awk 'FNR==1{print "
"}1' too_long_subjects.txt leading_lowercases.txt trailing_periods.txt too_long_body_lines.txt >> gitler.html
+echo "
" >> gitler.html
+if [ $result -ne 0 ]; then
+ echo "You should follow this guide when writing your commit messages.
" >> gitler.html
+ echo "
" >> gitler.html
+ echo "To change the commit message for a single-commit pull request:
" >> gitler.html
+ echo "git checkout <your_branch>
" >> gitler.html
+ echo "git commit --amend
" >> gitler.html
+ echo "git push origin HEAD:<your_branch_on_github> -f
" >> gitler.html
+ echo "
" >> gitler.html
+ echo "To change the commit messages for a multiple-commit pull request:
" >> gitler.html
+ echo "git checkout <your_branch>
" >> gitler.html
+ echo "git rebase -i <sha_of_parent_to_the_earliest_commit_you_want_to_change>
" >> gitler.html
+ echo "Replace \"pick\" with \"r\" or \"reword\" on the commits you need to change message for
" >> gitler.html
+ echo "git push origin HEAD:<your_branch_on_github> -f
" >> gitler.html
+ echo "
" >> gitler.html
+fi
+
+# Evaluate coding style
+astyle --options=.mystools/astyle/config/style.cfg -nq --recursive "*.h" "*.c" "*.cpp"
+git diff > restyling.patch
+if [ -s restyling.patch ]; then
+ echo "This commit is not meeting the coding standards, a mail with a patch has been sent to you that if applied to your PR, will make it meet the coding standards.
" >> gitler.html
+ echo "You apply the patch using:
" >> gitler.html
+ echo "git apply restyling.patch
" >> gitler.html
+ echo "
" >> gitler.html
+ result=1
+else
+ echo "This commit is meeting the coding standards, congratulations!
" >> gitler.html
+ echo "
" >> gitler.html
+ rm restyling.patch
+fi
+
+# Evaluate if there exists booleans in the code tree (not counting this file)
+if git grep -q boolean -- `git ls-files | grep -v gitler.sh`
+then
+ echo "This repository currently contain the boolean keyword. This should be avoided.
" >> gitler.html
+ echo "
" >> gitler.html
+ result=1
+else
+ echo "This repository does not contain any boolean keywords. This is good.
" >> gitler.html
+ echo "
" >> gitler.html
+fi
+
+if [ $result -ne 0 ]; then
+ echo "If you disagree to this, please discuss it in the GitHub pull request thread.
" >> gitler.html
+ echo "
" >> gitler.html
+fi
+echo "Yours sincerely, Gitler, on behalf of Jenkins
" >> gitler.html
+printf "%s" "" >> gitler.html
+exit $result
diff --git a/.ci/linux.groovy b/.ci/linux.groovy
new file mode 100644
index 00000000..672a530b
--- /dev/null
+++ b/.ci/linux.groovy
@@ -0,0 +1,71 @@
+#!groovy
+def buildLinux(config, String configuration, String key) {
+ def linux_configurer = './configure '
+ def linux_configure_standard_args = '--my-rs485-serial-port=/dev/ttyS0 --my-controller-ip-address=1.2.3.4 '+
+ '--my-mqtt-subscribe-topic-prefix=dummy --my-mqtt-publish-topic-prefix==dummy --my-mqtt-client-id=0 '
+ def linux_builder = 'make '
+ def linux_builder_standard_args = '-j1'
+ def linux_build_cmd = linux_builder + linux_builder_standard_args
+ def linux_configure_cmd = linux_configurer + linux_configure_standard_args + configuration
+ sh """#!/bin/bash
+ printf "\\e[1m\\e[32mBuilding \\e[0musing \\e[1m\\e[36m${linux_build_cmd} \\e[0mwith \\e[1m\\e[34m${linux_configure_standard_args} ${configuration}\\e[0m\\n"
+ cd ${config.library_root}
+ ${linux_configure_cmd}
+ ${linux_build_cmd} 2>> compiler_${key}.log"""
+ warnings canComputeNew: false, canResolveRelativePaths: false,
+ defaultEncoding: '',
+ excludePattern: '''.*/EEPROM\\.h,.*/Dns\\.cpp,.*/socket\\.cpp,.*/util\\.h,.*/Servo\\.cpp,
+ .*/Adafruit_NeoPixel\\.cpp,.*/UIPEthernet.*,.*/SoftwareSerial\\.cpp,
+ .*/pins_arduino\\.h,.*/Stream\\.cpp,.*/USBCore\\.cpp,.*/Wire\\.cpp,
+ .*/hardware/esp8266.*,.*/libraries/SD/.*''',
+ failedTotalAll: '', healthy: '', includePattern: '', messagesPattern: '',
+ parserConfigurations: [[parserName: 'GNU Make + GNU C Compiler (gcc)', pattern: config.library_root+'compiler_'+key+'.log']],
+ unHealthy: '', unstableTotalAll: '0'
+ sh """#!/bin/bash
+ echo "Compiler warnings/errors:"
+ printf "\\e[101m"
+ cat ${config.library_root}compiler_${key}.log
+ printf "\\e[0m"
+ rm ${config.library_root}compiler_${key}.log"""
+}
+
+def buildSerial(config) {
+ config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Linux builds - Serial GW)', 'Building...', '${BUILD_URL}flowGraphTable/')
+ buildLinux(config, '--my-debug=disable --my-transport=none --my-gateway=serial', 'Serial')
+ if (currentBuild.currentResult == 'UNSTABLE') {
+ config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (Linux builds - Serial GW)', 'Warnings found', '${BUILD_URL}warnings28Result/new')
+ error 'Termiated due to warnings found'
+ } else if (currentBuild.currentResult == 'FAILURE') {
+ config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Linux builds - Serial GW)', 'Build error', '${BUILD_URL}')
+ } else {
+ config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Linux builds - Serial GW)', 'Pass', '')
+ }
+}
+
+def buildEthernet(config) {
+ config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Linux builds - Ethernet GW)', 'Building...', '${BUILD_URL}flowGraphTable/')
+ buildLinux(config, '--my-debug=enable --my-transport=rs485 --my-gateway=ethernet', 'Ethernet')
+ if (currentBuild.currentResult == 'UNSTABLE') {
+ config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (Linux builds - Ethernet GW)', 'Warnings found', '${BUILD_URL}warnings28Result/new')
+ error 'Termiated due to warnings found'
+ } else if (currentBuild.currentResult == 'FAILURE') {
+ config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Linux builds - Ethernet GW)', 'Build error', '${BUILD_URL}')
+ } else {
+ config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Linux builds - Ethernet GW)', 'Pass', '')
+ }
+}
+
+def buildMQTT(config) {
+ config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Linux builds - MQTT GW)', 'Building...', '${BUILD_URL}flowGraphTable/')
+ buildLinux(config, '--my-debug=disable --my-transport=none --my-gateway=mqtt', 'MQTT')
+ if (currentBuild.currentResult == 'UNSTABLE') {
+ config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (Linux builds - MQTT GW)', 'Warnings found', '${BUILD_URL}warnings28Result/new')
+ error 'Termiated due to warnings found'
+ } else if (currentBuild.currentResult == 'FAILURE') {
+ config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Linux builds - MQTT GW)', 'Build error', '${BUILD_URL}')
+ } else {
+ config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Linux builds - MQTT GW)', 'Pass', '')
+ }
+}
+
+return this
\ No newline at end of file
diff --git a/.ci/pipeline.groovy b/.ci/pipeline.groovy
new file mode 100644
index 00000000..f3c244b5
--- /dev/null
+++ b/.ci/pipeline.groovy
@@ -0,0 +1,196 @@
+#!groovy
+
+def call(Closure body) {
+ def config = [:]
+ body.resolveStrategy = Closure.DELEGATE_FIRST
+ body.delegate = config
+ body()
+
+ config.pr = load(config.repository_root+'.ci/pr-toolbox.groovy')
+ def linux = load(config.repository_root+'.ci/linux.groovy')
+ def arduino = load(config.repository_root+'.ci/arduino.groovy')
+
+ if (env.CHANGE_ID) {
+ config.is_pull_request = true
+ echo "Building pull request: #"+env.CHANGE_ID+"\nTarget branch: "+env.CHANGE_TARGET
+ config.git_sha = sh(returnStdout: true,
+ script: """#!/bin/bash
+ cd ${config.repository_root}
+ git log -n 1 --pretty=format:'%H' refs/remotes/origin/PR-${env.CHANGE_ID}""").trim()
+ } else {
+ config.is_pull_request = false
+ echo "Building branch: "+env.BRANCH_NAME
+ config.git_sha = sh(returnStdout: true,
+ script: """#!/bin/bash
+ cd ${config.repository_root}
+ git log -n 1 --pretty=format:'%H' refs/remotes/origin/${env.BRANCH_NAME}""").trim()
+ config.pr.setBuildStatus(config, 'PENDING', 'Toll gate', 'Validating...', '${BUILD_URL}flowGraphTable/')
+ }
+
+ try {
+ ansiColor('xterm') {
+ if (config.is_pull_request) {
+ def gitler = load(config.repository_root+'.ci/gitler.groovy')
+ stage('Gitler') {
+ gitler(config)
+ }
+ }
+
+ stage('Preparation') {
+ checkout(changelog: false, poll: false, scm: [$class: 'GitSCM', branches: [[name: '*/master']],
+ extensions: [
+ [$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: true],
+ [$class: 'RelativeTargetDirectory', relativeTargetDir: 'hardware/MySensors/avr']
+ ],
+ userRemoteConfigs: [[url: 'https://github.com/mysensors/ArduinoHwAVR.git']]])
+ checkout(changelog: false, poll: false, scm: [$class: 'GitSCM', branches: [[name: '*/master']],
+ extensions: [
+ [$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: true],
+ [$class: 'RelativeTargetDirectory', relativeTargetDir: 'hardware/MySensors/samd']
+ ],
+ userRemoteConfigs: [[url: 'https://github.com/mysensors/ArduinoHwSAMD.git']]])
+ checkout(changelog: false, poll: false, scm: [$class: 'GitSCM', branches: [[name: '*/master']],
+ extensions: [
+ [$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: true],
+ [$class: 'RelativeTargetDirectory', relativeTargetDir: 'hardware/MySensors/nRF5']
+ ],
+ userRemoteConfigs: [[url: 'https://github.com/mysensors/ArduinoHwNRF5.git']]])
+
+ config.tests = findFiles(glob: config.library_root+'tests/**/*.ino')
+ config.examples = findFiles(glob: config.library_root+'examples/**/*.ino')
+
+ }
+
+ parallel Doxygen: {
+ if (!config.nightly_arduino_ide) {
+ stage('Doxygen') {
+ def doxygen = load(config.repository_root+'.ci/doxygen.groovy')
+ doxygen(config)
+ }
+ }
+ }, CodeAnalysis: {
+ if (!config.nightly_arduino_ide) {
+ if (config.is_pull_request) {
+ def analysis = load(config.repository_root+'.ci/static_analysis.groovy')
+ stage('Cppcheck') {
+ analysis.cppCheck(config)
+ }
+ }
+ }
+ }, LinuxBuilds: {
+ if (!config.nightly_arduino_ide) {
+ stage('LinuxGwSerial') {
+ linux.buildSerial(config)
+ }
+ stage('LinuxGwEthernet') {
+ linux.buildEthernet(config)
+ }
+ stage('LinuxGwMQTT') {
+ linux.buildMQTT(config)
+ }
+ }
+ }, ArduinoBuilds: {
+ lock(quantity: 1, resource: 'arduinoEnv') {
+ stage('MySensorsMicro (tests)') {
+ arduino.buildMySensorsMicro(config, config.tests, 'Tests')
+ }
+ stage('MySensorsGW (tests)') {
+ arduino.buildMySensorsGw(config, config.tests, 'Tests')
+ }
+ stage('nRF52832 (tests)') {
+ arduino.buildnRF52832(config, config.tests, 'Tests')
+ }
+ stage('nRF51822 (tests)') {
+ arduino.buildnRF51822(config, config.tests, 'Tests')
+ }
+ stage('nRF5 (tests)') {
+ arduino.buildnRF5(config, config.tests, 'Tests')
+ }
+ stage('ESP8266 (tests)') {
+ arduino.buildEsp8266(config, config.tests, 'Tests')
+ }
+ stage('ArduinoNano (tests)') {
+ arduino.buildArduinoNano(config, config.tests, 'Tests')
+ }
+ stage('ArduinoUno (tests)') {
+ arduino.buildArduinoUno(config, config.tests, 'Tests')
+ }
+ stage('ArduinoProMini (tests)') {
+ arduino.buildArduinoPro(config, config.tests, 'Tests')
+ }
+ stage('ArduinoMega (tests)') {
+ arduino.buildArduinoMega(config, config.tests, 'Tests')
+ }
+ stage('MySensorsMicro (examples)') {
+ arduino.buildMySensorsMicro(config, config.examples, 'Examples')
+ }
+ stage('MySensorsGW (examples)') {
+ arduino.buildMySensorsGw(config, config.examples, 'Examples')
+ }
+ // No point in building examples for nRF52832 yet
+ /*
+ stage('nRF52832 (examples)') {
+ arduino.buildnRF52832(config, config.examples, 'Examples')
+ }
+ */
+ // No point in building examples for nRF51822 yet
+ /*
+ stage('nRF51822 (examples)') {
+ arduino.buildnRF51822(config, config.examples, 'Examples')
+ }
+ */
+ stage('nRF5 (examples)') {
+ arduino.buildnRF5(config, config.examples, 'Examples')
+ }
+ stage('ESP8266 (examples)') {
+ arduino.buildEsp8266(config, config.examples, 'Examples')
+ }
+ stage('ArduinoNano (examples)') {
+ arduino.buildArduinoNano(config, config.examples, 'Examples')
+ }
+ stage('ArduinoUno (examples)') {
+ arduino.buildArduinoUno(config, config.examples, 'Examples')
+ }
+ stage('ArduinoProMini (examples)') {
+ arduino.buildArduinoPro(config, config.examples, 'Examples')
+ }
+ stage('ArduinoMega (examples)') {
+ arduino.buildArduinoMega(config, config.examples, 'Examples')
+ }
+ }
+ }, failFast: true
+ }
+ } catch(ex) {
+ currentBuild.result = 'FAILURE'
+ throw ex
+ } finally {
+ if (currentBuild.result != 'SUCCESS')
+ {
+ config.pr.setBuildStatus(config, 'ERROR', 'Toll gate', 'Failed', '${BUILD_URL}flowGraphTable/')
+ if (config.is_pull_request) {
+ slackSend color: 'danger',
+ message: "Job '${env.JOB_NAME} <${env.BUILD_URL}|#${env.BUILD_NUMBER}> <${env.CHANGE_URL}|PR#${env.CHANGE_ID} - ${env.CHANGE_TITLE}>' failed with result ${currentBuild.result}."
+ emailext (
+ subject: "Job '${env.JOB_NAME} #${env.BUILD_NUMBER}' failed",
+ body: """Job '${env.JOB_NAME} #${env.BUILD_NUMBER} (PR#${env.CHANGE_ID} - ${env.CHANGE_TITLE})' ended with result ${currentBuild.result}.
+
Check attached console output or here for a hint on what the problem might be.""",
+ mimeType: 'text/html', to: env.CHANGE_AUTHOR_EMAIL, attachLog: true, compressLog: false
+ )
+ } else {
+ slackSend color: 'danger',
+ message: "Job '${env.JOB_NAME} <${env.BUILD_URL}|#${env.BUILD_NUMBER}> ${env.BRANCH_NAME}]' failed with result ${currentBuild.result}."
+ emailext (
+ subject: "Job '${env.JOB_NAME} #${env.BUILD_NUMBER} ${env.BRANCH_NAME}' failed",
+ body: """Job '${env.JOB_NAME} #${env.BUILD_NUMBER} (${env.BRANCH_NAME})' ended with result ${currentBuild.result}.
+
Check attached console output or here for a hint on what the problem might be.""",
+ mimeType: 'text/html', to: 'builds@mysensors.org', attachLog: true, compressLog: false
+ )
+ }
+ }
+ else
+ {
+ config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate', 'Pass', '')
+ }
+ }
+}
+return this
\ No newline at end of file
diff --git a/.ci/pr-toolbox.groovy b/.ci/pr-toolbox.groovy
new file mode 100644
index 00000000..52b2ce05
--- /dev/null
+++ b/.ci/pr-toolbox.groovy
@@ -0,0 +1,16 @@
+#!groovy
+def setBuildStatus(config, String state, String context, String message, String backref) {
+ if (config.is_pull_request) {
+ step([$class: 'GitHubCommitStatusSetter',
+ reposSource: [$class: 'ManuallyEnteredRepositorySource', url: "https://github.com/${config.github_organization}/${config.repository_name}"],
+ errorHandlers: [[$class: 'ShallowAnyErrorHandler']],
+ contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: context],
+ commitShaSource: [$class: "ManuallyEnteredShaSource", sha: config.git_sha],
+ statusBackrefSource: [$class: 'ManuallyEnteredBackrefSource', backref: backref],
+ statusResultSource: [$class: 'ConditionalStatusResultSource',
+ results: [[$class: 'AnyBuildResult', message: message, state: state]]]]
+ )
+ }
+}
+
+return this
\ No newline at end of file
diff --git a/.ci/static_analysis.groovy b/.ci/static_analysis.groovy
new file mode 100644
index 00000000..41725cc9
--- /dev/null
+++ b/.ci/static_analysis.groovy
@@ -0,0 +1,40 @@
+#!groovy
+def cppCheck(config) {
+ config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Code analysis - Cppcheck)', 'Running...', '${BUILD_URL}flowGraphTable/')
+ // We could consider running Cppcheck for GNU as well, but to avoid so many duplicates, we stick to AVR
+ sh """#!/bin/bash +x
+ cd ${config.repository_root}
+ echo "Doing cppcheck for AVR..."
+ git diff --name-only origin/${env.CHANGE_TARGET}..${config.git_sha} | sed '/.ci\\/gitler.sh/d' | sed '/README.md/d' | cppcheck -j 4 --file-list=- --enable=style,information --platform=.mystools/cppcheck/config/avr.xml --suppressions-list=.mystools/cppcheck/config/suppressions.cfg --includes-file=.mystools/cppcheck/config/includes.cfg --language=c++ --xml --xml-version=2 2> cppcheck-avr.xml
+ cppcheck-htmlreport --file="cppcheck-avr.xml" --title="cppcheck-avr" --report-dir=cppcheck-avr_cppcheck_reports --source-dir=."""
+
+ publishHTML([allowMissing: false, alwaysLinkToLastBuild: false, keepAll: true,
+ reportDir: config.repository_root+'cppcheck-avr_cppcheck_reports',
+ reportFiles: 'index.html', reportName: 'CppCheck AVR', reportTitles: ''])
+
+ step([$class: 'ViolationsToGitHubRecorder',
+ config: [
+ repositoryName: config.repository_name,
+ pullRequestId: env.CHANGE_ID,
+ createCommentWithAllSingleFileComments: true,
+ createSingleFileComments: true,
+ commentOnlyChangedContent: true,
+ keepOldComments: false,
+ violationConfigs: [[pattern: '.*/cppcheck-avr\\.xml$', parser: 'CPPCHECK', reporter: 'Cppcheck'],]
+ ]
+ ])
+ ret = sh(returnStatus: true,
+ script: "#!/bin/bash +e\n"+
+ "cd ${config.repository_root}\n"+
+ "grep -q \"