-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
187 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
/* jscpd:ignore-start */ | ||
import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; | ||
import fs, { ensureDir } from 'fs-extra'; | ||
import c from "chalk"; | ||
import * as path from "path"; | ||
import { Messages, SfError } from '@salesforce/core'; | ||
import { AnyJson } from '@salesforce/ts-types'; | ||
import { createTempDir, execCommand, uxLog } from '../../../common/utils/index.js'; | ||
import { createBlankSfdxProject } from '../../../common/utils/projectUtils.js'; | ||
import { isProductionOrg } from '../../../common/utils/orgUtils.js'; | ||
|
||
|
||
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); | ||
const messages = Messages.loadMessages('sfdx-hardis', 'org'); | ||
|
||
export default class MkDocsToSalesforce extends SfCommand<any> { | ||
public static title = 'MkDocs to Salesforce'; | ||
|
||
public static description = `Generates MkDocs HTML pages and upload them to Salesforce as a static resource | ||
`; | ||
|
||
public static examples = [ | ||
'$ sf hardis:doc:mkdocs-to-salesforce', | ||
]; | ||
|
||
public static flags: any = { | ||
type: Flags.string({ | ||
char: 't', | ||
options: ["CICD", "Monitoring"], | ||
default: "CICD", | ||
description: 'Type of the documentation to generate. Default is "all"', | ||
}), | ||
debug: Flags.boolean({ | ||
char: 'd', | ||
default: false, | ||
description: messages.getMessage('debugMode'), | ||
}), | ||
websocket: Flags.string({ | ||
description: messages.getMessage('websocket'), | ||
}), | ||
skipauth: Flags.boolean({ | ||
description: 'Skip authentication check when a default username is required', | ||
}), | ||
}; | ||
|
||
// Set this to true if your command requires a project workspace; 'requiresProject' is false by default | ||
public static requiresProject = true; | ||
|
||
protected debugMode = false; | ||
/* jscpd:ignore-end */ | ||
|
||
public async run(): Promise<AnyJson> { | ||
const { flags } = await this.parse(MkDocsToSalesforce); | ||
const type = flags.type || "CICD"; | ||
const targetUsername = flags['target-org'].getUsername(); | ||
const conn = flags['target-org'].getConnection(); | ||
this.debugMode = flags.debug || false; | ||
|
||
// Check if the project is a MkDocs project | ||
const mkdocsYmlPath = path.join(process.cwd(), "mkdocs.yml"); | ||
if (!fs.existsSync(mkdocsYmlPath)) { | ||
throw new SfError('This command needs a mkdocs.yml config file. Generate one using "sf hardis:doc:project2markdown --with-history"'); | ||
} | ||
|
||
// Install Python dependencies | ||
const mkdocsLocalOk = await this.installMkDocs(); | ||
|
||
await this.generateMkDocsHTML(mkdocsLocalOk); | ||
|
||
// Create temp sfdx project | ||
const tmpDirForSfdxProject = await createTempDir(); | ||
const tmpProjectPath = await createBlankSfdxProject(tmpDirForSfdxProject); | ||
const defaultProjectPath = path.join(tmpProjectPath, "force-app", "main", "default"); | ||
|
||
// Create static resource folder | ||
const resName = `SfdxHardisMkDocsSite-${type}`; | ||
const { mkDocsResourcePath, vfPageMetaFile, tabMetaFile } = await this.createDocMetadatas(resName, defaultProjectPath, type); | ||
|
||
// Upload resource to remote org | ||
const deployRes = await this.uploadDocMetadatas(resName, targetUsername, conn, tmpProjectPath, mkDocsResourcePath, vfPageMetaFile, tabMetaFile); | ||
|
||
return { success: deployRes.status === 0 }; | ||
} | ||
|
||
private async installMkDocs() { | ||
uxLog(this, c.cyan("Managing mkdocs-material local installation...")); | ||
let mkdocsLocalOk = false; | ||
const installMkDocsRes = await execCommand("pip install mkdocs-material mdx_truly_sane_lists", this, { fail: false, output: true, debug: this.debugMode }); | ||
if (installMkDocsRes.status !== 0) { | ||
mkdocsLocalOk = true; | ||
} | ||
return mkdocsLocalOk; | ||
} | ||
|
||
private async generateMkDocsHTML(mkdocsLocalOk: boolean) { | ||
if (mkdocsLocalOk) { | ||
// Generate MkDocs HTML pages with local MkDocs | ||
uxLog(this, c.cyan("Generating HTML pages with mkdocs...")); | ||
const mkdocsBuildRes = await execCommand("mkdocs build", this, { fail: false, output: true, debug: this.debugMode }); | ||
if (mkdocsBuildRes.status !== 0) { | ||
throw new SfError('MkDocs build failed:\n' + mkdocsBuildRes.stderr + "\n" + mkdocsBuildRes.stdout); | ||
} | ||
} | ||
} | ||
|
||
private async createDocMetadatas(resName: string, defaultProjectPath: string, type: any) { | ||
uxLog(this, c.cyan(`Creating Static Resource ${resName} metadata...`)); | ||
const staticResourcePath = path.join(defaultProjectPath, "staticresources"); | ||
const mkDocsResourcePath = path.join(staticResourcePath, resName); | ||
await ensureDir(mkDocsResourcePath); | ||
await fs.move(path.join(process.cwd(), "site"), mkDocsResourcePath, { overwrite: true }); | ||
|
||
// Create Static resource metadata | ||
uxLog(this, c.cyan(`Creating Static Resource ${resName} metadata...`)); | ||
const mkDocsResourceFileName = path.join(staticResourcePath, `${resName}.resource-meta.xml`); | ||
const mkDocsResourceMeta = `<?xml version="1.0" encoding="UTF-8"?> | ||
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<cacheControl>Private</cacheControl> | ||
<contentType>application/x-zip-compressed</contentType> | ||
</StaticResource> | ||
`; | ||
await fs.writeFile(mkDocsResourceFileName, mkDocsResourceMeta); | ||
|
||
// Create visual force page | ||
uxLog(this, c.cyan(`Creating VisualForce page ${resName} metadata...`)); | ||
const vfPagesPath = path.join(defaultProjectPath, "pages"); | ||
const vfPageFileName = path.join(vfPagesPath, `${resName}.page`); | ||
const vfPageCode = `<apex:page > | ||
<iframe src="/resource/${resName}/index.html" width="100%" height="1000px" frameborder="0"></iframe> | ||
</apex:page> | ||
`; | ||
await fs.writeFile(vfPageFileName, vfPageCode); | ||
|
||
// Create visual force page metadata | ||
const vfPageMetaFile = path.join(vfPagesPath, `${resName}.page-meta.xml`); | ||
const vfPageMeta = `<?xml version="1.0" encoding="UTF-8"?> | ||
<ApexPage xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<apiVersion>62.0</apiVersion> | ||
<availableInTouch>false</availableInTouch> | ||
<confirmationTokenRequired>false</confirmationTokenRequired> | ||
<label>${resName}</label> | ||
</ApexPage> | ||
`; | ||
await fs.writeFile(vfPageMetaFile, vfPageMeta); | ||
|
||
// Create custom tab metadata | ||
const tabsPath = path.join(defaultProjectPath, "tabs"); | ||
const tabMetaFile = path.join(tabsPath, `${resName}.tab-meta.xml`); | ||
const tabMeta = `<?xml version="1.0" encoding="UTF-8"?> | ||
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<label>${type === 'CICD' ? 'Sfdx-Hardis DOC (from CI/CD)' : 'Sfdx-Hardis DOC (from Monitoring)'}</label> | ||
<motif>Custom46: Postage</motif> | ||
<page>${resName}</page> | ||
</CustomTab> | ||
`; | ||
await fs.writeFile(tabMetaFile, tabMeta); | ||
return { mkDocsResourcePath, vfPageMetaFile, tabMetaFile }; | ||
} | ||
|
||
private async uploadDocMetadatas(resName: string, targetUsername: any, conn: any, tmpProjectPath: string, mkDocsResourcePath: string, vfPageMetaFile: string, tabMetaFile: string) { | ||
let deployCommand = `sf project deploy start -m StaticResource:${resName} -m ApexPage:${resName} -m CustomTab:${resName}`; | ||
const isProdOrg = await isProductionOrg(targetUsername, { conn: conn }); | ||
if (isProdOrg) { | ||
deployCommand += " --test-level RunLocalTests"; | ||
} | ||
else { | ||
deployCommand += " --test-level NoTestRun"; | ||
} | ||
const deployRes = await execCommand(deployCommand, this, { cwd: tmpProjectPath, fail: false, output: true, debug: this.debugMode }); | ||
|
||
if (deployRes.status !== 0) { | ||
uxLog(this, c.red(`Deployment failed:\n${deployRes.stderr + "\n" + deployRes.stdout}`)); | ||
uxLog(this, c.yellow(`You can manually deploy the Static Resource ${resName},the VisualForce page ${resName} and the custom tab ${resName} to your org`)); | ||
uxLog(this, c.yellow(`- Static Resource: ${mkDocsResourcePath} (If you upload using UI, zip the folder and make sure to have index.html at the zip root)`)); | ||
uxLog(this, c.yellow(`- VisualForce page: ${vfPageMetaFile}`)); | ||
uxLog(this, c.yellow(`- Custom tab: ${tabMetaFile}`)); | ||
} | ||
else { | ||
uxLog(this, c.green(`SFDX Project documentation uploaded to salesforce and available in Custom Tab ${resName}`)); | ||
} | ||
return deployRes; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters