From 4919d1be521ff6d8fdeb81f9a0617c90e936c7aa Mon Sep 17 00:00:00 2001 From: "Vipul Gupta (@vipulgupta2048)" Date: Tue, 7 Nov 2023 05:22:09 +0530 Subject: [PATCH] minor: Automatically generate Capitano Configuration Signed-off-by: Vipul Gupta (@vipulgupta2048) --- automation/capitanodoc/capitanodoc.ts | 361 +++++++++----------------- 1 file changed, 128 insertions(+), 233 deletions(-) diff --git a/automation/capitanodoc/capitanodoc.ts b/automation/capitanodoc/capitanodoc.ts index 4a59451742..29090f34ca 100644 --- a/automation/capitanodoc/capitanodoc.ts +++ b/automation/capitanodoc/capitanodoc.ts @@ -17,6 +17,7 @@ import * as path from 'path'; import { MarkdownFileParser } from './utils'; +import { globSync } from 'glob'; /** * This is the skeleton of CLI documentation/reference web page at: @@ -26,249 +27,143 @@ import { MarkdownFileParser } from './utils'; * some content to this object. * * IMPORTANT - * - * Only build files listed here will be documented by Capitano - * Make sure to add your files in alphabetical order - * + * + * All commands need to be stored under a folder in lib/commands to maintain uniformity + * Generating docs will error out if directive not followed + * To add a custom heading for command docs, add the heading next to the folder name + * in the `commandHeadings` dictionary. + * + * This dictionary is the source of truth that creates the docs config which is used + * to generate the CLI documentation. By default, the folder name will be used. + * * Resources with plural names needs to have 2 sections if they have commands like: * "fleet, fleets" or "device, devices" or "tag, tags" * */ -const capitanoDoc = { - title: 'balena CLI Documentation', - introduction: '', - categories: [ - { - title: 'API Key', - files: ['build/commands/api-keys/index.js'], - }, - { - title: 'API Keys', - files: [ - 'build/commands/api-key/generate.js', - 'build/commands/api-key/revoke.js', - ], - }, - { - title: 'App', - files: ['build/commands/app/create.js'], - }, - { - title: 'Authentication', - files: [ - 'build/commands/login.js', - 'build/commands/logout.js', - 'build/commands/whoami.js', - ], - }, - { - title: 'Block', - files: ['build/commands/app/create.js'], - }, - { - title: 'Config', - files: [ - 'build/commands/config/generate.js', - 'build/commands/config/inject.js', - 'build/commands/config/read.js', - 'build/commands/config/reconfigure.js', - 'build/commands/config/write.js', - ], - }, - { - title: 'Deploy', - files: ['build/commands/build.js', 'build/commands/deploy.js'], - }, - { - title: 'Device', - files: [ - 'build/commands/device/deactivate.js', - 'build/commands/device/identify.js', - 'build/commands/device/index.js', - 'build/commands/device/init.js', - 'build/commands/device/local-mode.js', - 'build/commands/device/move.js', - 'build/commands/device/os-update.js', - 'build/commands/device/pin.js', - 'build/commands/device/public-url.js', - 'build/commands/device/purge.js', - 'build/commands/device/reboot.js', - 'build/commands/device/register.js', - 'build/commands/device/rename.js', - 'build/commands/device/restart.js', - 'build/commands/device/rm.js', - 'build/commands/device/shutdown.js', - 'build/commands/device/track-fleet.js', - 'build/commands/device/start-service.js', - 'build/commands/device/stop-service.js', - ], - }, - { - title: 'Devices', - files: [ - 'build/commands/devices/index.js', - 'build/commands/devices/supported.js', - ], - }, - { - title: 'Environment Variable', - files: [ - 'build/commands/env/add.js', - 'build/commands/env/rename.js', - 'build/commands/env/rm.js', - ], - }, - { - title: 'Environment Variables', - files: ['build/commands/envs.js'], - }, - { - title: 'Fleet', - files: [ - 'build/commands/fleet/create.js', - 'build/commands/fleet/index.js', - 'build/commands/fleet/pin.js', - 'build/commands/fleet/purge.js', - 'build/commands/fleet/rename.js', - 'build/commands/fleet/restart.js', - 'build/commands/fleet/rm.js', - 'build/commands/fleet/track-latest.js', - ], - }, - { - title: 'Fleets', - files: ['build/commands/fleets.js'], - }, - { - title: 'Help and Version', - files: ['help', 'build/commands/version.js'], - }, - { - title: 'Local', - files: [ - 'build/commands/local/configure.js', - 'build/commands/local/flash.js', - ], - }, - { - title: 'Logs', - files: ['build/commands/logs.js'], - }, - { - title: 'Network', - files: [ - 'build/commands/scan.js', - 'build/commands/ssh.js', - 'build/commands/tunnel.js', - ], - }, - { - title: 'Notes', - files: ['build/commands/note.js'], - }, - { - title: 'Organizations', - files: ['build/commands/orgs.js'], - }, - { - title: 'OS', - files: [ - 'build/commands/os/build-config.js', - 'build/commands/os/configure.js', - 'build/commands/os/download.js', - 'build/commands/os/initialize.js', - 'build/commands/os/versions.js', - ], - }, - { - title: 'Preload', - files: ['build/commands/preload.js'], - }, - { - title: 'Push', - files: ['build/commands/push.js'], - }, - { - title: 'Platform', - files: ['build/commands/join.js', 'build/commands/leave.js'], - }, - { - title: 'Release', - files: [ - 'build/commands/release/finalize.js', - 'build/commands/release/index.js', - 'build/commands/release/invalidate.js', - 'build/commands/release/validate.js', - ], - }, - { - title: 'Releases', - files: ['build/commands/releases.js'], - }, - { - title: 'Settings', - files: ['build/commands/settings.js'], - }, - { - title: 'Support', - files: ['build/commands/support.js'], - }, - { - title: 'SSH Key', - files: [ - 'build/commands/key/add.js', - 'build/commands/key/index.js', - 'build/commands/key/rm.js', - ], - }, - { - title: 'SSH Keys', - files: ['build/commands/keys.js'], - }, - { - title: 'Tags', - files: ['build/commands/tag/rm.js', 'build/commands/tag/set.js'], - }, - { - title: 'Tags', - files: ['build/commands/tags.js'], - }, - { - title: 'Utilities', - files: ['build/commands/util/available-drives.js'], - }, - ], -}; +// TODO: Generate an error if commands are not in their folder + +interface Category { + title: string; + files: string[]; +} + +interface Documentation { + title: string; + introduction: string; + categories: Category[]; +} + +// Mapping folders names to custom headings in the docs +let commandHeadings: { [key: string]: string } = { + 'api-key': "API Key", + 'api-keys': "API Keys", + 'app': "App", + "auth": "Authentication", + 'block': 'Block', + 'config': 'Config', + 'deploy': 'Deploy', + 'device': 'Device', + 'devices': 'Devices', + 'env': "Environment Variable", + 'envs': "Environment Variables", + 'fleet': "Fleet", + 'fleets': "Fleets", + 'help': "Help and Version", + 'key': "SSH Key", + 'keys': "SSH Keys", + 'local': "local", + 'logs': "logs", + 'note': "Notes", + 'orgs': "Organizations", + 'os': "OS", + 'platform': "Platform", + 'preload': "Preload", + 'push': "Push", + 'release': "Release", + 'releases': "Releases", + 'settings': "Settings", + 'support': "Support", + 'tag': 'Tag', + 'tags': 'Tags', + 'util': "Utilities", +} + +// Fetch all available commands +const allCommandsPaths = globSync('build/commands/**/*.js', { ignore: 'build/commands/internal/**' }) + +// Docs config template +let capitanoDoc: Documentation = { + title: 'balena CLI Documentation', + introduction: '', + categories: [] +} + +for (let commandPath of allCommandsPaths) { + let flag = false + let commandDir = path.basename(path.dirname(commandPath)) + + // Add a new entry if no existing headings found + if (!Object.keys(commandHeadings).includes(commandDir)) { + // Capitalize each word of directory name + // commandDir = commandDir.replace(/(^\w|\s\w)/g, word => word.toUpperCase()); + capitanoDoc.categories.push({ 'title': commandDir, 'files': [commandPath] }) + commandHeadings[commandDir] = commandDir + flag = true + } + + for (let commandHeading of Object.keys(commandHeadings)) { + if (commandDir === commandHeading) { + for (let category of capitanoDoc.categories) { + if (category["title"] === commandHeadings[commandDir]) { + if (flag) { + break + } + category["files"].push(commandPath) + flag = true + break + } + } + if (flag) { + break + } + capitanoDoc.categories.push({ 'title': commandHeadings[commandDir], 'files': [commandPath] }) + break + } + } +} + +// Sort categories alphabetically +capitanoDoc.categories = capitanoDoc.categories.sort((a, b) => a.title.localeCompare(b.title)) + /** - * Modify and return the `capitanoDoc` object above in order to render the - * CLI documentation/reference web page at: - * https://www.balena.io/docs/reference/cli/ + * Modify and return the `capitanoDoc` object above in order to generate the + * CLI documentation at docs/balena-cli.md * * This function parses the README.md file to extract relevant sections * for the documentation web page. */ export async function getCapitanoDoc(): Promise { - const readmePath = path.join(__dirname, '..', '..', 'README.md'); - const mdParser = new MarkdownFileParser(readmePath); - const sections: string[] = await Promise.all([ - mdParser.getSectionOfTitle('About').then((sectionLines: string) => { - // delete the title of the 'About' section for the web page - const match = /^(#+)\s+.+?\n\s*([^]*)/.exec(sectionLines); - if (!match || match.length < 3) { - throw new Error(`Error parsing section title`); - } - // match[1] has the title, match[2] has the rest - return match && match[2]; - }), - mdParser.getSectionOfTitle('Installation'), - mdParser.getSectionOfTitle('Choosing a shell (command prompt/terminal)'), - mdParser.getSectionOfTitle('Logging in'), - mdParser.getSectionOfTitle('Proxy support'), - mdParser.getSectionOfTitle('Support, FAQ and troubleshooting'), - mdParser.getSectionOfTitle('Deprecation policy'), - ]); - capitanoDoc.introduction = sections.join('\n'); - return capitanoDoc; + const readmePath = path.join(__dirname, '..', '..', 'README.md'); + const mdParser = new MarkdownFileParser(readmePath); + const sections: string[] = await Promise.all([ + mdParser.getSectionOfTitle('About').then((sectionLines: string) => { + // delete the title of the 'About' section for the web page + const match = /^(#+)\s+.+?\n\s*([^]*)/.exec(sectionLines); + if (!match || match.length < 3) { + throw new Error(`Error parsing section title`); + } + // match[1] has the title, match[2] has the rest + return match && match[2]; + }), + mdParser.getSectionOfTitle('Installation'), + mdParser.getSectionOfTitle('Choosing a shell (command prompt/terminal)'), + mdParser.getSectionOfTitle('Logging in'), + mdParser.getSectionOfTitle('Proxy support'), + mdParser.getSectionOfTitle('Support, FAQ and troubleshooting'), + mdParser.getSectionOfTitle('Deprecation policy'), + ]); + capitanoDoc.introduction = sections.join('\n'); + return capitanoDoc; }