Skip to content

Commit

Permalink
minor: Automatically generate Capitano Configuration
Browse files Browse the repository at this point in the history
Signed-off-by: Vipul Gupta (@vipulgupta2048) <[email protected]>
  • Loading branch information
vipulgupta2048 committed Nov 7, 2023
1 parent 4919d1b commit 90e0037
Showing 1 changed file with 99 additions and 104 deletions.
203 changes: 99 additions & 104 deletions automation/capitanodoc/capitanodoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ import { globSync } from 'glob';
* some content to this object.
*
* IMPORTANT
*
*
* 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.
*
* 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.
*
* 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"
*
Expand All @@ -44,97 +44,92 @@ import { globSync } from 'glob';
// TODO: Generate an error if commands are not in their folder

interface Category {
title: string;
files: string[];
title: string;
files: string[];
}

interface Documentation {
title: string;
introduction: string;
categories: Category[];
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",
}
const 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/**' })
const allCommandsPaths = globSync('build/commands/**/*.js', {
ignore: 'build/commands/internal/**',
});

// Docs config template
// Docs config template
let capitanoDoc: Documentation = {
title: 'balena CLI Documentation',
introduction: '',
categories: []
title: 'balena CLI Documentation',
introduction: '',
categories: [],
};

// Helper function to capitalize each word of directory name
function formatTitle(dir: string): string {
return dir.replace(/(^\w|\s\w)/g, (word) => word.toUpperCase());
}

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
}
}
// Create a map to track the categories for faster lookup
const categoriesMap: { [key: string]: Category } = {};

for (const commandPath of allCommandsPaths) {
const commandDir = path.basename(path.dirname(commandPath));
const heading = commandHeadings[commandDir] || formatTitle(commandDir);

if (!categoriesMap[heading]) {
categoriesMap[heading] = { title: heading, files: [] };
capitanoDoc.categories.push(categoriesMap[heading]);
}

categoriesMap[heading].files.push(commandPath);
}

// Sort categories alphabetically
capitanoDoc.categories = capitanoDoc.categories.sort((a, b) => a.title.localeCompare(b.title))
// Sort Category titles alhpabetically
capitanoDoc.categories = capitanoDoc.categories.sort((a, b) =>
a.title.localeCompare(b.title),
);

// Sort Category file paths alhpabetically
capitanoDoc.categories.forEach((category) => {
category.files.sort((a, b) => a.localeCompare(b));
});


/**
Expand All @@ -145,25 +140,25 @@ capitanoDoc.categories = capitanoDoc.categories.sort((a, b) => a.title.localeCom
* for the documentation web page.
*/
export async function getCapitanoDoc(): Promise<typeof 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;
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;
}

0 comments on commit 90e0037

Please sign in to comment.