Hivel
Hivel
Hivel
  • 👋Welcome to Hivel
  • 🚀Using Hivel
    • ⭐Cockpit Pro
      • ⬇️How to download reports
      • 🤝Meetings Breakdown
    • 🏃‍♀️Activity
    • ⛑️Work Item Breakdown
    • ➕Hivel Quadrant
    • 💰Investment
      • Issue Age
      • How to set up Products & Allocation tabs in the Investment Screen?
      • How to add Custom fields for Product and Allocation Label in Jira
    • 💎Performance Appraisal
    • 🎯Pull Request
      • Comments Categorization
      • Review Cycles
      • How to exclude outlier commits and PRs?
    • 🐞Quality (SonarQube)
    • ♨️Coding Hotspots
    • ⚽Goals
    • 👩‍🏫Process
    • 🖥️Coding
      • Understanding Rework, New Work, and Maintenance
    • 👨‍🎓Dev360
    • 🔔Slack Alerts and Notifications
  • Copilot Adoption
  • 📊Metrics & Definitions
    • 🚅Speed
      • Deployment Frequency
      • Coding Time
      • Review Time
      • Merge Time
      • Cycle Time
      • Pickup time
    • 💯Quality
      • Change Failure Rate
      • Maintenance
      • Rework
      • Mean Time to Restore (MTTR)
      • PRs merged without review
      • PR Reviewed
      • Flashy Reviews
      • PRs > 400 LoC
    • 📈Throughput
      • New Work%
      • How are Active Days calculated
      • PRs Open, PRs Ready to Review or Merge
  • 🔗Integrations
    • List of all integrations
    • GitHub
      • How do I Signup using GitHub?
      • How to integrate GitHub with Classic Token?
      • How to create Github fine-grained token for Hivel Integration?
      • How to reauthorize Github with a service account?
      • GitHub-Alternate SignUp Method Instructions
      • How to re-initiate GitHub integration with Hivel
    • Gitlab
      • Gitlab Server
      • Gitlab Cloud
      • How to ensure Gitlab token has access to required groups/repositories
    • BitBucket
      • BitBucket Integration
      • Validation at a metric level for BitBucket
    • Jira
      • How to integrate Jira Cloud with Hivel
      • How to Re-authorize Jira in Hivel?
        • Page
      • How to Integrate Jira with OAuth 2.0
      • How to Re-authorize Jira with OAuth 2.0
      • How to integrate Jira Server with Hivel
    • Azure DevOps
    • Google Calendar
      • How to integrate Google Calendar in Hivel
    • Microsoft Outlook
    • Slack
    • SonarQube
    • Okta
    • Jenkins
    • Jenkins Freestyle Integration with Ansible and Hivel Webhook
  • On-Prem Setup
    • On-Prem Installation Guide
    • Jira On-Prem
    • Gitlab On-Prem
    • SonarQube On-Prem
    • Application Setup Guide: User Sign-up and Integration
    • Creating a Bitbucket App Password
    • On-Prem Outlook Integration
  • Github Copilot Integration
  • ⚒️Setup
    • Sign Up
      • How to sign up to Hivel?
    • Users
      • How to invite more users to use Hivel?
      • How to add or update an user's email id?
      • How to merge users?
      • How to update the name of a user?
      • Can I see the data of a user or repo that I don’t have access to on my SCM tool?
      • How to archive users?
    • Teams
      • How to create teams?
      • How to delete a team?
      • How to modify a team?
      • How to create sub-teams?
    • Role-Based Access Control (RBAC)
    • ⚙️Configurations Explained
      • 🌴Branch Configurations
      • 🐞Hotfix Configurations
        • Track hotfixes via patch version pattern
      • Other Configurations
  • ⏭️Upcoming Features and Enhancements
  • Release Notes
    • Release Notes
      • Release Notes - October 2024
      • Release Notes - November 2024
      • Release Notes - December 2024
      • Release Notes - January 2025
      • Release Notes - February 2025
      • Release Notes - March 2025
      • Release Notes - April 2025
  • 🔐API Documentation
    • 🏁Tracking Releases and Incidents with Hivel
    • Deployment API
    • Create Incident API
  • 🌟Insights and Best Practices
    • Developer's Guide to Hivel
    • 🎖️Best Practices for Software Development Efficiency
    • 🧹Jira Best Practices
    • Tips and tricks to improve performance
      • How to improve Speed
        • What to do if my Cycle Time is high?
        • What to do if my Coding Time is high?
        • What to do if my Review Time is high?
        • What to do if my Merge Time is high?
      • How to improve Quality
        • What to do if my Rework is high?
        • What happens if there are too many Flashy Reviews and how to prevent them?
        • How to address and prevent unreviewed PRs
        • Strategies to Reduce Mean Time to Restore (MTTR)
        • Best Practices for Team Ownership in Code Review
        • How to identify root cases of high change failure rates?
        • Building a feedback loop for continuous code improvement
      • How to improve planning and throughput
        • Leveraging data for more effective sprint planning
        • What metrics can I use to prevent developer burnout?
      • How to track, manage, and reduce technical debt?
      • What are the impacts of context switching on developer productivity and how to reduce it?
      • How to build a data-driven culture of Engineering?
      • How to balance speed and quality?
  • FAQs
    • Why can't I remove a user from a team?
    • How to change a team owner?
    • How do we account for weekends in the metrics?
    • Are draft PRs considered for calculation of coding/cycle time?
    • Why do some metrics like PRs reviewed or merged have more than 100%?
    • How to mark leaves & absences?
    • Where can I see average PR sizes?
    • How do I link Pull Requests to Issues
    • Why is count of PRs reviewed or merged is different across screens?
    • Why is data of some members are not visible?
    • Why is there an abnormal spike in Cycle Time?
    • Why some of the repositories are not imported or synced?
    • How to get a report of monthwise developer activity metrics?
    • How can I see the progress against my goals?
    • How to see all the metrics by sprints or releases?
    • Why are commits done today are not reflecting immediately
    • Why Product and Allocation's previous data is not reflecting?
    • Why cannot I see cycle time against developers even though they have commits?
    • Why do some Jira issues show as spillover in Hivel even though they were completed in Jira?
    • Why is pickup time not included in cycle time?
    • How can I add a template in dashboards for my org to follow?
    • Can I get a detailed report of all the activity done by developer per day?
    • How do I exclude a PR from rework/maintenance/new work calculation?
    • Why don't I see delivery accuracy for Kanban boards?
    • Why do I see "NA" in the percentage change of a metric?
    • Why do I see "No existing user found" message while login
    • Why am I not able to select more than 6 months at a time?
    • Why does the filters change when I move to dashboards but in other screen it remains same?
    • How to validate the data on Hivel?
    • How to measure impact of Copilots using Hivel?
    • Easing into Kanban: How to set your team up for success
Powered by GitBook
On this page
  • 🧰 Prerequisites
  • Required Credentials for Integration
  • Retrieving Your Organization ID and API Key
  • Configuring Credentials in Jenkins
  • Step 2: Bind Secrets in Job Configuration
  • Step 3: Install Ansible on Jenkins Server
  • Step 4: Add Build Step – Execute Ansible Playbook
  • Step 5: Add Groovy Postbuild Script for Webhook Reporting

Was this helpful?

  1. Integrations

Jenkins Freestyle Integration with Ansible and Hivel Webhook

This guide walks you through integrating Jenkins Freestyle pipelines with Hivel using an Ansible playbook and webhook reporting. You’ll learn how to trigger a playbook, capture build and stage metadata, and send it to Hivel’s API endpoint for monitoring and analytics.

🧰 Prerequisites

Make sure the following are set up on your Jenkins server (typically an EC2 instance):

  • ✅ Jenkins installed and accessible

  • ✅ Admin access to Jenkins UI

  • ✅ Ansible installed and available in PATH

  • ✅ Git configured (for Git metadata collection)

📦 Required Plugin

Install the following plugin:

Plugin Name
Description

Allows running Groovy scripts after a build

Required Credentials for Integration

During the integration, you'll need:

  • Organization ID (Generated in the Hivel application)

  • API Key (Generated in the Hivel application)

Retrieving Your Organization ID and API Key

  1. Open the Hivel application and navigate to Integrations under the Settings.

  2. Locate the Jenkins Integration card and click Connect.

  3. Follow the given steps to start your integration.

  4. By clicking on generate your Organization ID and API Key will be generated.

  5. Use these fields while integrating with Jenkins. The steps for where to apply them are provided on the following page.

Configuring Credentials in Jenkins

  1. Log in to your Jenkins application.

  2. From the side navigation, select Manage Jenkins.

  3. Click on Credentials.

  4. Click on global under Stores scoped to Jenkins

  5. Choose Add Credentials - you will need to do this twice (once for the Organization ID and once for the API Key).

  6. From the dropdown, select Secret text.

  7. After selecting Secret text from the dropdown, fill in the details twice as follows:

    1. Adding the API Key

      1. ID: hivel-jenkins-api-key

      2. Secret: {your API key} (copied from Hivel)

      3. Description: This is the Jenkins API key from Hivel

      4. Click Save

    2. Repeat the process again for Adding the Organization ID

      1. ID: hivel-org-id

      2. Secret: {your Organization ID} (copied from Hivel)

      3. Description: This is the Jenkins Organization ID from Hivel

      4. Click Save

  8. Once you have completed the above steps, your Jenkins Global Credentials should display the following entries:

Step 2: Bind Secrets in Job Configuration

  1. Open your Freestyle job(pipeline in your dashboards)

  2. Go to the Environment section

  3. Check Use secret text(s) or file(s)

  4. Click Add and bind the credentials like this:

Variable Name
Credentials ID

HIVEL_ORG_ID

hivel-org-id

HIVEL_JENKINS_API_KEY

hivel-jenkins-api-key

Step 3: Install Ansible on Jenkins Server

Ensure Ansible is installed on the EC2/server where Jenkins is hosted.

For Amazon Linux 2:

sudo amazon-linux-extras enable ansible2
sudo yum install -y ansible

Run ansible --version on the Jenkins server to confirm installation.

Step 4: Add Build Step – Execute Ansible Playbook

Under Build Steps → Execute shell, add:

# Run Ansible playbook with JSON output
ANSIBLE_STDOUT_CALLBACK=json \
ansible-playbook ansible/dummy-stages.yml \
-i "localhost," --connection=local \
> ansible-output.json 2>&1 || true

This ensures that:

  • The playbook runs locally

  • Output is saved in JSON format to ansible-output.json

  • Even on errors, the script doesn’t fail the job

Note : replace the path of your ansible file with “ansible/dummy-stages.yml”. Invoking ansible like this will only help us to get insights from ansible playbook.

Step 5: Add Groovy Postbuild Script for Webhook Reporting

Under Post-build Actions:

  1. Add → Groovy Postbuild

  2. Uncheck the box labeled Use Groovy Sandbox

  3. Approve script (open will appear once you enter script in groovy text box)

  4. Save the job

if you check “use groovy sandbox” you might require to allow all script to be executed from settings section : go to Manage Jenkins → In-process Script Approval and approve the pending script.

Add groovy postbuild

{
  "org_id": 12345,
  "job_name": "freestyle-ansible-job",
  "build_number": 12,
  "status": "SUCCESS",
  "build_url": "https://jenkins.example.com/job/freestyle-ansible-job/12/",
  "start_time": "2025-04-08T10:12:00Z",
  "end_time": "2025-04-08T10:12:45Z",
  "duration": 45.25,
  "git": {
    "commit": "abc123...",
    "branch": "main",
    "author": "John Doe",
    "author_email": "john@example.com",
    "commit_message": "Add API integration",
    "repository": "git@example.com:project/repo.git"
  },
  "triggered_by": "john.doe",
  "stages": [
    {
      "stage_name": "Ensure packages installed",
      "start_time": "2025-04-08T10:12:05.000000Z",
      "end_time": "2025-04-08T10:12:10.000000Z",
      "duration": 5.0,
      "status": "SUCCESS",
      "message": "All packages present"
    }
  ]
}
/**
* JenkinsBuildData class handles all build data collection and processing
* for Jenkins freestyle jobs. It collects build metadata, Git information,
* and stage execution details, then sends them to a webhook endpoint.
*
* This script is designed to work with Jenkins freestyle jobs and expects:
* 1. A workspace directory with Git repository
* 2. Ansible output file (ansible-output.json) in the workspace
* 3. Environment variables for configuration
*/
import hudson.model.Cause
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import java.text.SimpleDateFormat
import java.util.TimeZone

class JenkinsBuildData {

   def env
   def manager
   def workspace
   def logger

   // API endpoint configuration
   static final String API_ENDPOINT = 'https://app.hivel.ai/insightlyapi/webhook/jenkins'
   static final int HTTP_TIMEOUT = 5000
   static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"
   static final String DATE_FORMAT_SIMPLE = "yyyy-MM-dd'T'HH:mm:ss'Z'"

   /**
    * Constructor initializes the build data collector with Jenkins environment
    * @param manager Jenkins build manager instance
    * @throws IllegalArgumentException if manager is null
    */
   JenkinsBuildData(manager) {
       if (!manager) {
           throw new IllegalArgumentException('Manager cannot be null')
       }
       this.manager = manager
       this.env = manager.build.environment
       this.workspace = new File(this.env.WORKSPACE)
       this.logger = manager.listener.logger
   }

   /**
    * Validates the workspace and required environment variables
    * @return boolean indicating if all required conditions are met
    */
   private boolean validateEnvironment() {
       if (!workspace.exists()) {
           logger.println "Error: Workspace directory does not exist: ${workspace.absolutePath}"
           return false
       }

       if (!env.JOB_NAME) {
           logger.println 'Error: JOB_NAME environment variable is not set'
           return false
       }

       if (!env.BUILD_NUMBER) {
           logger.println 'Error: BUILD_NUMBER environment variable is not set'
           return false
       }

       return true
   }

   /**
    * Calculates duration between two timestamps in seconds with 2 decimal precision
    * @param start Start timestamp
    * @param end End timestamp
    * @return Duration in seconds
    */
   private double calculateDuration(String start, String end) {
       if (!start || !end) return 0
       try {
           def sdf = new SimpleDateFormat(DATE_FORMAT)
           sdf.setTimeZone(TimeZone.getTimeZone('UTC'))
           def startDate = sdf.parse(start)
           def endDate = sdf.parse(end)
           def durationMs = endDate.time - startDate.time
           // Convert milliseconds to seconds and round to 2 decimal places
           return (durationMs / 1000.0).round(2)
       } catch (Exception e) {
           return 0
       }
   }

   /**
    * Parses build stages from ansible-output.json file
    * @return List of stage objects containing execution details
    */
   def parseAnsibleStages() {
       def stages = []
       try {
           def ansibleOutputFile = new File(workspace.toString() + '/ansible-output.json')
           if (!ansibleOutputFile.exists() || !ansibleOutputFile.text?.trim()) {
               return stages
           }

           def jsonContent = ansibleOutputFile.text.substring(
               ansibleOutputFile.text.indexOf('{'),
               ansibleOutputFile.text.lastIndexOf('}') + 1
           )
           def ansibleOutput = new JsonSlurper().parseText(jsonContent)

           if (!ansibleOutput.plays || !ansibleOutput.plays[0]?.tasks) {
               return stages
           }

           ansibleOutput.plays[0].tasks.each { task ->
               def hostInfo = task.hosts?.localhost
               if (!hostInfo || !(hostInfo.action in ['debug', 'fail'])) return

               def durationMs = calculateDuration(task.task.duration?.start, task.task.duration?.end)
               stages << [
                   stage_name : task.task.name ?: 'Unnamed Task',
                   start_time : task.task.duration?.start ?: '',
                   end_time   : task.task.duration?.end ?: '',
                   duration   : durationMs,
                   status     : determineTaskStatus(hostInfo),
                   message    : hostInfo.msg ?: hostInfo.message ?: 'No message provided'
               ]
           }
       } catch (Exception e) { }
       return stages
   }

   /**
    * Determines the status of a task based on its execution result
    * @param hostInfo Task execution information
    * @return Status string (FAILED, SKIPPED, CHANGED, SUCCESS, or UNKNOWN)
    */
   private String determineTaskStatus(hostInfo) {
       if (hostInfo.failed == true) return 'FAILED'
       if (hostInfo.skipped == true) return 'SKIPPED'
       if (hostInfo.changed == true) return 'CHANGED'
       if (hostInfo.failed != true && hostInfo.skipped != true) return 'SUCCESS'
       return 'UNKNOWN'
   }

   /**
    * Calculates build timing information
    * @return Map containing start_time, end_time (ISO8601 format), and duration (in seconds)
    */
   def getBuildTiming() {
       def startTime = manager.build.getStartTimeInMillis()
       def duration = manager.build.getDuration()
       def endTime = startTime + duration

       // If duration is 0, calculate from start time to current time
       if (duration == 0) {
           endTime = System.currentTimeMillis()
           duration = endTime - startTime
       }

       // Calculate duration in seconds and round to 2 decimal places
       def durationSec = Math.round(duration / 1000.0 * 100) / 100.0

       def dateFormat = new SimpleDateFormat(DATE_FORMAT_SIMPLE)
       dateFormat.setTimeZone(TimeZone.getTimeZone('UTC'))

       return [
           start_time: dateFormat.format(new Date(startTime)),
           end_time  : dateFormat.format(new Date(endTime)),
           duration  : durationSec
       ]
   }

   /**
    * Collects Git repository information
    * @return Map containing Git metadata (commit, branch, author, etc.)
    */
   def getGitInfo() {
       def gitCommit = env.GIT_COMMIT ?: executeCommand('git rev-parse HEAD')
       def gitBranch = (env.GIT_BRANCH ?: executeCommand('git rev-parse --abbrev-ref HEAD'))?.replaceAll('^origin/', '')
       def gitRepo = env.GIT_URL ?: executeCommand('git config --get remote.origin.url')

       def commitDetails = executeCommand("git log -1 --pretty=format:'%an|%ae|%s'").split('\\|', 3)

       return [
           commit        : gitCommit,
           branch        : gitBranch,
           author        : commitDetails.size() > 0 ? commitDetails[0].trim() : '',
           author_email  : commitDetails.size() > 1 ? commitDetails[1].trim() : '',
           commit_message: commitDetails.size() > 2 ? commitDetails[2].trim() : '',
           repository    : gitRepo
       ]
   }

   /**
    * Executes a shell command in the workspace directory
    * @param command Shell command to execute
    * @return Command output as string
    */
   def executeCommand(command) {
       try {
           def process = command.execute(null, workspace)
           process.waitFor()
           return process.text.trim()
       } catch (Exception e) {
           return ''
       }
   }

   /**
    * Determines who triggered the build
    * @return Username of the build triggerer
    */
   def getBuildTrigger() {
       def cause = manager.build.getCause(Cause.UserIdCause)
       return cause?.userName ?: 'Unknown'
   }

   /**
    * Builds the complete payload for the webhook
    * @param stages List of build stages
    * @return Complete payload map ready for JSON conversion
    */
   def buildPayload(stages) {
       def timing = getBuildTiming()
       def gitInfo = getGitInfo()

       return [
           org_id       : (env.HIVEL_ORG_ID ?: '0') as int,
           job_name     : env.JOB_NAME,
           build_number : env.BUILD_NUMBER as int,
           status       : manager.build.result.toString(),
           build_url    : env.BUILD_URL,
           start_time   : timing.start_time,
           end_time     : timing.end_time,
           duration     : timing.duration,
           git          : gitInfo,
           triggered_by : getBuildTrigger(),
           stages       : stages
       ]
   }

   /**
    * Sends the payload to the configured webhook endpoint
    * @param jsonPayload JSON string to send
    * @return boolean indicating if the request was successful
    */
   def sendHttpRequest(jsonPayload) {
       if (!jsonPayload) {
           logger.println 'Error: Cannot send empty payload'
           return false
       }

       def url = new URL(API_ENDPOINT)
       def conn = url.openConnection()

       try {
           conn.setRequestMethod('POST')
           conn.doOutput = true
           conn.setRequestProperty('Content-Type', 'application/json')

           def apiKey = env.HIVEL_JENKINS_API_KEY
           if (apiKey) {
               conn.setRequestProperty('API_KEY', apiKey)
           } else {
               logger.println 'Warning: HIVEL_JENKINS_API_KEY is not set'
           }

           conn.setConnectTimeout(HTTP_TIMEOUT)
           conn.setReadTimeout(HTTP_TIMEOUT)

           conn.outputStream.withWriter('UTF-8') { writer ->
               writer.write(jsonPayload)
           }

           def responseCode = conn.responseCode
           def responseText = conn.inputStream.text
           logger.println "Webhook response code: $responseCode"
           logger.println "Webhook response: $responseText"

           return responseCode >= 200 && responseCode < 300
       } catch (Exception e) {
           logger.println "Error sending request: ${e.message}"
           logger.println "Stack trace: ${e.stackTrace}"
           return false
       }
   }

}

// Main execution block
try {
   def buildData = new JenkinsBuildData(manager)

   // Validate environment before proceeding
   if (!buildData.validateEnvironment()) {
       throw new IllegalStateException('Environment validation failed')
   }

   def stages = buildData.parseAnsibleStages()
   def payload = buildData.buildPayload(stages)
   def jsonPayload = JsonOutput.toJson(payload)

   if (!buildData.sendHttpRequest(jsonPayload)) {
       throw new IllegalStateException('Failed to send build data to webhook')
   }

   manager.listener.logger.println 'Successfully processed and sent build data'
} catch (Exception e) {
   manager.listener.logger.println "Error: ${e.message}"
   manager.listener.logger.println "Stack trace: ${e.stackTrace}"
   throw e
}
PreviousJenkinsNextOn-Prem Setup

Last updated 1 month ago

Was this helpful?

If you find it difficult, don’t worry! You can always reach out to us for assistance at .

Paste the Groovy webhook script (Full Version) ()

🔗
support@hivel.ai
provided below
Groovy Postbuild Plugin