Jenkins 詹金斯:我如何将最新的性能更改列表添加到通知电子邮件中?

Jenkins 詹金斯:我如何将最新的性能更改列表添加到通知电子邮件中?,jenkins,perforce,changelist,email-ext,Jenkins,Perforce,Changelist,Email Ext,我有一个Jenkins CI设置,我们使用Perforce作为SCM 我已经配置了Email-ext插件,以便在构建中断时以及修复时通知开发人员 目前,通知电子邮件正文仅使用$DEFAULT\u内容,但我真的希望在电子邮件内容中指出最新的性能更改列表,因为这将对开发人员更有帮助 我在想变更列表编号,变更列表标题和提交人 关于如何做到这一点有什么想法吗?在詹金斯邮件列表的某个地方,我找到了这个模板,我认为它符合您的要求。 在jenkins根目录中创建一个文件夹“email templates”,将

我有一个Jenkins CI设置,我们使用Perforce作为SCM

我已经配置了Email-ext插件,以便在构建中断时以及修复时通知开发人员

目前,通知电子邮件正文仅使用$DEFAULT\u内容,但我真的希望在电子邮件内容中指出最新的性能更改列表,因为这将对开发人员更有帮助

我在想变更列表编号变更列表标题提交人


关于如何做到这一点有什么想法吗?

在詹金斯邮件列表的某个地方,我找到了这个模板,我认为它符合您的要求。 在jenkins根目录中创建一个文件夹“email templates”,将文件放入其中并命名为“p4htmlmail.jelly”。 然后在jenkins配置中,将
${JELLY_脚本,template=“p4htmlmail”}
放在email ext插件配置的默认内容字段中

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">

<link type="text/css" rel="stylesheet" href="${rooturl}static/e59dfe28/css/style.css"/>
<link type="text/css" rel="stylesheet" href="${rooturl}static/e59dfe28/css/color.css"/>

<STYLE>
a:link,a:visited {
  text-decoration: underline;
  <!--color: #3B5998;-->
  color: #8B9DC3
}

a.success:link,a.success:visited {
  text-decoration: underline;
  color: #338033;
}

a.failure:link,a.failure:visited {
  text-decoration: underline;
  color: #900;
}

a.unstable:link,a.unstable:visited {
  text-decoration: underline;
  color: #F0C000;
}

<!--
body {
  background-color: #DFE3EE;
}-->

.pane {
  margin-top: 4px;
  white-space: nowrap;
}

.pane TD {
  padding: 4px 4px 3px 4px;
}

TABLE.pane {
  width: 100%;
  border: none;
}

TD.style1 {
  color: #3B5998;
  width: 10%;
}

TD.pane {
  padding: 3px 4px 3px 4px;
  vertical-align: middle;
}

TD.pane-header {
  border-top: 1px #8B9DC3 solid;
  border-bottom: 1px #8B9DC3 solid;
  background-color: #DFE3EE;
  font-weight: bold;
  font-family: Verdana, Helvetica, sans serif;
  font-size: 11px;
  color: #8B9DC3;
  padding-top: 10px;
  padding-bottom: 10px;
}

TD.pane-header-success {
  border-top: 1px #338033 solid;
  border-bottom: 1px #338033 solid;
  background-color: #E0FFE0;
  font-weight: bold;
  font-family: Verdana, Helvetica, sans serif;
  font-size: 11px;
  color: #338033;
  padding-top: 10px;
  padding-bottom: 10px;
}

TD.pane-header-failure {
  border-top: 1px #900 solid;
  border-bottom: 1px #900 solid;
  background-color: #FCC;
  font-weight: bold;
  font-family: Verdana, Helvetica, sans serif;
  font-size: 11px;
  color: #900;
  padding-top: 10px;
  padding-bottom: 10px;
}

TD.pane-header-unstable {
  border-top: 1px #F0C000 solid;
  border-bottom: 1px #F0C000 solid;
  background-color: #FFFFCE;
  font-weight: bold;
  font-family: Verdana, Helvetica, sans serif;
  font-size: 11px;
  color: #F0C000;
  padding-top: 10px;
  padding-bottom: 10px;
}

TH.pane {
  font-weight: bold;
}

SPAN.style1 {
  font-weight: bold;
  color: #8B9DC3;
}

SPAN.success {
  color: #338033;
}

SPAN.failure {
  color: #900;
}

LI.style1 {
  list-style: cirle outside;
}

LI.failure {
  list-style: cirle outside;
  color: #900;
}


</STYLE>



<BODY>
<j:set var="spc" value="&amp;nbsp;&amp;nbsp;" />


<!-- GENERAL INFO -->

<TABLE class="pane">
 <TBODY>
  <TR>
    <j:choose>
      <j:when test="${build.result=='SUCCESS'}">
    <TD colspan="2" class="pane-header-success">
    <SPAN>Build Result Summary - <A class="success" href="${rooturl}${build.url}">See full build details</A></SPAN>
     </TD>
      </j:when>
      <j:when test="${build.result=='FAILURE'}">
    <TD colspan="2" class="pane-header-failure">
    <SPAN>Build Result Summary - <A class="failure" href="${rooturl}${build.url}">See full build details</A></SPAN>
     </TD>
      </j:when>
      <j:otherwise>
    <TD colspan="2" class="pane-header-unstable">
    <SPAN>Build Result Summary - <A class="unstable" href="${rooturl}${build.url}">See full build details</A></SPAN>
     </TD>
      </j:otherwise>
    </j:choose>
  </TR>

  <TR>
    <TD class="style1">Project:</TD>
    <TD><A href="${rooturl}${project.url}">${project.name}</A></TD>
  </TR>

  <TR>
    <TD class="style1">Completion:</TD>
    <TD>${it.timestampString}</TD>
  </TR>

  <TR>
    <TD class="style1">Duration:</TD>
    <TD>${build.durationString}</TD>
  </TR>

  <TR>
    <TD class="style1">Status:</TD>
    <TD>
    <j:choose>
      <j:when test="${build.result=='SUCCESS'}">
    <IMG SRC="${rooturl}static/e59dfe28/images/16x16/blue.png"/>
      </j:when>
      <j:when test="${build.result=='FAILURE'}">
    <IMG SRC="${rooturl}static/e59dfe28/images/16x16/red.png"/>
      </j:when>
      <j:otherwise>
    <IMG SRC="${rooturl}static/e59dfe28/images/16x16/yellow.png"/>
      </j:otherwise>
    </j:choose>
    ${build.result}
    </TD>
  </TR>
  <TR>
    <TD class="style1">Help:</TD>
    <TD>
    Help text goes here.
    </TD>
  </TR>
 </TBODY>
</TABLE>
<BR/>


<!-- CHANGE SET -->

<j:set var="changeSet" value="${build.changeSet}" />
<j:if test="${changeSet!=null}">
  <j:set var="hadChanges" value="false" />
  <TABLE class="pane">
    <TBODY>
      <TR>
        <TD colspan="2" class="pane-header">
      <SPAN>Code Changes - <A href="${rooturl}${build.url}/changes">See full change details</A></SPAN>
        </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">
        <SPAN class="style1">
          Change <B>${cs.changeNumber}</B> by
          <B>${aUser!=null?aUser.displayName:cs.author.displayName}: </B>
          '${cs.msgAnnotated}'
        </SPAN>
      </TD>
        </TR>
        <j:forEach var="p" items="${cs.affectedFiles}">
      <TR>
        <TD width="2%">
          <j:switch on="${p.editType.name}">
        <j:case value="add">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-ADD.gif"/>
        </j:case>
        <j:case value="branch">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-BRANCH.gif"/>
        </j:case>
        <j:case value="delete">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-DELETE.gif"/>
        </j:case>
        <j:case value="edit">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-EDIT.gif"/>
        </j:case>
        <j:case value="import">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-IMPORT.gif"/>
        </j:case>
        <j:case value="integrate">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-INTEGRATE.gif"/>
        </j:case>
        <j:case value="move_add">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-MOVE_ADD.gif"/>
        </j:case>
        <j:case value="move_delete">
          <IMG SRC="${rooturl}plugin/perforce/icons/action-MOVE_DELETE.gif"/>
        </j:case>
        <j:default>
           ${p.editType.name} 
        </j:default>
          </j:switch>
        </TD>
        <TD><SPAN>${p.path}</SPAN></TD>
      </TR>
        </j:forEach>
      </j:forEach>
      <j:if test="${!hadChanges}">
        <TR><TD colspan="2">No Changes</TD></TR>
      </j:if>
    </TBODY>
  </TABLE>
<BR/>
</j:if>

<!-- JUNIT TEST OUTPUT -->

<j:set var="junitResultList" value="${it.JUnitTestResult}" />
<j:if test="${junitResultList.isEmpty()!=true}">
<TABLE class="pane">
 <TBODY>
    <TR>
       <TD colspan="2" class="pane-header">
      <SPAN>Unit Tests - <A href="${rooturl}${build.url}/testReport">See full test details</A></SPAN>
       </TD>
    </TR>

    <j:forEach var="junitResult" items="${it.JUnitTestResult}">
      <j:forEach var="packageResult" items="${junitResult.getChildren()}">
    <TR>
       <TD>
    Name: ${packageResult.getName()}, Failed: <SPAN class="failure">${packageResult.getFailCount()}</SPAN> test(s), Passed: <SPAN class="success">${packageResult.getPassCount()}</SPAN> test(s), Skipped: ${packageResult.getSkipCount()} test(s), Total: ${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()} test(s)
       </TD>
    </TR>
        <j:forEach var="failed_test" items="${packageResult.getFailedTests()}">
    <TR>
       <TD>
        <UL>
          <B>
        <LI class="failure">Failed: ${failed_test.getFullName()}</LI>
      </B>
    </UL>
       </TD>
    </TR>
        </j:forEach>
      </j:forEach>
    </j:forEach>
 </TBODY>
</TABLE>
<BR/>
</j:if>

<!-- CONSOLE OUTPUT -->

<j:getStatic var="resultFailure" field="FAILURE" className="hudson.model.Result"/>
<j:if test="${build.result==resultFailure}">
<TABLE class="pane">
 <TBODY>
  <TR>
     <TD colspan="2" class="pane-header">
    <SPAN>Console Output - <A href="${rooturl}${build.url}/console">See full console details</A></SPAN>
     </TD>
  </TR>

  <j:forEach var="line" items="${build.getLog(100)}">
  <TR>
     <TD>${line}</TD>
  </TR>
  </j:forEach>
 </TBODY>
</TABLE>
<BR/>
</j:if>


</BODY>
</j:jelly>

a:链接,a:已访问{
文字装饰:下划线;
颜色:#8B9DC3
}
a、 成功:链接,a。成功:访问{
文字装饰:下划线;
颜色:#338033;
}
a、 失败:链接,a。失败:已访问{
文字装饰:下划线;
颜色:#900;
}
a、 不稳定:链接,a.不稳定:已访问{
文字装饰:下划线;
颜色:#F0C000;
}
.窗格{
利润上限:4倍;
空白:nowrap;
}
.TD{
填充:4px4p3px4px;
}
TABLE.pane{
宽度:100%;
边界:无;
}
TD.1{
颜色:#3B5998;
宽度:10%;
}
TD.pane{
填充:3px4px3px4px;
垂直对齐:中间对齐;
}
TD.pane-header{
边框顶部:1px#8B9DC3实心;
边框底部:1px#8B9DC3实心;
背景色:#DFE3EE;
字体大小:粗体;
字体系列:Verdana、Helvetica、无衬线;
字体大小:11px;
颜色:#8B9DC3;
填充顶部:10px;
垫底:10px;
}
TD.pane-header-success{
边框顶部:1px#338033实心;
边框底部:1px#338033实心;
背景色:#e00ffe0;
字体大小:粗体;
字体系列:Verdana、Helvetica、无衬线;
字体大小:11px;
颜色:#338033;
填充顶部:10px;
垫底:10px;
}
TD.pane-header-failure{
边框顶部:1px#900实心;
边框底部:1px#900实心;
背景色:#FCC;
字体大小:粗体;
字体系列:Verdana、Helvetica、无衬线;
字体大小:11px;
颜色:#900;
填充顶部:10px;
垫底:10px;
}
TD.pane-header-unstable{
边框顶部:1px#F0C000实心;
边框底部:1px#F0C000实心;
背景色:#FFFFCE;
字体大小:粗体;
字体系列:Verdana、Helvetica、无衬线;
字体大小:11px;
颜色:#F0C000;
填充顶部:10px;
垫底:10px;
}
第四窗格{
字体大小:粗体;
}
SPAN.style1{
字体大小:粗体;
颜色:#8B9DC3;
}
成功{
颜色:#338033;
}
SPAN.故障{
颜色:#900;
}
LI.1{
列表样式:外部卷曲;
}
李:失败{
列表样式:外部卷曲;
颜色:#900;
}
生成结果摘要-
生成结果摘要-
生成结果摘要-
项目:
完成:
${it.timestampString}
持续时间:
${build.durationString}
地位:
${build.result}
帮助:
这里是帮助文本。

代码更改- 更改${cs.changeNumber}by ${aUser!=null?aUser.displayName:cs.author.displayName}: “${cs.msgAnnotated}” ${p.editType.name} ${p.path} 不变
单元测试- 名称:${packageResult.getName()},失败:${packageResult.getFailCount()}测试,通过:${packageResult.getPassCount()}测试,跳过:${packageResult.getSkipCount()}测试,总计:${packageResult.getPassCount()+packageResult.getFailCount()+packageResult.getSkipCount()}测试
  • 失败:${Failed_test.getFullName()}

控制台输出- ${line}

只需使用Groovy模板,请参阅感谢您的精彩回复!这几乎正是我所需要的。我发现这个jelly脚本报告构建成功/失败,但没有获取我的构建变更集(即“无变更”)。但是,当我在Perforce作业上运行相同的模板时,将忠实地呈现变更列表信息。我想知道-我可以从这个脚本中引用第二个作业的变更集吗?i、 e.参考Performce作业的变更集,但报告的作业的构建/失败信息?我是否理解您的意思,您有两个作业?一个用于从perforce同步,另一个用于构建?实际上,我们通常只有一项同步和构建的工作。如果构建失败,提交者将收到一封邮件,其中包括触发构建的更改。抱歉,让第二份工作获取另一份工作的变更单信息是不可能的(或不容易的)。这是正确的-正如您所描述的,我们有两份工作。如果我找不到一种简单的方法来引用jelly temple中的Performce作业,那么我可能会尝试@JoshUnger推荐的groovy模板