邮件通知模板
使用方法:将以下内容保存到$JENKINS_HOME/email-templates/detailed_email.jelly
,然后直接在default content
中调用${JELLY_SCRIPT,template="detailed_email"}
即可。
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define"> <html> <head> <title>${project.name}</title> <style> body table, td, th, p, h1, h2 { margin:0; font:normal normal 100% 'Avenir Next'; background-color: #ffffff; } h1, h2 { border-bottom:none 1px #999999; padding:5px; margin-top:10px; margin-bottom:10px; color: #000000; font: normal bold 130% 'Avenir Next'; background-color:#f0f0f0; } tr.gray { background-color:#f0f0f0; } h2 { padding:5px; margin-top:5px; margin-bottom:5px; font: italic bold 110% 'Avenir Next'; } .bg2 { color:black; background-color:#E0E0E0; font-size:110% } th { font-weight: bold; } tr, td, th { padding:2px; } td.test_passed { color:green; } td.test_failed { color:red; } td.center { text-align: center; } td.test_skipped { color:grey; } .console { font: normal normal 90% Courier New, monotype; padding:0px; margin:0px; } div.content, div.header { background: #ffffff; border: none 1px #666; margin: 2px; content: 2px; padding: 2px; } table.border, th.border, td.border { border: 1px solid grey; border-collapse:collapse; } </style> </head> <body> <div class="header"> <j:set var="spc" value="&nbsp;&nbsp;" /> <!-- GENERAL INFO --> <table> <tr class="gray"> <td align="right"> <j:choose> <j:when test="${build.result=='SUCCESS'}"> <img src="${rooturl}static/e59dfe28/images/32x32/green.gif" /> </j:when> <j:when test="${build.result=='FAILURE'}"> <img src="${rooturl}static/e59dfe28/images/32x32/red.gif" /> </j:when> <j:when test="${build.result=='UNSTABLE'}"> <img src="${rooturl}static/e59dfe28/images/32x32/yellow.gif" /> </j:when> <j:otherwise> <img src="${rooturl}static/e59dfe28/images/32x32/green.gif" /> </j:otherwise> </j:choose> </td> <td align="left"> <j:choose> <j:when test="${empty(build.result)}"> <b style="font-size: 170%;">BUILD SUCCESS</b> </j:when> <j:otherwise> <b style="font-size: 170%;">BUILD ${build.result}</b> </j:otherwise> </j:choose> </td> </tr> <tr> <td><b>Build URL</b></td> <td> <a href="${rooturl}${build.url}">${rooturl}${build.url}</a> </td> </tr> <tr> <td><b>Project:</b></td> <td>${project.name}</td> </tr> <tr> <td><b>Date of build:</b></td> <td>${it.timestampString}</td> </tr> <tr> <td><b>Build duration:</b></td> <td>${build.durationString}</td> </tr> <tr> <td><b>Build cause:</b></td> <td> <j:forEach var="cause" items="${build.causes}">${cause.shortDescription} </j:forEach> </td> </tr> </table> </div>
<!-- CHANGE SET --> <div class="content"> <j:forEach var="changeSet" items="${build.changeSets}" varStatus="loop" begin="0" end="1"/> <!--j:set var="changeSet" value="${build.changeSet}[0]" / --> <j:if test="${changeSet!=null}"> <j:set var="hadChanges" value="false" /> <TABLE width="100%"> <TR><TD class="bg1" colspan="2"><a href="${rooturl}${build.url}/changes"> <h1>Changes</h1> </a></TD></TR> <j:forEach var="cs" items="${changeSet}" varStatus="loop"> <j:set var="hadChanges" value="true" /> <j:set var="aUser" value="${cs.hudsonUser}"/> <TR> <TD colspan="2" class="bg2">${spc}Revision <B>${cs.commitId?:cs.revision?:cs.changeNumber}</B> by <B>${aUser!=null?aUser.displayName:cs.author.displayName}: </B> <B>(${cs.msgAnnotated})</B> </TD> </TR> <j:forEach var="p" items="${cs.affectedFiles}"> <TR> <TD width="10%">${spc}${p.editType.name}</TD> <TD>${p.path}</TD> </TR> </j:forEach> </j:forEach> <j:if test="${!hadChanges}"> <TR><TD colspan="2">No Changes</TD></TR> </j:if> </TABLE> <BR/> </j:if></div>
<!-- HEALTH TEMPLATE --> <div class="content"> <j:set var="healthIconSize" value="16x16" /> <j:set var="healthReports" value="${project.buildHealthReports}" /> <j:if test="${healthReports!=null}"> <h1>Health Report</h1> <table> <tr> <th>W</th> <th>Description</th> <th>Score</th> </tr> <j:forEach var="healthReport" items="${healthReports}"> <tr> <td> <img src="${rooturl}${healthReport.getIconUrl(healthIconSize)}" /> </td> <td>${healthReport.description}</td> <td>${healthReport.score}</td> </tr> </j:forEach> </table> <br /> </j:if> </div>
<!-- JUnit TEMPLATE --> <j:set var="junitResultList" value="${it.JUnitTestResult}" /> <j:if test="${junitResultList.isEmpty()!=true}"> <j:forEach var="testResult" items="${junitResultList}"> <j:set var="failCount" value="${failCount + testResult.getFailCount()}"/> <j:set var="skipCount" value="${skipCount + testResult.getSkipCount()}"/> <j:set var="passCount" value="${passCount + testResult.getPassCount()}"/> </j:forEach> <div class="content"> <a href="${rooturl}${build.url}/testReport"> <h1>Tests Reports: <span style="color: green">Passed: ${passCount}</span>, <span style="color: #ff0000">Failed: ${failCount}</span>, <span style="color: grey">Ignored: ${skipCount}</span> </h1> </a> <!-- <h2> Test Trend </h2> <img alt="[Test result trend chart]" src="${rooturl}${project.url}/test/trend?failureOnly=false" usemap="#map0" /> <h2> JUnit Tests </h2> --> <table class="border"> <tr> <th class="border">Package</th> <th class="border">Failed</th> <th class="border">Passed</th> <th class="border">Skipped</th> <th class="border">Total</th> </tr> <j:forEach var="junitResult" items="${it.JUnitTestResult}"> <j:forEach var="packageResult" items="${junitResult.getChildren()}"> <tr> <td class="border"> <tt>${packageResult.getName()}</tt> </td> <td class="border test_failed">${packageResult.getFailCount()}</td> <td class="border test_passed">${packageResult.getPassCount()}</td> <td class="border test_skipped">${packageResult.getSkipCount()}</td> <td class="border"> <b>${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()} </b> </td> </tr> <j:forEach var="failed_test" items="${packageResult.getFailedTests()}"> <tr> <td class="test_failed" colspan="5"> <tt>${failed_test.getFullName()}</tt> </td> </tr> </j:forEach> </j:forEach> </j:forEach> </table> <br /> </div> </j:if>
<!-- COBERTURA TEMPLATE -->
<j:set var="coberturaAction" value="${it.coberturaAction}" /> <j:if test="${coberturaAction!=null}"> <div class="content"> <j:set var="coberturaResult" value="${coberturaAction.result}" /> <j:if test="${coberturaResult!=null}"> <a href="${rooturl}${build.url}/cobertura"> <h1>Cobertura Report</h1> </a> <h2>Project Coverage Summary</h2> <h3>Trend</h3> <img src="${rooturl}${build.url}cobertura/graph" /> <h3>Project Coverage summary</h3> <table class="border"> <tr> <th class="border">Name</th> <j:forEach var="metric" items="${coberturaResult.metrics}"> <th class="border">${metric.name}</th> </j:forEach> </tr> <tr> <td class="border">${coberturaResult.name}</td> <j:forEach var="metric" items="${coberturaResult.metrics}"> <td class="border" data="${coberturaResult.getCoverage(metric).percentageFloat}">${coberturaResult.getCoverage(metric).percentage}% (${coberturaResult.getCoverage(metric)}) </td> </j:forEach> </tr> </table> <j:if test="${coberturaResult.sourceCodeLevel}"> <h2>Source</h2> <j:choose> <j:when test="${coberturaResult.sourceFileAvailable}"> <div style="overflow-x:scroll;"> <table class="source"> <thead> <tr> <th colspan="3">${coberturaResult.relativeSourcePath} </th> </tr> </thead> ${coberturaResult.sourceFileContent} </table> </div> </j:when> <j:otherwise> <p> <i>Source code is unavailable</i> </p> </j:otherwise> </j:choose> </j:if> <j:forEach var="element" items="${coberturaResult.childElements}"> <j:set var="childMetrics" value="${coberturaResult.getChildMetrics(element)}" /> <h2>Coverage Breakdown by ${element.displayName}</h2> <table class="border"> <tr> <th class="border">Name</th> <j:forEach var="metric" items="${childMetrics}"> <th class="border">${metric.name}</th> </j:forEach> </tr> <j:forEach var="c" items="${coberturaResult.children}"> <j:set var="child" value="${coberturaResult.getChild(c)}" /> <tr> <td class="border"> ${child.xmlTransform(child.name)} </td> <j:forEach var="metric" items="${childMetrics}"> <j:set var="childResult" value="${child.getCoverage(metric)}" /> <j:choose> <j:when test="${childResult!=null}"> <td class="border" data="${childResult.percentageFloat}">${childResult.percentage}% (${childResult}) </td> </j:when> <j:otherwise> <td class="border" data="101">N/A</td> </j:otherwise> </j:choose> </j:forEach> </tr> </j:forEach> </table> </j:forEach> </j:if> <br /> </div> </j:if>
<!-- Static Analysis --> <j:set var="actions" value="${it.staticAnalysisActions}" /> <j:if test="${!actions.isEmpty()}"> <div class="content"> <h1>Static Analysis Results</h1> <table> <tr> <th></th> <th>Name</th> <th>Result</th> <th>Total</th> <th>High</th> <th>Normal</th> <th>Low</th> </tr> <j:forEach var="action" items="${actions}"> <tr> <td> <img src="${rooturl}${action.smallImageName}" /> </td> <td> <a href="${rooturl}${build.url}/${action.urlName}">${action.displayName}</a> </td> <td class="center"> <j:choose> <j:when test="${action.result.pluginResult=='SUCCESS'}"> <img src="${rooturl}static/e59dfe28/images/16x16/blue.gif" /> </j:when> <j:when test="${action.result.pluginResult=='FAILURE'}"> <img src="${rooturl}static/e59dfe28/images/16x16/red.gif" /> </j:when> <j:otherwise> <img src="${rooturl}static/e59dfe28/images/16x16/yellow.gif" /> </j:otherwise> </j:choose> </td> <td class="center">${action.result.numberOfAnnotations} </td> <td class="center">${action.result.getNumberOfAnnotations('HIGH')} </td> <td class="center">${action.result.getNumberOfAnnotations('NORMAL')} </td> <td class="center">${action.result.getNumberOfAnnotations('LOW')} </td> </tr> </j:forEach> </table> </div> </j:if>
<!-- clangScan Analysis --> <j:set var="clangScanAction" value='${it.getAction("jenkins.plugins.clangscanbuild.actions.ClangScanBuildAction")}' /> <j:if test="${clangScanAction!=null}"> <div class="content"> <a href="${rooturl}${build.url}clangScanBuildBugs"> <h1>Clang Scan Static Analysis Results</h1> </a> <h2> Trend </h2> <img lazymap="${rooturl}${project.url}clangScanBuildTrend/graph" alt="[Clang Scan Static Analysis Results trend chart]" src="${rooturl}${project.url}clangScanBuildTrend/graph" usemap="#map0" /> <h2> Result </h2> <j:set var="summary" value="${clangScanAction.loadBugSummary()}" /> <p> There are ${summary.getBugCount()} clang Scan bugs </p> <j:if test="${summary.getBugCount() !=0 }"> <table border="1px" class="pane"> <tr> <th> </th> <th>File</th> <th>Bug Type</th> <th>Category</th> <th>Description</th> <th>Details </th> </tr> <j:forEach var="clangBug" items="${summary.getBugs()}"> <j:choose> <j:when test="${clangBug.isNewBug()}"> <j:set var="isNew" value="new" /> </j:when> <j:otherwise> <j:set var="isNew" value="" /> </j:otherwise> </j:choose> <tr> <td>${isNew}</td> <td>${clangBug.getSourceFile()}</td> <td>${clangBug.getBugType()}</td> <td>${clangBug.getBugCategory()}</td> <td>${clangBug.getBugDescription()}</td> <td> <a href="${rooturl}${build.url}/clangScanBuildBugs/browse/${clangBug.getReportFile()}#EndPath"> detail </a> </td> </tr> </j:forEach> </table>
</j:if>
</div> </j:if>
<!-- ARTIFACTS --> <j:set var="artifacts" value="${build.artifacts}" /> <j:if test="${artifacts!=null and artifacts.size()>0}"> <div class="content"> <h1>Build Artifacts</h1> <ul> <j:forEach var="f" items="${artifacts}"> <li> <a href="${rooturl}${build.url}artifact/${f}">${f}</a> </li> </j:forEach> </ul> </div> </j:if>
<!-- MAVEN ARTIFACTS --> <j:set var="mbuilds" value="${build.moduleBuilds}" /> <j:if test="${mbuilds!=null}"> <div class="content"> <h1>Build Artifacts</h1> <j:forEach var="m" items="${mbuilds}"> <h2>${m.key.displayName}</h2> <j:forEach var="mvnbld" items="${m.value}"> <j:set var="artifacts" value="${mvnbld.artifacts}" /> <j:if test="${artifacts!=null and artifacts.size()>0}"> <ul> <j:forEach var="f" items="${artifacts}"> <li> <a href="${rooturl}${mvnbld.url}artifact/${f}">${f}</a> </li> </j:forEach> </ul> </j:if> </j:forEach> </j:forEach> <br /> </div> </j:if> </body> </html></j:jelly>
<STYLE>BODY, TABLE, TD, TH, P { font-family:Verdana,Helvetica,sans serif; font-size:11px; color:black;}h1 { color:black; }h2 { color:black; }h3 { color:black; }TD.bg1 { color:white; background-color:#0000C0; font-size:120% }TD.bg2 { color:white; background-color:#4040FF; font-size:110% }TD.bg3 { color:white; background-color:#8080FF; }TD.test_passed { color:blue; }TD.test_failed { color:red; }TD.console { font-family:Courier New; }</STYLE><BODY>
<TABLE> <TR><TD align="right"><IMG SRC="${rooturl}static/e59dfe28/images/32x32/<%= (build.result == null || build.result.toString() == 'SUCCESS') ? "blue.gif" : build.result.toString() == 'FAILURE' ? 'red.gif' : 'yellow.gif' %>" /> </TD><TD valign="center"><B style="font-size: 200%;">BUILD ${build.result ?: 'SUCCESSFUL'}</B></TD></TR> <TR><TD>URL</TD><TD><A href="${rooturl}${build.url}">${rooturl}${build.url}</A></TD></TR> <TR><TD>Project:</TD><TD>${project.name}</TD></TR> <TR><TD>Date:</TD><TD>${it.timestampString}</TD></TR> <TR><TD>Duration:</TD><TD>${build.durationString}</TD></TR> <TR><TD>Cause:</TD><TD><% build.causes.each() { cause -> %> ${cause.shortDescription} <% } %></TD></TR></TABLE><BR/>
<!-- CHANGE SET --><% def changeSets = build.changeSetsif(changeSets != null) { def hadChanges = false %> <TABLE width="100%"> <TR><TD class="bg1" colspan="2"><B>CHANGES</B></TD></TR>
<% changeSets.each() { cs_list -> cs_list.each() { cs -> hadChanges = true %> <TR> <TD colspan="2" class="bg2"> Revision <B><%= cs.metaClass.hasProperty('commitId') ? cs.commitId : cs.metaClass.hasProperty('revision') ? cs.revision : cs.metaClass.hasProperty('changeNumber') ? cs.changeNumber : "" %></B> by <B><%= cs.author %>: </B> <B>(${cs.msgAnnotated})</B> </TD> </TR><% cs.affectedFiles.each() { p -> %> <TR> <TD width="10%"> ${p.editType.name}</TD> <TD>${p.path}</TD> </TR><% } } } if(!hadChanges) { %> <TR><TD colspan="2">No Changes</TD></TR><% } %> </TABLE><BR/><% } %>
<!-- ARTIFACTS --><% def artifacts = build.artifactsif(artifacts != null && artifacts.size() > 0) { %> <TABLE width="100%"> <TR><TD class="bg1"><B>BUILD ARTIFACTS</B></TD></TR> <TR> <TD><% artifacts.each() { f -> %> <li> <a href="${rooturl}${build.url}artifact/${f}">${f}</a> </li><% } %> </TD> </TR> </TABLE><BR/><% } %>
<!-- MAVEN ARTIFACTS --><%try { def mbuilds = build.moduleBuilds if(mbuilds != null) { %> <TABLE width="100%"> <TR><TD class="bg1"><B>BUILD ARTIFACTS</B></TD></TR><% try { mbuilds.each() { m -> %> <TR><TD class="bg2"><B>${m.key.displayName}</B></TD></TR><% m.value.each() { mvnbld -> def artifactz = mvnbld.artifacts if(artifactz != null && artifactz.size() > 0) { %> <TR> <TD><% artifactz.each() { f -> %> <li> <a href="${rooturl}${mvnbld.url}artifact/${f}">${f}</a> </li><% } %> </TD> </TR><% } } } } catch(e) { // we don't do anything } %> </TABLE><BR/><% }}catch(e) { // we don't do anything}%>
<!-- JUnit TEMPLATE -->
<% def junitResultList = it.JUnitTestResulttry { def cucumberTestResultAction = it.getAction("org.jenkinsci.plugins.cucumber.jsontestsupport.CucumberTestResultAction") junitResultList.add(cucumberTestResultAction.getResult())} catch(e) { //cucumberTestResultAction not exist in this build}if (junitResultList.size() > 0) { %> <TABLE width="100%"> <TR><TD class="bg1" colspan="2"><B>${junitResultList.first().displayName}</B></TD></TR> <% junitResultList.each{ junitResult -> %> <% junitResult.getChildren().each { packageResult -> %> <TR><TD class="bg2" colspan="2"> 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)</TD></TR> <% packageResult.getFailedTests().each{ failed_test -> %> <TR bgcolor="white"><TD class="test_failed" colspan="2"><B><li>Failed: ${failed_test.getFullName()} </li></B></TD></TR> <% } } } %> </TABLE> <BR/><%} %>
<!-- CONSOLE OUTPUT --><% if(build.result==hudson.model.Result.FAILURE) { %><TABLE width="100%" cellpadding="0" cellspacing="0"><TR><TD class="bg1"><B>CONSOLE OUTPUT</B></TD></TR><% build.getLog(100).each() { line -> %> <TR><TD class="console">${org.apache.commons.lang.StringEscapeUtils.escapeHtml(line)}</TD></TR><% } %></TABLE><BR/><% } %>
</BODY>
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title></head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0"> <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif"> <tr> <td>各位同事,大家好,以下为${PROJECT_NAME }项目构建信息</td> </tr> <tr> <td><br /> <b><font color="#0B610B">构建信息</font></b> <hr size="2" width="100%" align="center" /></td> </tr> <tr> <td> <ul> <li>项目名称 : ${PROJECT_NAME}</li> <li>构建编号 : 第${BUILD_NUMBER}次构建</li> <li>触发原因: ${CAUSE}</li> <li>构建状态: ${BUILD_STATUS}</li> <li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li> <li>构建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li> <li>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li> <li>项目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li> </ul> </td> </tr> <tr> <td><b><font color="#0B610B">历史变更记录:</font></b> <hr size="2" width="100%" align="center" /></td> </tr> <tr> <td> ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n:<br />%c<br />",showPaths=true,changesFormat="<pre>[%a]<br />%m</pre>",pathFormat=" %p"} </td> </tr> </table></body></html>
部分内容来源于网络。