From cc9036c852cb6c43a60eec419a493bcdd1375e78 Mon Sep 17 00:00:00 2001 From: harshitha-cstk Date: Tue, 18 Aug 2026 13:20:30 +0530 Subject: [PATCH 1/5] fix(bulk-operations): surface errors on console when console logs are off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bulk command failures were silently swallowed when log.showConsoleLogs was disabled — handleAndLogError only reaches the console through the winston error transport, which is silenced in that mode, so the terminal showed nothing on failure (e.g. an invalid stack API key). Print a user-facing error line in BaseBulkCommand.catch() when console logs are off, reusing cliErrorHandler.classifyError so the message matches the friendly text written to the log file. Guarded to avoid double-printing when console logs are on. Ref: DX-10224 Co-Authored-By: Claude Opus 4.8 --- .../src/base-bulk-command.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/contentstack-bulk-operations/src/base-bulk-command.ts b/packages/contentstack-bulk-operations/src/base-bulk-command.ts index 3251e19ab..1eaa5560e 100644 --- a/packages/contentstack-bulk-operations/src/base-bulk-command.ts +++ b/packages/contentstack-bulk-operations/src/base-bulk-command.ts @@ -5,6 +5,8 @@ import { createLogContext, getLogPath, handleAndLogError, + cliErrorHandler, + cliux, FlagInput, getChalk, loadChalk, @@ -663,13 +665,19 @@ export abstract class BaseBulkCommand extends Command { * This includes errors during init, run, and other phases */ async catch(error: Error): Promise { - // Check if this is a DisplayedError (should be shown to user) - // if (error.name === 'DisplayedError') { - // process.exit(1); - // } - // For other errors, use the CLI utilities error handler handleAndLogError(error); + + // handleAndLogError only reaches the console when log.showConsoleLogs is enabled + // (the winston error transport is silenced otherwise), so a failure would leave the + // terminal completely silent when the user has console logs turned off. Print a + // user-facing error line here to fill that gap, guarded so we don't double-print when + // console logs are on and handleAndLogError already emitted the error. + const showConsoleLogs = Boolean(configHandler.get('log')?.showConsoleLogs); + if (!showConsoleLogs) { + const friendlyMessage = cliErrorHandler.classifyError(error)?.message || error?.message || 'Unknown error'; + cliux.print(`Error: ${friendlyMessage}`, { color: 'red' }); + } } abstract run(): Promise; From e93b7bdcf396a0b770e7146ee6b5160ed91d7cac Mon Sep 17 00:00:00 2001 From: harshitha-cstk Date: Tue, 18 Aug 2026 15:16:43 +0530 Subject: [PATCH 2/5] update error msg --- .../contentstack-bulk-operations/src/base-bulk-command.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contentstack-bulk-operations/src/base-bulk-command.ts b/packages/contentstack-bulk-operations/src/base-bulk-command.ts index 1eaa5560e..594f48819 100644 --- a/packages/contentstack-bulk-operations/src/base-bulk-command.ts +++ b/packages/contentstack-bulk-operations/src/base-bulk-command.ts @@ -675,8 +675,8 @@ export abstract class BaseBulkCommand extends Command { // console logs are on and handleAndLogError already emitted the error. const showConsoleLogs = Boolean(configHandler.get('log')?.showConsoleLogs); if (!showConsoleLogs) { - const friendlyMessage = cliErrorHandler.classifyError(error)?.message || error?.message || 'Unknown error'; - cliux.print(`Error: ${friendlyMessage}`, { color: 'red' }); + const errorMessage = cliErrorHandler.classifyError(error)?.message || error?.message || 'Unknown error'; + cliux.print(`Error: ${errorMessage}`, { color: 'red' }); } } From db16fe2034c9146e241284d822ebb43ea5ac19a8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 18 Aug 2026 20:05:36 +0530 Subject: [PATCH 3/5] fix: source console visibility from the console-log policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 2 of the logger-scoping change; step 1 landed in the cli repo (console-policy module, logger filter, progress-manager self-seeding, console-policy init hook). Console visibility is now a process-wide policy resolved once, before any command runs, from static inputs: CS_CLI_CONSOLE_LOGS, then user config (on `true` only), then the plugin's csdxConfig.showConsoleLogs, then files-only. Plugins no longer decide it, thread it, or persist it. - Remove the log.progressSupportedModule writes and the clearProgressModuleSetting calls; nothing reads that key any more. - Drop the seeding boilerplate at all 17 progress-manager call sites — CLIProgressManager reads the policy itself, so createSimple/ createNested no longer take showConsoleLogs. - Reduce the local withLoadingSpinner wrappers to a plain delegation; the manager now skips the spinner in console-log mode internally. - Re-source the sites that choose their own content (audit's banners, the `if (!showConsoleLogs) cliux.print` compensation in export, import and import-setup) to isConsoleLogEnabled(). - Stop audit persisting `log.showConsoleLogs: false` into the user's config. That value silently outranked every plugin declaration; step 1 makes a persisted `false` read as "no opinion", and the two halves have to ship together. - Declare csdxConfig.showConsoleLogs on query-export and cli-tsgen, whose only user-facing output is log.*. Console logs and the progress UI are mutually exclusive: opting in to console logs turns the bars, spinners and headers off, so the two consumers of the terminal can never interleave. --- .talismanrc | 4 ++ .../src/export/base.ts | 6 +- .../src/export/spaces.ts | 6 +- .../src/import/base.ts | 6 +- .../src/import/spaces.ts | 6 +- .../test/unit/export/base.test.ts | 8 +-- .../test/unit/import/base.test.ts | 6 +- .../src/audit-base-command.ts | 20 +----- .../contentstack-audit/src/modules/assets.ts | 5 +- .../src/modules/base-class.ts | 17 +---- .../test/unit/audit-base-command.test.ts | 27 ++++---- .../test/unit/modules/base-class.test.ts | 29 ++++---- .../src/base-bulk-command.ts | 16 +---- .../test/unit/base-bulk-command.test.ts | 14 +--- packages/contentstack-cli-tsgen/package.json | 1 + .../src/commands/cm/stacks/clone.ts | 20 ++---- .../test/commands/cm/stacks/clone.test.ts | 12 +--- .../src/commands/cm/export-to-csv.ts | 5 ++ .../src/utils/teams-export.ts | 1 + .../src/commands/cm/stacks/export.ts | 10 +-- .../src/export/modules/base-class.ts | 16 +---- .../src/utils/export-config-handler.ts | 4 -- .../src/commands/cm/stacks/import-setup.ts | 10 +-- .../src/import/modules/base-setup.ts | 17 +---- .../src/utils/import-config-handler.ts | 4 -- .../test/unit/modules/base-setup.test.ts | 66 +++++++------------ .../src/commands/cm/stacks/import.ts | 13 +--- .../src/import/modules/base-class.ts | 16 +---- .../src/utils/import-config-handler.ts | 4 -- .../contentstack-query-export/package.json | 1 + .../src/utils/personalization-api-adapter.ts | 17 +---- .../src/utils/variant-api-adapter.ts | 16 +---- 32 files changed, 127 insertions(+), 276 deletions(-) diff --git a/.talismanrc b/.talismanrc index 7efa0e61f..0315195c9 100644 --- a/.talismanrc +++ b/.talismanrc @@ -39,4 +39,8 @@ fileignoreconfig: checksum: a64a4d396eddd936a63b799eff58c5c6660b5dcaa3a310fd8b09a027932f1789 - filename: packages/contentstack-migration/README.md checksum: e96006c1a948f766c88ae972b29582fa58eaf8184606bf011eebddc5a06cd7b6 +- filename: packages/contentstack-asset-management/test/unit/import/base.test.ts + checksum: 5985575816f6e298c6c4e8169389d960b2a9ab3d43336e8c9e9b25302ba10353 +- filename: packages/contentstack-asset-management/test/unit/export/base.test.ts + checksum: 68711c9a31e14982195efdb352903fe7782085a6a56a10b963cd0fd8db7ec668 version: "" diff --git a/packages/contentstack-asset-management/src/export/base.ts b/packages/contentstack-asset-management/src/export/base.ts index 13e4016e1..43dd2ac91 100644 --- a/packages/contentstack-asset-management/src/export/base.ts +++ b/packages/contentstack-asset-management/src/export/base.ts @@ -1,6 +1,6 @@ import { resolve as pResolve } from 'node:path'; import { writeFile } from 'node:fs/promises'; -import { FsUtility, log, CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { FsUtility, log, CLIProgressManager } from '@contentstack/cli-utilities'; import type { CSAssetsAPIConfig } from '../types/cs-assets-api'; import type { ExportContext } from '../types/export-types'; @@ -48,9 +48,7 @@ export class CSAssetsExportAdapter extends CSAssetsAdapter { this.progressManager = this.parentProgressManager; return this.parentProgressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } diff --git a/packages/contentstack-asset-management/src/export/spaces.ts b/packages/contentstack-asset-management/src/export/spaces.ts index df1bc6f57..9e831e25a 100644 --- a/packages/contentstack-asset-management/src/export/spaces.ts +++ b/packages/contentstack-asset-management/src/export/spaces.ts @@ -1,6 +1,6 @@ import { resolve as pResolve } from 'node:path'; import { mkdir } from 'node:fs/promises'; -import { log, CLIProgressManager, configHandler, handleAndLogError } from '@contentstack/cli-utilities'; +import { log, CLIProgressManager, handleAndLogError } from '@contentstack/cli-utilities'; import type { AssetManagementExportOptions, CSAssetsAPIConfig } from '../types/cs-assets-api'; import type { ExportContext } from '../types/export-types'; @@ -193,9 +193,7 @@ export class ExportSpaces { this.progressManager = this.parentProgressManager; return this.parentProgressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(CS_ASSETS_MAIN_PROCESS_NAME, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(CS_ASSETS_MAIN_PROCESS_NAME); return this.progressManager; } } diff --git a/packages/contentstack-asset-management/src/import/base.ts b/packages/contentstack-asset-management/src/import/base.ts index 7bb8e9914..6aa10cb68 100644 --- a/packages/contentstack-asset-management/src/import/base.ts +++ b/packages/contentstack-asset-management/src/import/base.ts @@ -1,5 +1,5 @@ import { resolve as pResolve } from 'node:path'; -import { CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { CLIProgressManager } from '@contentstack/cli-utilities'; import type { CSAssetsAPIConfig, ImportContext } from '../types/cs-assets-api'; import { CSAssetsAdapter } from '../utils/cs-assets-api-adapter'; @@ -46,9 +46,7 @@ export class CSAssetsImportAdapter extends CSAssetsAdapter { this.progressManager = this.parentProgressManager; return this.parentProgressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } diff --git a/packages/contentstack-asset-management/src/import/spaces.ts b/packages/contentstack-asset-management/src/import/spaces.ts index faec9d13d..e2ac54d45 100644 --- a/packages/contentstack-asset-management/src/import/spaces.ts +++ b/packages/contentstack-asset-management/src/import/spaces.ts @@ -1,7 +1,7 @@ import { join, resolve as pResolve } from 'node:path'; import { mkdirSync, readdirSync, statSync } from 'node:fs'; import { writeFile } from 'node:fs/promises'; -import { log, CLIProgressManager, configHandler, handleAndLogError } from '@contentstack/cli-utilities'; +import { log, CLIProgressManager, handleAndLogError } from '@contentstack/cli-utilities'; import type { CSAssetsAPIConfig, @@ -231,9 +231,7 @@ export class ImportSpaces { this.progressManager = this.parentProgressManager; return this.parentProgressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(CS_ASSETS_MAIN_PROCESS_NAME, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(CS_ASSETS_MAIN_PROCESS_NAME); return this.progressManager; } } diff --git a/packages/contentstack-asset-management/test/unit/export/base.test.ts b/packages/contentstack-asset-management/test/unit/export/base.test.ts index 22f41d67e..dabae6c36 100644 --- a/packages/contentstack-asset-management/test/unit/export/base.test.ts +++ b/packages/contentstack-asset-management/test/unit/export/base.test.ts @@ -105,8 +105,7 @@ describe('CSAssetsExportAdapter (base)', () => { }); describe('createNestedProgress', () => { - it('should create a new CLIProgressManager with the given name and showConsoleLogs flag', () => { - sinon.stub(configHandler, 'get').returns({ showConsoleLogs: true }); + it('should create a new CLIProgressManager with the given name', () => { const fakeProgress = { tick: sinon.stub() } as any; const createNestedStub = sinon.stub(CLIProgressManager, 'createNested').returns(fakeProgress); @@ -126,15 +125,14 @@ describe('CSAssetsExportAdapter (base)', () => { expect(result).to.equal(fakeParent); }); - it('should default showConsoleLogs to false when log config is missing', () => { - sinon.stub(configHandler, 'get').returns(null); + it('should not pass a showConsoleLogs argument — the manager resolves the policy itself', () => { const fakeProgress = { tick: sinon.stub() } as any; const createNestedStub = sinon.stub(CLIProgressManager, 'createNested').returns(fakeProgress); const adapter = new TestAdapter(apiConfig, exportContext); adapter.callCreateNestedProgress('test'); - expect(createNestedStub.firstCall.args[1]).to.be.false; + expect(createNestedStub.firstCall.args).to.deep.equal(['test']); }); }); diff --git a/packages/contentstack-asset-management/test/unit/import/base.test.ts b/packages/contentstack-asset-management/test/unit/import/base.test.ts index eee4c3c9b..86db72a3b 100644 --- a/packages/contentstack-asset-management/test/unit/import/base.test.ts +++ b/packages/contentstack-asset-management/test/unit/import/base.test.ts @@ -75,7 +75,6 @@ describe('CSAssetsImportAdapter (base)', () => { describe('createNestedProgress', () => { it('creates a CLIProgressManager when no parent is set', () => { - sinon.stub(configHandler, 'get').returns({ showConsoleLogs: true }); const fakeProgress = { tick: sinon.stub() } as any; const createNestedStub = sinon.stub(CLIProgressManager, 'createNested').returns(fakeProgress); const adapter = new TestImportAdapter(apiConfig, importContext); @@ -92,13 +91,12 @@ describe('CSAssetsImportAdapter (base)', () => { expect(result).to.equal(fakeParent); }); - it('defaults showConsoleLogs to false when log config is missing', () => { - sinon.stub(configHandler, 'get').returns(null); + it('passes no showConsoleLogs argument — the manager resolves the policy itself', () => { const fakeProgress = { tick: sinon.stub() } as any; const createNestedStub = sinon.stub(CLIProgressManager, 'createNested').returns(fakeProgress); const adapter = new TestImportAdapter(apiConfig, importContext); adapter.callCreateNestedProgress('test'); - expect(createNestedStub.firstCall.args[1]).to.be.false; + expect(createNestedStub.firstCall.args).to.deep.equal(['test']); }); }); diff --git a/packages/contentstack-audit/src/audit-base-command.ts b/packages/contentstack-audit/src/audit-base-command.ts index e099280c7..ffca7ebae 100644 --- a/packages/contentstack-audit/src/audit-base-command.ts +++ b/packages/contentstack-audit/src/audit-base-command.ts @@ -10,9 +10,8 @@ import { TableFlags, TableHeader, log, - configHandler, + isConsoleLogEnabled, CLIProgressManager, - clearProgressModuleSetting, readContentTypeSchemas, readGlobalFieldSchemas, generateUid, @@ -73,15 +72,6 @@ export abstract class AuditBaseCommand extends BaseCommand { this.currentCommand = command; - // Set progress supported module and console logs setting BEFORE any log calls - // This ensures the logger respects the setting when it's initialized - const logConfig = configHandler.get('log') || {}; - // Default to false so progress bars are shown instead of console logs - if (logConfig.showConsoleLogs === undefined) { - configHandler.set('log.showConsoleLogs', false); - } - configHandler.set('log.progressSupportedModule', 'audit'); - // Initialize global summary for progress tracking CLIProgressManager.initializeGlobalSummary('AUDIT', '', 'Auditing content...'); @@ -190,9 +180,6 @@ export abstract class AuditBaseCommand extends BaseCommand = await new ModuleDataReader(cloneDeep(constructorParam)).run(); log.debug(`Data module wise: ${JSON.stringify(dataModuleWise)}`, this.auditContext); - // Extract logConfig and showConsoleLogs once before the loop to reuse throughout - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; + // Resolve the console-log policy once before the loop to reuse throughout + const showConsoleLogs = isConsoleLogEnabled(); for (const module of this.sharedConfig.flags.modules || this.sharedConfig.modules) { // Update audit context with current module diff --git a/packages/contentstack-audit/src/modules/assets.ts b/packages/contentstack-audit/src/modules/assets.ts index 6d574fa4b..4468e95aa 100644 --- a/packages/contentstack-audit/src/modules/assets.ts +++ b/packages/contentstack-audit/src/modules/assets.ts @@ -1,6 +1,6 @@ import { join, resolve } from 'path'; import { existsSync, readFileSync, readdirSync, writeFileSync } from 'fs'; -import { FsUtility, sanitizePath, cliux, log, configHandler } from '@contentstack/cli-utilities'; +import { FsUtility, sanitizePath, cliux, log, isConsoleLogEnabled } from '@contentstack/cli-utilities'; import { ContentTypeStruct, CtConstructorParam, ModuleConstructorParam, EntryStruct } from '../types'; import auditConfig from '../config'; import { $t, auditFixMsg, auditMsg, commonMsg } from '../messages'; @@ -298,8 +298,7 @@ export default class Assets extends BaseClass { */ async lookForReference(): Promise { log.debug('Starting asset reference validation', this.config.auditContext); - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; + const showConsoleLogs = isConsoleLogEnabled(); if (!this.resolvedBasePaths.length) { this.resolvedBasePaths = this.resolveAssetBasePaths(); diff --git a/packages/contentstack-audit/src/modules/base-class.ts b/packages/contentstack-audit/src/modules/base-class.ts index d1a8329cf..17c2afc13 100644 --- a/packages/contentstack-audit/src/modules/base-class.ts +++ b/packages/contentstack-audit/src/modules/base-class.ts @@ -1,4 +1,4 @@ -import { CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { CLIProgressManager } from '@contentstack/cli-utilities'; import { ConfigType, ModuleConstructorParam } from '../types'; export default abstract class BaseClass { @@ -15,9 +15,7 @@ export default abstract class BaseClass { */ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createSimple(moduleName, total, showConsoleLogs); + this.progressManager = CLIProgressManager.createSimple(moduleName, total); return this.progressManager; } @@ -26,9 +24,7 @@ export default abstract class BaseClass { */ protected createNestedProgress(moduleName: string): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } @@ -44,13 +40,6 @@ export default abstract class BaseClass { * Execute action with loading spinner (if console logs are disabled) */ protected async withLoadingSpinner(message: string, action: () => Promise): Promise { - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - - if (showConsoleLogs) { - // If console logs are enabled, don't show spinner, just execute the action - return await action(); - } return await CLIProgressManager.withLoadingSpinner(message, action); } } diff --git a/packages/contentstack-audit/test/unit/audit-base-command.test.ts b/packages/contentstack-audit/test/unit/audit-base-command.test.ts index a74e51768..d4e6c94e3 100644 --- a/packages/contentstack-audit/test/unit/audit-base-command.test.ts +++ b/packages/contentstack-audit/test/unit/audit-base-command.test.ts @@ -5,7 +5,11 @@ import { resolve } from 'path'; import { fancy } from 'fancy-test'; import { PassThrough } from 'stream'; import { expect } from 'chai'; -import { ux, cliux, CLIProgressManager, configHandler, clearProgressModuleSetting } from '@contentstack/cli-utilities'; +import { ux, cliux, CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { + setConsoleLogPolicy, + resetConsoleLogPolicy, +} from '@contentstack/cli-utilities/lib/logger/console-policy'; import { AuditBaseCommand } from '../../src/audit-base-command'; import { @@ -437,7 +441,6 @@ describe('AuditBaseCommand class', () => { try { CLIProgressManager.clearGlobalSummary(); - clearProgressModuleSetting(); } catch (e) { // Ignore } @@ -529,10 +532,12 @@ describe('AuditBaseCommand class', () => { // Import print function from the correct path const logModule = require('../../src/util/log'); printSpy = sinon.spy(logModule, 'print'); - configHandlerGetStub = sinon.stub(configHandler, 'get'); + configHandlerGetStub = sinon.stub(configHandler, 'get').returns({}); }); afterEach(() => { + resetConsoleLogPolicy(); + try { // Clear global summary first CLIProgressManager.clearGlobalSummary(); @@ -584,15 +589,15 @@ describe('AuditBaseCommand class', () => { .stub(FieldRule.prototype, 'run', () => ({ fr_1: {} })) .stub(AuditBaseCommand.prototype, 'showOutputOnScreenWorkflowsAndExtension', () => {}) .stub(fs, 'createWriteStream', () => new PassThrough()) - .it('should hide spinner messages when showConsoleLogs is false', async function() { + .it('should hide spinner messages when the console-log policy is off', async function() { this.timeout(5000); // Set timeout to 5 seconds if (!configHandlerGetStub || !printSpy) { throw new Error('Spies not initialized'); } - configHandlerGetStub.returns({ showConsoleLogs: false }); + setConsoleLogPolicy(false); await AuditCMD.run(['--data-dir', resolve(__dirname, 'mock', 'contents')]); - - // Print should not be called for spinner messages when showConsoleLogs is false + + // Print should not be called for spinner messages when the console-log policy is off const printCalls = printSpy.getCalls(); const spinnerCalls = printCalls.filter((call: any) => call.args[0]?.[0]?.message?.includes('scanning') @@ -620,15 +625,15 @@ describe('AuditBaseCommand class', () => { .stub(FieldRule.prototype, 'run', () => ({ fr_1: {} })) .stub(AuditBaseCommand.prototype, 'showOutputOnScreenWorkflowsAndExtension', () => {}) .stub(fs, 'createWriteStream', () => new PassThrough()) - .it('should show spinner messages when showConsoleLogs is true', async function() { + .it('should show spinner messages when the console-log policy is on', async function() { this.timeout(5000); // Set timeout to 5 seconds if (!configHandlerGetStub || !printSpy) { throw new Error('Spies not initialized'); } - configHandlerGetStub.returns({ showConsoleLogs: true }); + setConsoleLogPolicy(true); await AuditCMD.run(['--data-dir', resolve(__dirname, 'mock', 'contents')]); - - // Print should be called for spinner messages when showConsoleLogs is true + + // Print should be called for spinner messages when the console-log policy is on const printCalls = printSpy.getCalls(); const spinnerCalls = printCalls.filter((call: any) => call.args[0]?.[0]?.message?.includes('scanning') diff --git a/packages/contentstack-audit/test/unit/modules/base-class.test.ts b/packages/contentstack-audit/test/unit/modules/base-class.test.ts index dc0b5563c..c952fd478 100644 --- a/packages/contentstack-audit/test/unit/modules/base-class.test.ts +++ b/packages/contentstack-audit/test/unit/modules/base-class.test.ts @@ -3,6 +3,10 @@ import { fancy } from 'fancy-test'; import sinon from 'sinon'; import { resolve } from 'node:path'; import { CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { + setConsoleLogPolicy, + resetConsoleLogPolicy, +} from '@contentstack/cli-utilities/lib/logger/console-policy'; import config from '../../../src/config'; import BaseClass from '../../../src/modules/base-class'; @@ -99,6 +103,8 @@ describe('BaseClass Progress Manager', () => { }); afterEach(() => { + resetConsoleLogPolicy(); + try { // Complete any running progress managers if (testInstance && testInstance['progressManager']) { @@ -165,12 +171,12 @@ describe('BaseClass Progress Manager', () => { } }); - fancy.it('should respect showConsoleLogs setting from config', () => { - configHandler.set('log.showConsoleLogs', true); + fancy.it('should create a manager under either console-log policy', () => { + setConsoleLogPolicy(true); const progress1 = testInstance.testCreateSimpleProgress('test-module', 100); expect(progress1).to.be.instanceOf(CLIProgressManager); - configHandler.set('log.showConsoleLogs', false); + setConsoleLogPolicy(false); const progress2 = testInstance.testCreateSimpleProgress('test-module-2', 100); expect(progress2).to.be.instanceOf(CLIProgressManager); @@ -184,8 +190,7 @@ describe('BaseClass Progress Manager', () => { } }); - fancy.it('should default showConsoleLogs to false when not set', () => { - configHandler.set('log', {}); + fancy.it('should create a manager under the default (files-only) policy', () => { const progress = testInstance.testCreateSimpleProgress('test-module', 100); expect(progress).to.be.instanceOf(CLIProgressManager); @@ -215,8 +220,8 @@ describe('BaseClass Progress Manager', () => { } }); - fancy.it('should respect showConsoleLogs setting from config', () => { - configHandler.set('log.showConsoleLogs', false); + fancy.it('should create a nested manager when the console-log policy is off', () => { + setConsoleLogPolicy(false); const progress = testInstance.testCreateNestedProgress('test-module'); expect(progress).to.be.instanceOf(CLIProgressManager); @@ -231,8 +236,8 @@ describe('BaseClass Progress Manager', () => { }); describe('withLoadingSpinner', () => { - fancy.it('should execute action directly when showConsoleLogs is true', async () => { - configHandler.set('log.showConsoleLogs', true); + fancy.it('should not start a spinner when the console-log policy is on', async () => { + setConsoleLogPolicy(true); const action = sinon.stub().resolves('result'); const result = await testInstance.testWithLoadingSpinner('Loading...', action); @@ -242,8 +247,8 @@ describe('BaseClass Progress Manager', () => { expect(mockOra.called).to.be.false; }); - fancy.it('should use spinner when showConsoleLogs is false', async () => { - configHandler.set('log.showConsoleLogs', false); + fancy.it('should use the spinner when the console-log policy is off', async () => { + setConsoleLogPolicy(false); const action = sinon.stub().resolves('result'); const result = await testInstance.testWithLoadingSpinner('Loading...', action); @@ -253,7 +258,7 @@ describe('BaseClass Progress Manager', () => { }); fancy.it('should handle errors in action', async () => { - configHandler.set('log.showConsoleLogs', true); + setConsoleLogPolicy(true); const error = new Error('Test error'); const action = sinon.stub().rejects(error); diff --git a/packages/contentstack-bulk-operations/src/base-bulk-command.ts b/packages/contentstack-bulk-operations/src/base-bulk-command.ts index 3251e19ab..ecef6bbd2 100644 --- a/packages/contentstack-bulk-operations/src/base-bulk-command.ts +++ b/packages/contentstack-bulk-operations/src/base-bulk-command.ts @@ -8,9 +8,7 @@ import { FlagInput, getChalk, loadChalk, - configHandler, CLIProgressManager, - clearProgressModuleSetting, } from '@contentstack/cli-utilities'; import config from './config'; @@ -306,13 +304,6 @@ export abstract class BaseBulkCommand extends Command { * Build operation configuration */ protected async buildConfiguration(flags: any): Promise { - // Enable the progress-bar UI + suppress the timestamped console logs for the whole command. - // Set here (command lifecycle, runs before the first log call) rather than in the pure - // buildConfig() util so it isn't triggered by direct/unit-test callers of buildConfig. - // Cleared on every exit path: finally() on normal runs, and finalizeProgressSummary() before - // the validation exit(1) below and the revert/retry exit(0). - configHandler.set('log.progressSupportedModule', 'bulk-operations'); - this.bulkOperationConfig = buildConfig(flags); // buildConfig splits comma-separated oclif `multiple` values; mirror onto flags so @@ -463,7 +454,6 @@ export abstract class BaseBulkCommand extends Command { * success. The printed status URL remains the source of truth for the real publish outcome. */ protected recordModuleSummary(result: BulkOperationResult, submittedCount: number): void { - const showConsoleLogs = Boolean(configHandler.get('log')?.showConsoleLogs); const publishMode = this.bulkOperationConfig?.publishMode || PublishMode.BULK; const total = result?.total || submittedCount || 0; @@ -482,7 +472,7 @@ export abstract class BaseBulkCommand extends Command { failed = Math.min(Math.max(failed, 0), total); success = Math.min(Math.max(success, 0), total - failed); - const progress = CLIProgressManager.createSimple(this.resourceType, total, showConsoleLogs); + const progress = CLIProgressManager.createSimple(this.resourceType, total); for (let i = 0; i < success; i++) progress.tick(true); for (let i = 0; i < failed; i++) progress.tick(false); progress.complete(failed === 0); @@ -491,13 +481,11 @@ export abstract class BaseBulkCommand extends Command { /** * Print the run-level summary once and clear progress state. Idempotent: subclasses call * finally() explicitly AND oclif calls it again, so clearing the summary after printing makes - * the second invocation a no-op. Also clears the progress-module flag so it never leaks into - * a later command in the same process (mirrors export/import/clone). + * the second invocation a no-op. */ protected finalizeProgressSummary(): void { CLIProgressManager.printGlobalSummary(); CLIProgressManager.clearGlobalSummary(); - clearProgressModuleSetting(); } /** diff --git a/packages/contentstack-bulk-operations/test/unit/base-bulk-command.test.ts b/packages/contentstack-bulk-operations/test/unit/base-bulk-command.test.ts index dc6c6e83f..4db00505a 100644 --- a/packages/contentstack-bulk-operations/test/unit/base-bulk-command.test.ts +++ b/packages/contentstack-bulk-operations/test/unit/base-bulk-command.test.ts @@ -532,9 +532,7 @@ describe('BaseBulkCommand', () => { sandbox.stub(cliUtils.CLIProgressManager, 'initializeGlobalSummary').returns({} as any); sandbox.stub(cliUtils.CLIProgressManager, 'printGlobalSummary').callsFake(() => {}); sandbox.stub(cliUtils.CLIProgressManager, 'clearGlobalSummary').callsFake(() => {}); - // clearProgressModuleSetting is a frozen re-export (not stubbable); let the real one run - // and drive/assert it through configHandler instead. - sandbox.stub(cliUtils.configHandler, 'get').returns({ showConsoleLogs: false }); + sandbox.stub(cliUtils.configHandler, 'get').returns({}); sandbox.stub(cliUtils.configHandler, 'set').callsFake(() => {}); }); @@ -637,16 +635,6 @@ describe('BaseBulkCommand', () => { expect(cliUtils.CLIProgressManager.printGlobalSummary.calledOnce).to.be.true; expect(cliUtils.CLIProgressManager.clearGlobalSummary.calledOnce).to.be.true; }); - - it('clears the persisted progress-module flag', () => { - // Simulate the flag being set, then verify clearProgressModuleSetting removes it. - cliUtils.configHandler.get.returns({ progressSupportedModule: 'bulk-operations', showConsoleLogs: false }); - - (command as any).finalizeProgressSummary(); - - expect(cliUtils.configHandler.set.calledWith('log', sinon.match((v: any) => !('progressSupportedModule' in v)))).to - .be.true; - }); }); }); diff --git a/packages/contentstack-cli-tsgen/package.json b/packages/contentstack-cli-tsgen/package.json index 21a6e4d04..62c419773 100644 --- a/packages/contentstack-cli-tsgen/package.json +++ b/packages/contentstack-cli-tsgen/package.json @@ -67,6 +67,7 @@ "format": "eslint \"src/**/*.ts\" --fix" }, "csdxConfig": { + "showConsoleLogs": true, "shortCommandName": { "tsgen": "TSGEN" } diff --git a/packages/contentstack-clone/src/commands/cm/stacks/clone.ts b/packages/contentstack-clone/src/commands/cm/stacks/clone.ts index 2e826f738..b7e9ac2ea 100644 --- a/packages/contentstack-clone/src/commands/cm/stacks/clone.ts +++ b/packages/contentstack-clone/src/commands/cm/stacks/clone.ts @@ -1,5 +1,6 @@ import { Command } from '@contentstack/cli-command'; import { + cliux, configHandler, flags, isAuthenticated, @@ -159,10 +160,6 @@ Use this plugin to automate the process of cloning a stack in few steps. async run(): Promise { try { const self = this; - // Clear any stale progressSupportedModule persisted from a previous run so that - // auth/pre-flight errors always reach the console regardless of showConsoleLogs setting. - // It will be re-set inside handleClone() once authentication passes. - configHandler.set('log.progressSupportedModule', null); const { flags: cloneCommandFlags } = await self.parse(StackCloneCommand); const { yes, @@ -181,7 +178,6 @@ Use this plugin to automate the process of cloning a stack in few steps. } = cloneCommandFlags; const handleClone = async (): Promise => { - configHandler.set('log.progressSupportedModule', 'clone'); const listOfTokens = configHandler.get('tokens'); const authenticationMethod = this.determineAuthenticationMethod( sourceManagementTokenAlias, @@ -234,20 +230,18 @@ Use this plugin to automate the process of cloning a stack in few steps. config.source_stack = listOfTokens[sourceManagementTokenAlias].apiKey; log.debug(`Using source token alias: ${sourceManagementTokenAlias}`, cloneContext); } else if (sourceManagementTokenAlias) { - log.warn( - `Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`, - cloneContext, - ); + const msg = `Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`; + log.warn(msg, cloneContext); + cliux.print(msg, { color: 'yellow' }); } if (destinationManagementTokenAlias && listOfTokens?.[destinationManagementTokenAlias]) { config.destination_alias = destinationManagementTokenAlias; config.target_stack = listOfTokens[destinationManagementTokenAlias].apiKey; log.debug(`Using destination token alias: ${destinationManagementTokenAlias}`, cloneContext); } else if (destinationManagementTokenAlias) { - log.warn( - `Provided destination token alias (${destinationManagementTokenAlias}) not found in your config.!`, - cloneContext, - ); + const msg = `Provided destination token alias (${destinationManagementTokenAlias}) not found in your config.!`; + log.warn(msg, cloneContext); + cliux.print(msg, { color: 'yellow' }); } if (importWebhookStatus) { config.importWebhookStatus = importWebhookStatus; diff --git a/packages/contentstack-clone/test/commands/cm/stacks/clone.test.ts b/packages/contentstack-clone/test/commands/cm/stacks/clone.test.ts index b62603d7a..d39109890 100644 --- a/packages/contentstack-clone/test/commands/cm/stacks/clone.test.ts +++ b/packages/contentstack-clone/test/commands/cm/stacks/clone.test.ts @@ -543,7 +543,7 @@ describe('StackCloneCommand', () => { flags: mockFlags, }); const configHandlerGetStub = sandbox.stub(cliUtilities.configHandler, 'get').returns(undefined); - const configHandlerSetStub = sandbox.stub(cliUtilities.configHandler, 'set'); + sandbox.stub(cliUtilities.configHandler, 'set'); const logStub = { error: sandbox.stub(), warn: sandbox.stub(), debug: sandbox.stub(), info: sandbox.stub() }; sandbox.stub(cliUtilities, 'log').value(logStub); // exit(1) throws inside run()'s own try/catch which swallows it — stub cleanUp to @@ -557,8 +557,6 @@ describe('StackCloneCommand', () => { expect(exitStub.calledOnce).to.be.true; expect(logStub.error.called).to.be.true; expect(logStub.error.firstCall.args[0]).to.include('Please login'); - // progressSupportedModule must NOT be set when auth fails - expect(configHandlerSetStub.calledWith('log.progressSupportedModule', 'clone')).to.be.false; }); it('should exit when management token aliases provided but not authenticated and branches provided', async () => { @@ -571,7 +569,7 @@ describe('StackCloneCommand', () => { }, }); const configHandlerGetStub = sandbox.stub(cliUtilities.configHandler, 'get').returns(undefined); - const configHandlerSetStub = sandbox.stub(cliUtilities.configHandler, 'set'); + sandbox.stub(cliUtilities.configHandler, 'set'); const logStub = { error: sandbox.stub(), warn: sandbox.stub(), debug: sandbox.stub(), info: sandbox.stub() }; sandbox.stub(cliUtilities, 'log').value(logStub); const cleanUpStub = sandbox.stub(command, 'cleanUp').resolves(); @@ -583,8 +581,6 @@ describe('StackCloneCommand', () => { expect(exitStub.calledOnce).to.be.true; expect(logStub.error.called).to.be.true; expect(logStub.error.firstCall.args[0]).to.include('Log in'); - // progressSupportedModule must NOT be set when auth fails - expect(configHandlerSetStub.calledWith('log.progressSupportedModule', 'clone')).to.be.false; }); it('should handle run error and cleanup', async () => { @@ -794,7 +790,7 @@ describe('StackCloneCommand', () => { 'destination-management-token-alias': 'dest-alias', }, }); - const configHandlerSetStub = sandbox.stub(cliUtilities.configHandler, 'set'); + sandbox.stub(cliUtilities.configHandler, 'set'); const configHandlerStub = sandbox.stub(cliUtilities.configHandler, 'get'); // Stub authorisationType to 'OAUTH' to make isAuthenticated() return true configHandlerStub.callsFake((key: string) => { @@ -836,8 +832,6 @@ describe('StackCloneCommand', () => { expect(cloneHandlerExecuteStub.calledOnce).to.be.true; // Verify all config flags were set expect(logStub.debug.called).to.be.true; - // progressSupportedModule must be set inside handleClone (after auth passes) - expect(configHandlerSetStub.calledWith('log.progressSupportedModule', 'clone')).to.be.true; }); it('should handle CloneHandler.execute error (covers line 263)', async () => { diff --git a/packages/contentstack-export-to-csv/src/commands/cm/export-to-csv.ts b/packages/contentstack-export-to-csv/src/commands/cm/export-to-csv.ts index bcda11584..f074dd87a 100644 --- a/packages/contentstack-export-to-csv/src/commands/cm/export-to-csv.ts +++ b/packages/contentstack-export-to-csv/src/commands/cm/export-to-csv.ts @@ -280,6 +280,7 @@ export default class ExportToCsvCommand extends BaseCommand { } log.success('Export completed successfully', this.commandContext); + cliux.print('Export completed successfully', { color: 'green' }); } catch (error) { log.debug('Export failed', { ...this.commandContext, error }); handleAndLogError(error, this.commandContext); @@ -408,6 +409,7 @@ export default class ExportToCsvCommand extends BaseCommand { } log.success('Entries exported successfully', this.commandContext); + cliux.print('Entries exported successfully', { color: 'green' }); } catch (error) { log.debug('Entries export failed', { ...this.commandContext, error }); handleAndLogError(error, this.commandContext, 'Failed to export entries'); @@ -460,6 +462,7 @@ export default class ExportToCsvCommand extends BaseCommand { write(this, listOfUsers, fileName, 'organization details', delimiter); log.success('Users exported successfully', this.commandContext); + cliux.print('Users exported successfully', { color: 'green' }); } catch (error) { log.debug('Users export failed', { ...this.commandContext, error }); handleAndLogError(error, this.commandContext, 'Failed to export users'); @@ -498,6 +501,7 @@ export default class ExportToCsvCommand extends BaseCommand { await exportTeams(managementAPIClient, organization, teamUid, delimiter); log.success('Teams exported successfully', this.commandContext); + cliux.print('Teams exported successfully', { color: 'green' }); } catch (error) { log.debug('Teams export failed', { ...this.commandContext, error }); handleAndLogError(error, this.commandContext, 'Failed to export teams'); @@ -573,6 +577,7 @@ export default class ExportToCsvCommand extends BaseCommand { }); log.success('Taxonomies exported successfully', this.commandContext); + cliux.print('Taxonomies exported successfully', { color: 'green' }); } catch (error) { log.debug('Taxonomies export failed', { ...this.commandContext, error }); handleAndLogError(error, this.commandContext, 'Failed to export taxonomies'); diff --git a/packages/contentstack-export-to-csv/src/utils/teams-export.ts b/packages/contentstack-export-to-csv/src/utils/teams-export.ts index 4b78dc64f..d97b2b277 100644 --- a/packages/contentstack-export-to-csv/src/utils/teams-export.ts +++ b/packages/contentstack-export-to-csv/src/utils/teams-export.ts @@ -96,6 +96,7 @@ export async function exportTeams( await exportRoleMappings(managementAPIClient, allTeamsData, teamUid, delimiter); log.success('Teams export completed', logContext); + cliux.print('Teams export completed', { color: 'green' }); } /** diff --git a/packages/contentstack-export/src/commands/cm/stacks/export.ts b/packages/contentstack-export/src/commands/cm/stacks/export.ts index 5a769cfad..c1ca25400 100644 --- a/packages/contentstack-export/src/commands/cm/stacks/export.ts +++ b/packages/contentstack-export/src/commands/cm/stacks/export.ts @@ -9,11 +9,11 @@ import { pathValidator, sanitizePath, configHandler, + isConsoleLogEnabled, log, handleAndLogError, getSessionLogPath, CLIProgressManager, - clearProgressModuleSetting, loadChalk, } from '@contentstack/cli-utilities'; @@ -130,17 +130,13 @@ export default class ExportCommand extends Command { // Print comprehensive summary at the end if (!exportConfig.branches) CLIProgressManager.printGlobalSummary(); - if (!configHandler.get('log')?.showConsoleLogs) { + if (!isConsoleLogEnabled()) { cliux.print(`The log has been stored at '${sessionLogPath}'`, { color: 'green' }); } - // Clear progress module setting now that export is complete - clearProgressModuleSetting(); } catch (error) { - // Clear progress module setting even on error - clearProgressModuleSetting(); handleAndLogError(error); const sessionLogPath = getSessionLogPath(); - if (!configHandler.get('log')?.showConsoleLogs) { + if (!isConsoleLogEnabled()) { cliux.print(`Error: ${error}`, { color: 'red' }); cliux.print(`The log has been stored at '${sessionLogPath}'`, { color: 'green' }); } diff --git a/packages/contentstack-export/src/export/modules/base-class.ts b/packages/contentstack-export/src/export/modules/base-class.ts index d591e3355..2d6e5ecae 100644 --- a/packages/contentstack-export/src/export/modules/base-class.ts +++ b/packages/contentstack-export/src/export/modules/base-class.ts @@ -8,7 +8,6 @@ import isEqual from 'lodash/isEqual'; import { log, CLIProgressManager, - configHandler, getSessionLogPath, handleAndLogError, } from '@contentstack/cli-utilities'; @@ -82,9 +81,7 @@ export default abstract class BaseClass { */ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; // Default to true for better UX - this.progressManager = CLIProgressManager.createSimple(moduleName, total, showConsoleLogs); + this.progressManager = CLIProgressManager.createSimple(moduleName, total); return this.progressManager; } @@ -93,9 +90,7 @@ export default abstract class BaseClass { */ protected createNestedProgress(moduleName: string): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; // Default to true for better UX - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } @@ -140,13 +135,6 @@ export default abstract class BaseClass { } protected async withLoadingSpinner(message: string, action: () => Promise): Promise { - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - - if (showConsoleLogs) { - // If console logs are enabled, don't show spinner, just execute the action - return await action(); - } return await CLIProgressManager.withLoadingSpinner(message, action); } diff --git a/packages/contentstack-export/src/utils/export-config-handler.ts b/packages/contentstack-export/src/utils/export-config-handler.ts index 522bc9990..d835586a4 100644 --- a/packages/contentstack-export/src/utils/export-config-handler.ts +++ b/packages/contentstack-export/src/utils/export-config-handler.ts @@ -17,10 +17,6 @@ import { filter, includes } from 'lodash'; import { ExportConfig } from '../types'; const setupConfig = async (exportCmdFlags: any, context?: any): Promise => { - // Set progress supported module FIRST, before any log calls - // This ensures the logger respects the showConsoleLogs setting correctly - configHandler.set('log.progressSupportedModule', 'export'); - let config = merge({}, defaultConfig); // Track authentication method diff --git a/packages/contentstack-import-setup/src/commands/cm/stacks/import-setup.ts b/packages/contentstack-import-setup/src/commands/cm/stacks/import-setup.ts index 8f55bcd75..9665d2a3c 100644 --- a/packages/contentstack-import-setup/src/commands/cm/stacks/import-setup.ts +++ b/packages/contentstack-import-setup/src/commands/cm/stacks/import-setup.ts @@ -12,6 +12,7 @@ import { log, handleAndLogError, configHandler, + isConsoleLogEnabled, createLogContext, cliux, loadChalk @@ -107,11 +108,10 @@ export default class ImportSetupCommand extends Command { log.success(successMessage, importSetupConfig.context); log.success(backupPathMessage, importSetupConfig.context); - // log.success maps to the info level, which is suppressed on the console for - // progress-supported modules when showConsoleLogs is false. Print the backup - // folder path directly so it is always visible, regardless of that setting. - const showConsoleLogs = configHandler.get('log')?.showConsoleLogs ?? false; - if (!showConsoleLogs) { + // log.success maps to the info level, which only reaches the console when the + // console-log policy is on. Print the backup folder path directly so it is + // always visible when it is off. + if (!isConsoleLogEnabled()) { cliux.print(successMessage); cliux.print(backupPathMessage); } diff --git a/packages/contentstack-import-setup/src/import/modules/base-setup.ts b/packages/contentstack-import-setup/src/import/modules/base-setup.ts index 69bea1cde..b392aabb8 100644 --- a/packages/contentstack-import-setup/src/import/modules/base-setup.ts +++ b/packages/contentstack-import-setup/src/import/modules/base-setup.ts @@ -1,7 +1,7 @@ import { log, fsUtil } from '../../utils'; import { ApiOptions, CustomPromiseHandler, EnvType, ImportConfig, ModuleClassParams } from '../../types'; import { chunk, entries, isEmpty, isEqual, last } from 'lodash'; -import { CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { CLIProgressManager } from '@contentstack/cli-utilities'; export default class BaseImportSetup { public config: ImportConfig; @@ -214,9 +214,7 @@ export default class BaseImportSetup { */ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createSimple(moduleName, total, showConsoleLogs); + this.progressManager = CLIProgressManager.createSimple(moduleName, total); return this.progressManager; } @@ -225,9 +223,7 @@ export default class BaseImportSetup { */ protected createNestedProgress(moduleName: string): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } @@ -243,13 +239,6 @@ export default class BaseImportSetup { * Show a loading spinner before initializing progress */ protected async withLoadingSpinner(message: string, action: () => Promise): Promise { - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - - if (showConsoleLogs) { - // If console logs are enabled, don't show spinner, just execute the action - return await action(); - } return await CLIProgressManager.withLoadingSpinner(message, action); } } diff --git a/packages/contentstack-import-setup/src/utils/import-config-handler.ts b/packages/contentstack-import-setup/src/utils/import-config-handler.ts index 6b2481961..eec37f278 100644 --- a/packages/contentstack-import-setup/src/utils/import-config-handler.ts +++ b/packages/contentstack-import-setup/src/utils/import-config-handler.ts @@ -8,10 +8,6 @@ import login from './login-handler'; import { ImportConfig } from '../types'; const setupConfig = async (importCmdFlags: any): Promise => { - // Set progress supported module FIRST, before any log calls - // This ensures the logger respects the showConsoleLogs setting correctly - configHandler.set('log.progressSupportedModule', 'import-setup'); - const config: ImportConfig = merge({}, defaultConfig); // setup the config // if (importCmdFlags['config']) { diff --git a/packages/contentstack-import-setup/test/unit/modules/base-setup.test.ts b/packages/contentstack-import-setup/test/unit/modules/base-setup.test.ts index 620ea95da..3520b4659 100644 --- a/packages/contentstack-import-setup/test/unit/modules/base-setup.test.ts +++ b/packages/contentstack-import-setup/test/unit/modules/base-setup.test.ts @@ -3,7 +3,11 @@ import { stub, restore, SinonStub } from 'sinon'; import BaseImportSetup from '../../../src/import/modules/base-setup'; import * as loggerModule from '../../../src/utils/logger'; import { ImportConfig } from '../../../src/types'; -import { CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { CLIProgressManager } from '@contentstack/cli-utilities'; +import { + setConsoleLogPolicy, + resetConsoleLogPolicy, +} from '@contentstack/cli-utilities/lib/logger/console-policy'; describe('BaseImportSetup', () => { let baseSetup: BaseImportSetup; @@ -122,7 +126,6 @@ describe('BaseImportSetup', () => { }); describe('Progress Manager', () => { - let configHandlerGetStub: SinonStub; let createSimpleStub: SinonStub; let createNestedStub: SinonStub; let withLoadingSpinnerStub: SinonStub; @@ -138,45 +141,40 @@ describe('BaseImportSetup', () => { completeProcess: stub().returnsThis(), }; - configHandlerGetStub = stub(configHandler, 'get'); createSimpleStub = stub(CLIProgressManager, 'createSimple'); createNestedStub = stub(CLIProgressManager, 'createNested'); withLoadingSpinnerStub = stub(CLIProgressManager, 'withLoadingSpinner'); }); afterEach(() => { + resetConsoleLogPolicy(); restore(); }); describe('createSimpleProgress', () => { - it('should create a simple progress manager with default showConsoleLogs', () => { - configHandlerGetStub.returns({}); + it('should create a simple progress manager', () => { createSimpleStub.returns(mockProgressManager); const result = (baseSetup as any).createSimpleProgress('test-module', 100); - expect(configHandlerGetStub.calledWith('log')).to.be.true; expect(createSimpleStub.calledOnce).to.be.true; - expect(createSimpleStub.firstCall.args[0]).to.equal('test-module'); - expect(createSimpleStub.firstCall.args[1]).to.equal(100); - expect(createSimpleStub.firstCall.args[2]).to.equal(false); + expect(createSimpleStub.firstCall.args).to.deep.equal(['test-module', 100]); expect(result).to.equal(mockProgressManager); expect((baseSetup as any).currentModuleName).to.equal('test-module'); expect((baseSetup as any).progressManager).to.equal(mockProgressManager); }); - it('should create a simple progress manager with showConsoleLogs enabled', () => { - configHandlerGetStub.returns({ showConsoleLogs: true }); + it('should not forward a policy argument when the console-log policy is on', () => { + setConsoleLogPolicy(true); createSimpleStub.returns(mockProgressManager); const result = (baseSetup as any).createSimpleProgress('test-module', 50); - expect(createSimpleStub.firstCall.args[2]).to.equal(true); + expect(createSimpleStub.firstCall.args).to.deep.equal(['test-module', 50]); expect(result).to.equal(mockProgressManager); }); it('should create a simple progress manager without total count', () => { - configHandlerGetStub.returns({}); createSimpleStub.returns(mockProgressManager); (baseSetup as any).createSimpleProgress('test-module'); @@ -186,28 +184,25 @@ describe('BaseImportSetup', () => { }); describe('createNestedProgress', () => { - it('should create a nested progress manager with default showConsoleLogs', () => { - configHandlerGetStub.returns({}); + it('should create a nested progress manager', () => { createNestedStub.returns(mockProgressManager); const result = (baseSetup as any).createNestedProgress('test-module'); - expect(configHandlerGetStub.calledWith('log')).to.be.true; expect(createNestedStub.calledOnce).to.be.true; - expect(createNestedStub.firstCall.args[0]).to.equal('test-module'); - expect(createNestedStub.firstCall.args[1]).to.equal(false); + expect(createNestedStub.firstCall.args).to.deep.equal(['test-module']); expect(result).to.equal(mockProgressManager); expect((baseSetup as any).currentModuleName).to.equal('test-module'); expect((baseSetup as any).progressManager).to.equal(mockProgressManager); }); - it('should create a nested progress manager with showConsoleLogs enabled', () => { - configHandlerGetStub.returns({ showConsoleLogs: true }); + it('should not forward a policy argument when the console-log policy is on', () => { + setConsoleLogPolicy(true); createNestedStub.returns(mockProgressManager); const result = (baseSetup as any).createNestedProgress('test-module'); - expect(createNestedStub.firstCall.args[1]).to.equal(true); + expect(createNestedStub.firstCall.args).to.deep.equal(['test-module']); expect(result).to.equal(mockProgressManager); }); }); @@ -244,45 +239,35 @@ describe('BaseImportSetup', () => { }); describe('withLoadingSpinner', () => { - it('should execute action directly when showConsoleLogs is enabled', async () => { - configHandlerGetStub.returns({ showConsoleLogs: true }); - const action = stub().resolves('result'); - - const result = await (baseSetup as any).withLoadingSpinner('Loading...', action); - - expect(action.calledOnce).to.be.true; - expect(withLoadingSpinnerStub.called).to.be.false; - expect(result).to.equal('result'); - }); - - it('should use CLIProgressManager.withLoadingSpinner when showConsoleLogs is disabled', async () => { - configHandlerGetStub.returns({ showConsoleLogs: false }); + it('should delegate to CLIProgressManager.withLoadingSpinner even when the policy is on', async () => { + setConsoleLogPolicy(true); const action = stub().resolves('result'); withLoadingSpinnerStub.resolves('result'); const result = await (baseSetup as any).withLoadingSpinner('Loading...', action); expect(withLoadingSpinnerStub.calledOnce).to.be.true; - expect(withLoadingSpinnerStub.firstCall.args[0]).to.equal('Loading...'); expect(withLoadingSpinnerStub.firstCall.args[1]).to.equal(action); expect(result).to.equal('result'); }); - it('should use CLIProgressManager.withLoadingSpinner when log config is empty', async () => { - configHandlerGetStub.returns({}); + it('should use CLIProgressManager.withLoadingSpinner when the policy is off', async () => { const action = stub().resolves('result'); withLoadingSpinnerStub.resolves('result'); const result = await (baseSetup as any).withLoadingSpinner('Loading...', action); expect(withLoadingSpinnerStub.calledOnce).to.be.true; + expect(withLoadingSpinnerStub.firstCall.args[0]).to.equal('Loading...'); + expect(withLoadingSpinnerStub.firstCall.args[1]).to.equal(action); expect(result).to.equal('result'); }); - it('should handle errors in action when showConsoleLogs is enabled', async () => { - configHandlerGetStub.returns({ showConsoleLogs: true }); + it('should handle errors in action when the policy is on', async () => { + setConsoleLogPolicy(true); const error = new Error('Action failed'); const action = stub().rejects(error); + withLoadingSpinnerStub.rejects(error); try { await (baseSetup as any).withLoadingSpinner('Loading...', action); @@ -292,8 +277,7 @@ describe('BaseImportSetup', () => { } }); - it('should handle errors in action when showConsoleLogs is disabled', async () => { - configHandlerGetStub.returns({ showConsoleLogs: false }); + it('should handle errors in action when the policy is off', async () => { const error = new Error('Action failed'); const action = stub().rejects(error); withLoadingSpinnerStub.rejects(error); diff --git a/packages/contentstack-import/src/commands/cm/stacks/import.ts b/packages/contentstack-import/src/commands/cm/stacks/import.ts index e2b7a0cad..687ae4fba 100644 --- a/packages/contentstack-import/src/commands/cm/stacks/import.ts +++ b/packages/contentstack-import/src/commands/cm/stacks/import.ts @@ -8,10 +8,10 @@ import { log, handleAndLogError, configHandler, + isConsoleLogEnabled, getSessionLogPath, CLIProgressManager, cliux, - clearProgressModuleSetting, createLogContext, } from '@contentstack/cli-utilities'; @@ -195,12 +195,7 @@ export default class ImportCommand extends Command { ); } this.logSuccessAndBackupMessages(backupDir, importConfig); - // Clear progress module setting now that import is complete - clearProgressModuleSetting(); } catch (error) { - // Clear progress module setting even on error - clearProgressModuleSetting(); - handleAndLogError(error); this.logAndPrintErrorDetails(error, importConfig); } @@ -219,8 +214,7 @@ export default class ImportCommand extends Command { log.info(logMsg); log.info(backupDirMsg); - const showConsoleLogs = configHandler.get('log')?.showConsoleLogs; - if (!showConsoleLogs) { + if (!isConsoleLogEnabled()) { cliux.print(`Error: ${error}`, { color: 'red' }); cliux.print(logMsg, { color: 'blue' }); cliux.print(backupDirMsg, { color: 'blue' }); @@ -236,8 +230,7 @@ export default class ImportCommand extends Command { log.success(logMsg, importConfig.context); log.info(backupDirMsg, importConfig.context); - const showConsoleLogs = configHandler.get('log')?.showConsoleLogs; - if (!showConsoleLogs) { + if (!isConsoleLogEnabled()) { cliux.print(logMsg, { color: 'blue' }); cliux.print(backupDirMsg, { color: 'blue' }); } diff --git a/packages/contentstack-import/src/import/modules/base-class.ts b/packages/contentstack-import/src/import/modules/base-class.ts index 1fb60cd00..06bd792d3 100644 --- a/packages/contentstack-import/src/import/modules/base-class.ts +++ b/packages/contentstack-import/src/import/modules/base-class.ts @@ -19,7 +19,6 @@ import { WorkflowData, RoleData, CLIProgressManager, - configHandler, getSessionLogPath } from '@contentstack/cli-utilities'; import { ImportConfig, ModuleClassParams } from '../../types'; @@ -126,9 +125,7 @@ export default abstract class BaseClass { */ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; // Default to true for better UX - this.progressManager = CLIProgressManager.createSimple(moduleName, total, showConsoleLogs); + this.progressManager = CLIProgressManager.createSimple(moduleName, total); return this.progressManager; } @@ -137,9 +134,7 @@ export default abstract class BaseClass { */ protected createNestedProgress(moduleName: string): CLIProgressManager { this.currentModuleName = moduleName; - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; // Default to true for better UX - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } @@ -182,13 +177,6 @@ export default abstract class BaseClass { } protected async withLoadingSpinner(message: string, action: () => Promise): Promise { - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - - if (showConsoleLogs) { - // If console logs are enabled, don't show spinner, just execute the action - return await action(); - } return await CLIProgressManager.withLoadingSpinner(message, action); } diff --git a/packages/contentstack-import/src/utils/import-config-handler.ts b/packages/contentstack-import/src/utils/import-config-handler.ts index 244ca4002..21f316dae 100644 --- a/packages/contentstack-import/src/utils/import-config-handler.ts +++ b/packages/contentstack-import/src/utils/import-config-handler.ts @@ -18,10 +18,6 @@ import { ImportConfig } from '../types'; import { existsSync } from 'fs'; const setupConfig = async (importCmdFlags: any, context?: any): Promise => { - // Set progress supported module FIRST, before any log calls - // This ensures the logger respects the showConsoleLogs setting correctly - configHandler.set('log.progressSupportedModule', 'import'); - let config: ImportConfig = merge({}, defaultConfig); // Track authentication method let authenticationMethod = 'unknown'; diff --git a/packages/contentstack-query-export/package.json b/packages/contentstack-query-export/package.json index c87abf7e5..3ad7ff9b2 100644 --- a/packages/contentstack-query-export/package.json +++ b/packages/contentstack-query-export/package.json @@ -87,6 +87,7 @@ "repositoryPrefix": "<%- repo %>/blob/main/packages/contentstack-query-export/<%- commandPath %>" }, "csdxConfig": { + "showConsoleLogs": true, "shortCommandName": { "cm:stacks:export-query": "EXPRTQRY", "cm:export:query": "EXPRTQRY" diff --git a/packages/contentstack-variants/src/utils/personalization-api-adapter.ts b/packages/contentstack-variants/src/utils/personalization-api-adapter.ts index ee2ed613d..b19a4d069 100644 --- a/packages/contentstack-variants/src/utils/personalization-api-adapter.ts +++ b/packages/contentstack-variants/src/utils/personalization-api-adapter.ts @@ -1,5 +1,5 @@ import { AdapterHelper } from './adapter-helper'; -import { HttpClient, authenticationHandler, log, CLIProgressManager, configHandler } from '@contentstack/cli-utilities'; +import { HttpClient, authenticationHandler, log, CLIProgressManager } from '@contentstack/cli-utilities'; import { ProjectStruct, @@ -66,9 +66,7 @@ export class PersonalizationAdapter extends AdapterHelper impl return this.progressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createSimple(moduleName, total, showConsoleLogs); + this.progressManager = CLIProgressManager.createSimple(moduleName, total); return this.progressManager; } @@ -84,9 +82,7 @@ export class PersonalizationAdapter extends AdapterHelper impl return this.progressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } @@ -105,13 +101,6 @@ export class PersonalizationAdapter extends AdapterHelper impl * Execute action with loading spinner for initial setup tasks */ protected async withLoadingSpinner(message: string, action: () => Promise): Promise { - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - - if (showConsoleLogs) { - // If console logs are enabled, don't show spinner, just execute the action - return await action(); - } return await CLIProgressManager.withLoadingSpinner(message, action); } diff --git a/packages/contentstack-variants/src/utils/variant-api-adapter.ts b/packages/contentstack-variants/src/utils/variant-api-adapter.ts index 611d4c909..e84ec08f7 100644 --- a/packages/contentstack-variants/src/utils/variant-api-adapter.ts +++ b/packages/contentstack-variants/src/utils/variant-api-adapter.ts @@ -10,7 +10,6 @@ import { authenticationHandler, log, CLIProgressManager, - configHandler, } from '@contentstack/cli-utilities'; import { @@ -439,9 +438,7 @@ export class VariantAdapter { return this.progressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createSimple(moduleName, total, showConsoleLogs); + this.progressManager = CLIProgressManager.createSimple(moduleName, total); return this.progressManager; } @@ -457,9 +454,7 @@ export class VariantAdapter { return this.progressManager; } - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - this.progressManager = CLIProgressManager.createNested(moduleName, showConsoleLogs); + this.progressManager = CLIProgressManager.createNested(moduleName); return this.progressManager; } @@ -478,13 +473,6 @@ export class VariantAdapter { * Execute action with loading spinner for initial setup tasks */ protected async withLoadingSpinner(message: string, action: () => Promise): Promise { - const logConfig = configHandler.get('log') || {}; - const showConsoleLogs = logConfig.showConsoleLogs ?? false; - - if (showConsoleLogs) { - // If console logs are enabled, don't show spinner, just execute the action - return await action(); - } return await CLIProgressManager.withLoadingSpinner(message, action); } From 84c3996693681af464233603af1b3e44cf5e97a8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 19 Aug 2026 12:12:33 +0530 Subject: [PATCH 4/5] updated export csv --- packages/contentstack-export-to-csv/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/contentstack-export-to-csv/package.json b/packages/contentstack-export-to-csv/package.json index d9bfb9886..117d45db9 100644 --- a/packages/contentstack-export-to-csv/package.json +++ b/packages/contentstack-export-to-csv/package.json @@ -54,6 +54,7 @@ "repositoryPrefix": "<%- repo %>/blob/main/packages/contentstack-export-to-csv/<%- commandPath %>" }, "csdxConfig": { + "showConsoleLogs": true, "shortCommandName": { "cm:export-to-csv": "EXPRTCSV" } From 4156decc4f3094d561fe1fad6febdd179dee82ec Mon Sep 17 00:00:00 2001 From: harshitha-cstk Date: Wed, 19 Aug 2026 16:58:49 +0530 Subject: [PATCH 5/5] fix(bulk-operations): use console-log policy for catch() error visibility Post-merge, the console-log state moved from configHandler.get('log'). showConsoleLogs to the process-wide console-policy module, and the configHandler import was dropped. catch() still referenced configHandler, breaking the build (TS2304). Switch the guard to isConsoleLogEnabled() so the friendly error line still prints on failure when console logs are off, without double-printing when they are on. Matches the convention the rest of the file now follows. Ref: DX-10224 Co-Authored-By: Claude Opus 4.8 --- .../contentstack-bulk-operations/src/base-bulk-command.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contentstack-bulk-operations/src/base-bulk-command.ts b/packages/contentstack-bulk-operations/src/base-bulk-command.ts index 09031b917..b67f9862d 100644 --- a/packages/contentstack-bulk-operations/src/base-bulk-command.ts +++ b/packages/contentstack-bulk-operations/src/base-bulk-command.ts @@ -11,6 +11,7 @@ import { getChalk, loadChalk, CLIProgressManager, + isConsoleLogEnabled, } from '@contentstack/cli-utilities'; import config from './config'; @@ -656,13 +657,12 @@ export abstract class BaseBulkCommand extends Command { // For other errors, use the CLI utilities error handler handleAndLogError(error); - // handleAndLogError only reaches the console when log.showConsoleLogs is enabled + // handleAndLogError only reaches the console when the console-log policy is enabled // (the winston error transport is silenced otherwise), so a failure would leave the // terminal completely silent when the user has console logs turned off. Print a // user-facing error line here to fill that gap, guarded so we don't double-print when // console logs are on and handleAndLogError already emitted the error. - const showConsoleLogs = Boolean(configHandler.get('log')?.showConsoleLogs); - if (!showConsoleLogs) { + if (!isConsoleLogEnabled()) { const errorMessage = cliErrorHandler.classifyError(error)?.message || error?.message || 'Unknown error'; cliux.print(`Error: ${errorMessage}`, { color: 'red' }); }