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

251 lines (250 loc) • 12.8 kB
import sortArray from "sort-array"; import { prettifyFieldName } from "./flowVisualiser/nodeFormatUtils.js"; import { isIntegration, isPreprod, isProduction } from "./orgConfigUtils.js"; export class BranchStrategyMermaidBuilder { branchesAndOrgs; gitBranches; salesforceOrgs = []; salesforceDevOrgsGroup = []; gitLinks = []; deployLinks = []; sbDevLinks = []; retrofitLinks = []; mermaidLines = []; featureBranchNb = 0; constructor(branchesAndOrgs) { this.branchesAndOrgs = branchesAndOrgs; } build(options) { this.listGitBranchesAndLinks(); this.listSalesforceOrgsAndLinks(); this.generateMermaidLines(); if (options.withMermaidTag) { this.mermaidLines.unshift("```mermaid"); this.mermaidLines.push("```"); } return options.format === "list" ? this.mermaidLines : this.mermaidLines.join("\n"); } listGitBranchesAndLinks() { const branchesWhoAreMergeTargets = []; const branchesMergingInPreprod = []; this.gitBranches = this.branchesAndOrgs.map((branchAndOrg) => { const nodeName = branchAndOrg.branchName + "Branch"; for (const mergeTarget of branchAndOrg.mergeTargets || []) { if (!branchesWhoAreMergeTargets.includes(mergeTarget)) { branchesWhoAreMergeTargets.push(mergeTarget); } if (isPreprod(mergeTarget)) { branchesMergingInPreprod.push(branchAndOrg.branchName); } this.gitLinks.push({ source: nodeName, target: mergeTarget + "Branch", type: "gitMerge", label: "Merge" }); } return { name: branchAndOrg.branchName, nodeName: nodeName, label: branchAndOrg.branchName, class: isProduction(branchAndOrg.branchName) ? "gitMain" : "gitMajor", level: branchAndOrg.level }; }); // Create feature branches for branches that are not merge targets const noMergeTargetBranchAndOrg = this.branchesAndOrgs.filter((branchAndOrg) => !branchesWhoAreMergeTargets.includes(branchAndOrg.branchName)); if (branchesMergingInPreprod.length < 2 && !noMergeTargetBranchAndOrg.find((branchAndOrg) => isPreprod(branchAndOrg.branchName))) { // We must check if a 'preprod' branch exists before adding it to the array. // The .find() method returns undefined if no matching element is found. // Without this check, an 'undefined' value could be pushed to the array, // causing a null pointer exception later when the code tries to access the 'branchName' property. const preprodBranch = this.branchesAndOrgs.find((branchAndOrg) => isPreprod(branchAndOrg.branchName)); if (preprodBranch) { noMergeTargetBranchAndOrg.push(preprodBranch); } } for (const branchAndOrg of noMergeTargetBranchAndOrg) { const nameBase = isPreprod(branchAndOrg.branchName) ? "hotfix" : "feature"; const level = branchAndOrg.level - 1; this.salesforceDevOrgsGroup.push(branchAndOrg.branchName); this.addFeatureBranch(nameBase, level, branchAndOrg); this.addFeatureBranch(nameBase, level, branchAndOrg); } // Add retrofit link only if it does not mess with the diagram display :/ if (branchesMergingInPreprod.length < 2) { const mainBranch = this.branchesAndOrgs.find((branchAndOrg) => isProduction(branchAndOrg.branchName)); const preprodBranch = this.branchesAndOrgs.find((branchAndOrg) => isPreprod(branchAndOrg.branchName)); const integrationBranch = this.branchesAndOrgs.find((branchAndOrg) => isIntegration(branchAndOrg.branchName)); if (mainBranch && preprodBranch && integrationBranch) { this.retrofitLinks.push({ source: mainBranch.branchName + "Branch", target: integrationBranch.branchName + "Branch", type: "gitMerge", label: "Retrofit from RUN to BUILD" }); } } // Sort branches & links this.gitBranches = sortArray(this.gitBranches, { by: ['level', 'name'], order: ['asc', 'asc'] }); this.gitLinks = sortArray(this.gitLinks, { by: ['level', 'source'], order: ['asc', 'asc'] }); } addFeatureBranch(nameBase, level, branchAndOrg) { this.featureBranchNb++; const nameBase1 = nameBase + this.featureBranchNb; const nodeName1 = nameBase + "Branch" + this.featureBranchNb; this.gitBranches.push({ name: nameBase1, nodeName: nodeName1, label: nameBase1, class: "gitFeature", level: level, group: branchAndOrg.branchName }); this.gitLinks.push({ source: nodeName1, target: this.gitBranches.find((gitBranch) => gitBranch.name === branchAndOrg.branchName)?.nodeName || "ERROR", type: "gitMerge", label: "Merge" }); } listSalesforceOrgsAndLinks() { for (const gitBranch of this.gitBranches) { const branchAndOrg = this.branchesAndOrgs.find((branchAndOrg) => branchAndOrg.branchName === gitBranch.name); if (branchAndOrg) { // Major org const nodeName = branchAndOrg.branchName + "Org"; this.salesforceOrgs.push({ name: branchAndOrg.branchName, nodeName: branchAndOrg.branchName + "Org", label: isProduction(branchAndOrg.branchName) ? "Production Org" : prettifyFieldName(branchAndOrg.branchName) + " Org", class: gitBranch.class === "gitMain" ? "salesforceProd" : gitBranch.class === "gitMajor" ? "salesforceMajor" : "salesforceDev", level: branchAndOrg.level }); this.deployLinks.push({ source: gitBranch.nodeName, target: nodeName, type: "sfDeploy", label: "Deploy to Org", level: branchAndOrg.level }); } else { const nodeName = gitBranch.name + "Org"; this.salesforceOrgs.push({ name: gitBranch.name, nodeName: nodeName, label: "Dev " + prettifyFieldName(gitBranch.name), class: "salesforceDev", level: gitBranch.level, group: gitBranch.group }); this.sbDevLinks.push({ source: nodeName, target: gitBranch.nodeName, type: "sfPushPull", label: "Push / Pull", level: gitBranch.level, }); } } // Sort orgs & links this.salesforceOrgs = sortArray(this.salesforceOrgs, { by: ['level', 'name'], order: ['desc', 'asc'] }); this.deployLinks = sortArray(this.deployLinks, { by: ['level', 'source'], order: ['desc', 'asc'] }); this.sbDevLinks = sortArray(this.sbDevLinks, { by: ['level', 'source'], order: ['asc', 'asc'] }); } generateMermaidLines() { /* jscpd:ignore-start */ this.mermaidLines.push("flowchart LR"); this.mermaidLines.push(""); // Git branches this.mermaidLines.push(this.indent("subgraph GitBranches [Git Branches]", 1)); this.mermaidLines.push(this.indent("direction TB", 2)); for (const gitBranch of this.gitBranches) { this.mermaidLines.push(this.indent(`${gitBranch.nodeName}["${gitBranch.label}"]:::${gitBranch.class}`, 2)); } this.mermaidLines.push(this.indent("end", 1)); this.mermaidLines.push(""); // Salesforce orgs this.mermaidLines.push(this.indent("subgraph SalesforceOrgs [Salesforce Major Orgs]", 1)); this.mermaidLines.push(this.indent("direction TB", 2)); for (const salesforceOrg of this.salesforceOrgs.filter((salesforceOrg) => ["salesforceProd", "salesforceMajor"].includes(salesforceOrg.class))) { this.mermaidLines.push(this.indent(`${salesforceOrg.nodeName}(["<b>${salesforceOrg.label}</b>"]):::${salesforceOrg.class}`, 2)); } this.mermaidLines.push(this.indent("end", 1)); this.mermaidLines.push(""); // Salesforce dev orgs for (const devOrgsGroup of this.salesforceDevOrgsGroup) { this.mermaidLines.push(this.indent(`subgraph SalesforceDevOrgs${devOrgsGroup} [Salesforce Dev Orgs]`, 1)); this.mermaidLines.push(this.indent("direction TB", 2)); for (const salesforceOrg of this.salesforceOrgs.filter((salesforceOrg) => salesforceOrg.group === devOrgsGroup && (salesforceOrg.name.startsWith("feature") || salesforceOrg.name.startsWith("hotfix")))) { this.mermaidLines.push(this.indent(`${salesforceOrg.nodeName}(["${salesforceOrg.label}"]):::${salesforceOrg.class}`, 2)); } this.mermaidLines.push(this.indent("end", 1)); this.mermaidLines.push(""); } // Links this.addLinks(this.gitLinks); this.addLinks(this.deployLinks); this.addLinks(this.sbDevLinks); this.addLinks(this.retrofitLinks); // Classes and styles this.mermaidLines.push(...this.listClassesAndStyles()); for (const salesforceDevOrgsGroup of this.salesforceDevOrgsGroup) { this.mermaidLines.push(`style SalesforceDevOrgs${salesforceDevOrgsGroup} fill:#EBF6FF,color:#000000,stroke:#0077B5,stroke-width:1px;`); } /* jscpd:ignore-end */ const allLinks = [...this.gitLinks, ...this.deployLinks, ...this.sbDevLinks, ...this.retrofitLinks]; let pos = 0; const positions = {}; for (const link of allLinks) { if (!positions[link.type]) { positions[link.type] = []; } positions[link.type].push(pos); pos++; } const linksDef = this.listLinksDef(); for (const key of Object.keys(positions)) { const styleDef = linksDef[key]; this.mermaidLines.push(`linkStyle ${positions[key].join(",")} ${styleDef}`); } } addLinks(links) { for (const link of links) { if (link.type === "gitMerge") { this.mermaidLines.push(this.indent(`${link.source} ==>|"${link.label}"| ${link.target}`, 1)); } else if (link.type === "sfDeploy") { this.mermaidLines.push(this.indent(`${link.source} -. ${link.label} .-> ${link.target}`, 1)); } else if (link.type === "sfPushPull") { this.mermaidLines.push(this.indent(`${link.source} <-. ${link.label} .-> ${link.target}`, 1)); } } this.mermaidLines.push(""); } listClassesAndStyles() { const classesAndStyles = ` classDef salesforceDev fill:#9BC3FF,stroke:#2B65D9,stroke-width:2px,color:#000000,font-weight:bold,border-radius:10px; classDef salesforceMajor fill:#67B7D1,stroke:#004D66,stroke-width:2px,color:#FFFFFF,font-weight:bold,border-radius:10px; classDef salesforceProd fill:#4C98C3,stroke:#003B5A,stroke-width:2px,color:#FFFFFF,font-weight:bold,border-radius:10px; classDef gitMajor fill:#FFCA76,stroke:#E65C00,stroke-width:2px,color:#000000,font-weight:bold,border-radius:10px; classDef gitMain fill:#F97B8B,stroke:#CC2936,stroke-width:2px,color:#000000,font-weight:bold,border-radius:10px; classDef gitFeature fill:#B0DE87,stroke:#2D6A4F,stroke-width:2px,color:#000000,font-weight:bold,border-radius:10px; style GitBranches fill:#F4F5F9,color:#000000,stroke:#8B72B2,stroke-width:1px; style SalesforceOrgs fill:#F1F7F5,color:#000000,stroke:#468C70,stroke-width:1px; `; return classesAndStyles.split("\n"); } listLinksDef() { return { "gitMerge": "stroke:#4B0082,stroke-width:4px,color:#4B0082,background-color:transparent;", "sfDeploy": "stroke:#4169E1,stroke-width:2px,color:#4169E1,background-color:transparent;", "sfPushPull": "stroke:#5F9EA0,stroke-width:2px,color:#5F9EA0,background-color:transparent;" }; } indent(str, number) { return ' '.repeat(number) + str; } } //# sourceMappingURL=branchStrategyMermaidBuilder.js.map