Implement Jenkins pipelines as code (#948)

Jenkins has been updated with new jobs that scan and execute
Jenkinsfiles. These Jenkinsfiles encapsulates all logic for building
and validating the commits in both pull requests and merged pull
requests.

Filestructure is as follows:
  .ci/Jenkinsfile - This is the main Jenkinsfile that handles normal
                    pull request builds as well as merged pull request
                    post-validation.
		    Any repository in the MySensors GitHub organization
		    can implement this file and Jenkins will pick up
		    and execute it automatically.
  .ci/Jenkinsfile-nightly - This perform merged pull request
                            post-validation using the nightly builds of
			    the Arduino IDE to provide early compliancy
			    status of the MySensors library for
			    upcoming releases of the Arduino IDE
  .ci/ - This folder contain all the auxiliary scripts executed by the
         Jenkinsfiles as well

Fixes #934
This commit is contained in:
Patrick Fallberg
2017-10-13 20:55:02 +02:00
committed by GitHub
parent 8cb9b26920
commit 901258f165
12 changed files with 962 additions and 3 deletions

18
.ci/Jenkinsfile vendored Normal file
View File

@@ -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
}
}

18
.ci/Jenkinsfile-nightly Normal file
View File

@@ -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
}
}

351
.ci/arduino.groovy Normal file
View File

@@ -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

47
.ci/doxygen.groovy Normal file
View File

@@ -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

105
.ci/gitler.groovy Normal file
View File

@@ -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: """<p>Job '${env.JOB_NAME} [<a href="${env.CHANGE_URL}">PR#${env.CHANGE_ID}</a> - ${env.CHANGE_TITLE}]' failed because code style does not follow the standards.</p>
A patch to rectify the errors is attached. You apply the patch using:<br>
git apply restyling.patch<p>
If you disagree to this, please discuss it <a href="${env.CHANGE_URL}">here</a>.<p>
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

93
.ci/gitler.sh Executable file
View File

@@ -0,0 +1,93 @@
#!/bin/bash
# Yes, this script can be improved immensly...
cd ..
result=0
echo "No subjects are of invalid size<br>" > too_long_subjects.txt
echo "No subjects with leading lower case characters<br>" > leading_lowercases.txt
echo "No subjects with trailing periods<br>" > trailing_periods.txt
echo "No body lines are too wide<br>" > too_long_body_lines.txt
too_long_subjects=`awk 'length > 50' subjects.txt`
if [ -n "$too_long_subjects" ]; then
echo "<b>Commit subjects that are too wide (&gt;50 characters):</b>" > too_long_subjects.txt
echo "$too_long_subjects" >> too_long_subjects.txt
sed -i -e 's/$/<br>/' too_long_subjects.txt
result=1
fi
leading_lowercases=`awk '/^[[:lower:][:punct:]]/' subjects.txt`
if [ -n "$leading_lowercases" ]; then
echo "<b>Commit subjects with leading lowercase characters:</b>" > leading_lowercases.txt
echo "$leading_lowercases" >> leading_lowercases.txt
sed -i -e 's/$/<br>/' leading_lowercases.txt
result=1
fi
trailing_periods=`awk '/(\.)$/' subjects.txt`
if [ -n "$trailing_periods" ]; then
echo "<b>Commit subjects with trailing periods:</b>" > trailing_periods.txt
echo "$trailing_periods" >> trailing_periods.txt
sed -i -e 's/$/<br>/' trailing_periods.txt
result=1
fi
too_long_body_lines=`awk 'length > 72' bodies.txt`
if [ -n "$too_long_body_lines" ]; then
echo "<b>Body lines that are too wide (&gt;72 characters):</b>" > too_long_body_lines.txt
echo "$too_long_body_lines" >> too_long_body_lines.txt
sed -i -e 's/$/<br>/' too_long_body_lines.txt
result=1
fi
printf "%s" "<html>" > gitler.html
awk 'FNR==1{print "<br>"}1' too_long_subjects.txt leading_lowercases.txt trailing_periods.txt too_long_body_lines.txt >> gitler.html
echo "<br>" >> gitler.html
if [ $result -ne 0 ]; then
echo "You should follow <a href="http://chris.beams.io/posts/git-commit">this guide</a> when writing your commit messages.<br>" >> gitler.html
echo "<br>" >> gitler.html
echo "To change the commit message for a single-commit pull request:<br>" >> gitler.html
echo "git checkout &lt;your_branch&gt;<br>" >> gitler.html
echo "git commit --amend<br>" >> gitler.html
echo "git push origin HEAD:&lt;your_branch_on_github&gt; -f<br>" >> gitler.html
echo "<br>" >> gitler.html
echo "To change the commit messages for a multiple-commit pull request:<br>" >> gitler.html
echo "git checkout &lt;your_branch&gt;<br>" >> gitler.html
echo "git rebase -i &lt;sha_of_parent_to_the_earliest_commit_you_want_to_change&gt;<br>" >> gitler.html
echo "Replace \"pick\" with \"r\" or \"reword\" on the commits you need to change message for<br>" >> gitler.html
echo "git push origin HEAD:&lt;your_branch_on_github&gt; -f<br>" >> gitler.html
echo "<br>" >> 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 "<b>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.</b><br>" >> gitler.html
echo "You apply the patch using:<br>" >> gitler.html
echo "git apply restyling.patch<br>" >> gitler.html
echo "<br>" >> gitler.html
result=1
else
echo "This commit is meeting the coding standards, congratulations!<br>" >> gitler.html
echo "<br>" >> 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 "<b>This repository currently contain the boolean keyword. This should be avoided.</b><br>" >> gitler.html
echo "<br>" >> gitler.html
result=1
else
echo "This repository does not contain any boolean keywords. This is good.<br>" >> gitler.html
echo "<br>" >> gitler.html
fi
if [ $result -ne 0 ]; then
echo "<b>If you disagree to this, please discuss it in the GitHub pull request thread.</b><br>" >> gitler.html
echo "<br>" >> gitler.html
fi
echo "Yours sincerely, Gitler, on behalf of Jenkins<br>" >> gitler.html
printf "%s" "</html>" >> gitler.html
exit $result

71
.ci/linux.groovy Normal file
View File

@@ -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

196
.ci/pipeline.groovy Normal file
View File

@@ -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} <a href="${env.BUILD_URL}">#${env.BUILD_NUMBER}</a> (<a href="${env.CHANGE_URL}">PR#${env.CHANGE_ID} - ${env.CHANGE_TITLE}</a>)' ended with result ${currentBuild.result}.
<br>Check attached console output or <a href="${env.BUILD_URL}">here</a> 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} <a href="${env.BUILD_URL}">#${env.BUILD_NUMBER}</a> (${env.BRANCH_NAME})' ended with result ${currentBuild.result}.
<br>Check attached console output or <a href="${env.BUILD_URL}">here</a> 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

16
.ci/pr-toolbox.groovy Normal file
View File

@@ -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

View File

@@ -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 \"<td>0</td><td>total</td>\" 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

View File

@@ -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/)
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/)

View File

@@ -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