├── .gitignore ├── Makefile ├── README.md ├── config.xml ├── hudson.plugins.warnings.WarningsPublisher.xml └── python-static-analysis.jelly /.gitignore: -------------------------------------------------------------------------------- 1 | *.sc 2 | *.log 3 | output.xml 4 | xunit.xml 5 | coverage.xml 6 | .coverage 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC_DIR=. 2 | 3 | all: clean sloc test flakes lint clone 4 | 5 | sloc: 6 | sloccount --duplicates --wide --details $(SRC_DIR) | fgrep -v .git > sloccount.sc || : 7 | 8 | test: 9 | cd $(SRC_DIR) && nosetests --verbose --with-xunit --xunit-file=../xunit.xml --with-xcoverage --xcoverage-file=../coverage.xml || : 10 | 11 | flakes: 12 | find $(SRC_DIR) -name *.py|egrep -v '^./tests/'|xargs pyflakes > pyflakes.log || : 13 | 14 | lint: 15 | find $(SRC_DIR) -name *.py|egrep -v '^./tests/' | xargs pylint --output-format=parseable --reports=y > pylint.log || : 16 | 17 | clone: 18 | clonedigger --cpd-output $(SRC_DIR) || : 19 | 20 | clean: 21 | rm -f pyflakes.log 22 | rm -f pylint.log 23 | rm -f sloccount.sc 24 | rm -f output.xml 25 | rm -f coverage.xml 26 | rm -f xunit.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jenkins python template 2 | 3 | Use these files to scaffold your Continuous Integration Server within your python projects. 4 | 5 | This was inspirated by [the PHP jenkins template project](http://jenkins-php.org/) ([github](https://github.com/sebastianbergmann/php-jenkins-template)) by Sebastian Bergmann. 6 | 7 | 8 | 9 | ## Requirements 10 | 11 | On your jenkins instance, you must install some QA python tools: 12 | 13 | * [SLOCCount](http://www.dwheeler.com/sloccount/) 14 | 15 | $ (sudo) apt-get install sloccount 16 | 17 | * [pylint](http://pypi.python.org/pypi/pylint) 18 | 19 | $ (sudo) pip install pylint 20 | 21 | * [Clone Digger](http://clonedigger.sourceforge.net/) 22 | 23 | $ (sudo) easy_install clonedigger 24 | 25 | * [nosetests](http://readthedocs.org/docs/nose/en/latest/) & [nosexcover](http://pypi.python.org/pypi/nosexcover) 26 | 27 | $ (sudo) pip install nosetests 28 | $ (sudo) pip install nosexcover 29 | 30 | * [pyflakes](http://pypi.python.org/pypi/pyflakes) 31 | 32 | $ (sudo) pip install pyflakes 33 | 34 | 35 | ## Jenkins Plugins 36 | 37 | You also need to install some plugins in your jenkins server: 38 | 39 | * [Jenkins SLOCCount Plug-in](http://wiki.jenkins-ci.org/display/JENKINS/SLOCCount+Plugin) 40 | * [Jenkins xUnit plugin](http://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin) 41 | * [Jenkins Violations plugin](http://wiki.jenkins-ci.org/display/JENKINS/Violations) 42 | * [Warnings Plug-in](https://wiki.jenkins-ci.org/display/JENKINS/Warnings+Plugin) 43 | * [Cobertura plugin](https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin) 44 | 45 | 46 | ## Jenkins configuration 47 | 48 | Install the hudson.plugins.warnings.WarningsPublisher.xml file on the jenkins server, then reload the configuration. 49 | 50 | Or, if you prefer to click in the web interface: 51 | 52 | 1. Go to Manage Jenkins -> Configure System 53 | 2. Scroll down to Compiler Warnings -> Parsers 54 | 3. Add a new parser with the following details: 55 | 56 | 1. Name and others descriptive fields: 57 | 58 | PyFlakes 59 | 60 | 2. Regular Expression: 61 | 62 | ^\s*(.*):(\d+):\s*(.*)$ 63 | 64 | 3. Mapping Script: 65 | 66 | import hudson.plugins.warnings.parser.Warning 67 | 68 | String fileName = matcher.group(1) 69 | String lineNumber = matcher.group(2) 70 | String message = matcher.group(3) 71 | 72 | return new Warning(fileName, Integer.parseInt(lineNumber), "Dynamic Parser", "-", message); 73 | 74 | 4. Example Log Message: 75 | 76 | src/fbproxy/fbresponse.py:2: 'json' imported but unused 77 | 78 | 79 | ## Using the Job Template 80 | 81 | 1. Check out the `python-jenkins-template` project from Git: 82 | 83 | cd $JENKINS_HOME/jobs 84 | git clone git://github.com/bobuss/python-jenkins-template.git python-template 85 | chown -R jenkins:nogroup python-template/ 86 | 87 | 2. Reload Jenkins' configuration, for instance using the Jenkins CLI: 88 | 89 | java -jar jenkins-cli.jar -s http://: reload-configuration 90 | 91 | 3. Click on "New Job". 92 | 93 | 4. Enter a "Job name". 94 | 95 | 5. Select "Copy existing job" and enter "python-template" into the "Copy from" field. 96 | 97 | 6. Click "OK". 98 | 99 | 7. Disable the "Disable Build" option. 100 | 101 | 8. Fill in your "Source Code Management" information. 102 | 103 | 9. Configure a "Build Trigger", for instance "Poll SCM". 104 | 105 | 10. Click "Save". 106 | 107 | 108 | ## In your project 109 | 110 | Get the `Makefile` and adapt it (source directory, tasks). Make sure to generate the right files (or change the jenkins config): 111 | 112 | * pyflakes.log 113 | * pylint.log 114 | * sloccount.sc 115 | * output.xml 116 | * coverage.xml 117 | * xunit.xml 118 | 119 | You can also adapt your `.gitignore` file, in order to avoid to commit the generated files. 120 | 121 | 122 | ## Some other resources 123 | 124 | * [http://www.wallix.org/2011/06/29/how-to-use-jenkins-for-python-development/](http://www.wallix.org/2011/06/29/how-to-use-jenkins-for-python-development/) 125 | * [http://www.alexconrad.org/2011/10/jenkins-and-python.html](http://www.alexconrad.org/2011/10/jenkins-and-python.html) 126 | 127 | 128 | ## License 129 | 130 | (The MIT License) 131 | 132 | Copyright (c) 2012 Bertrand Tornil 133 | 134 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 135 | 136 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 137 | 138 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 139 | 140 | 141 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | true 9 | true 10 | false 11 | false 12 | 13 | false 14 | 15 | 16 | make all 17 | 18 | 19 | 20 | 21 | 22 | 23 | low 24 | [WARNINGS] 25 | 26 | false 27 | false 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | false 39 | true 40 | 41 | 42 | pyflakes.log 43 | PyFlakes 44 | 45 | 46 | 47 | 48 | 49 | **/coverage.xml 50 | false 51 | 52 | 53 | 54 | CONDITIONAL 55 | 70 56 | 57 | 58 | LINE 59 | 80 60 | 61 | 62 | METHOD 63 | 80 64 | 65 | 66 | 67 | 68 | 69 | 70 | CONDITIONAL 71 | 0 72 | 73 | 74 | LINE 75 | 0 76 | 77 | 78 | METHOD 79 | 0 80 | 81 | 82 | 83 | 84 | 85 | 86 | CONDITIONAL 87 | 0 88 | 89 | 90 | LINE 91 | 0 92 | 93 | 94 | METHOD 95 | 0 96 | 97 | 98 | 99 | ASCII 100 | 101 | 102 | xunit.xml 103 | false 104 | 105 | 106 | 107 | sloccount.sc 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | checkstyle 119 | 120 | checkstyle 121 | 10 122 | 999 123 | 999 124 | false 125 | 126 | 127 | 128 | 129 | codenarc 130 | 131 | codenarc 132 | 10 133 | 999 134 | 999 135 | false 136 | 137 | 138 | 139 | 140 | cpd 141 | 142 | cpd 143 | 10 144 | 999 145 | 999 146 | false 147 | output.xml 148 | 149 | 150 | 151 | cpplint 152 | 153 | cpplint 154 | 10 155 | 999 156 | 999 157 | false 158 | 159 | 160 | 161 | 162 | csslint 163 | 164 | csslint 165 | 10 166 | 999 167 | 999 168 | false 169 | 170 | 171 | 172 | 173 | findbugs 174 | 175 | findbugs 176 | 10 177 | 999 178 | 999 179 | false 180 | 181 | 182 | 183 | 184 | fxcop 185 | 186 | fxcop 187 | 10 188 | 999 189 | 999 190 | false 191 | 192 | 193 | 194 | 195 | gendarme 196 | 197 | gendarme 198 | 10 199 | 999 200 | 999 201 | false 202 | 203 | 204 | 205 | 206 | jcreport 207 | 208 | jcreport 209 | 10 210 | 999 211 | 999 212 | false 213 | 214 | 215 | 216 | 217 | jslint 218 | 219 | jslint 220 | 10 221 | 999 222 | 999 223 | false 224 | 225 | 226 | 227 | 228 | pep8 229 | 230 | pep8 231 | 10 232 | 999 233 | 999 234 | false 235 | 236 | 237 | 238 | 239 | pmd 240 | 241 | pmd 242 | 10 243 | 999 244 | 999 245 | false 246 | 247 | 248 | 249 | 250 | pylint 251 | 252 | pylint 253 | 10 254 | 999 255 | 999 256 | false 257 | pylint.log 258 | 259 | 260 | 261 | simian 262 | 263 | simian 264 | 10 265 | 999 266 | 999 267 | false 268 | 269 | 270 | 271 | 272 | stylecop 273 | 274 | stylecop 275 | 10 276 | 999 277 | 999 278 | false 279 | 280 | 281 | 282 | 283 | 100 284 | 285 | 286 | default 287 | 288 | 289 | 290 | 291 | -------------------------------------------------------------------------------- /hudson.plugins.warnings.WarningsPublisher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PyFlakes 7 | ^\s*(.*):(\d+):\s*(.*)$ 8 | 15 | src/fbproxy/fbresponse.py:2: 'json' imported but unused 16 | 17 | pyflakes 18 | PyFlakes 19 | 20 | 21 | -------------------------------------------------------------------------------- /python-static-analysis.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 39 | 40 | 41 | 42 | 43 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | BUILD ${build.result}
Build URL${rooturl}${build.url}
Project:${project.name}
Date of build:${it.timestampString}
Build duration:${build.durationString}
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
CHANGES
${spc}Revision ${cs.commitId?:cs.revision?:cs.changeNumber} by 57 | ${aUser!=null?aUser.displayName:cs.author.displayName}: 58 | (${cs.msgAnnotated}) 59 |
${spc}${p.editType.name}${p.path}
No Changes
72 |
73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 91 |
BUILD ARTIFACTS
84 | 85 |
  • 86 | ${f} 87 |
  • 88 |
    89 |
    92 |
    93 |
    94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 115 | 116 | 117 | 118 | 119 |
    BUILD ARTIFACTS
    ${m.key.displayName}
    109 | 110 |
  • 111 | ${f} 112 |
  • 113 |
    114 |
    120 |
    121 |
    122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 |
    PMD Result
    Total:${pmd.result.numberOfWarnings}
    High:${pmd.result.getNumberOfAnnotations('HIGH')}
    Normal:${pmd.result.getNumberOfAnnotations('NORMAL')}
    Low:${pmd.result.getNumberOfAnnotations('LOW')}
    New:${pmd.result.numberOfNewWarnings}
    Fixed:${pmd.result.numberOfFixedWarnings}
    View Report
    137 |
    138 |
    139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 |
    JUnit Tests
    Name: ${packageResult.getName()} Failed: ${packageResult.getFailCount()} test(s), Passed: ${packageResult.getPassCount()} test(s), Skipped: ${packageResult.getSkipCount()} test(s), Total: ${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()} test(s)
  • Failed: ${failed_test.getFullName()}
  • 155 |
    156 |
    157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 |
    Cobertura Report
    166 |
    Project Coverage Summary
    167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 180 | 181 | 182 |
    Name${metric.name}
    ${coberturaResult.name}${coberturaResult.getCoverage(metric).percentage}% 178 | (${coberturaResult.getCoverage(metric)}) 179 |
    183 | 184 | 185 |

    Source

    186 | 187 | 188 |
    189 | 190 | 191 | 192 | 193 | 194 | 195 | ${coberturaResult.sourceFileContent} 196 | 197 |
    ${coberturaResult.relativeSourcePath}
    198 |
    199 |
    200 | 201 |

    202 | Source code is unavailable 203 |

    204 |
    205 |
    206 |
    207 | 208 | 209 | 210 |
    Coverage Breakdown by ${element.displayName}
    211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 225 | 226 | 227 | 228 | 229 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 |
    Name${metric.name}
    223 | ${child.xmlTransform(child.name)} 224 | ${childResult.percentage}% 230 | (${childResult}) 231 | N/A
    241 |
    242 |
    243 |
    244 |
    245 | 246 | 247 | 248 | 249 | 250 | 251 |
    Static Analysis Results
    252 |
    253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 268 | 271 | 284 | 285 | 286 | 287 | 288 | 289 | 290 |
    NameResultTotalHighNormalLow
    266 | 267 | 269 | ${action.displayName} 270 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | ${action.result.numberOfAnnotations} ${action.result.getNumberOfAnnotations('HIGH')} ${action.result.getNumberOfAnnotations('NORMAL')} ${action.result.getNumberOfAnnotations('LOW')}
    291 |
    292 |
    293 | 294 |
    295 | 296 | 297 | 298 | 299 |
    Console Output
    300 |
    301 | 302 | 303 | 304 | 307 | 308 | 309 |
    305 | ${line} 306 |
    310 |
    311 |
    312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 |
    Error Output
    ${line}
    321 |
    322 |
    323 | 324 | 325 |
    --------------------------------------------------------------------------------