{
  "id": "qhsM9rmYFzfC28fo",
  "name": "Automate daily Jenkins test reports with AI and HTTP Requests",
  "tags": [],
  "nodes": [
    {
      "id": "dcee8db7-84ad-449c-ac90-5ebb30ff4288",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        2552,
        464
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "8f0e6821-e44a-47d6-b131-f8f8ece1f1bf",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "width": 832,
        "height": 656,
        "content": "## Automate daily Jenkins test reports with AI and HTTP Requests\nAs a test automation engineer, staying on top of daily test runs in Jenkins is essential. This workflow automates that process: it pulls specific test details from a Google Sheet, retrieves data from your local Jenkins environment, and uses AI to generate a concise summary report to be sent via email.\n\n**Who's it for**\n* Test automation engineers using Jenkins.\n* QA teams looking to streamline daily reporting.\n\n**How it works** \n* Scheduled Trigger: The workflow runs automatically at a pre-defined time.\n* Dynamic Data Retrieval: It constructs an HTTP request based on the data in your Google Sheet to fetch specific Jenkins results.\n* AI Optimization: Only relevant data is extracted to minimize AI token usage and focus on the most important metrics.\n* Summarization: The AI groups the results and formats them into a clear, professional email.\n* Distribution: The report is sent to every recipient listed in the MailingList column.\n\n\n**How to set up** \n* In the Google Sheet, set the BaseUrl, Environment, FeatureClass and Feature in order to build up the Jenkins url in their corresponding columns, for example:\nBaseUrl\t   | Environment\t|  FeatureClass\t      |Feature |MailingList |\n&lt;BaseUrl&gt; |&lt;environment&gt;\t| &lt;FeaturClassName&gt; |&lt;Featurename&gt;  |\t&lt;mail&gt;     |\n* Define Recipients: In the MailingList column, add the email addresses of the people who need to receive the report. If there are multiple recipients, ensure they are separated by commas.\n\n\n**Requirements** \n* Access to your Jenkins server.\n* An AI API key (e.g., Gemini, OpenAI).\n* A Google Cloud project with the Google Sheets API enabled."
      },
      "typeVersion": 1
    },
    {
      "id": "52dadb42-ffe8-40fb-8b38-c51bfe7f2639",
      "name": "Retrieve Google Sheet data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1136,
        240
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 897459827,
          "cachedResultUrl": "",
          "cachedResultName": "DataRequest"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "1a0296af-cd8a-4b24-b0f8-62619f4e40cd",
      "name": "Aggregate the data for AI processing",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2256,
        240
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "fa008380-afea-4fc9-8870-81f62109038f",
      "name": "Jenkins Report Analyzer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2480,
        240
      ],
      "parameters": {
        "text": "Use the provided data to create an overview of the failed (\"Gefaald\"), skipped (\"Overgeslagen\"), passed (\"Pass\") and total (\"Totaal\") tests for each feature.\nIf it has failed (\"Gefaald\") tests, list them by name.\n\nSummize them per environment and per feature and combine them in one email.",
        "options": {
          "systemMessage": "=You are a Jenkins Report analyzer, and your job is to get the results of each environment (develop = test, master = acceptance), and report back the results of each environment and which tests passed and/or failed.\nIf failed, show the specific failed test and the corresponding error.\nUse the data of {{ $json.data.map(item => {\n    const buildInfo = JSON.parse(item.build_Number);\n    const pkg = JSON.parse(item.test_data);\n    \n    const header = `### ENV: ${item.environment.toUpperCase()} | BUILD: ${buildInfo.build_number} ###\\n`;\n    \n    const body = `PACKAGE: ${pkg.name} (Pass: ${pkg.passCount}, Fail: ${pkg.failCount})\\n` + \n        pkg.child.map(story => \n            `  STORY: ${story.name}\\n` + \n            story.child.map(test => \n                `    [${test.status}] ${test.name}${test.status === 'FAILED' ? '\\n    ERROR: ' + (test.errorDetails ? test.errorDetails.trim() : 'No details available') : ''}`\n            ).join('\\n')\n        ).join('\\n');\n\n    return header + body;\n}).join('\\n\\n====================\\n\\n') }}\n\nBeautify the HTML using inline CSS styling for an email. Format it like a modern newsletter. Output only the results without any header elements.\nKeep the formatting and layout each mail the same.\nGenerate a professional HTML email report\nOutput ONLY the raw HTML code. \nDO NOT include introductory text (like 'Here is your report').\nDO NOT include markdown code blocks (no ```html).\nDO NOT include explanations, justifications, or Python code.\nStart your response with <html> and end it with </html>.\""
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "d4fed334-f0f9-4089-87c0-11e5673131bb",
      "name": "Send the test results based on the MailingList",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2832,
        240
      ],
      "parameters": {
        "sendTo": "={{ $('Retrieve Google Sheet data').first().json.MailingList }}",
        "message": "={{ $json.output }}",
        "options": {},
        "subject": "=Jenkins - Test Results Report - {{new Date().toLocaleDateString()}}"
      },
      "typeVersion": 2.2
    },
    {
      "id": "7a354845-5797-465d-9544-ac0b08fef0df",
      "name": "Perform HTTP Request to Jenkins Server for build numbers",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "The first request is needed to have the build numbers available.",
      "position": [
        1360,
        240
      ],
      "parameters": {
        "url": "={{ $node[\"Retrieve Google Sheet data\"].json.BaseUrl }}/{{ $node[\"Retrieve Google Sheet data\"].json.Environment }}/lastCompletedBuild/testReport/{{ $node[\"Retrieve Google Sheet data\"].json.FeatureClass }}.{{ $node[\"Retrieve Google Sheet data\"].json.Feature }}/",
        "options": {}
      },
      "typeVersion": 4.4
    },
    {
      "id": "ef12a5f6-cfa9-4885-b769-f5e27213529d",
      "name": "Extract the build number",
      "type": "n8n-nodes-base.html",
      "position": [
        1584,
        240
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "build_number",
              "cssSelector": "#breadcrumbs > li:nth-child(3) > a"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1f5caaf9-9837-4c9f-adf6-9ff9597f7ac0",
      "name": "Set the fields correctly for AI",
      "type": "n8n-nodes-base.set",
      "position": [
        2032,
        240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2720d3fe-5902-4d86-b436-71d2e592f82d",
              "name": "environment",
              "type": "string",
              "value": "={{ $node[\"Retrieve Google Sheet data\"].json.Environment }}"
            },
            {
              "id": "bfe0fce6-ec66-45f7-847a-e1b8673b0994",
              "name": "build_Number",
              "type": "string",
              "value": "={{ $node[\"Extract the build number\"].json }}"
            },
            {
              "id": "733f8aa4-dd97-4c63-9cc4-5d8283a04851",
              "name": "payload",
              "type": "string",
              "value": "={{ $json.test_data }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "1ce71933-6135-4e49-8d60-38d04dd542ee",
      "name": "Perform HTTP Request with depth 2 to Jenkins Server",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1808,
        240
      ],
      "parameters": {
        "url": "={{ $node[\"Retrieve Google Sheet data\"].json.BaseUrl }}/{{ $node[\"Retrieve Google Sheet data\"].json.Environment }}/lastCompletedBuild/testReport/{{ $node[\"Retrieve Google Sheet data\"].json.FeatureClass }}.{{ $node[\"Retrieve Google Sheet data\"].json.Feature }}/api/json?depth=2",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "text",
              "outputPropertyName": "test_data"
            }
          }
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "177fa0fa-f952-440f-8f62-a42b02570a7c",
      "name": "Trigger workflow daily on set time",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        912,
        240
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9,
              "triggerAtMinute": 5
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "68c7a94b-5cc5-4f07-9a34-a84e97727fc9",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1312,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 368,
        "content": "## Retrieve the Jenkins Test Data\n* First we have to retrieve the report in order to get the build numbers, needed for the AI to know which report we are talking about.\n* Then we extract the build numbers from the test reports using the HTML extract node.\n* In order to have a complete test report, we perform an additional HTTP request but with an \"/api/json?depth=2\" to get all data, including the failed results."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "831c055d-561d-4cec-b17c-281f6578fc82",
  "connections": {
    "Jenkins Report Analyzer": {
      "main": [
        [
          {
            "node": "Send the test results based on the MailingList",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract the build number": {
      "main": [
        [
          {
            "node": "Perform HTTP Request with depth 2 to Jenkins Server",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Jenkins Report Analyzer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Google Sheet data": {
      "main": [
        [
          {
            "node": "Perform HTTP Request to Jenkins Server for build numbers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set the fields correctly for AI": {
      "main": [
        [
          {
            "node": "Aggregate the data for AI processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger workflow daily on set time": {
      "main": [
        [
          {
            "node": "Retrieve Google Sheet data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate the data for AI processing": {
      "main": [
        [
          {
            "node": "Jenkins Report Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Perform HTTP Request with depth 2 to Jenkins Server": {
      "main": [
        [
          {
            "node": "Set the fields correctly for AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Perform HTTP Request to Jenkins Server for build numbers": {
      "main": [
        [
          {
            "node": "Extract the build number",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}