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:
+ git apply restyling.patch

+ 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 \"0total\" cppcheck-avr_cppcheck_reports/index.html || exit_code=\$?\n"+ + "exit \$((exit_code == 0 ? 0 : 1))") + if (ret == 1) { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Code analysis - Cppcheck)', 'Issues found (but are not considered critical)', '${BUILD_URL}CppCheck_AVR/index.html') + //currentBuild.result = 'UNSTABLE' + //error 'Terminating due to Cppcheck error' + } else { + config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Code analysis - Cppcheck)', 'Pass', '') + } +} + +return this \ No newline at end of file diff --git a/README.md b/README.md index 301da5da..18ea2124 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ Please visit www.mysensors.org for more information Doxygen ------- -[master](https://ci.mysensors.org/job/Verifiers/job/MySensors/branch/master/Doxygen_HTML/index.html) [development](https://ci.mysensors.org/job/Verifiers/job/MySensors/branch/development/Doxygen_HTML/index.html) +[master](https://www.mysensors.org/apidocs/index.html) [development](https://www.mysensors.org/apidocs-beta/index.html) CI statuses ----------- Current build status of master branch: [![Build Status](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/master/badge/icon)](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/master/) -Current build status of development branch: [![Build Status](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/development/badge/icon)](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/development/) +Current build status of development branch: [![Build Status](https://ci.mysensors.org/job/MySensors/job/MySensors/job/development/badge/icon)](https://ci.mysensors.org/job/MySensors/job/MySensors/job/development/) Current build status of master branch (nightly build of Arduino IDE): [![Build Status](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/master/badge/icon)](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/master/) -Current build status of development branch (nightly build of Arduino IDE): [![Build Status](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/development/badge/icon)](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/development/) \ No newline at end of file +Current build status of development branch (nightly build of Arduino IDE): [![Build Status](https://ci.mysensors.org/job/MySensors-nightly-IDE/job/MySensors/job/development/badge/icon)](https://ci.mysensors.org/job/MySensors-nightly-IDE/job/MySensors/job/development/) \ No newline at end of file diff --git a/hal/architecture/MyHw.h b/hal/architecture/MyHw.h index 2afe9ca0..3fd9c366 100644 --- a/hal/architecture/MyHw.h +++ b/hal/architecture/MyHw.h @@ -26,6 +26,10 @@ #ifndef MyHw_h #define MyHw_h +/** + * @def MY_HWID_PADDING_BYTE + * @brief HwID padding byte + */ #define MY_HWID_PADDING_BYTE (0xAAu) // Implement these as functions or macros