Jira to Azure DevOps Configuration questions

Originally asked by George Smith on 17 March 2023 (original question)


Hello, any documentation or other community posts on the below points would be amazing!

  • How to map Components (Jira) into a Components custom field in ADO
  • How to retain parent-child links when Story->Epic (in Jira) becomes User Story->Feature (ADO)
  • Is there any way to clean up the formatting of fields copied across? We have lots of tables/images that are just coming across as a massive block of text
  • When I manually Exalate, the item gets auto assigned to me it seems – can this be changed?
  • In Jira we have a custom field for ASOS Team(s) (multi select) which I want to populate as part of the area path in ADO:
    For example – Jira - ASOS Team(s) = Alpha
    Azure DevOps desired behaviour for Area Path = WebPlatform/Alpha

Thank you!

George


Answer by Ariel Aguilar on 21 March 2023

Hi there,

Hope this helps!

Components sync from Jira to ADO:

Jira Outgoing:

replica.components = issue.components

Azure Incoming:

workItem."Components field" = replica.components?.first().name

Epic Parent child relationship Jira to ADO:

Jira Outgoing:

replica.customFields."Epic Link" =  issue.customFields."Epic Link"

Azure Incoming:

if (replica.customFields."Epic Link".id){
    def localParent = syncHelper.getLocalIssueKeyFromRemoteId(replica.customFields."Epic Link".id.toLong())
    if(localParent){
        workItem.parentId = localParent.id
    }
}

Clean format Description, Summary, Comments or customFields Jira to ADO:

Jira Outgoing:

replica.description = nodeHelper.getHtmlField(issue, "description")
replica.summary = nodeHelper.getHtmlField(issue, "summary")
replica.comments = nodeHelper.getHtmlComments(issue)
replica."MyCustomField" = nodeHelper.getHtmlField(issue, "customfield_10111")

Azure Incoming:

workItem.description  = replica.description
workItem.summary      = replica.summary
workItem.comments     = commentHelper.mergeComments(workItem, replica)
workItem."Azure Field" = replica."MyCustomField"

Clean format Description, Summary, Comments or customFields ADO to Jira:
Azure Outgoing:

replica.summary = workItem.summary
replica.description = workItem.description
replica.comments = nodeHelper.stripHtmlFromComments(workItem.comments)
replica."Azure Field" = workItem."Azure Field"

Jira Incoming:

import com.exalate.transform.HtmlToWiki

HtmlToWiki htw = new HtmlToWiki()                                  
issue.summary = htw.transform(replica.summary)
issue.description = htw.transform(replica.description)
issue.comments = commentHelper.mergeComments(issue, replica)
issue.customFields."Jira Field".value = replica."Azure Field"?.value

Sync Assignee Jira to ADO, auto assign?

You might need to check if there is any automation in place or misconfiguration. You may comment out the assignee field to see if the behaviour persists. If it does, then ADO is the responsible.

//workItem.assignee = replica.assignee

Sync Custom Field Multi Select Jira to areaPath ADO:

Outgoing Jira

replica.customFields."Multi" = issue.customFields."Multi"

Incoming Azure

def multiMap = ["Alpha":"WebPlatform\\Alpha",
"Beta":"WebPlatform\\Beta",
"Gama":"WebPlatform\\Gama"]
areaOption = replica.customFields."Multi".value?.collect {it -> it?.value}
workItem.areaPath = multiMap[areaOption]

Kind regards,

Ariel


Answer by Nicolas Brown on 21 March 2023

Thanks Ariel Aguilar

  • For components - how should I handle items that do not have a component? As they are bringing the following error:
  • The HTML formatting works great - thank you!
  • I couldn’t get parent child linking to work, here is my code:
    Jira Outgoing:
replica.key            = issue.key
replica.type           = issue.type
replica.reporter       = issue.reporter
replica.summary        = issue.summary
replica.description    = nodeHelper.getHtmlField(issue, "description")
replica.labels         = issue.labels
replica.comments       = nodeHelper.getHtmlComments(issue)
replica.status         = issue.status
replica.parentId       = issue.parentId
replica.priority       = issue.priority
replica.attachments    = issue.attachments
replica.project        = issue.project
replica.components     = issue.components
replica.AcceptanceCriteria = nodeHelper.getHtmlField(issue, "customfield_10301")
replica.customFields."Epic Link" =  issue.customFields."Epic Link"

ADO Incoming:

if(firstSync){
   // Set type name from source entity, if not found set a default
   workItem.projectKey  =  "WebPlatform"
   def typeMap = [
       "Story" : "User Story",
       "Epic" : "Feature",
       "Bug" : "Bug",
       "Sub-task" : "Task",
       ]
   workItem.typeName = nodeHelper.getIssueType(typeMap[replica.type?.name],workItem.projectKey)?.name 
}

workItem.summary      = replica.summary
workItem.description  = replica.description
workItem.attachments  = attachmentHelper.mergeAttachments(workItem, replica)
workItem.comments     = commentHelper.mergeComments(workItem, replica)
workItem.labels       = replica.labels
workItem.priority     = replica.priority
workItem."Microsoft.VSTS.Common.AcceptanceCriteria" = replica.AcceptanceCriteria
workItem."Components" = replica.components?.first().name

if (replica.customFields."Epic Link".id){
    def localParent = syncHelper.getLocalIssueKeyFromRemoteId(replica.customFields."Epic Link".id.toLong())
    if(localParent){
        workItem.parentId = localParent.id
    }
}

def statusMap = [

       // "remote status name": "local status name"
         "To Do" : "New",
         "Backlog" : "New",
         "Refining" : "New",
         "Discovery" : "New",
         "Refined" : "New",
         "3 Amigo" : "Approved",
         "Ready for Dev" : "Approved",
         "In Progress" : "Active",
         "In Development" : "Active",
         "Ready for Code Review" : "Active",
         "Code Review" : "Active",
         "Ready for Testing" : "Active",
         "In Testing" : "Test",
         "Test" : "Test",
         "Branch Test" : "Test",
         "Merge Test" : "Test",
         "Ready for Release" : "Resolved",
         "On Hold" : "Resolved",
         "Live" : "Closed",
         "Done" : "Closed"
   ]
   
def remoteStatusName = replica.status.name
issue.setStatus(statusMap[remoteStatusName] ?: remoteStatusName)



Comments:

Ariel Aguilar commented on 21 March 2023

I see, you might try to change the line to:

workItem.``"Components field" = replica.components?.first()?.name

When you say the epic/story relation is not working, is the Epic already synchronized?

Nicolas Brown commented on 22 March 2023

Unfortunately that doesn’t work either (old community)

Epic isn’t synchronizing (but it also is being copied into ADO as a Feature)

Nicolas Brown commented on 22 March 2023

Hey Ariel Aguilar I had a call with Dhiren Notani today and he talked me through the component part. It was just a small tweak to:

if (replica.components){
workItem."Components" = replica.components?.first().name
}

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.