From cd5c85ef3e486070e2635e8890724439c3496476 Mon Sep 17 00:00:00 2001 From: Nicolas Vuillamy Date: Sun, 5 Jan 2025 23:16:31 +0100 Subject: [PATCH] Add branch & orgs strategy MermaidJS diagram in documentation --- CHANGELOG.md | 4 + config/sfdx-hardis.jsonschema.json | 17 ++ .../screenshot-doc-branches-strategy.jpg | Bin 0 -> 63198 bytes docs/salesforce-project-documentation.md | 4 + .../sfdx-hardis-json-schema-parameters.html | 40 ++- src/commands/hardis/doc/project2markdown.ts | 16 +- .../utils/branchStrategyMermaidBuilder.ts | 241 ++++++++++++++++++ src/common/utils/orgConfigUtils.ts | 74 +++++- 8 files changed, 389 insertions(+), 7 deletions(-) create mode 100644 docs/assets/images/screenshot-doc-branches-strategy.jpg create mode 100644 src/common/utils/branchStrategyMermaidBuilder.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b284904..0f54b120c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image `hardisgroupcom/sfdx-hardis@beta` +## [5.13.0] 2025-01-05 + +- [hardis:doc:project2markdown](https://sfdx-hardis.cloudity.com/hardis/doc/project2markdown/) Add branch & orgs strategy MermaidJS diagram in documentation + ## [5.12.0] 2025-01-04 - New command [hardis:doc:mkdocs-to-salesforce](https://sfdx-hardis.cloudity.com/hardis/doc/mkdocs-to-salesforce/) to generate static HTML doc and host it in a Static Resource and a VisualForce page diff --git a/config/sfdx-hardis.jsonschema.json b/config/sfdx-hardis.jsonschema.json index 4f738c747..483249d07 100644 --- a/config/sfdx-hardis.jsonschema.json +++ b/config/sfdx-hardis.jsonschema.json @@ -991,6 +991,23 @@ "title": "Metadata to retrofit", "type": "array" }, + "mergeTargets": { + "$id": "#/properties/mergeTargets", + "description": "In branch-scoped config file, declares the list of branches that the current one can have as merge target. For example, integration will have mergeTargets [uat]", + "examples": [ + [ + "preprod" + ], + [ + "integration" + ] + ], + "items": { + "type": "string" + }, + "title": "Merge target branches", + "type": "array" + }, "monitoringCommands": { "$id": "#/properties/monitoringCommands", "description": "List of monitoring commands to run with command hardis:org:monitor:all", diff --git a/docs/assets/images/screenshot-doc-branches-strategy.jpg b/docs/assets/images/screenshot-doc-branches-strategy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..207306e34696e75079ff489ec78643a9c59d340d GIT binary patch literal 63198 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 SD7Wh$A^rdJSqS9k@c#m@2qX^x literal 0 HcmV?d00001 diff --git a/docs/salesforce-project-documentation.md b/docs/salesforce-project-documentation.md index 59fb7dedf..7795d5d92 100644 --- a/docs/salesforce-project-documentation.md +++ b/docs/salesforce-project-documentation.md @@ -10,6 +10,10 @@ With a single command, you can generate a Web Site documenting your Salesforce m ![](assets/images/project-documentation.gif) +If it is a sfdx-hardis CI/CD project, a diagram of the branches and orgs strategy will be generated. + +![](assets/images/screenshot-doc-branches-strategy.jpg) + ## How To generate - Use the Git repository containing your SFDX project, or create it easily using [sfdx-hardis Monitoring](salesforce-monitoring-home.md), or simply calling [BackUp command](hardis/org/monitor/backup.md) diff --git a/docs/schema/sfdx-hardis-json-schema-parameters.html b/docs/schema/sfdx-hardis-json-schema-parameters.html index 2db1ff115..e3e404816 100644 --- a/docs/schema/sfdx-hardis-json-schema-parameters.html +++ b/docs/schema/sfdx-hardis-json-schema-parameters.html @@ -3061,6 +3061,44 @@

+
+
+
+

+ +

+
+ +
+
+ +

Doc: Deploy to Salesforce Org

Type: boolean Default: false
+

Automatically deploy MkDocs HTML documentation from CI/CD Workflows to Salesforce org as static resource

+
+ + + + + +
+
Example:
+
true
+
+
+
+
+
+
@@ -5825,6 +5863,6 @@

\ No newline at end of file diff --git a/src/commands/hardis/doc/project2markdown.ts b/src/commands/hardis/doc/project2markdown.ts index e02958815..ff8285168 100644 --- a/src/commands/hardis/doc/project2markdown.ts +++ b/src/commands/hardis/doc/project2markdown.ts @@ -17,6 +17,7 @@ import { listFlowFiles } from '../../../common/utils/projectUtils.js'; import { generateFlowMarkdownFile, generateHistoryDiffMarkdown, generateMarkdownFileWithMermaid } from '../../../common/utils/mermaidUtils.js'; import { MetadataUtils } from '../../../common/metadata-utils/index.js'; import { PACKAGE_ROOT_DIR } from '../../../settings.js'; +import { BranchStrategyMermaidBuilder } from '../../../common/utils/branchStrategyMermaidBuilder.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('sfdx-hardis', 'org'); @@ -69,6 +70,12 @@ If Flow history doc always display a single state, you probably need to update y ![Screenshot project documentation](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/screenshot-project-doc-2.jpg) +If it is a sfdx-hardis CI/CD project, a diagram of the branches and orgs strategy will be generated. + +![](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/screenshot-doc-branches-strategy.jpg) + +If you have a complex strategy, you might need to input property **mergeTargets** in branch-scoped sfdx-hardis.yml file to have a correct diagram. + ${this.htmlInstructions} `; @@ -417,8 +424,15 @@ ${Project2Markdown.htmlInstructions} const branchesOrgsLines: string[] = []; const majorOrgs = await listMajorOrgs(); if (majorOrgs.length > 0) { + + branchesOrgsLines.push(...[ + "## Branches & Orgs strategy", + "", + ]); + const mermaidLines = new BranchStrategyMermaidBuilder(majorOrgs).build({ withMermaidTag: true, format: "list" }); + branchesOrgsLines.push(...mermaidLines); + branchesOrgsLines.push(...[ - "## Major branches and orgs", "", "| Git branch | Salesforce Org | Deployment Username |", "| :--------- | :------------- | :------------------ |" diff --git a/src/common/utils/branchStrategyMermaidBuilder.ts b/src/common/utils/branchStrategyMermaidBuilder.ts new file mode 100644 index 000000000..09cdd441d --- /dev/null +++ b/src/common/utils/branchStrategyMermaidBuilder.ts @@ -0,0 +1,241 @@ +import sortArray from "sort-array"; +import { prettifyFieldName } from "./flowVisualiser/nodeFormatUtils.js"; +import { isIntegration, isPreprod, isProduction } from "./orgConfigUtils.js"; + + +export class BranchStrategyMermaidBuilder { + private branchesAndOrgs: any[]; + private gitBranches: any[]; + private salesforceOrgs: any[] = []; + private gitLinks: any[] = []; + private deployLinks: any[] = []; + private sbDevLinks: any[] = []; + private retrofitLinks: any[] = []; + private mermaidLines: string[] = []; + + constructor(branchesAndOrgs: any[]) { + this.branchesAndOrgs = branchesAndOrgs; + } + + public build(options: { format: "list" | "string", withMermaidTag: boolean }): string | string[] { + 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"); + } + + private listGitBranchesAndLinks(): void { + const branchesWhoAreMergeTargets: string[] = []; + const branchesMergingInPreprod: string[] = []; + 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))) { + noMergeTargetBranchAndOrg.push(this.branchesAndOrgs.find((branchAndOrg) => isPreprod(branchAndOrg.branchName))); + } + for (const branchAndOrg of noMergeTargetBranchAndOrg) { + const nameBase = isPreprod(branchAndOrg.branchName) ? "hotfix" : "feature"; + const level = branchAndOrg.level - 1 + const nameBase1 = nameBase + "1"; + const nodeName1 = nameBase + "Branch" + "1" + this.gitBranches.push({ + name: nameBase1, + nodeName: nodeName1, + label: nameBase1, + class: "gitFeature", + level: level + }); + this.gitLinks.push({ + source: nodeName1, + target: this.gitBranches.find((gitBranch) => gitBranch.name === branchAndOrg.branchName)?.nodeName || "ERROR", + type: "gitMerge", + label: "Merge" + }); + const nameBase2 = nameBase + "2"; + const nodeName2 = nameBase + "Branch" + "2" + this.gitBranches.push({ + name: nameBase2, + nodeName: nodeName2, + label: nameBase2, + class: "gitFeature", + level: level + }); + this.gitLinks.push({ + source: nodeName2, + target: this.gitBranches.find((gitBranch) => gitBranch.name === branchAndOrg.branchName)?.nodeName || "ERROR", + type: "gitMerge", + label: "Merge", + level: level + }); + } + 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'] }); + } + + private listSalesforceOrgsAndLinks(): any { + 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 + }); + 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'] }); + } + + private generateMermaidLines() { + 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 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}(["${salesforceOrg.label}"]):::${salesforceOrg.class}`, 2)); + } + this.mermaidLines.push(this.indent("end", 1)); + this.mermaidLines.push(""); + + // Salesforce dev orgs + this.mermaidLines.push(this.indent("subgraph SalesforceDevOrgs [Salesforce Orgs BUILD]", 1)); + this.mermaidLines.push(this.indent("direction TB", 2)); + for (const salesforceOrg of this.salesforceOrgs.filter((salesforceOrg) => salesforceOrg.name.startsWith("feature"))) { + this.mermaidLines.push(this.indent(`${salesforceOrg.nodeName}(["${salesforceOrg.label}"]):::${salesforceOrg.class}`, 2)); + } + this.mermaidLines.push(this.indent("end", 1)); + this.mermaidLines.push(""); + + // Salesforce dev orgs run + this.mermaidLines.push(this.indent("subgraph SalesforceDevOrgsRun [Salesforce Orgs RUN]", 1)); + this.mermaidLines.push(this.indent("direction TB", 2)); + for (const salesforceOrg of this.salesforceOrgs.filter((salesforceOrg) => 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()); + } + + private 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(): string[] { + const classesAndStyles = ` classDef salesforceDev fill:#A9E8F8,stroke:#004E8A,stroke-width:2px,color:black,font-weight:bold,border-radius:10px; + classDef salesforceMajor fill:#0088CE,stroke:#004E8A,stroke-width:2px,color:white,font-weight:bold,border-radius:10px; + classDef salesforceProd fill:blue,stroke:#004E8A,stroke-width:2px,color:white,font-weight:bold,border-radius:10px; + classDef gitMajor fill:#FFC107,stroke:#D84315,stroke-width:2px,color:black,font-weight:bold,border-radius:10px; + classDef gitMain fill:#FF6F61,stroke:#FF6F00,stroke-width:2px,color:black,font-weight:bold,border-radius:10px; + classDef gitFeature fill:#B5EAD7,stroke:#2E7D32,stroke-width:2px,color:black,font-weight:bold,border-radius:10px; + + style GitBranches fill:#F4F4F9,stroke:#7C4DFF,stroke-width:1px; + style SalesforceOrgs fill:#E8F5E9,stroke:#1B5E20,stroke-width:1px; + style SalesforceDevOrgs fill:#E1F5FE,stroke:#0288D1,stroke-width:1px; + style SalesforceDevOrgsRun fill:#F3E5F5,stroke:#6A1B9A,stroke-width:1px; +` + return classesAndStyles.split("\n"); + } + + private indent(str: string, number: number): string { + return ' '.repeat(number) + str; + } +} \ No newline at end of file diff --git a/src/common/utils/orgConfigUtils.ts b/src/common/utils/orgConfigUtils.ts index 468e872f0..651718bfb 100644 --- a/src/common/utils/orgConfigUtils.ts +++ b/src/common/utils/orgConfigUtils.ts @@ -176,35 +176,99 @@ export async function listMajorOrgs() { const majorOrgsSorted: any = []; // Main for (const majorOrg of majorOrgs) { - if (majorOrg?.branchName?.toLowerCase().startsWith("main") || majorOrg?.branchName?.toLowerCase().startsWith("prod")) { + if (isProduction(majorOrg?.branchName || "")) { + majorOrg.level = majorOrg.level || 100; majorOrgsSorted.push(majorOrg); } } // Preprod for (const majorOrg of majorOrgs) { - if (majorOrg?.branchName?.toLowerCase().startsWith("preprod") || majorOrg?.branchName?.toLowerCase().startsWith("staging")) { + if (isPreprod(majorOrg?.branchName || "")) { + majorOrg.level = majorOrg.level || 90; + majorOrgsSorted.push(majorOrg); + } + } + // uat run + for (const majorOrg of majorOrgs) { + if (isUatRun(majorOrg?.branchName || "")) { + majorOrg.level = majorOrg.level || 80; majorOrgsSorted.push(majorOrg); } } // uat for (const majorOrg of majorOrgs) { - if (majorOrg?.branchName?.toLowerCase().startsWith("uat") || majorOrg?.branchName?.toLowerCase().startsWith("recette")) { + if (isUat(majorOrg?.branchName || "")) { + majorOrg.level = majorOrg.level || 70; majorOrgsSorted.push(majorOrg); } } // integration for (const majorOrg of majorOrgs) { - if (majorOrg?.branchName?.toLowerCase().startsWith("integ")) { + if (isIntegration(majorOrg?.branchName || "")) { + majorOrg.level = majorOrg.level || 50; majorOrgsSorted.push(majorOrg); } } // Add remaining major branches for (const majorOrg of sortArray(majorOrgs, { by: ['branchName'], order: ['asc'] }) as any[]) { if (majorOrgsSorted.filter(org => org.branchName === majorOrg.branchName).length === 0) { + majorOrg.level = majorOrg.level || 40; majorOrgsSorted.push(majorOrg); } } - return majorOrgsSorted; + const completedMajorOrgs = majorOrgsSorted.map((majorOrg: any) => { + if (majorOrg?.mergeTargets?.length > 0) { + return majorOrg; + } + majorOrg.mergeTargets = guessMatchingMergeTargets(majorOrg.branchName, majorOrgs); + return majorOrg; + }); + return completedMajorOrgs; +} + +function guessMatchingMergeTargets(branchName: string, majorOrgs: any[]): string[] { + if (isProduction(branchName)) { + return []; + } + else if (isPreprod(branchName)) { + return majorOrgs.filter(org => isProduction(org.branchName)).map(org => org.branchName); + } + else if (isUat(branchName)) { + return majorOrgs.filter(org => isPreprod(org.branchName)).map(org => org.branchName); + } + else if (isUatRun(branchName)) { + return majorOrgs.filter(org => isPreprod(org.branchName)).map(org => org.branchName); + } + else if (isIntegration(branchName)) { + return majorOrgs.filter(org => isUat(org.branchName)).map(org => org.branchName); + } + uxLog(this, c.yellow(`Unable to guess merge targets for ${branchName}. +Please set them manually in config/branches/.sfdx-hardis.${branchName}.yml +Example: +mergeTargets: + - preprod +`)); + return []; +} + +export function isProduction(branchName) { + return branchName.toLowerCase().startsWith("prod") || branchName.toLowerCase().startsWith("main"); +} + +export function isPreprod(branchName) { + return branchName.toLowerCase().startsWith("preprod") || branchName.toLowerCase().startsWith("staging"); +} + +export function isUat(branchName) { + return (branchName.toLowerCase().startsWith("uat") || branchName.toLowerCase().startsWith("recette")) && !branchName.toLowerCase().includes("run"); +} + +export function isIntegration(branchName) { + return branchName.toLowerCase().startsWith("integ"); +} + +export function isUatRun(branchName) { + return (branchName.toLowerCase().startsWith("uat") || branchName.toLowerCase().startsWith("recette")) && branchName.toLowerCase().includes("run"); } export async function checkSfdxHardisTraceAvailable(conn: Connection) {