For every Epic you create in Jira, you can add “child” issues to indicate the list of tasks and subtasks to be performed. However, GitHub issues don’t support this type of parent-child hierarchy.
To keep track of information coming in from Jira Cloud, your connection should be able to replicate this hierarchy on the GitHub repo as task lists.
Also, you need to sync the assignee between both systems.
But why is all this necessary?
- It fetches information about the user or issue for both Jira and GitHub users.
- It keeps both teams on their respective systems without putting sensitive data at risk.
- It helps internal teams and admins to collaborate without context-switching.
- It helps organizations keep track of users making changes on both sides.
- Developers can keep track of open-source contributions from outside collaborators.
Use Case Requirements
Now that you understand why syncing task lists and assignees between Jira and GitHub, let’s go through the technical requirements of this use case.
- Getting authorization with the right access tokens,
- Fetching the right API name from the fields on both sides,
- Arranging the tasks on the GitHub site according to the established hierarchy,
- Mapping the correct Epics and tasks, as well as the assignee,
- Establishing sync rules for the incoming and outgoing data,
- Setting triggers to update the fields automatically,
- Closing the GitHub issue marks the Jira ticket as “Done”.
How to Sync Task Lists and Assignees Using Exalate
For starters, you need to choose a reliable solution that supports connections between both platforms.
Exalate is a bidirectional integration solution that works with Zendesk, Azure DevOps, ServiceNow, Jira, Salesforce, GitHub, etc.
Why Exalate?
- It supports trigger-based integration and bulk operations.
- It provides an integration as a service (IaaS) option for MSPs.
- You can use its Groovy scripting engine for complex use cases.
- It simplifies scripting with the advanced AI Assist script builder.
How to Configure Exalate for Jira GitHub Sync
First, install Exalate on GitHub and Jira from the respective marketplaces.
Next, follow the instructions in this comprehensive guide to establish a connection between them.
To set up an advanced Jira GitHub integration, choose Script Mode.
Once done, create an Epic in Jira. Then, add a few children (issues) before designating an assignee.
To configure the sync, open Exalate in your Jira dashboard, go to the connection you want to edit, and click on the “Edit connection” icon.

You have two options:
- Outgoing sync (on the Jira side) refers to the data to be sent over to the GitHub side.
- Incoming sync (on the GitHub side) refers to the data to be received from the issue on Jira.
Under the “Rules” tab on the Jira side, enter the following code snippet:
Jira Outgoing Sync
replica.key = issue.key replica.type = issue.type replica.assignee = issue.assignee replica.reporter = issue.reporter replica.summary = issue.summary replica.description = issue.description replica.labels = issue.labels replica.comments = issue.comments replica.resolution = issue.resolution replica.status = issue.status replica.parentId = issue.parentId replica.priority = issue.priority replica.attachments = issue.attachments replica.project = issue.project replica.project.versions = [] replica.project.components = [] replica.”child” = [] if (issue.issueType.name == “Epic”){ def js = new groovy.json.JsonSlurper() def jql = “cf[10014]=${issue.key}”.toString() def localIssue = new JiraClient(httpClient).http(“GET”, “/rest/api/latest/search”, [“jql”:[jql]], null, [:]) { response -> if (response.code >= 300 && response.code != 404) throw new com.exalate.api.exception.IssueTrackerException(“Failed to perform the request GET /rest/servicedeskapi/request/${issue.id}/feedback (status ${response.code}), and body was: \n\”${response.body}\”\nPlease contact Exalate Support: “.toString() + response.body) if (response.code == 404) return null def txt = response.body as String txt = js.parseText(txt) txt.issues.each { it -> replica.”child” += it.id } } } replica.customFields.”GitHub Repository” = issue.customFields.”GitHub Repository” |
The rules in this Jira Outgoing sync fetch the entities (key, id, string, txt, etc.) and objects in the Epic and send them over to GitHub.
GitHub Incoming Sync
if(firstSync){ if (replica.customFields.”GitHub Repository”?.value?.value == “Project Mars”) issue.repository = “majid-org/project-mars” else issue.repository = “majid-org/demo-repo” issue.summary = replica.summary issue.description = replica.description store(issue) syncHelper.syncBackAfterProcessing() } issue.summary = replica.summary issue.description = replica.description if (replica.issueType.name != “Task”){ if (replica?.”child”.size != 0){ issue.description += “\n\n______\nRelated Issues\n______” replica.”child”.each{ it -> def localParent = syncHelper.getLocalKeyFromRemoteId(it, “issue”)?.key issue.description += “\n- [ ] #${localParent}” } } } if (replica.issueType.name == “Task”){ if (replica?.parentId){ issue.description += “\n\n______\nParent Issue\n______” def localParent = syncHelper.getLocalKeyFromRemoteId(replica.parentId, “issue”)?.key issue.description += “\n- [ ] #${localParent}” } } def userMap = [ “[email protected]” : “syed-majidhassan”, “[email protected]” :”dhirennotani19″]if (replica?.assignee) issue.assignee = nodeHelper.getUserByUsername(userMap[replica.assignee?.email])else { issue.assignee = null issue.assignees = null} |
This code snippet maps the fields and entities coming in from the Jira side to specific repositories while maintaining the hierarchy and parent-child relationship. You can also map specific assignees to different email addresses.
Under the “Rules” tab on the GitHub side, enter the following code snippet:
GitHub Outgoing Sync
replica.key = issue.key replica.assignee = issue.assignee // Support for multiple assignees to a single issue replica.assignees = issue.assignees replica.reporter = issue.reporter replica.summary = issue.summary replica.description = issue.description replica.labels = issue.labels replica.comments = issue.comments replica.status = issue.status replica.project = issue.project replica.id = issueKey.idStr |
This code snippet allows you to send different entities to Jira Cloud, including all the assignees for an issue. It also fetches the issue ID and replicates it on the other side.
Jira Incoming Sync
if(firstSync){ issue.projectKey = “CM” issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: “Task” } issue.customFields.”SNOW Company”.value = replica.id issue.summary = replica.summary issue.comments = commentHelper.mergeComments(issue, replica) issue.attachments = attachmentHelper.mergeAttachments(issue, replica) issue.labels = replica.labels def statusMapping = [“open”:”To Do”, “closed”:”Done”] def remoteStatusName = replica.status.name issue.setStatus(statusMapping[remoteStatusName]) def userMap = [ “syed-majidhassan” : “[email protected]”, “dhirennotani19″ :”[email protected]”]def defaultUser = nodeHelper.getUserByEmail(“defaultuseremail”)issue.assignee = nodeHelper.getUserByEmail(userMap[replica.assignee?.username]) ?: defaultUser if(issue.typeName == “Task”){ def a = “” def matcher = replica.description =~ /#(\d{1,3})$/ if (matcher) a = replica.description.split(“#”)[1] def localIssue = syncHelper.getLocalIssueKeyFromRemoteId(issue.’SNOW Company’.toLong()) def res = httpClient.get(“/rest/api/3/issue/${localIssue.urn}”) res = httpClient.get(“/rest/api/3/issue/${res.fields.parent.key}”) def localParent = syncHelper.getLocalIssueKeyFromRemoteId(res.fields.customfield_10094.toLong()) syncHelper.eventSchedulerNotificationService.scheduleSyncEventNoChangeCheck(connection, localParent) } |
With this snippet, you can fetch the status of any issue (task) and have it reflect on the GitHub issue. You will also be able to indicate the assignees and users by email and username.

On the GitHub issue, you can find a link to the Epic added as a task list. This will appear as an active link.

When you click on the Epic link, you can see the tasks and the ID reflected as task lists. The status of each task will also appear on the issue.
Note: You can also use AI Assist to generate code snippets for this use case. Just make sure to review the code first before publishing changes.
Congratulations! You have now set rules and triggers to automate the process of syncing Jira Cloud with GitHub.

project=CM and “GitHub Repository” != null |
This trigger establishes the condition that if the Epic is from the project named “CM” and the GitHub Repository is actually active.
Start monitoring things in order to adjust the rules according to the demands of specific projects and issues.
You can also watch the video to see a comprehensive implementation of the use case.
If you still have questions or want to see how Exalate is tailored to your specific use case, book a demo with one of our experts right away.
Recommended Reads: