Skip to content

Commit 11c4def

Browse files
authored
Implement Jenkins pipelines as code (#2)
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
1 parent 560bf07 commit 11c4def

File tree

8 files changed

+451
-0
lines changed

8 files changed

+451
-0
lines changed

.ci/Jenkinsfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!groovy
2+
node {
3+
deleteDir() // Purge workspace
4+
// Checkout the repo to get the necessary groovy scripts (and place it in the required subdirectory)
5+
dir('hardware/MySensors/nRF5') {
6+
checkout scm
7+
}
8+
def pipeline = load('hardware/MySensors/nRF5/.ci/pipeline.groovy')
9+
10+
// Invoke the main pipeline
11+
pipeline {
12+
library_root = 'MySensors/' // Location of the MySensors library
13+
repository_root = 'hardware/MySensors/nRF5/' // Location of the repository root
14+
github_organization = 'mysensors' // Name of the GitHub Organization
15+
repository_name = 'ArduinoHwNRF5' // Name of the repository on GitHub
16+
nightly_arduino_ide = false // Pick Arduino IDE variant to use
17+
}
18+
}

.ci/Jenkinsfile-nightly

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!groovy
2+
node {
3+
deleteDir() // Purge workspace
4+
// Checkout the repo to get the necessary groovy scripts (and place it in the required subdirectory)
5+
dir('hardware/MySensors/nRF5') {
6+
checkout scm
7+
}
8+
def pipeline = load('hardware/MySensors/nRF5/.ci/pipeline.groovy')
9+
10+
// Invoke the main pipeline
11+
pipeline {
12+
library_root = 'MySensors/' // Location of the MySensors library
13+
repository_root = 'hardware/MySensors/nRF5/' // Location of the repository root
14+
github_organization = 'mysensors' // Name of the GitHub Organization
15+
repository_name = 'ArduinoHwNRF5' // Name of the repository on GitHub
16+
nightly_arduino_ide = true // Pick Arduino IDE variant to use
17+
}
18+
}

.ci/arduino.groovy

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!groovy
2+
def buildArduino(config, String buildFlags, String sketch, String key) {
3+
def root = '/opt/arduino-1.8.2/'
4+
if (config.nightly_arduino_ide)
5+
{
6+
root = '/opt/arduino-nightly/'
7+
}
8+
def esp8266_tools = '/opt/arduino-nightly/hardware/esp8266com/esp8266/tools'
9+
def jenkins_root = '/var/lib/jenkins/'
10+
def builder = root+'arduino-builder'
11+
def standard_args = ' -warnings="all"' //-verbose=true
12+
def builder_specifics = ' -hardware '+root+'hardware -tools '+root+'hardware/tools/avr -tools '+
13+
root+'tools-builder -tools '+esp8266_tools+' -built-in-libraries '+root+'libraries'
14+
def jenkins_packages = jenkins_root+'.arduino15/packages'
15+
def site_specifics = ' -hardware '+jenkins_packages+' -tools '+jenkins_packages
16+
def repo_specifics = ' -hardware hardware -libraries . '
17+
def build_cmd = builder+standard_args+builder_specifics+site_specifics+repo_specifics+buildFlags
18+
sh """#!/bin/bash
19+
printf "\\e[1m\\e[32mBuilding \\e[34m${sketch} \\e[0musing \\e[1m\\e[36m${build_cmd}\\e[0m\\n"
20+
${build_cmd} ${sketch} 2>> compiler_${key}.log"""
21+
}
22+
23+
def parseWarnings(String key) {
24+
warnings canResolveRelativePaths: false, canRunOnFailed: true, categoriesPattern: '',
25+
defaultEncoding: '',
26+
excludePattern: '''.*/EEPROM\\.h,.*/Dns\\.cpp,.*/socket\\.cpp,.*/util\\.h,.*/Servo\\.cpp,
27+
.*/Adafruit_NeoPixel\\.cpp,.*/UIPEthernet.*,.*/SoftwareSerial\\.cpp,
28+
.*/pins_arduino\\.h,.*/Stream\\.cpp,.*/USBCore\\.cpp,.*/Wire\\.cpp,
29+
.*/hardware/esp8266.*,.*/libraries/SD/.*''',
30+
healthy: '', includePattern: '', messagesPattern: '',
31+
parserConfigurations: [[parserName: 'Arduino/AVR', pattern: 'compiler_'+key+'.log']],
32+
unHealthy: '', unstableNewAll: '0', unstableTotalAll: '0'
33+
sh """#!/bin/bash
34+
echo "Compiler warnings/errors:"
35+
printf "\\e[101m"
36+
cat compiler_${key}.log
37+
printf "\\e[0m"
38+
rm compiler_${key}.log"""
39+
}
40+
41+
def buildnRF52832(config, sketches, String key) {
42+
def fqbn = '-fqbn=MySensors:nRF5:MyBoard_nRF52832:bootcode=none,lfclk=lfxo,reset=notenable -prefs build.f_cpu=16000000 -prefs build.mcu=cortex-m4'
43+
config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (nRF52832 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/')
44+
try {
45+
buildArduino(config, fqbn, config.repository_root+'libraries/MyNRF5Board/examples/MyNRF5Board/MyNRF5Board.ino', key+'_nRF52832')
46+
} catch (ex) {
47+
echo "Build failed with: "+ ex.toString()
48+
config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF52832 - '+key+')', 'Build error', '${BUILD_URL}')
49+
throw ex
50+
} finally {
51+
parseWarnings(key+'_nRF52832')
52+
}
53+
if (currentBuild.currentResult == 'UNSTABLE') {
54+
config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (nRF52832 - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new')
55+
if (config.is_pull_request) {
56+
error 'Termiated due to warnings found'
57+
}
58+
} else if (currentBuild.currentResult == 'FAILURE') {
59+
config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF52832 - '+key+')', 'Build error', '${BUILD_URL}')
60+
} else {
61+
config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (nRF52832 - '+key+')', 'Pass', '')
62+
}
63+
}
64+
65+
def buildnRF51822(config, sketches, String key) {
66+
def fqbn = '-fqbn=MySensors:nRF5:MyBoard_nRF51822:chip=xxaa,bootcode=none,lfclk=lfxo -prefs build.f_cpu=16000000 -prefs build.mcu=cortex-m0'
67+
config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (nRF51822 - '+key+')', 'Building...', '${BUILD_URL}flowGraphTable/')
68+
try {
69+
buildArduino(config, fqbn, config.repository_root+'libraries/MyNRF5Board/examples/MyNRF5Board/MyNRF5Board.ino', key+'_nRF51822')
70+
} catch (ex) {
71+
echo "Build failed with: "+ ex.toString()
72+
config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF51822 - '+key+')', 'Build error', '${BUILD_URL}')
73+
throw ex
74+
} finally {
75+
parseWarnings(key+'_nRF51822')
76+
}
77+
if (currentBuild.currentResult == 'UNSTABLE') {
78+
config.pr.setBuildStatus(config, 'ERROR', 'Toll gate (nRF51822 - '+key+')', 'Warnings found', '${BUILD_URL}warnings2Result/new')
79+
if (config.is_pull_request) {
80+
error 'Termiated due to warnings found'
81+
}
82+
} else if (currentBuild.currentResult == 'FAILURE') {
83+
config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (nRF51822 - '+key+')', 'Build error', '${BUILD_URL}')
84+
} else {
85+
config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (nRF51822 - '+key+')', 'Pass', '')
86+
}
87+
}
88+
89+
return this

.ci/gitler.groovy

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!groovy
2+
def call(config) {
3+
config.pr.setBuildStatus(config, 'PENDING', 'Toll gate (Gitler)', 'Checking...', '${BUILD_URL}flowGraphTable/')
4+
5+
dir(config.repository_root) {
6+
step([$class: 'GitChangelogRecorder', config: [configFile: 'git-changelog-settings.json',
7+
createFileTemplateContent: '''
8+
{{#commits}}
9+
{{{messageTitle}}}
10+
{{/commits}}
11+
''',
12+
createFileTemplateFile: '', createFileUseTemplateContent: true,
13+
createFileUseTemplateFile: false, customIssues: [[link: '', name: '', pattern: '', title: ''],
14+
[link: '', name: '', pattern: '', title: '']], dateFormat: 'YYYY-MM-dd HH:mm:ss',
15+
file: 'subjects.txt', fromReference: env.CHANGE_TARGET, fromType: 'ref', gitHubApi: '',
16+
gitHubApiTokenCredentialsId: '', gitHubIssuePattern: '#([0-9]+)', gitHubToken: '',
17+
gitLabApiTokenCredentialsId: '', gitLabProjectName: '', gitLabServer: '', gitLabToken: '',
18+
ignoreCommitsIfMessageMatches: '^Merge.*',
19+
ignoreCommitsWithoutIssue: false, ignoreTagsIfNameMatches: '',
20+
jiraIssuePattern: '\\b[a-zA-Z]([a-zA-Z]+)-([0-9]+)\\b', jiraPassword: '', jiraServer: '',
21+
jiraUsername: '', jiraUsernamePasswordCredentialsId: '', mediaWikiPassword: '',
22+
mediaWikiTemplateContent: '', mediaWikiTemplateFile: '', mediaWikiTitle: '', mediaWikiUrl: '',
23+
mediaWikiUseTemplateContent: false, mediaWikiUseTemplateFile: false, mediaWikiUsername: '',
24+
noIssueName: 'No issue', readableTagName: '/([^/]+?)$', showSummary: false,
25+
showSummaryTemplateContent: '', showSummaryTemplateFile: '', showSummaryUseTemplateContent: false,
26+
showSummaryUseTemplateFile: false, subDirectory: '', timeZone: 'UTC',
27+
toReference: config.git_sha, toType: 'commit', untaggedName: 'Unreleased',
28+
useConfigFile: false, useFile: true, useGitHub: false, useGitHubApiTokenCredentials: false,
29+
useGitLab: false, useGitLabApiTokenCredentials: false, useIgnoreTagsIfNameMatches: false,
30+
useJira: false, useJiraUsernamePasswordCredentialsId: false, useMediaWiki: false,
31+
useReadableTagName: false, useSubDirectory: false]
32+
])
33+
step([$class: 'GitChangelogRecorder', config: [configFile: 'git-changelog-settings.json',
34+
createFileTemplateContent: '''
35+
{{#commits}}
36+
{{#messageBodyItems}}
37+
{{.}}
38+
{{/messageBodyItems}}
39+
{{/commits}}
40+
''',
41+
createFileTemplateFile: '', createFileUseTemplateContent: true,
42+
createFileUseTemplateFile: false, customIssues: [[link: '', name: '', pattern: '', title: ''],
43+
[link: '', name: '', pattern: '', title: '']], dateFormat: 'YYYY-MM-dd HH:mm:ss',
44+
file: 'bodies.txt', fromReference: env.CHANGE_TARGET, fromType: 'ref', gitHubApi: '',
45+
gitHubApiTokenCredentialsId: '', gitHubIssuePattern: '#([0-9]+)', gitHubToken: '',
46+
gitLabApiTokenCredentialsId: '', gitLabProjectName: '', gitLabServer: '', gitLabToken: '',
47+
ignoreCommitsIfMessageMatches: '^Merge.*',
48+
ignoreCommitsWithoutIssue: false, ignoreTagsIfNameMatches: '',
49+
jiraIssuePattern: '\\b[a-zA-Z]([a-zA-Z]+)-([0-9]+)\\b', jiraPassword: '', jiraServer: '',
50+
jiraUsername: '', jiraUsernamePasswordCredentialsId: '', mediaWikiPassword: '',
51+
mediaWikiTemplateContent: '', mediaWikiTemplateFile: '', mediaWikiTitle: '', mediaWikiUrl: '',
52+
mediaWikiUseTemplateContent: false, mediaWikiUseTemplateFile: false, mediaWikiUsername: '',
53+
noIssueName: 'No issue', readableTagName: '/([^/]+?)$', showSummary: false,
54+
showSummaryTemplateContent: '', showSummaryTemplateFile: '', showSummaryUseTemplateContent: false,
55+
showSummaryUseTemplateFile: false, subDirectory: '', timeZone: 'UTC',
56+
toReference: config.git_sha, toType: 'commit', untaggedName: 'Unreleased',
57+
useConfigFile: false, useFile: true, useGitHub: false, useGitHubApiTokenCredentials: false,
58+
useGitLab: false, useGitLabApiTokenCredentials: false, useIgnoreTagsIfNameMatches: false,
59+
useJira: false, useJiraUsernamePasswordCredentialsId: false, useMediaWiki: false,
60+
useReadableTagName: false, useSubDirectory: false]
61+
])
62+
}
63+
64+
ret = sh(returnStatus: true,
65+
script:"""#!/bin/bash
66+
cd ${config.repository_root}/.ci
67+
./gitler.sh""")
68+
69+
publishHTML([allowMissing: true, alwaysLinkToLastBuild: false, keepAll: true,
70+
reportDir: config.repository_root,
71+
reportFiles: 'gitler.html', reportName: 'Gitler report', reportTitles: ''])
72+
if (ret == 1) {
73+
config.pr.setBuildStatus(config, 'FAILURE', 'Toll gate (Gitler)', 'Commit(s) does not meet coding standards', '${BUILD_URL}Gitler_report/gitler.html')
74+
currentBuild.currentResult == 'FAILURE'
75+
echo "Termiated due to Gitler assert" // For BFA
76+
error 'Termiated due to Gitler assert'
77+
} else {
78+
config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate (Gitler)', 'Pass', '')
79+
}
80+
}
81+
82+
return this

.ci/gitler.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/bin/bash
2+
# Yes, this script can be improved immensly...
3+
cd ..
4+
5+
result=0
6+
7+
echo "No subjects are of invalid size<br>" > too_long_subjects.txt
8+
echo "No subjects with leading lower case characters<br>" > leading_lowercases.txt
9+
echo "No subjects with trailing periods<br>" > trailing_periods.txt
10+
echo "No body lines are too wide<br>" > too_long_body_lines.txt
11+
12+
too_long_subjects=`awk 'length > 50' subjects.txt`
13+
if [ -n "$too_long_subjects" ]; then
14+
echo "<b>Commit subjects that are too wide (&gt;50 characters):</b>" > too_long_subjects.txt
15+
echo "$too_long_subjects" >> too_long_subjects.txt
16+
sed -i -e 's/$/<br>/' too_long_subjects.txt
17+
result=1
18+
fi
19+
leading_lowercases=`awk '/^[[:lower:][:punct:]]/' subjects.txt`
20+
if [ -n "$leading_lowercases" ]; then
21+
echo "<b>Commit subjects with leading lowercase characters:</b>" > leading_lowercases.txt
22+
echo "$leading_lowercases" >> leading_lowercases.txt
23+
sed -i -e 's/$/<br>/' leading_lowercases.txt
24+
result=1
25+
fi
26+
trailing_periods=`awk '/(\.)$/' subjects.txt`
27+
if [ -n "$trailing_periods" ]; then
28+
echo "<b>Commit subjects with trailing periods:</b>" > trailing_periods.txt
29+
echo "$trailing_periods" >> trailing_periods.txt
30+
sed -i -e 's/$/<br>/' trailing_periods.txt
31+
result=1
32+
fi
33+
34+
too_long_body_lines=`awk 'length > 72' bodies.txt`
35+
if [ -n "$too_long_body_lines" ]; then
36+
echo "<b>Body lines that are too wide (&gt;72 characters):</b>" > too_long_body_lines.txt
37+
echo "$too_long_body_lines" >> too_long_body_lines.txt
38+
sed -i -e 's/$/<br>/' too_long_body_lines.txt
39+
result=1
40+
fi
41+
42+
printf "%s" "<html>" > gitler.html
43+
awk 'FNR==1{print "<br>"}1' too_long_subjects.txt leading_lowercases.txt trailing_periods.txt too_long_body_lines.txt >> gitler.html
44+
echo "<br>" >> gitler.html
45+
if [ $result -ne 0 ]; then
46+
echo "You should follow <a href="http://chris.beams.io/posts/git-commit">this guide</a> when writing your commit messages.<br>" >> gitler.html
47+
echo "<br>" >> gitler.html
48+
echo "To change the commit message for a single-commit pull request:<br>" >> gitler.html
49+
echo "git checkout &lt;your_branch&gt;<br>" >> gitler.html
50+
echo "git commit --amend<br>" >> gitler.html
51+
echo "git push origin HEAD:&lt;your_branch_on_github&gt; -f<br>" >> gitler.html
52+
echo "<br>" >> gitler.html
53+
echo "To change the commit messages for a multiple-commit pull request:<br>" >> gitler.html
54+
echo "git checkout &lt;your_branch&gt;<br>" >> gitler.html
55+
echo "git rebase -i &lt;sha_of_parent_to_the_earliest_commit_you_want_to_change&gt;<br>" >> gitler.html
56+
echo "Replace \"pick\" with \"r\" or \"reword\" on the commits you need to change message for<br>" >> gitler.html
57+
echo "git push origin HEAD:&lt;your_branch_on_github&gt; -f<br>" >> gitler.html
58+
echo "<br>" >> gitler.html
59+
fi
60+
61+
# Evaluate if there exists booleans in the code tree (not counting this file)
62+
if git grep -q boolean -- `git ls-files | grep -v gitler.sh`
63+
then
64+
echo "<b>This repository currently contain the boolean keyword. This should be avoided.</b><br>" >> gitler.html
65+
echo "<br>" >> gitler.html
66+
result=1
67+
else
68+
echo "This repository does not contain any boolean keywords. This is good.<br>" >> gitler.html
69+
echo "<br>" >> gitler.html
70+
fi
71+
72+
if [ $result -ne 0 ]; then
73+
echo "<b>If you disagree to this, please discuss it in the GitHub pull request thread.</b><br>" >> gitler.html
74+
echo "<br>" >> gitler.html
75+
fi
76+
echo "Yours sincerely, Gitler, on behalf of Jenkins<br>" >> gitler.html
77+
printf "%s" "</html>" >> gitler.html
78+
exit $result

0 commit comments

Comments
 (0)