Skip to content

Commit f25157a

Browse files
Fixed merge conflits
1 parent 0e8aac9 commit f25157a

38 files changed

Lines changed: 4272 additions & 2405 deletions

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 Contentstack
3+
Copyright (c) 2026 Contentstack
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

package-lock.json

Lines changed: 2368 additions & 1918 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/contentstack-audit/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ $ npm install -g @contentstack/cli-audit
1919
$ csdx COMMAND
2020
running command...
2121
$ csdx (--version|-v)
22-
@contentstack/cli-audit/1.16.1 darwin-arm64 node-v22.14.0
22+
@contentstack/cli-audit/2.0.0-beta.0 darwin-arm64 node-v22.14.0
2323
$ csdx --help [COMMAND]
2424
USAGE
2525
$ csdx COMMAND

packages/contentstack-audit/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/cli-audit",
3-
"version": "1.16.1",
3+
"version": "2.0.0-beta.0",
44
"description": "Contentstack audit plugin",
55
"author": "Contentstack CLI",
66
"homepage": "https://github.com/contentstack/cli",
@@ -18,11 +18,11 @@
1818
"/oclif.manifest.json"
1919
],
2020
"dependencies": {
21-
"@contentstack/cli-command": "~1.7.0",
21+
"@contentstack/cli-command": "~1.6.1",
2222
"@contentstack/cli-utilities": "~1.15.0",
2323
"@oclif/core": "^4.3.0",
2424
"@oclif/plugin-help": "^6.2.28",
25-
"@oclif/plugin-plugins": "^5.4.54",
25+
"@oclif/plugin-plugins": "^5.4.38",
2626
"chalk": "^4.1.2",
2727
"fast-csv": "^4.3.6",
2828
"fs-extra": "^11.3.0",

packages/contentstack-audit/src/audit-base-command.ts

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { v4 as uuid } from 'uuid';
55
import isEmpty from 'lodash/isEmpty';
66
import { join, resolve } from 'path';
77
import cloneDeep from 'lodash/cloneDeep';
8-
import { cliux, sanitizePath, TableFlags, TableHeader, log, configHandler } from '@contentstack/cli-utilities';
8+
import { cliux, sanitizePath, TableFlags, TableHeader, log, configHandler, CLIProgressManager, clearProgressModuleSetting } from '@contentstack/cli-utilities';
99
import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';
1010
import config from './config';
1111
import { print } from './util/log';
@@ -71,11 +71,23 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
7171
*/
7272
async start(command: CommandNames): Promise<boolean> {
7373
this.currentCommand = command;
74+
75+
// Set progress supported module and console logs setting BEFORE any log calls
76+
// This ensures the logger respects the setting when it's initialized
77+
const logConfig = configHandler.get('log') || {};
78+
// Default to false so progress bars are shown instead of console logs
79+
if (logConfig.showConsoleLogs === undefined) {
80+
configHandler.set('log.showConsoleLogs', false);
81+
}
82+
configHandler.set('log.progressSupportedModule', 'audit');
83+
7484
// Initialize audit context
7585
this.auditContext = this.createAuditContext();
7686
log.debug(`Starting audit command: ${command}`, this.auditContext);
7787
log.info(`Starting audit command: ${command}`, this.auditContext);
78-
88+
89+
// Initialize global summary for progress tracking
90+
CLIProgressManager.initializeGlobalSummary('AUDIT', '', 'Auditing content...');
7991

8092
await this.promptQueue();
8193
await this.createBackUp();
@@ -163,6 +175,12 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
163175
}
164176
}
165177

178+
// Print comprehensive summary at the end
179+
CLIProgressManager.printGlobalSummary();
180+
181+
// Clear progress module setting now that audit is complete
182+
clearProgressModuleSetting();
183+
166184
return (
167185
!isEmpty(missingCtRefs) ||
168186
!isEmpty(missingGfRefs) ||
@@ -222,47 +240,59 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
222240

223241
let dataModuleWise: Record<string, any> = await new ModuleDataReader(cloneDeep(constructorParam)).run();
224242
log.debug(`Data module wise: ${JSON.stringify(dataModuleWise)}`, this.auditContext);
243+
244+
// Extract logConfig and showConsoleLogs once before the loop to reuse throughout
245+
const logConfig = configHandler.get('log') || {};
246+
const showConsoleLogs = logConfig.showConsoleLogs ?? true;
247+
225248
for (const module of this.sharedConfig.flags.modules || this.sharedConfig.modules) {
226249
// Update audit context with current module
227250
this.auditContext = this.createAuditContext(module);
228251
log.debug(`Starting audit for module: ${module}`, this.auditContext);
229252
log.info(`Starting audit for module: ${module}`, this.auditContext);
230253

231-
print([
232-
{
233-
bold: true,
234-
color: 'whiteBright',
235-
message: this.$t(this.messages.AUDIT_START_SPINNER, { module }),
236-
},
237-
]);
254+
// Only show spinner message if console logs are enabled (compatible with line-by-line logs)
255+
if (showConsoleLogs) {
256+
print([
257+
{
258+
bold: true,
259+
color: 'whiteBright',
260+
message: this.$t(this.messages.AUDIT_START_SPINNER, { module }),
261+
},
262+
]);
263+
}
238264

239265
constructorParam['moduleName'] = module;
240266

241267
switch (module) {
242268
case 'assets':
243269
log.info('Executing assets audit', this.auditContext);
244-
missingEnvLocalesInAssets = await new Assets(cloneDeep(constructorParam)).run();
270+
const assetsTotalCount = dataModuleWise['assets']?.Total || 0;
271+
missingEnvLocalesInAssets = await new Assets(cloneDeep(constructorParam)).run(false, assetsTotalCount);
245272
await this.prepareReport(module, missingEnvLocalesInAssets);
246273
this.getAffectedData('assets', dataModuleWise['assets'], missingEnvLocalesInAssets);
247274
log.success(`Assets audit completed. Found ${Object.keys(missingEnvLocalesInAssets || {}).length} issues`, this.auditContext);
248275
break;
249276
case 'content-types':
250277
log.info('Executing content-types audit', this.auditContext);
251-
missingCtRefs = await new ContentType(cloneDeep(constructorParam)).run();
278+
const contentTypesTotalCount = dataModuleWise['content-types']?.Total || 0;
279+
missingCtRefs = await new ContentType(cloneDeep(constructorParam)).run(false, contentTypesTotalCount);
252280
await this.prepareReport(module, missingCtRefs);
253281
this.getAffectedData('content-types', dataModuleWise['content-types'], missingCtRefs);
254282
log.success(`Content-types audit completed. Found ${Object.keys(missingCtRefs || {}).length} issues`, this.auditContext);
255283
break;
256284
case 'global-fields':
257285
log.info('Executing global-fields audit', this.auditContext);
258-
missingGfRefs = await new GlobalField(cloneDeep(constructorParam)).run();
286+
const globalFieldsTotalCount = dataModuleWise['global-fields']?.Total || 0;
287+
missingGfRefs = await new GlobalField(cloneDeep(constructorParam)).run(false, globalFieldsTotalCount);
259288
await this.prepareReport(module, missingGfRefs);
260289
this.getAffectedData('global-fields', dataModuleWise['global-fields'], missingGfRefs);
261290
log.success(`Global-fields audit completed. Found ${Object.keys(missingGfRefs || {}).length} issues`, this.auditContext);
262291
break;
263292
case 'entries':
264293
log.info('Executing entries audit', this.auditContext);
265-
missingEntry = await new Entries(cloneDeep(constructorParam)).run();
294+
const entriesTotalCount = dataModuleWise['entries']?.Total || 0;
295+
missingEntry = await new Entries(cloneDeep(constructorParam)).run(entriesTotalCount);
266296
missingEntryRefs = missingEntry.missingEntryRefs ?? {};
267297
missingSelectFeild = missingEntry.missingSelectFeild ?? {};
268298
missingMandatoryFields = missingEntry.missingMandatoryFields ?? {};
@@ -286,27 +316,30 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
286316
break;
287317
case 'workflows':
288318
log.info('Executing workflows audit', this.auditContext);
319+
const workflowsTotalCount = dataModuleWise['workflows']?.Total || 0;
289320
missingCtRefsInWorkflow = await new Workflows({
290321
ctSchema,
291322
moduleName: module,
292323
config: this.sharedConfig,
293324
fix: this.currentCommand === 'cm:stacks:audit:fix',
294-
}).run();
325+
}).run(workflowsTotalCount);
295326
await this.prepareReport(module, missingCtRefsInWorkflow);
296327
this.getAffectedData('workflows', dataModuleWise['workflows'], missingCtRefsInWorkflow);
297328
log.success(`Workflows audit completed. Found ${Object.keys(missingCtRefsInWorkflow || {}).length} issues`, this.auditContext);
298329

299330
break;
300331
case 'extensions':
301332
log.info('Executing extensions audit', this.auditContext);
302-
missingCtRefsInExtensions = await new Extensions(cloneDeep(constructorParam)).run();
333+
const extensionsTotalCount = dataModuleWise['extensions']?.Total || 0;
334+
missingCtRefsInExtensions = await new Extensions(cloneDeep(constructorParam)).run(extensionsTotalCount);
303335
await this.prepareReport(module, missingCtRefsInExtensions);
304336
this.getAffectedData('extensions', dataModuleWise['extensions'], missingCtRefsInExtensions);
305337
log.success(`Extensions audit completed. Found ${Object.keys(missingCtRefsInExtensions || {}).length} issues`, this.auditContext);
306338
break;
307339
case 'custom-roles':
308340
log.info('Executing custom-roles audit', this.auditContext);
309-
missingRefInCustomRoles = await new CustomRoles(cloneDeep(constructorParam)).run();
341+
const customRolesTotalCount = dataModuleWise['custom-roles']?.Total || 0;
342+
missingRefInCustomRoles = await new CustomRoles(cloneDeep(constructorParam)).run(customRolesTotalCount);
310343
await this.prepareReport(module, missingRefInCustomRoles);
311344
this.getAffectedData('custom-roles', dataModuleWise['custom-roles'], missingRefInCustomRoles);
312345
log.success(`Custom-roles audit completed. Found ${Object.keys(missingRefInCustomRoles || {}).length} issues`, this.auditContext);
@@ -318,25 +351,29 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
318351
const data = this.getCtAndGfSchema();
319352
constructorParam.ctSchema = data.ctSchema;
320353
constructorParam.gfSchema = data.gfSchema;
321-
missingFieldRules = await new FieldRule(cloneDeep(constructorParam)).run();
354+
const fieldRulesTotalCount = dataModuleWise['content-types']?.Total || 0;
355+
missingFieldRules = await new FieldRule(cloneDeep(constructorParam)).run(fieldRulesTotalCount);
322356
await this.prepareReport(module, missingFieldRules);
323357
this.getAffectedData('field-rules', dataModuleWise['content-types'], missingFieldRules);
324358
log.success(`Field-rules audit completed. Found ${Object.keys(missingFieldRules || {}).length} issues`, this.auditContext);
325359
break;
326360
}
327361

328-
print([
329-
{
330-
bold: true,
331-
color: 'whiteBright',
332-
message: this.$t(this.messages.AUDIT_START_SPINNER, { module }),
333-
},
334-
{
335-
bold: true,
336-
message: ' done',
337-
color: 'whiteBright',
338-
},
339-
]);
362+
// Only show completion message if console logs are enabled
363+
if (showConsoleLogs) {
364+
print([
365+
{
366+
bold: true,
367+
color: 'whiteBright',
368+
message: this.$t(this.messages.AUDIT_START_SPINNER, { module }),
369+
},
370+
{
371+
bold: true,
372+
message: ' done',
373+
color: 'whiteBright',
374+
},
375+
]);
376+
}
340377
}
341378

342379
log.debug('Scan and fix process completed', this.auditContext);

packages/contentstack-audit/src/modules/assets.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { join, resolve } from 'path';
22
import { existsSync, readFileSync, writeFileSync } from 'fs';
33
import { FsUtility, sanitizePath, cliux, log } from '@contentstack/cli-utilities';
44
import {
5-
ConfigType,
65
ContentTypeStruct,
76
CtConstructorParam,
87
ModuleConstructorParam,
@@ -12,13 +11,13 @@ import auditConfig from '../config';
1211
import { $t, auditFixMsg, auditMsg, commonMsg } from '../messages';
1312
import values from 'lodash/values';
1413
import { keys } from 'lodash';
14+
import BaseClass from './base-class';
1515

16-
/* The `ContentType` class is responsible for scanning content types, looking for references, and
17-
generating a report in JSON and CSV formats. */
18-
export default class Assets {
16+
/* The `Assets` class is responsible for scanning assets, looking for missing environment/locale references,
17+
and generating a report in JSON and CSV formats. */
18+
export default class Assets extends BaseClass {
1919
protected fix: boolean;
2020
public fileName: string;
21-
public config: ConfigType;
2221
public folderPath: string;
2322
public currentUid!: string;
2423
public currentTitle!: string;
@@ -30,7 +29,7 @@ export default class Assets {
3029
public moduleName: keyof typeof auditConfig.moduleConfig;
3130

3231
constructor({ fix, config, moduleName }: ModuleConstructorParam & CtConstructorParam) {
33-
this.config = config;
32+
super({ config });
3433
this.fix = fix ?? false;
3534
this.moduleName = this.validateModules(moduleName!, this.config.moduleConfig);
3635
this.fileName = config.moduleConfig[this.moduleName].fileName;
@@ -52,25 +51,36 @@ export default class Assets {
5251
/**
5352
* The `run` function checks if a folder path exists, sets the schema based on the module name,
5453
* iterates over the schema and looks for references, and returns a list of missing references.
54+
* @param returnFixSchema - If true, returns the fixed schema instead of missing references
55+
* @param totalCount - Total number of assets to process (for progress tracking)
5556
* @returns the `missingEnvLocales` object.
5657
*/
57-
async run(returnFixSchema = false) {
58-
log.debug(`Starting ${this.moduleName} audit process`, this.config.auditContext);
59-
log.debug(`Data directory: ${this.folderPath}`, this.config.auditContext);
60-
log.debug(`Fix mode: ${this.fix}`, this.config.auditContext);
61-
62-
if (!existsSync(this.folderPath)) {
63-
log.debug(`Skipping ${this.moduleName} audit - path does not exist`, this.config.auditContext);
64-
log.warn(`Skipping ${this.moduleName} audit`, this.config.auditContext);
65-
cliux.print($t(auditMsg.NOT_VALID_PATH, { path: this.folderPath }), { color: 'yellow' });
66-
return returnFixSchema ? [] : {};
67-
}
58+
async run(returnFixSchema = false, totalCount?: number) {
59+
try {
60+
log.debug(`Starting ${this.moduleName} audit process`, this.config.auditContext);
61+
log.debug(`Data directory: ${this.folderPath}`, this.config.auditContext);
62+
log.debug(`Fix mode: ${this.fix}`, this.config.auditContext);
6863

69-
log.debug('Loading prerequisite data (locales and environments)', this.config.auditContext);
70-
await this.prerequisiteData();
64+
if (!existsSync(this.folderPath)) {
65+
log.debug(`Skipping ${this.moduleName} audit - path does not exist`, this.config.auditContext);
66+
log.warn(`Skipping ${this.moduleName} audit`, this.config.auditContext);
67+
cliux.print($t(auditMsg.NOT_VALID_PATH, { path: this.folderPath }), { color: 'yellow' });
68+
return returnFixSchema ? [] : {};
69+
}
70+
71+
// Load prerequisite data with loading spinner
72+
await this.withLoadingSpinner('ASSETS: Loading prerequisite data (locales and environments)...', async () => {
73+
await this.prerequisiteData();
74+
});
75+
76+
// Create progress manager if we have a total count
77+
if (totalCount && totalCount > 0) {
78+
const progress = this.createSimpleProgress(this.moduleName, totalCount);
79+
progress.updateStatus('Validating asset references...');
80+
}
7181

72-
log.debug('Starting asset Reference, Environment and Locale validation', this.config.auditContext);
73-
await this.lookForReference();
82+
log.debug('Starting asset Reference, Environment and Locale validation', this.config.auditContext);
83+
await this.lookForReference();
7484

7585
if (returnFixSchema) {
7686
log.debug(`Returning fixed schema with ${this.schema?.length || 0} items`, this.config.auditContext);
@@ -86,9 +96,15 @@ export default class Assets {
8696
}
8797
}
8898

89-
const totalIssues = Object.keys(this.missingEnvLocales).length;
90-
log.debug(`${this.moduleName} audit completed. Found ${totalIssues} assets with missing environment/locale references`, this.config.auditContext);
91-
return this.missingEnvLocales;
99+
const totalIssues = Object.keys(this.missingEnvLocales).length;
100+
log.debug(`${this.moduleName} audit completed. Found ${totalIssues} assets with missing environment/locale references`, this.config.auditContext);
101+
102+
this.completeProgress(true);
103+
return this.missingEnvLocales;
104+
} catch (error: any) {
105+
this.completeProgress(false, error?.message || 'Assets audit failed');
106+
throw error;
107+
}
92108
}
93109

94110
/**
@@ -227,6 +243,11 @@ export default class Assets {
227243
const remainingPublishDetails = this.assets[assetUid].publish_details?.length || 0;
228244
log.debug(`Asset ${assetUid} now has ${remainingPublishDetails} valid publish details`, this.config.auditContext);
229245

246+
// Track progress for each asset processed
247+
if (this.progressManager) {
248+
this.progressManager.tick(true, `asset: ${assetUid}`, null);
249+
}
250+
230251
if (this.fix) {
231252
log.debug(`Fixing asset ${assetUid}`, this.config.auditContext);
232253
log.info($t(auditFixMsg.ASSET_FIX, { uid: assetUid }), this.config.auditContext);

0 commit comments

Comments
 (0)