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

286 lines • 12.3 kB
import JiraApi from "jira-client"; 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"; export class JiraProvider extends TicketProviderRoot { jiraClient = null; constructor() { super(); const jiraOptions = { protocol: "https", host: (getEnvVar("JIRA_HOST") || "").replace("https://", ""), apiVersion: "3", strictSSL: true, }; // Basic Auth if (getEnvVar("JIRA_EMAIL") && getEnvVar("JIRA_TOKEN")) { jiraOptions.username = getEnvVar("JIRA_EMAIL") || ""; jiraOptions.password = getEnvVar("JIRA_TOKEN") || ""; this.isActive = true; } // Personal access token if (getEnvVar("JIRA_PAT")) { jiraOptions.bearer = getEnvVar("JIRA_PAT") || ""; this.isActive = true; } if (this.isActive) { this.jiraClient = new JiraApi(jiraOptions); } } static isAvailable() { if ( // Basic auth getEnvVar("JIRA_HOST") && getEnvVar("JIRA_TOKEN") && getEnvVar("JIRA_EMAIL")) { return true; } if ( // Personal Access Token getEnvVar("JIRA_HOST") && getEnvVar("JIRA_PAT")) { return true; } return false; } getLabel() { return "sfdx-hardis JIRA connector"; } // eslint-disable-next-line @typescript-eslint/no-unused-vars static async getTicketsFromString(text, options = {}) { const tickets = []; // Extract JIRA tickets using URL references const jiraUrlRegex = /(https:\/\/.*(jira|atlassian\.net).*\/[A-Z0-9]+-\d+\b)/g; const jiraMatches = await extractRegexMatches(jiraUrlRegex, text); for (const jiraTicketUrl of jiraMatches) { const pattern = /https:\/\/.*\/([A-Z0-9]+-\d+\b)/; const match = jiraTicketUrl.match(pattern); if (match) { const ticketId = match[1]; if (!tickets.some((ticket) => ticket.url === jiraTicketUrl || ticket.id === ticketId)) { tickets.push({ provider: "JIRA", url: jiraTicketUrl, id: ticketId, }); } } } // Extract JIRA tickets using Identifiers const jiraBaseUrl = getEnvVar("JIRA_HOST") || "https://define.JIRA_HOST.in.cicd.variables/"; const jiraRegex = getEnvVar("JIRA_TICKET_REGEX") || "(?<=[^a-zA-Z0-9_-]|^)([A-Za-z0-9]{2,10}-\\d{1,6})(?=[^a-zA-Z0-9_-]|$)"; const jiraRefRegex = new RegExp(jiraRegex, "gm"); const jiraRefs = await extractRegexMatches(jiraRefRegex, text); const jiraBaseUrlBrowse = jiraBaseUrl.replace(/\/$/, "") + "/browse/"; for (const jiraRef of jiraRefs) { const jiraTicketUrl = jiraBaseUrlBrowse + jiraRef; if (!tickets.some((ticket) => ticket.url === jiraTicketUrl || ticket.id === jiraRef)) { tickets.push({ provider: "JIRA", url: jiraTicketUrl, id: jiraRef, }); } } const ticketsSorted = sortArray(tickets, { by: ["id"], order: ["asc"] }); return ticketsSorted; } async collectTicketsInfo(tickets) { const jiraTicketsNumber = tickets.filter((ticket) => ticket.provider === "JIRA").length; if (jiraTicketsNumber > 0) { uxLog(this, c.cyan(`[JiraProvider] Now trying to collect ${jiraTicketsNumber} tickets infos from JIRA server ` + process.env.JIRA_HOST + " ...")); } for (const ticket of tickets) { if (ticket.provider === "JIRA") { let ticketInfo = null; try { ticketInfo = await this.jiraClient.getIssue(ticket.id); } catch (e) { uxLog(this, c.yellow(`[JiraApi] Error while trying to get ${ticket.id} information: ${e.message}`)); } if (ticketInfo) { const body = ticketInfo?.fields?.description?.content?.length > 0 ? ticketInfo.fields?.description?.content?.map((content) => content.text).join("\n") : ""; ticket.foundOnServer = true; ticket.subject = ticketInfo?.fields?.summary || ""; ticket.body = body; ticket.status = ticketInfo.fields?.status?.id || ""; ticket.statusLabel = ticketInfo.fields?.status?.name || ""; if (ticket.subject === "") { uxLog(this, c.yellow("[JiraProvider] Unable to collect JIRA ticket info for " + ticket.id)); if (JSON.stringify(ticketInfo).includes("<!DOCTYPE html>")) { uxLog(this, c.grey("[JiraProvider] This is probably a JIRA auth config issue, as HTML is returned")); } else { uxLog(this, c.grey(JSON.stringify(ticketInfo))); } ticket.foundOnServer = false; } uxLog(this, c.grey("[JiraProvider] Collected data for ticket " + ticket.id)); } else { uxLog(this, c.yellow("[JiraProvider] Unable to get JIRA issue " + ticket.id)); } } } return tickets; } async postDeploymentComments(tickets, org, pullRequestInfo) { uxLog(this, c.cyan(`[JiraProvider] Try to post comments on ${tickets.length} tickets...`)); const genericHtmlResponseError = "Probably config/access error since response is HTML"; const orgMarkdown = JSON.parse(await getOrgMarkdown(org, "jira")); const branchMarkdown = JSON.parse(await getBranchMarkdown("jira")); const tag = await this.getDeploymentTag(); const commentedTickets = []; const taggedTickets = []; for (const ticket of tickets) { if (ticket.foundOnServer) { // Build comment let prTitle = ""; let prUrl = ""; let prAuthor = ""; if (pullRequestInfo) { prUrl = pullRequestInfo.webUrl; if (prUrl) { prTitle = pullRequestInfo.title; prAuthor = pullRequestInfo?.authorName; } } const jiraComment = this.getJiraDeploymentCommentAdf(orgMarkdown.label, orgMarkdown.url, branchMarkdown.label, branchMarkdown.url || "", prTitle, prUrl, prAuthor); // Post comment try { const commentPostRes = await this.jiraClient.addCommentAdvanced(ticket.id, { body: jiraComment }); if (JSON.stringify(commentPostRes).includes("<!DOCTYPE html>")) { throw new SfError(genericHtmlResponseError); } commentedTickets.push(ticket); } catch (e6) { uxLog(this, c.yellow(`[JiraProvider] Error while posting comment on ${ticket.id}: ${e6.message}`)); } // Add deployment label to JIRA ticket try { const issueUpdate = { update: { labels: [{ add: tag }], }, }; await this.jiraClient.updateIssue(ticket.id, issueUpdate); taggedTickets.push(ticket); } catch (e6) { if (e6.message != null && e6.message.includes("<!doctype html>")) { e6.message = genericHtmlResponseError; } uxLog(this, c.yellow(`[JiraProvider] Error while adding label ${tag} on ${ticket.id}: ${e6.message}`)); } } } // Summary if (commentedTickets.length > 0 || taggedTickets.length > 0) { uxLog(this, c.gray(`[JiraProvider] Posted comments on ${commentedTickets.length} ticket(s): ` + commentedTickets.map((ticket) => ticket.id).join(", "))); uxLog(this, c.gray(`[JiraProvider] Added label ${tag} on ${taggedTickets.length} ticket(s): ` + taggedTickets.map((ticket) => ticket.id).join(", "))); } return tickets; } getJiraDeploymentCommentAdf(orgName, orgUrl, branchName, branchUrl, prTitle, prUrl, prAuthor) { const comment = { version: 1, type: "doc", content: [ { type: "paragraph", content: [ { type: "text", text: "Deployed by ", }, { type: "text", text: "sfdx-hardis", marks: [ { type: "link", attrs: { href: "${CONSTANTS.DOC_URL_ROOT}/", }, }, ], }, { type: "text", text: " in ", }, { type: "text", text: orgName, marks: [ { type: "link", attrs: { href: orgUrl, }, }, { type: "strong", }, ], }, { type: "text", text: " from branch ", }, { type: "text", text: branchName, marks: [ { type: "link", attrs: { href: branchUrl, }, }, { type: "strong", }, ], }, ], }, { type: "paragraph", content: [ { type: "text", text: "Related PR: ", }, { type: "text", text: prTitle, marks: [ { type: "link", attrs: { href: prUrl, }, }, ], }, { type: "text", text: `, by ${prAuthor}`, }, ], }, ], }; return comment; } } //# sourceMappingURL=jiraProvider.js.map