UNPKG

sfdx-hardis

Version:

Swiss-army-knife Toolbox for Salesforce. Allows you to define a complete CD/CD Pipeline. Orchestrate base commands and assist users with interactive wizards

190 lines • 9.22 kB
/* jscpd:ignore-start */ import * as azdev from "azure-devops-node-api"; import { TicketProviderRoot } from "./ticketProviderRoot.js"; import c from "chalk"; import sortArray from "sort-array"; import { getBranchMarkdown, getOrgMarkdown } from "../utils/notifUtils.js"; import { extractRegexMatches, uxLog } from "../utils/index.js"; import { SfError } from "@salesforce/core"; import { getEnvVar } from "../../config/index.js"; /* jscpd:ignore-end */ export class AzureBoardsProvider extends TicketProviderRoot { serverUrl; azureApi; teamProject; constructor() { super(); // Azure server url must be provided in SYSTEM_COLLECTIONURI. ex: https:/dev.azure.com/mycompany this.serverUrl = getEnvVar("SYSTEM_COLLECTIONURI"); // a Personal Access Token must be defined this.token = getEnvVar("CI_SFDX_HARDIS_AZURE_TOKEN") || getEnvVar("SYSTEM_ACCESSTOKEN"); this.teamProject = getEnvVar("SYSTEM_TEAMPROJECT"); if (this.serverUrl && this.token && this.teamProject) { this.isActive = true; } if (this.isActive) { const authHandler = azdev.getHandlerFromToken(this.token || ""); this.azureApi = new azdev.WebApi(this.serverUrl || "", authHandler); } } static isAvailable() { if ( // Basic auth getEnvVar("SYSTEM_COLLECTIONURI") && getEnvVar("SYSTEM_ACCESSTOKEN") && getEnvVar("SYSTEM_TEAMPROJECT")) { return true; } return false; } getLabel() { return "sfdx-hardis JIRA connector"; } static async getTicketsFromString(text, prInfo) { const tickets = []; // Extract Azure Boards Work Items const azureBoardsUrlRegex = /(https:\/\/.*\/_workitems\/edit\/[0-9]+)/g; const azureBoardUrlsMatches = await extractRegexMatches(azureBoardsUrlRegex, text); for (const azureTicketUrl of azureBoardUrlsMatches) { const pattern = /https:\/\/.*\/_workitems\/edit\/([0-9]+)/; const match = azureTicketUrl.match(pattern); if (match) { if (!tickets.some((ticket) => ticket.url === azureTicketUrl)) { tickets.push({ provider: "AZURE", url: azureTicketUrl, id: match[1], }); } } } const ticketsSorted = sortArray(tickets, { by: ["id"], order: ["asc"] }); if (!this.isAvailable()) { return ticketsSorted; } // Get tickets from Azure commits if (prInfo?.providerInfo?.commits) { const azureBoardsProvider = new AzureBoardsProvider(); const azureApi = azureBoardsProvider.azureApi; const azureGitApi = await azureApi.getGitApi(); const repositoryId = getEnvVar("BUILD_REPOSITORY_ID") || ""; const commitIds = prInfo?.providerInfo?.commits.filter((commit) => commit.hash).map((commit) => commit.hash); const azureCommits = []; for (const commitId of commitIds) { const commitRefs = await azureGitApi.getCommits(repositoryId, { fromCommitId: commitId, toCommitId: commitId, includeWorkItems: true }); azureCommits.push(...commitRefs); } for (const commit of azureCommits) { for (const workItem of commit?.workItems || []) { if (!tickets.some((ticket) => ticket.id === workItem.id)) { tickets.push({ provider: "AZURE", url: workItem.url || "", id: workItem.id || "", }); } } } } // Get tickets from Azure PR if (prInfo?.providerInfo?.workItemRefs?.length) { for (const workItemRef of prInfo.providerInfo.workItemRefs) { if (!tickets.some((ticket) => ticket.id === workItemRef.id)) { tickets.push({ provider: "AZURE", url: workItemRef.url, id: workItemRef.id, }); } } } return ticketsSorted; } // Call Azure Work Items apis to gather more information from the ticket identifiers async collectTicketsInfo(tickets) { const azureTicketsNumber = tickets.filter((ticket) => ticket.provider === "AZURE").length; if (azureTicketsNumber > 0) { uxLog(this, c.cyan(`[AzureBoardsProvider] Now trying to collect ${azureTicketsNumber} tickets infos from Azure Boards Server ` + process.env.SYSTEM_COLLECTIONURI + " ...")); } const azureWorkItemApi = await this.azureApi.getWorkItemTrackingApi(this.serverUrl || ""); for (const ticket of tickets) { if (ticket.provider === "AZURE") { const ticketInfo = await azureWorkItemApi.getWorkItem(Number(ticket.id)); if (ticketInfo && ticketInfo?.fields) { ticket.foundOnServer = true; ticket.subject = ticketInfo.fields["System.Title"] || ""; ticket.status = ticketInfo.fields["System.State"] || ""; ticket.statusLabel = ticketInfo.fields["System.State"] || ""; if (ticketInfo?._links && ticketInfo._links["html"] && ticketInfo._links["html"]["href"]) { ticket.url = ticketInfo?._links["html"]["href"]; } uxLog(this, c.grey("[AzureBoardsProvider] Collected data for Work Item " + ticket.id)); } else { uxLog(this, c.yellow("[AzureBoardsProvider] Unable to get Azure Boards WorkItem " + ticket.id + "\n" + c.grey(JSON.stringify(ticketInfo)))); } } } return tickets; } async postDeploymentComments(tickets, org, pullRequestInfo) { uxLog(this, c.cyan(`[AzureBoardsProvider] Try to post comments on ${tickets.length} work items...`)); const orgMarkdown = await getOrgMarkdown(org, "html"); const branchMarkdown = await getBranchMarkdown("html"); const tag = await this.getDeploymentTag(); const commentedTickets = []; const taggedTickets = []; const azureWorkItemApi = await this.azureApi.getWorkItemTrackingApi(this.serverUrl || ""); for (const ticket of tickets) { if (ticket.foundOnServer) { let azureBoardsComment = `Deployed from branch ${branchMarkdown} to org ${orgMarkdown}`; if (pullRequestInfo) { const prUrl = pullRequestInfo.webUrl || ""; if (prUrl) { const prAuthor = pullRequestInfo.authorName; azureBoardsComment += `<br/><br/>PR: <a href="${prUrl}">${pullRequestInfo.title}</a>` + (prAuthor ? ` by ${prAuthor}` : ""); } } // Post comment try { const commentPostRes = await azureWorkItemApi.addComment({ text: azureBoardsComment }, this.teamProject || "", Number(ticket.id)); if (commentPostRes?.id && commentPostRes?.id > 0) { commentedTickets.push(ticket); } else { throw new SfError("commentPostRes: " + commentPostRes); } } catch (e6) { uxLog(this, c.yellow(`[AzureBoardsProvider] Error while posting comment on ${ticket.id}\n${e6.message}\n${c.grey(e6.stack)}`)); } // Add tag try { const patchDocument = [ { op: "add", path: "/fields/System.Tags", value: tag, }, ]; const workItem = await azureWorkItemApi.updateWorkItem({}, patchDocument, Number(ticket.id), this.teamProject || ""); if (workItem?.id && workItem?.id > 0) { taggedTickets.push(ticket); } else { throw new SfError("tag workItem: " + workItem); } } catch (e6) { uxLog(this, c.yellow(`[AzureBoardsProvider] Error while adding tag ${tag} on ${ticket.id}\n${e6.message} \n${c.grey(e6.stack)} `)); } } } uxLog(this, c.gray(`[AzureBoardsProvider] Posted comments on ${commentedTickets.length} work item(s): ` + commentedTickets.map((ticket) => ticket.id).join(", "))); uxLog(this, c.gray(`[AzureBoardsProvider] Added tag ${tag} on ${taggedTickets.length} work item(s): ` + taggedTickets.map((ticket) => ticket.id).join(", "))); return tickets; } } //# sourceMappingURL=azureBoardsProvider.js.map