diff --git a/lib/cli/README.md b/lib/cli/README.md index 2d2b6fc69c..7efa67a7f3 100644 --- a/lib/cli/README.md +++ b/lib/cli/README.md @@ -18,12 +18,24 @@ adf-cli --help ## Developing -Link the project as a global tool +### Quick Setup + +Link the project as a global tool (builds and links in one command): ```bash -npm link +npm run link ``` +This will build the CLI and make the `adf-cli` command available globally on your system. + +When you're done developing: + +```bash +npm run unlink +``` + +### Development Mode + Build the tool in the **develop** mode (automatically watches for changes and rebuilds the commands): ```bash @@ -38,6 +50,21 @@ DEVELOP=true adf-cli In develop mode, the CLI takes the prebuilt scripts from the dist folder. +### Manual Workflow + +If you need more control, you can manually build and link: + +```bash +# Build the CLI +npm run build + +# Or build with full distribution (includes copying resources) +npm run dist + +# Link from the dist directory +cd ../../dist/libs/cli && npm link +``` + ## Commands | **Commands** | **Description** | diff --git a/lib/cli/bin/adf-cli b/lib/cli/bin/adf-cli index ae1337b9d6..53bbf85fd1 100755 --- a/lib/cli/bin/adf-cli +++ b/lib/cli/bin/adf-cli @@ -1,5 +1,5 @@ #!/usr/bin/env node -const minimist = require('minimist'); +const { parseArgs } = require('node:util'); const { resolve, join } = require('node:path'); const { readFileSync, existsSync } = require('node:fs'); const { argv, exit, env, cwd } = require('node:process'); @@ -10,10 +10,22 @@ function printHelp() { console.log(`${name} v${version}`); } -const args = minimist(argv.slice(2), { - boolean: ['verbose'] +const { values, positionals } = parseArgs({ + args: argv.slice(2), + options: { + verbose: { + type: 'boolean' + } + }, + allowPositionals: true, + strict: false }); +const args = { + ...values, + _: positionals +}; + if (args._.length === 0) { printHelp(); exit(1); diff --git a/lib/cli/package.json b/lib/cli/package.json index 00551d1096..ddd9643c95 100644 --- a/lib/cli/package.json +++ b/lib/cli/package.json @@ -17,15 +17,15 @@ "scripts": { "build": "tsc -p tsconfig.json", "develop": "tsc -p tsconfig.json --watch", - "dist": "rm -rf ../../dist/libs/cli && npm run build && cp -R ./bin ../../dist/libs/cli && cp -R ./resources ../../dist/libs/cli && cp -R ./templates ../../dist/libs/cli && cp ./package.json ../../dist/libs/cli" + "dist": "rm -rf ../../dist/libs/cli && npm run build && cp -R ./bin ../../dist/libs/cli && cp -R ./resources ../../dist/libs/cli && cp -R ./templates ../../dist/libs/cli && cp ./package.json ../../dist/libs/cli", + "link": "npm run dist && cd ../../dist/libs/cli && npm link", + "unlink": "cd ../../dist/libs/cli && npm unlink" }, "dependencies": { "@alfresco/js-api": ">=8.4.0-0", - "commander": "^6.2.1", "ejs": "^3.1.10", "license-checker": "^25.0.1", "node-fetch": "^2.7.0", - "rxjs": "7.8.2", "shelljs": "^0.10.0", "spdx-license-list": "^5.0.0" }, diff --git a/lib/cli/scripts/audit.ts b/lib/cli/scripts/audit.ts index f2e7a32868..c1a53b768a 100644 --- a/lib/cli/scripts/audit.ts +++ b/lib/cli/scripts/audit.ts @@ -22,9 +22,7 @@ import * as ejs from 'ejs'; import * as path from 'path'; import * as fs from 'fs'; import { argv, exit } from 'node:process'; -import { Command } from 'commander'; - -const program = new Command(); +import { parseArgs } from 'node:util'; interface AuditCommandArgs { package?: string; @@ -39,19 +37,39 @@ interface AuditCommandArgs { * @returns void */ export default function main(_args: string[], workingDir: string) { - program - .description('Generate an audit report') - .usage('audit [options]') - .option('-p, --package ', 'Path to package file (default: package.json in working directory)') - .option('-d, --outDir ', 'Ouput directory (default: working directory)') - .parse(argv); - if (argv.includes('-h') || argv.includes('--help')) { - program.outputHelp(); + console.log(` +Usage: audit [options] + +Generate an audit report + +Options: + -p, --package Path to package file (default: package.json in working directory) + -d, --outDir Output directory (default: working directory) + -h, --help Display help for command +`); exit(0); } - const options: AuditCommandArgs = program.opts(); + const { values } = parseArgs({ + args: argv.slice(2), + options: { + package: { + type: 'string', + short: 'p' + }, + outDir: { + type: 'string', + short: 'd' + } + }, + allowPositionals: true + }); + + const options: AuditCommandArgs = { + package: values.package as string | undefined, + outDir: values.outDir as string | undefined + }; let packagePath = path.resolve(workingDir, 'package.json'); diff --git a/lib/cli/scripts/changelog.ts b/lib/cli/scripts/changelog.ts index 1b1fd1a4a5..98afd83b1c 100644 --- a/lib/cli/scripts/changelog.ts +++ b/lib/cli/scripts/changelog.ts @@ -20,15 +20,13 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { argv, exit } from 'node:process'; +import { parseArgs } from 'node:util'; import * as shell from 'shelljs'; import * as path from 'path'; -import { Command } from 'commander'; import { logger } from './logger'; import * as fs from 'fs'; import * as ejs from 'ejs'; -const program = new Command(); - interface Commit { hash: string; author: string; @@ -151,28 +149,74 @@ function commitAuthorAllowed(commit: Commit, authorFilter: string): boolean { * @returns void */ export default function main(_args: string[], workingDir: string) { - program - .description('Generate changelog report for two branches of git repository') - .version('0.0.1', '-v, --version') - .usage('changelog [options]') - .option('-r, --range ', 'Commit range, e.g. origin/master..develop', 'origin/master..develop') - .option('-d, --dir ', 'Working directory (default: working directory)') - .option('-m, --max ', 'Limit the number of commits to output') - .option('-o, --output ', 'Output directory, will use console output if not defined') - .option('--skip ', 'Skip number commits before starting to show the commit output') - .option('-f, --format ', 'Output format (md, html)', 'md') - .option('-e --exclude ', 'Exclude authors from the output, comma-delimited list') - .parse(argv); - if (argv.includes('-h') || argv.includes('--help')) { - program.outputHelp(); + console.log(` +Usage: changelog [options] + +Generate changelog report for two branches of git repository + +Options: + -v, --version Output the version number + -r, --range Commit range, e.g. origin/master..develop (default: "origin/master..develop") + -d, --dir Working directory (default: working directory) + -m, --max Limit the number of commits to output + -o, --output Output directory, will use console output if not defined + --skip Skip number commits before starting to show the commit output + -f, --format Output format (md, html) (default: "md") + -e, --exclude Exclude authors from the output, comma-delimited list + -h, --help Display help for command +`); exit(0); } - const options = program.opts(); + if (argv.includes('-v') || argv.includes('--version')) { + console.log('0.0.1'); + exit(0); + } - const dir = path.resolve(options.dir || workingDir); - const { range, skip, max, format, output, exclude } = options; + const { values } = parseArgs({ + args: argv.slice(2), + options: { + range: { + type: 'string', + short: 'r', + default: 'origin/master..develop' + }, + dir: { + type: 'string', + short: 'd' + }, + max: { + type: 'string', + short: 'm' + }, + output: { + type: 'string', + short: 'o' + }, + skip: { + type: 'string' + }, + format: { + type: 'string', + short: 'f', + default: 'md' + }, + exclude: { + type: 'string', + short: 'e' + } + }, + allowPositionals: true + }); + + const dir = path.resolve((values.dir as string) || workingDir); + const range = values.range as string; + const skip = values.skip ? parseInt(values.skip as string, 10) : undefined; + const max = values.max ? parseInt(values.max as string, 10) : undefined; + const format = values.format as string; + const output = values.output as string | undefined; + const exclude = values.exclude as string | undefined; const remote = getRemote(dir); diff --git a/lib/cli/scripts/check-cs-env.ts b/lib/cli/scripts/check-cs-env.ts index bbbff87db8..800dbd2c8e 100755 --- a/lib/cli/scripts/check-cs-env.ts +++ b/lib/cli/scripts/check-cs-env.ts @@ -17,7 +17,7 @@ import { AlfrescoApi /*, NodesApi, UploadApi*/ } from '@alfresco/js-api'; import { argv, exit } from 'node:process'; -import { Command } from 'commander'; +import { parseArgs } from 'node:util'; import { logger } from './logger'; interface CheckCsEnvArgs { @@ -27,8 +27,6 @@ interface CheckCsEnvArgs { time?: number; retry?: number; } - -const program = new Command(); const MAX_RETRY = 3; const TIMEOUT = 20000; @@ -39,18 +37,63 @@ let alfrescoJsApi: AlfrescoApi; * Check CS environment command */ export default async function main() { - program - .version('0.1.0') - .description('Check Content service is up ') - .usage('check-cs-env [options]') - .option('--host [type]', 'Remote environment host adf.lab.com ') - .option('-p, --password [type]', 'password ') - .option('-u, --username [type]', 'username ') - .option('-t, --time [type]', 'time ') - .option('-r, --retry [type]', 'retry ') - .parse(argv); + if (argv.includes('-h') || argv.includes('--help')) { + console.log(` +Usage: check-cs-env [options] + +Check Content service is up + +Options: + -v, --version Output the version number + --host Remote environment host adf.lab.com + -p, --password Password + -u, --username Username + -t, --time Time in milliseconds + -r, --retry Retry count + -h, --help Display help for command +`); + exit(0); + } + + if (argv.includes('-v') || argv.includes('--version')) { + console.log('0.1.0'); + exit(0); + } + + const { values } = parseArgs({ + args: argv.slice(2), + options: { + host: { + type: 'string' + }, + password: { + type: 'string', + short: 'p' + }, + username: { + type: 'string', + short: 'u' + }, + time: { + type: 'string', + short: 't' + }, + retry: { + type: 'string', + short: 'r' + } + }, + allowPositionals: true + }); + + const opts: CheckCsEnvArgs = { + host: values.host as string | undefined, + username: values.username as string | undefined, + password: values.password as string | undefined, + time: values.time ? parseInt(values.time as string, 10) : undefined, + retry: values.retry ? parseInt(values.retry as string, 10) : undefined + }; - const opts = program.opts(); await checkEnv(opts); // TODO: https://alfresco.atlassian.net/browse/ACS-5873 // await checkDiskSpaceFullEnv(); diff --git a/lib/cli/scripts/check-plugin-env.ts b/lib/cli/scripts/check-plugin-env.ts index beb8dee890..d9724aff9d 100644 --- a/lib/cli/scripts/check-plugin-env.ts +++ b/lib/cli/scripts/check-plugin-env.ts @@ -15,14 +15,13 @@ * limitations under the License. */ -import { argv } from 'node:process'; +import { argv, exit } from 'node:process'; +import { parseArgs } from 'node:util'; import { CheckEnv } from './plugins/check-env'; -import { Command } from 'commander'; import { ProcessServiceCheckPlugin } from './plugins/process-service-check-plugin'; import { ProcessAutomationCheckPlugin } from './plugins/process-automation-check-plugin'; import { GovernanceCheckPlugin } from './plugins/governance-check-plugin'; -const program = new Command(); let pluginEnv: CheckEnv; interface CheckPluginArgs { @@ -39,18 +38,74 @@ interface CheckPluginArgs { * Check environment plugin */ export default async function main() { - program - .version('0.1.0') - .option('--host [type]', 'Remote environment host') - .option('--pluginName [type]', 'pluginName') - .option('--clientId [type]', 'sso client', 'alfresco') - .option('--appName [type]', 'appName ', 'Deployed appName on activiti-cloud') - .option('-p, --password [type]', 'password ') - .option('-u, --username [type]', 'username ') - .option('--ui, --uiName [type]', 'uiName', 'Deployed app UI type on activiti-cloud') - .parse(argv); + if (argv.includes('-h') || argv.includes('--help')) { + console.log(` +Usage: check-plugin-env [options] - const options = program.opts(); +Check plugin status + +Options: + -v, --version Output the version number + --host Remote environment host + --pluginName Plugin name (processService, processAutomation, governance) + --clientId SSO client (default: "alfresco") + --appName Deployed appName on activiti-cloud + -p, --password Password + -u, --username Username + --ui, --uiName Deployed app UI type on activiti-cloud + -h, --help Display help for command +`); + exit(0); + } + + if (argv.includes('-v') || argv.includes('--version')) { + console.log('0.1.0'); + exit(0); + } + + const { values } = parseArgs({ + args: argv.slice(2), + options: { + host: { + type: 'string' + }, + pluginName: { + type: 'string' + }, + clientId: { + type: 'string', + default: 'alfresco' + }, + appName: { + type: 'string' + }, + password: { + type: 'string', + short: 'p' + }, + username: { + type: 'string', + short: 'u' + }, + ui: { + type: 'string' + }, + uiName: { + type: 'string' + } + }, + allowPositionals: true + }); + + const options: CheckPluginArgs = { + host: values.host as string | undefined, + pluginName: values.pluginName as 'processService' | 'processAutomation' | 'governance' | undefined, + clientId: values.clientId as string | undefined, + appName: values.appName as string | undefined, + username: values.username as string | undefined, + password: values.password as string | undefined, + uiName: (values.ui || values.uiName) as string | undefined + }; pluginEnv = new CheckEnv(options.host, options.username, options.password, options.clientId); await pluginEnv.checkEnv(); diff --git a/lib/cli/scripts/init-aae-env.ts b/lib/cli/scripts/init-aae-env.ts index 033ebe708d..4882be8130 100755 --- a/lib/cli/scripts/init-aae-env.ts +++ b/lib/cli/scripts/init-aae-env.ts @@ -17,14 +17,12 @@ * limitations under the License. */ -import { Command } from 'commander'; +import { parseArgs } from 'node:util'; import fetch from 'node-fetch'; import * as fs from 'fs'; import { logger } from './logger'; import { AlfrescoApi, AlfrescoApiConfig } from '@alfresco/js-api'; import { argv, exit } from 'node:process'; - -const program = new Command(); const ACTIVITI_CLOUD_APPS = require('./resources').ACTIVITI_CLOUD_APPS; let alfrescoJsApiModeler: AlfrescoApi; @@ -730,32 +728,103 @@ async function sleep(time: number) { * Init AAE environment command */ export default async function main() { - program - .version('0.1.0') - .description( - 'The following command is in charge of Initializing the activiti cloud env with the default apps' + - 'adf-cli init-aae-env --host "gateway_env" --modelerUsername "modelerusername" --modelerPassword "modelerpassword" --devopsUsername "devevopsusername" --devopsPassword "devopspassword"' - ) - .option('-h, --host [type]', 'Host gateway') - .option('--oauth [type]', 'SSO host') - .option('--clientId [type]', 'sso client') - .option('--secret [type]', 'sso secret', '') - .option('--scope [type]', 'sso scope', 'openid') - .option('--tokenEndpoint [type]', 'discovery token Endpoint', 'auth/realms/${clientId}/protocol/openid-connect/token') - .option('--modelerUsername [type]', 'username of a user with role ACTIVIT_MODELER') - .option('--modelerPassword [type]', 'modeler password') - .option('--devopsUsername [type]', 'username of a user with role ACTIVIT_DEVOPS') - .option('--devopsPassword [type]', 'devops password') - .option('--tag [type]', 'tag name of the codebase') - .option('--envs [type...]', 'environment ids of the envs where to deploy the app') - .parse(argv); + if (argv.includes('--help')) { + console.log(` +Usage: init-aae-env [options] - if (argv.includes('-h') || argv.includes('--help')) { - program.outputHelp(); +Initialize the activiti cloud env with the default apps + +Example: + adf-cli init-aae-env --host "gateway_env" --modelerUsername "modelerusername" \\ + --modelerPassword "modelerpassword" --devopsUsername "devopsusername" \\ + --devopsPassword "devopspassword" + +Options: + -v, --version Output the version number + -h, --host Host gateway + --oauth SSO host + --clientId SSO client + --secret SSO secret (default: "") + --scope SSO scope (default: "openid") + --tokenEndpoint Discovery token endpoint (default: "auth/realms/\${clientId}/protocol/openid-connect/token") + --modelerUsername Username of a user with role ACTIVIT_MODELER + --modelerPassword Modeler password + --devopsUsername Username of a user with role ACTIVIT_DEVOPS + --devopsPassword Devops password + --tag Tag name of the codebase + --envs Environment ids of the envs where to deploy the app + --help Display help for command +`); return; } - const options = initializeDefaultToken(program.opts() as ConfigArgs); + if (argv.includes('-v') || argv.includes('--version')) { + console.log('0.1.0'); + exit(0); + } + + const { values } = parseArgs({ + args: argv.slice(2), + options: { + host: { + type: 'string', + short: 'h' + }, + oauth: { + type: 'string' + }, + clientId: { + type: 'string' + }, + secret: { + type: 'string', + default: '' + }, + scope: { + type: 'string', + default: 'openid' + }, + tokenEndpoint: { + type: 'string', + default: 'auth/realms/${clientId}/protocol/openid-connect/token' + }, + modelerUsername: { + type: 'string' + }, + modelerPassword: { + type: 'string' + }, + devopsUsername: { + type: 'string' + }, + devopsPassword: { + type: 'string' + }, + tag: { + type: 'string' + }, + envs: { + type: 'string', + multiple: true + } + }, + allowPositionals: true + }); + + const options = initializeDefaultToken({ + host: values.host as string, + oauth: values.oauth as string, + clientId: values.clientId as string, + secret: values.secret as string, + scope: values.scope as string, + tokenEndpoint: values.tokenEndpoint as string, + modelerUsername: values.modelerUsername as string, + modelerPassword: values.modelerPassword as string, + devopsUsername: values.devopsUsername as string, + devopsPassword: values.devopsPassword as string, + tag: values.tag as string, + envs: (values.envs as string[]) || [] + } as ConfigArgs); args = { host: options.host, diff --git a/lib/cli/scripts/init-acs-env.ts b/lib/cli/scripts/init-acs-env.ts index 501c18aed4..e97d81f991 100755 --- a/lib/cli/scripts/init-acs-env.ts +++ b/lib/cli/scripts/init-acs-env.ts @@ -17,7 +17,7 @@ import { AlfrescoApi, SharedlinksApi, FavoritesApi, NodesApi, UploadApi, NodeEntry } from '@alfresco/js-api'; import { exit, argv } from 'node:process'; -import { Command } from 'commander'; +import { parseArgs } from 'node:util'; import { createReadStream } from 'fs'; import * as path from 'path'; import { logger } from './logger'; @@ -28,8 +28,6 @@ interface InitAcsEnvArgs { username?: string; password?: string; } - -const program = new Command(); const MAX_RETRY = 10; let counter = 0; const TIMEOUT = 6000; @@ -41,15 +39,57 @@ let alfrescoJsApi: AlfrescoApi; * Init ACS environment command */ export default async function main() { - program - .version('0.1.0') - .option('--host [type]', 'Remote environment host') - .option('--clientId [type]', 'sso client', 'alfresco') - .option('-p, --password [type]', 'password ') - .option('-u, --username [type]', 'username ') - .parse(argv); + if (argv.includes('-h') || argv.includes('--help')) { + console.log(` +Usage: init-acs-env [options] + +Initialize ACS environment + +Options: + -v, --version Output the version number + --host Remote environment host + --clientId SSO client (default: "alfresco") + -p, --password Password + -u, --username Username + -h, --help Display help for command +`); + exit(0); + } + + if (argv.includes('-v') || argv.includes('--version')) { + console.log('0.1.0'); + exit(0); + } + + const { values } = parseArgs({ + args: argv.slice(2), + options: { + host: { + type: 'string' + }, + clientId: { + type: 'string', + default: 'alfresco' + }, + password: { + type: 'string', + short: 'p' + }, + username: { + type: 'string', + short: 'u' + } + }, + allowPositionals: true + }); + + const opts: InitAcsEnvArgs = { + host: values.host as string | undefined, + clientId: values.clientId as string | undefined, + username: values.username as string | undefined, + password: values.password as string | undefined + }; - const opts = program.opts(); await checkEnv(opts); logger.info(`***** Step initialize ACS *****`); diff --git a/lib/cli/scripts/init-aps-env.ts b/lib/cli/scripts/init-aps-env.ts index bdbb3a5e95..b1e7a3ab21 100755 --- a/lib/cli/scripts/init-aps-env.ts +++ b/lib/cli/scripts/init-aps-env.ts @@ -26,12 +26,11 @@ import { AppDefinitionUpdateResultRepresentation } from '@alfresco/js-api'; import { argv, exit } from 'node:process'; +import { parseArgs } from 'node:util'; import { spawnSync } from 'node:child_process'; import { createReadStream } from 'node:fs'; -import { Command } from 'commander'; import * as path from 'path'; import { logger } from './logger'; -import { throwError } from 'rxjs'; interface InitApsEnvArgs { host?: string; @@ -40,8 +39,6 @@ interface InitApsEnvArgs { password?: string; license?: string; } - -const program = new Command(); const MAX_RETRY = 10; let counter = 0; const TIMEOUT = 6000; @@ -56,16 +53,62 @@ let alfrescoJsApi: AlfrescoApi; * Init APS command */ export default async function main() { - program - .version('0.1.0') - .option('--host [type]', 'Remote environment host') - .option('--clientId [type]', 'sso client', 'alfresco') - .option('-p, --password [type]', 'password ') - .option('-u, --username [type]', 'username ') - .option('--license [type]', 'APS license S3 path ') - .parse(argv); + if (argv.includes('-h') || argv.includes('--help')) { + console.log(` +Usage: init-aps-env [options] + +Initialize APS environment + +Options: + -v, --version Output the version number + --host Remote environment host + --clientId SSO client (default: "alfresco") + -p, --password Password + -u, --username Username + --license APS license S3 path + -h, --help Display help for command +`); + exit(0); + } + + if (argv.includes('-v') || argv.includes('--version')) { + console.log('0.1.0'); + exit(0); + } + + const { values } = parseArgs({ + args: argv.slice(2), + options: { + host: { + type: 'string' + }, + clientId: { + type: 'string', + default: 'alfresco' + }, + password: { + type: 'string', + short: 'p' + }, + username: { + type: 'string', + short: 'u' + }, + license: { + type: 'string' + } + }, + allowPositionals: true + }); + + const opts: InitApsEnvArgs = { + host: values.host as string | undefined, + clientId: values.clientId as string | undefined, + username: values.username as string | undefined, + password: values.password as string | undefined, + license: values.license as string | undefined + }; - const opts = program.opts(); await checkEnv(opts); logger.info(`***** Step 1 - Check License *****`); @@ -207,9 +250,9 @@ async function hasDefaultTenant(tenantId: number, tenantName: string): Promise', 'Path to package file (default: package.json in working directory)') - .option('-d, --outDir ', 'Ouput directory (default: working directory)') - .parse(argv); - if (argv.includes('-h') || argv.includes('--help')) { - program.outputHelp(); + console.log(` +Usage: licenses [options] + +Generate a licenses report + +Options: + -p, --package Path to package file (default: package.json in working directory) + -d, --outDir Output directory (default: working directory) + -h, --help Display help for command +`); exit(0); } - const options: LicensesCommandArgs = program.opts(); + const { values } = parseArgs({ + args: argv.slice(2), + options: { + package: { + type: 'string', + short: 'p' + }, + outDir: { + type: 'string', + short: 'd' + } + }, + allowPositionals: true + }); + + const options: LicensesCommandArgs = { + package: values.package as string | undefined, + outDir: values.outDir as string | undefined + }; + let packagePath = path.resolve(workingDir, 'package.json'); if (options.package) {