diff --git a/src/commands/hardis/doc/project2markdown.ts b/src/commands/hardis/doc/project2markdown.ts index fc387cf95..1f9d2fe92 100644 --- a/src/commands/hardis/doc/project2markdown.ts +++ b/src/commands/hardis/doc/project2markdown.ts @@ -15,6 +15,7 @@ import { listMajorOrgs } from '../../../common/utils/orgConfigUtils.js'; import { glob } from 'glob'; import { listFlowFiles } from '../../../common/utils/projectUtils.js'; import { generateFlowMarkdownFile, generateMarkdownFileWithMermaid } from '../../../common/utils/mermaidUtils.js'; +import { MetadataUtils } from '../../../common/metadata-utils/index.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('sfdx-hardis', 'org'); @@ -54,6 +55,10 @@ _sfdx-hardis docker image is alpine-based and does not succeed to run mermaid/pu ]; public static flags: any = { + "diff-only": Flags.boolean({ + default: false, + description: "Generate documentation only for changed files (used for monitoring)", + }), debug: Flags.boolean({ char: 'd', default: false, @@ -70,6 +75,7 @@ _sfdx-hardis docker image is alpine-based and does not succeed to run mermaid/pu // Set this to true if your command requires a project workspace; 'requiresProject' is false by default public static requiresProject = true; + protected diffOnly: boolean = false; protected packageXmlCandidates: any[]; protected outputMarkdownRoot = "docs" protected outputMarkdownIndexFile = path.join(this.outputMarkdownRoot, "index.md") @@ -81,6 +87,7 @@ _sfdx-hardis docker image is alpine-based and does not succeed to run mermaid/pu public async run(): Promise { const { flags } = await this.parse(Project2Markdown); + this.diffOnly = flags["diff-only"] === true ? true : false; this.debugMode = flags.debug || false; this.packageXmlCandidates = this.listPackageXmlCandidates(); @@ -144,21 +151,30 @@ _sfdx-hardis docker image is alpine-based and does not succeed to run mermaid/pu uxLog(this, c.cyan("Generating Flows Visual documentation... (if you don't want it, define GENERATE_FLOW_DOC=false in your environment variables)")); await fs.ensureDir(path.join(this.outputMarkdownRoot, "flows")); const packageDirs = this.project?.getPackageDirectories(); + const updatedFlowNames = !this.diffOnly ? + [] : + (await MetadataUtils.listChangedFiles()).filter(f => f?.path?.endsWith(".flow-meta.xml")).map(f => path.basename(f.path, ".flow-meta.xml")); const flowFiles = await listFlowFiles(packageDirs); const flowErrors: string[] = []; const flowWarnings: string[] = []; + const flowSkips: string[] = []; const flowDescriptions: any[] = []; for (const flowFile of flowFiles) { + const flowName = path.basename(flowFile, ".flow-meta.xml"); uxLog(this, c.grey(`Generating markdown for Flow ${flowFile}...`)); const flowXml = (await fs.readFile(flowFile, "utf8")).toString(); const flowContent = await parseXmlFile(flowFile); flowDescriptions.push({ - name: path.basename(flowFile).replace(".flow-meta.xml", ""), + name: flowName, description: flowContent?.Flow?.description?.[0] || "", type: flowContent?.Flow?.processType?.[0] === "Flow" ? "ScreenFlow" : flowContent?.Flow?.start?.[0]?.triggerType?.[0] ?? (flowContent?.Flow?.processType?.[0] || "ERROR (Unknown)"), object: flowContent?.Flow?.start?.[0]?.object?.[0] || flowContent?.Flow?.processMetadataValues?.filter(pmv => pmv.name[0] === "ObjectType")?.[0]?.value?.[0]?.stringValue?.[0] || "" }); - const outputFlowMdFile = path.join(this.outputMarkdownRoot, "flows", path.basename(flowFile).replace(".flow-meta.xml", ".md")); + if (this.diffOnly && !updatedFlowNames.includes(flowName)) { + flowSkips.push(flowFile); + continue; + } + const outputFlowMdFile = path.join(this.outputMarkdownRoot, "flows", flowName + ".md"); const genRes = await generateFlowMarkdownFile(flowFile, flowXml, outputFlowMdFile); if (!genRes) { flowErrors.push(flowFile); @@ -173,18 +189,21 @@ _sfdx-hardis docker image is alpine-based and does not succeed to run mermaid/pu continue; } } + if (flowSkips.length > 0) { + uxLog(this, c.yellow(`Skipped generation for ${flowSkips.length} Flows that have not been updated: ${this.humanDisplay(flowSkips)}`)); + } uxLog(this, c.green(`Successfully generated ${flowFiles.length - flowWarnings.length - flowErrors.length} Flows documentation`)); if (flowWarnings.length > 0) { - uxLog(this, c.yellow(`Partially generated documentation (Markdown with mermaidJs but without SVG) for ${flowWarnings.length} Flows: ${flowWarnings.join(", ")}`)); + uxLog(this, c.yellow(`Partially generated documentation (Markdown with mermaidJs but without SVG) for ${flowWarnings.length} Flows: ${this.humanDisplay(flowWarnings)}`)); } if (flowErrors.length > 0) { - uxLog(this, c.yellow(`Error generating documentation for ${flowErrors.length} Flows: ${flowErrors.join(", ")}`)); + uxLog(this, c.yellow(`Error generating documentation for ${flowErrors.length} Flows: ${this.humanDisplay(flowErrors)}`)); } // Write table on doc index const flowTableLines = await this.buildFlowsTable(flowDescriptions, 'flows/'); this.mdLines.push(...flowTableLines); - this.mdLines.push(...["___", ""]) + this.mdLines.push(...["___", ""]); // Write index file for flow folder await fs.ensureDir(path.join(this.outputMarkdownRoot, "flows")); @@ -194,6 +213,10 @@ _sfdx-hardis docker image is alpine-based and does not succeed to run mermaid/pu uxLog(this, c.green(`Successfully generated doc index for Flows at ${flowIndexFile}`)); } + private humanDisplay(flows) { + return flows.map(flow => path.basename(flow, ".flow-meta.xml")).join(", "); + } + private async buildFlowsTable(flowDescriptions: any[], prefix: string) { const lines: string[] = []; lines.push(...[ diff --git a/src/commands/hardis/org/monitor/backup.ts b/src/commands/hardis/org/monitor/backup.ts index 5abfc2aef..e59038c1e 100644 --- a/src/commands/hardis/org/monitor/backup.ts +++ b/src/commands/hardis/org/monitor/backup.ts @@ -238,7 +238,7 @@ This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/sales // Run project documentation generation try { - await Project2Markdown.run(); + await Project2Markdown.run(["--diff-only"]); } catch (e: any) { uxLog(this, c.yellow("Error while generating project documentation " + e.message)); } diff --git a/src/common/utils/mermaidUtils.ts b/src/common/utils/mermaidUtils.ts index fe7192707..842f3c48c 100644 --- a/src/common/utils/mermaidUtils.ts +++ b/src/common/utils/mermaidUtils.ts @@ -77,7 +77,7 @@ export async function generateMarkdownFileWithMermaidDocker(outputFlowMdFile: st return true; } catch (e: any) { uxLog(this, c.yellow(`Error generating mermaidJs Graphs in ${outputFlowMdFile} documentation with Docker: ${e.message}`) + "\n" + c.grey(e.stack)); - if (JSON.stringify(e).includes("Cannot connect to the Docker daemon")) { + if (JSON.stringify(e).includes("Cannot connect to the Docker daemon") || JSON.stringify(e).includes("daemon is not running")) { globalThis.mermaidUnavailableTools = (globalThis.mermaidUnavailableTools || []).concat("docker"); uxLog(this, c.yellow("[Mermaid] Docker unavailable: do not try again")); }