Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
"<itwin-id>"
]
},
{
"type": "node",
"request": "launch",
"name": "Launch docs generator",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\bin\\run",
"args": [
"docs-generator"
]
},
{
"name": "Debug All Tests",
"skipFiles": [
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/formating/formatting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Command formatting tests', async () => {
userPlugins: false,
});

allCommands = config.commands.filter(command => !command.id.startsWith("plugins") && !command.id.startsWith("help"))
allCommands = config.commands.filter(command => !command.id.startsWith("plugins") && !command.id.startsWith("help") && !command.hidden)
.map((command) =>
({
cmd: command,
Expand Down
78 changes: 75 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,87 @@
},
"topicSeparator": " ",
"topics": {
"hello": {
"description": "Say hello to the world and others"
"auth": {
"description": "Authenticate itp with Bentley."
},
"itwin": {
"description": "Work with iTwins."
},
"itwin repository": {
"description": "Work with iTwin repositories."
},
"imodel": {
"description": "Work with iModels of an iTwin."
},
"imodel view": {
"description": "Work with views for an iModel."
},
"imodel changeset": {
"description": "Work with changesets of an iModel."
},
"imodel namedversion": {
"description": "Work with named versions of an iModel."
},
"imodel connection": {
"description": "Work with iModel connections."
},
"imodel connection run": {
"description": "Work with iModel connection runs."
},
"imodel connection sourcefile": {
"description": "Work with iModel connection source files."
},
"access-control": {
"description": "Manage iTwin access control."
},
"access-control permissions": {
"description": "Get permission information."
},
"access-control role": {
"description": "Manage iTwin access control roles."
},
"access-control group": {
"description": "Manage iTwin access control groups."
},
"access-control member": {
"description": "Manage members that can access an iTwin."
},
"access-control member owner": {
"description": "Manage access control for iTwin owners."
},
"access-control member group": {
"description": "Manage groups that can access an iTwin."
},
"access-control member user": {
"description": "Manage users that can access an iTwin."
},
"storage": {
"description": "Interact with the storage repository of an iTwin."
},
"storage folder": {
"description": "Interact with folders inside a storage repository."
},
"storage file": {
"description": "Interact with files inside a storage repository."
},
"user": {
"description": "Access user information."
},
"changed-elements": {
"description": "Compare two changesets of an iModel."
},
"workflows": {
"description": "Workflow reference for combined commands."
},
"combined-commands": {
"description": "Workflow reference for combined commands."
}
},
"additionalHelpFlags": ["-h"]
},
"repository": "iTwin/itwin-cli",
"scripts": {
"clean": "shx rm -rf dist",
"clean": "shx rm -rf dist tsconfig.tsbuildinfo",
"build": "npm run clean && tsc -b",
"lint": "eslint . --ext .ts",
"postpack": "shx rm -f oclif.manifest.json",
Expand Down
2 changes: 1 addition & 1 deletion pipelines/scripts/install-and-test-installer.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ if ($version -like "itp/* win32-x64 node-v20.16.0") {
}

$help = & itp itwin --help
if ($help -like "Create an iTwin*") {
if ($help -like "Work with iTwins.*") {
Write-Output "Help output check passed"
} else {
Write-Error "Unexpected help output: $help"
Expand Down
4 changes: 2 additions & 2 deletions pipelines/test_installers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
displayName: 'Check itp version'
- script: |
itwin_help=$(itp itwin --help)
if [[ $itwin_help == "Create an iTwin"* ]]; then
if [[ $itwin_help == "Work with iTwins."* ]]; then
echo "Help output is correct: $itwin_help"
else
echo "Unexpected help output: $itwin_help"
Expand Down Expand Up @@ -85,7 +85,7 @@ jobs:
displayName: 'Check itp version'
- script: |
itwin_help=$(itp itwin --help)
if [[ $itwin_help == "Create an iTwin"* ]]; then
if [[ $itwin_help == "Work with iTwins."* ]]; then
echo "Help output is correct: $itwin_help"
else
echo "Unexpected help output: $itwin_help"
Expand Down
6 changes: 6 additions & 0 deletions src/commands/access-control/group/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@

import { Flags } from "@oclif/core";

import { apiReference } from "../../../extensions/api-reference.js";
import BaseCommand from "../../../extensions/base-command.js";

export default class CreateAccessControlGroup extends BaseCommand {
static apiReference : apiReference = {
link: "https://developer.bentley.com/apis/access-control-v2/operations/create-itwin-group/",
name: "Create iTwin Group",
}

static description = 'Create a new group for an iTwin.';

static examples = [
Expand Down
112 changes: 112 additions & 0 deletions src/commands/docs-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Command, Config, Flags } from "@oclif/core";
import fs from "node:fs";
import path from "node:path";

import BaseCommand from "../extensions/base-command.js";

export default class DocsGenerator extends BaseCommand {
static description = "Generate command and overview markdown files for the CLI.";

static flags = {
"output-dir": Flags.string({
char: "o",
description: "The output directory for the documentation files.",

}),
}

static hidden = true;

generateCommandMarkdown(command: Command.Loadable, flags: [string, Command.Flag.Cached][]): string {
const options = flags.length > 0
?
flags.filter(([_, flag]) => !flag.hidden && flag.helpGroup !== "GLOBAL")
.sort(([_, flag]) => (flag.required ? -1 : 1))
.map(([name, flag]) => {
const required = flag.required ? "**Required:** Yes" : "**Required:** No";
const typeValue = flag.type === "option" ? flag.helpValue ?? "" : "<flag>";
const type = `**Type:** \`${typeValue}\``;
const description = flag.description ?? "";
const flagName = flag.char ? `-${flag.char}, --${name}` : `--${name}`;
return `- **\`${flagName}\`** \n ${description} \n ${type} ${required}`;
})
.join("\n\n")
: "";



let examplesText = "";

if (command.examples){
for (const example of command.examples) {
if (typeof example === "string") {
examplesText += `\n${example}`;
}

if(typeof example === "object") {
examplesText += `\n# ${example.description}\n${example.command}\n`;
}
}
}

const commandName = command.id.split(":").join(" ");

examplesText = examplesText.replaceAll("<%= config.bin %>", "itp").replaceAll("<%= command.id %>", commandName).trimEnd();

const apiReference = command.apiReference as string;
const apiReferenceName = command.apiReferenceName as string;

return `# itp ${commandName}\n\n${command.description || ""}\n\n## Options\n\n${options}\n\n## Examples\n\n\`\`\`bash${examplesText}\n\`\`\`\n\n## API Reference\n\n[${apiReferenceName}](${apiReference})`;
}

async generateDocs(config: Config, basePath: string) {
const filteredCommands = config.commands.filter(c => !c.id.includes("help") && !c.id.includes("plugins") && !c.hidden);
if(!filteredCommands) {
return;
}

for (const command of filteredCommands) {
const markdown: string = this.generateCommandMarkdown(command, Object.entries(command.flags));

const commandDepth = command.id.split(":");
let filePath = basePath;
for (const depth of commandDepth) {
filePath = `${filePath}/${depth}`;
}

this.writeToFile(filePath, markdown);
}
}

async run() {
const {flags} = await this.parse(DocsGenerator);

const config = await Config.load(
{
devPlugins: false,
root: process.cwd(),
userPlugins: false,
}
);

this.generateDocs(config, flags["output-dir"] ?? `${config.root}/docs`);
}

writeToFile(filePath: string, markdown: string) {
const finalPath = `${filePath}.md`;
console.log(`Writing to directory: ${finalPath}`);
console.log(markdown);

const dirName = path.dirname(finalPath);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, {
recursive: true,
});
}

fs.writeFileSync(finalPath, markdown);
}
}



4 changes: 4 additions & 0 deletions src/extensions/api-reference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type apiReference = {
link: string;
name: string;
};