1 minute to read
exportJiraSprintChangelogIssues
About This Task
This task exports a simplified (key and summary) list of Jira issues for a specific sprint defined in the task configuration. Only a few additional fields (such as assignee) can be switched using configuration flags.
Once you define the sprint, the relevant AsciiDoc and Excel files will be generated. If a sprint is not defined in the configuration, changelogs for all sprints that match the configuration will be saved in separate AsciiDoc files and in different tabs within an Excel file.
The task configuration can be found within Config.gradle
. In addition to the configuration snippet below, it is important to configure the Jira API and credentials in the Jira section of the configuration inside the same file.
Configuration
// Sprint changelog configuration generate changelog lists based on tickets in sprints of an Jira instance.
// This feature requires at least Jira API & credentials to be properly set in Jira section of this configuration
sprintChangelog = [:]
sprintChangelog.with {
sprintState = 'closed' // it is possible to define multiple states, i.e. 'closed, active, future'
ticketStatus = "Done, Closed" // it is possible to define multiple ticket statuses, i.e. "Done, Closed, 'in Progress'"
showAssignee = false
showTicketStatus = false
showTicketType = true
sprintBoardId = 12345 // Jira instance probably have multiple boards; here it can be defined which board should be used
// Output folder for this task inside main outputPath
resultsFolder = 'Sprints'
// if sprintName is not defined or sprint with that name isn't found, release notes will be created on for all sprints that match sprint state configuration
sprintName = 'PRJ Sprint 1' // if sprint with a given sprintName is found, release notes will be created just for that sprint
allSprintsFilename = 'Sprints_Changelogs' // Extension will be automatically added.
}
Source
task exportJiraSprintChangelog(
description: 'exports all jira issues from Sprint for release notes',
group: 'docToolchain'
) {
doLast {
// Pre defined ticket fields for Changelog based on Jira Sprints
def defaultTicketFields = 'summary,status,assignee,issuetype'
// retrieving sprints for a given board
def sprints = { apiSprints, headers, boardId, sprintState ->
apiSprints.get(path: "agile/latest/board/${boardId}/sprint",
query:[state: "${sprintState}"],
headers: headers
).data
}
// retrieving issues for given sprint
def issues = { apiIssues, headers, boardId, sprintId, status ->
apiIssues.get(path: "agile/latest/board/${boardId}/sprint/${sprintId}/issue",
query: ['jql' : "status in (${status}) ORDER BY type DESC, status ASC",
'maxResults': 1000,
fields: defaultTicketFields
],
headers: headers
).data
}
// preparing target folder for generated files
final String taskSubfolderName = config.sprintChangelog.resultsFolder
final File targetFolder = new File(targetDir + File.separator + taskSubfolderName)
if (!targetFolder.exists()) targetFolder.mkdirs()
logger.debug("Output folder for 'exportJiraSprintChangelog' task is: '${targetFolder}'")
// Getting configuration
def jiraRoot = config.jira.api
def jiraProject = config.jira.project
def sprintState = config.sprintChangelog.sprintState
def ticketStatusForReleaseNotes = config.sprintChangelog.ticketStatus
def sprintBoardId = config.sprintChangelog.sprintBoardId
def showAssignee = config.sprintChangelog.showAssignee
def showTicketStatus = config.sprintChangelog.showTicketStatus
def showTicketType = config.sprintChangelog.showTicketType
def sprintName = config.sprintChangelog.sprintName
def allSprintsFilename = config.sprintChangelog.allSprintsFilename
logger.info("\n==========================\nJira Release notes config\n==========================")
logger.info("Spring Board ID: ${sprintBoardId}")
logger.info("Show assignees: ${showAssignee}. Show ticket status: ${showTicketStatus}. Show ticket type: ${showTicketType}")
logger.info("Filtering for sprints with configured state: '${sprintState}'")
logger.info("Filtering for issues with configured statuses: ${ticketStatusForReleaseNotes}")
logger.info("Attempt to generate release notes for sprint with a name: '${sprintName}'")
logger.info("Filename used for all sprints: '${allSprintsFilename}'")
def api = new groovyx.net.http.RESTClient(jiraRoot + '/rest/')
api.encoderRegistry = new groovyx.net.http.EncoderRegistry(charset: 'utf-8')
def headers = [
'Authorization': "Basic " + config.jira.credentials,
'Content-Type' : 'application/json; charset=utf-8'
]
def allChangelogsFilename = "${allSprintsFilename}.xlsx"
logger.quiet("Changelogs of all sprints will be saved in '${allChangelogsFilename}' file")
def changelogsXls = new File(targetFolder, allChangelogsFilename)
def changelogsXlsFos = new FileOutputStream(changelogsXls)
Workbook wb = new XSSFWorkbook();
CreationHelper hyperlinkHelper = wb.getCreationHelper();
String rgbS = "A7A7A7"
byte[] rgbB = Hex.decodeHex(rgbS) // get byte array from hex string
XSSFColor color = new XSSFColor(rgbB, null) //IndexedColorMap has no usage until now. So it can be set null.
XSSFCellStyle headerCellStyle = (XSSFCellStyle) wb.createCellStyle()
headerCellStyle.setFillForegroundColor(color)
headerCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND)
// prepare tickets according to configuration
def columns = ['key'].plus(defaultTicketFields.split(',').collect())
if (!showAssignee) { columns = columns.minus('assignee')}
if (!showTicketStatus) { columns = columns.minus('status')}
if (!showTicketType) { columns = columns.minus('issuetype')}
logger.info("Release notes will contain following info: ${columns}")
logger.info("\n=====================\n Sprints\n=====================")
//
def allMatchedSprints = sprints(api, headers, sprintBoardId, sprintState).values
def foundExactSprint = allMatchedSprints.any {it.name == sprintName}
logger.info("All sprints that matched configuration: ${allMatchedSprints.size()}")
def sprintsForChangelog = foundExactSprint ? allMatchedSprints.stream().filter() {it.name == sprintName} : allMatchedSprints
logger.info("Found exact Sprint with name '${sprintName}': ${foundExactSprint}.")
sprintsForChangelog.each { sprint ->
logger.quiet("\nSprint: $sprint.name [id: $sprint.id] state <$sprint.state>")
/* ================================================
Create new worksheet inside existing excel file
================================================ */
String safeSprintName = WorkbookUtil.createSafeSheetName("${sprint.name}")
def ws = wb.createSheet(safeSprintName)
// Add titles (typically key & summary, but assignee, ticket status, ticket type can be configured in Config.groovy too)
def titleRow = ws.createRow(0);
int cellNumber = 0;
columns.each {columnTitle -> titleRow.createCell(cellNumber++).setCellValue("${columnTitle.capitalize()}")}
def lastRow = titleRow.getRowNum()
titleRow.setRowStyle(headerCellStyle)
// set summary (at position 1) column wider than other columns
ws.setColumnWidth(1, 35*256)
/* =========================================
AsciiDoc file for each sprint
========================================= */
def asciidocFilename = "${sprint.name.replaceAll(" ", "_")}.adoc"
logger.info("Results will be saved in '${asciidocFilename}' file")
def changeLogAdoc = new File(targetFolder, "${asciidocFilename}")
changeLogAdoc.write(".Table ${sprint.name} Changelog\n", 'utf-8')
changeLogAdoc.append("|=== \n")
// AsciiDoc table columns
columns.each {columnTitle -> changeLogAdoc.append("|${columnTitle} ", 'utf-8')}
/* =========================================
Add tickets for the sprint
========================================= */
issues(api, headers, sprintBoardId, sprint.id, ticketStatusForReleaseNotes).issues.each {issue ->
def assignee = "${issue.fields.assignee ? issue.fields.assignee.displayName : 'unassigned'} "
def message = showAssignee ? "by ${assignee} ": ""
logger.quiet("Issue: [$issue.key] '$issue.fields.summary' ${message}<$issue.fields.status.name>")
/* ===========================
Write ticket to Excel
=========================== */
int cellPosition = 0
def row = ws.createRow(++lastRow)
Hyperlink link = hyperlinkHelper.createHyperlink(HyperlinkType.URL)
link.setAddress("${jiraRoot}/browse/${issue.key}")
Cell cellWithUrl = row.createCell(cellPosition)
cellWithUrl.setCellValue("${issue.key}")
cellWithUrl.setHyperlink(link)
row.createCell(++cellPosition).setCellValue("${issue.fields.summary}")
/* =============================
Write ticket to Asciidoc
============================= */
changeLogAdoc.append("\n", 'utf-8')
changeLogAdoc.append("| ${jiraRoot}/browse/${issue.key}[${issue.key}] ", 'utf-8')
changeLogAdoc.append("| ${issue.fields.summary} ", 'utf-8')
/* === Write ticket status, assignee, ticket typee if configured to both Asciidoc & Excel files === */
if (showTicketStatus) {
row.createCell(++cellPosition).setCellValue("${issue.fields.status.name}")
changeLogAdoc.append("| ${issue.fields.status.name} ", 'utf-8')
}
if (showAssignee) {
row.createCell(++cellPosition).setCellValue("${assignee}")
changeLogAdoc.append("| ${assignee}", 'utf-8')
}
if (showTicketType) {
row.createCell(++cellPosition).setCellValue("${issue.fields.issuetype.name}")
changeLogAdoc.append("| ${issue.fields.issuetype.name} ", 'utf-8')
}
}
// Close the asciidoc table
changeLogAdoc.append("\n|=== \n",'utf-8')
// Set auto-width to KEY column
ws.autoSizeColumn(0);
}
// Write to Excel file
wb.write(changelogsXlsFos)
}
}
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.