diff --git a/.github/workflows/check-plugin-docs.js b/.github/workflows/check-plugin-docs.js index 49090e1e2..ecae2e8e0 100644 --- a/.github/workflows/check-plugin-docs.js +++ b/.github/workflows/check-plugin-docs.js @@ -50,6 +50,14 @@ async function checkPluginDocs() { ); } } + // Validate that local zip files are not used in production + if (typeof pluginEntry.zipFile !== "undefined") { + throw new Error( + `Local ZIP file being used for "${ + title || pluginEntry.path || repo + }". The zipFile option should only be used for local development. Please omit the zipFile attribute and ensure the plugin entry points to a remote repository.` + ); + } // Attempt to fetch plugin docs files const docsMdxFiles = await fetchPluginDocs({ repo, tag: version }); const mdxFilesByComponent = docsMdxFiles.reduce((acc, mdxFile) => { diff --git a/website/components/remote-plugin-docs/utils/fetch-dev-plugin-docs.js b/website/components/remote-plugin-docs/utils/fetch-dev-plugin-docs.js new file mode 100644 index 000000000..1f7be6108 --- /dev/null +++ b/website/components/remote-plugin-docs/utils/fetch-dev-plugin-docs.js @@ -0,0 +1,54 @@ +const path = require('path') +const validatePluginDocsFiles = require('./validate-plugin-docs-files') +const AdmZip = require('adm-zip') + +// Given a zipFile path, +// +// return [null, docsMdxFiles] if docs files +// are successfully fetched and valid, +// where docsMdxFiles is an array of { filePath, fileString } items. +// +// otherwise, return [err, null] +// where err is an error message describing whether the +// docs files were missing or invalid, with a path to resolution +async function fetchDevPluginDocs(zipFile) { + const [err, docsMdxFiles] = await parseZipFile(zipFile) + if (err) { + const errMsg = `Invalid plugin dev docs file ${zipFile}. ${err}` + throw new Error(errMsg) + } + return docsMdxFiles +} + +// Given a docs.zip filepath, +// which is a compressed "docs" folder, +// +// return [null, docsMdxFiles] if docs files +// are successfully fetched and valid, +// where docsMdxFiles is an array of { filePath, fileString } items. +// +// otherwise, return [err, null] +// where err is an error message describing whether the +// docs files were missing or invalid, with a path to resolution +async function parseZipFile(zipFile) { + const responseZip = new AdmZip(zipFile) + const docsEntries = responseZip.getEntries() + // Validate the file paths within the "docs" folder + const docsFilePaths = docsEntries.map((e) => e.entryName) + const validationError = validatePluginDocsFiles(docsFilePaths) + if (validationError) return [validationError, null] + // If valid, filter for MDX files only, and return + // a { filePath, fileString } object for each mdx file + const docsMdxFiles = docsEntries + .filter((e) => { + return path.extname(e.entryName) === '.mdx' + }) + .map((e) => { + const filePath = e.entryName + const fileString = e.getData().toString() + return { filePath, fileString } + }) + return [null, docsMdxFiles] +} + +module.exports = fetchDevPluginDocs diff --git a/website/components/remote-plugin-docs/utils/resolve-nav-data.js b/website/components/remote-plugin-docs/utils/resolve-nav-data.js index dd2f667c7..6aabb0345 100644 --- a/website/components/remote-plugin-docs/utils/resolve-nav-data.js +++ b/website/components/remote-plugin-docs/utils/resolve-nav-data.js @@ -2,6 +2,7 @@ const fs = require('fs') const path = require('path') const grayMatter = require('gray-matter') const fetchPluginDocs = require('./fetch-plugin-docs') +const fetchDevPluginDocs = require('./fetch-dev-plugin-docs') const validateFilePaths = require('@hashicorp/react-docs-sidenav/utils/validate-file-paths') const validateRouteStructure = require('@hashicorp/react-docs-sidenav/utils/validate-route-structure') @@ -139,8 +140,14 @@ async function resolvePluginEntryDocs(pluginConfigEntry, currentPath) { version, pluginTier, sourceBranch = 'main', + zipFile = '', } = pluginConfigEntry - const docsMdxFiles = await fetchPluginDocs({ repo, tag: version }) + var docsMdxFiles + if (zipFile !== '') { + docsMdxFiles = await fetchDevPluginDocs(zipFile) + } else { + docsMdxFiles = await fetchPluginDocs({ repo, tag: version }) + } // We construct a special kind of "NavLeaf" node, with a remoteFile property, // consisting of a { filePath, fileString, sourceUrl }, where: // - filePath is the path to the source file in the source repo diff --git a/website/content/docs/plugins/creation/index.mdx b/website/content/docs/plugins/creation/index.mdx index d25245348..8c3cbe7c9 100644 --- a/website/content/docs/plugins/creation/index.mdx +++ b/website/content/docs/plugins/creation/index.mdx @@ -278,6 +278,33 @@ If a plugin maintainer wishes to only include a specific version of released doc The `"sourceBranch"` key in the above configuration ensures potential contributors can link back to source files in the plugin repository from the Packer docs site. If a `"sourceBranch"` value is not present, it will default to `"main"`. +#### Testing Plugin Documentation + +Before publishing the `docs.zip` file, you might want to preview your documentation changes. +We provide a mechanism that allows to preview how the docs will look like within +the Packer documentation. + +Follow the next steps to get the Packer website running and preview the documentation changes: + +- Get the [Packer source code](https://github.com/hashicorp/packer). Our website code is under the [website folder](https://github.com/hashicorp/packer/tree/master/website). +- Generate the `docs.zip` file. You can find above the steps to do so. +- Add the `zipFile` attribute to the plugin entry in `docs-remote-plugins.json`. The value should be the full path of the `docs.zip` generated. For example: + +```json +{ + "title": "Scaffolding", + "path": "scaffolding", + "repo": "hashicorp/packer-plugin-scaffolding", + "version": "latest", + "sourceBranch": "main", + "zipFile": "/Users/myuser/Packer/plugins/packer-plugin-scaffolding/docs.zip" +} +``` + +- Go to the [website folder](https://github.com/hashicorp/packer/tree/master/website). + In the website README, follow the steps to [run the website with node](https://github.com/hashicorp/packer/tree/master/website#with-node). +- Once the website is up and running, the plugin documentation should be available in `http://localhost:3000/docs`. + ### Plugin Development Tips and FAQs #### Working Examples