Search code examples
jenkins-pipelinejenkins-pluginsjenkins-groovyemail-extjenkins-email-ext

How to format the matched lines using BUILD_LOG_REGEX in JenkinsFile


I have a JenkinsFile where I am using emailext to send mail for the automation results:

def subject = "Jenkins Build ${currentBuild.currentResult}: Job ${env.JOB_NAME}"
def color = currentBuild.currentResult == 'FAILURE' ? 'red' : 'green'

    emailext (
    mimeType: 'text/html',
    attachLog: true,
    subject: subject,
    body: 
    """
    <h3><font color='${color}'>${currentBuild.currentResult}: Job ${env.JOB_NAME} build ${env.BUILD_NUMBER} </h3></font><br> More info at: ${env.BUILD_URL}<br><br>
    Total test case:  \${TEST_COUNTS,var="total"} <br>
    Pass:               \${TEST_COUNTS,var="pass"}  <br>
    Fail:               \${TEST_COUNTS,var="fail"}  <br>
    <br>
    ${htmlTable}
    <br>
    \${BUILD_LOG_REGEX, regex="^.*Check out job at", showTruncatedLines="false",substText=" Access the test recording here:"}<br><br>
    <p>Click the link below to view the report:</p><a href="${env.BUILD_URL}/QmateReport">View Report</a>
    """,
    to: '[email protected]'
)       

For this line \${BUILD_LOG_REGEX, regex="^.*Check out job at", showTruncatedLines="false",substText=" Access the test recording here:"}<br><br>

I am getting multiple matches and it's all coming in an unformatted way, in one line.

Is there a way to get those matched records in email in NextLine

This is the result we are getting in email. enter image description here

I would like to see this in NextLine like

Access the Recording Here:
Access the Recording Here:

I tried to use the below two points

  1. Use /n in substText
\${BUILD_LOG_REGEX, regex="^.*Check out job at", showTruncatedLines="false",substText=" Access the test recording here: \n"}<br><be> 
  1. Use Actual Newline Character: Instead of \n, try using an actual newline character within the string. You can achieve this by pressing Enter after the colon and continuing the string on the next line. For example:
\${BUILD_LOG_REGEX, regex="^.*Check out job at", showTruncatedLines="false",substText=" Access the test recording here: 
"}

But nothing is working.


Solution

  • Preamble

    The BUILD_LOG_REGEX macro has always supported a addNewline attribute with a default value of true since it was introduced in the token-macro-plugin 2.0-beta release. It should add a new line character at the end of each match by default.

    If the provided solutions do not work I would request the following:

    1. The full, sanitized Jenkinsfile
    2. The full, sanitized build log
    3. Your Jenkins version, Email Ext Plugin version, and Token Macro Plugin version
    4. The raw, sanitized HTML from the resulting email

    List of Solutions

    I would recommend trying two different solutions.

    1. Explicitly add the addNewline="true" attribute to the BUILD_LOG_REGEX macro
    2. Add both the addNewline="true" and matchedLineHtmlStyle attributes ( matchedLineHtmlStyle allows you to provide inline CSS styling on expression matches )

    If these two solution's don't work you may be able to add a println() directly after the code that prints Check out job at to your build log, then add linesAfter="1" as an attribute to the BUILD_LOG_REGEX macro. However, I was not able to get this to work.

    Additionally, I've had some luck wrapping BUILD_LOG_REGEX in <pre> as follows <pre>\${BUILD_LOG_REGEX,....}</pre>. You may also want to tinker with that. However, using the two attributes from solution 2 I can produce a well-formatted email that matches your requirement.

    Example Implementation of Solution 2

    Since solution #2 is a superset of solution #1 I am not providing sample code for solution #1. Here is your code fitted with these recommended changes.

    emailext (
        mimeType: 'text/html',
        attachLog: true,
        subject: subject,
        body: 
        """
        <h3><font color='${color}'>${currentBuild.currentResult}: Job ${env.JOB_NAME} build ${env.BUILD_NUMBER} </font></h3><br> More info at: ${env.BUILD_URL}<br><br>
        Total test case:  \${TEST_COUNTS,var="total"} <br>
        Pass:               \${TEST_COUNTS,var="pass"}  <br>
        Fail:               \${TEST_COUNTS,var="fail"}  <br>
        <br>
        ${htmlTable}
        <br>
        \${BUILD_LOG_REGEX, regex="^.*Check out job at", showTruncatedLines="false", matchedLineHtmlStyle="margin-bottom: 1em; display: inline-block;", addNewline="true", substText=" Access the test recording here:"}<br><br>
        <p>Click the link below to view the report:</p><a href="${env.BUILD_URL}/QmateReport">View Report</a>
        """,
        to: '[email protected]'
    )       
    

    Here is the large pipeline I wrote to simulate your “environment”. Notably, I could not replicate your problem except by setting addNewline=“false” so here I am explicitly setting it to true. Additionally I broke some of your tokens because they don't exist in my system.

    pipeline {
        
        agent any
    
        stages {
            stage('release app0') {
                steps {
                    script {
                        def output = sh(script: 'echo Check out job at random1 enADFASDFASDFGASd  ASDFGASDGASGASDFGASDFG ASDFASDFAFA ASDFASDFA ASDFASDF ASDFASDFASDF ASDFASDFAS ', returnStdout: true).trim()
                        echo output
                    }
                }
            }
            stage('release app1') {
                steps {
                    script {
                        def output = sh(script: 'echo Check out job at random2 end KEJHAIDFA 349MQRG90U 1T9 QUER9GQ34TU 9034UT02U 34T', returnStdout: true).trim()
                        echo output
                    }
                }
            }
            stage('release app2') {
                steps {
                    script {
                        def output = sh(script: 'echo Check out job at random3 end  0U4 U13TU 1349-TU -134UTU 134TU134T-134TU-1-34TU-134 UT-1', returnStdout: true).trim()
                        echo output
                    }
                }
            }
            stage('release app3') {
                steps {
                    script {
                        def output = sh(script: 'echo Check out job at random4 end  903U90 349 U340U 909 39034 T034U030403040 34 4', returnStdout: true).trim()
                        echo output
                    }
                }
            }
            stage('release app4') {
                steps {
                    script {
                        def output = sh(script: 'echo Check out job at random5 end  490U39434T0 - V 4U-34U- 34 34-U-', returnStdout: true).trim()
                        echo output
                    }
                }
            }
        }
        
        post { 
            success {
                emailext (
                    mimeType: 'text/html',
                    subject: "asdf",
                    body: 
                    """
                    <h3><font color='#ff0000'>${currentBuild.currentResult}: Job ${env.JOB_NAME} build ${env.BUILD_NUMBER}</font></h3> <br> More info at: ${env.BUILD_URL}<br><br>
                    Total test case:  {TEST_COUNTS,var="total"} <br>
                    Pass:               {TEST_COUNTS,var="pass"}  <br>
                    Fail:               {TEST_COUNTS,var="fail"}  <br>
                    <br>
            
                    <br>
                    \${BUILD_LOG_REGEX, regex="^.*Check out job at", showTruncatedLines="false", matchedLineHtmlStyle="margin-bottom: 1em; display: inline-block;", addNewline="true", substText=" Access the test recording here:"}<br><br>
                    <p>Click the link below to view the report:</p><a href="${env.BUILD_URL}/QmateReport">View Report</a>
                    """,
                    to: '[email protected]'
                )       
     
            }
        }
        
    }
    

    Resulting email

    The resulting email looks like the following for me:

    resulting email

    The reason you see line duplication is due my test script ( it's grabbing the echo command and the stdout ). Yours won't do that assuming the string is produced by the output of a command that does not contain the string itself.