11const { Command } = require ( '@contentstack/cli-command' ) ;
2- const { configHandler, flags, isAuthenticated, managementSDKClient } = require ( '@contentstack/cli-utilities' ) ;
2+ const { configHandler, flags, isAuthenticated, managementSDKClient, log , handleAndLogError } = require ( '@contentstack/cli-utilities' ) ;
33const { CloneHandler } = require ( '../../../lib/util/clone-handler' ) ;
44const path = require ( 'path' ) ;
55const { rimraf } = require ( 'rimraf' ) ;
@@ -9,6 +9,44 @@ const { readdirSync, readFileSync } = require('fs');
99let config = { } ;
1010
1111class StackCloneCommand extends Command {
12+ /**
13+ * Determine authentication method based on user preference
14+ */
15+ determineAuthenticationMethod ( sourceManagementTokenAlias , destinationManagementTokenAlias ) {
16+ // Track authentication method
17+ let authenticationMethod = 'unknown' ;
18+
19+ // Determine authentication method based on user preference
20+ if ( sourceManagementTokenAlias || destinationManagementTokenAlias ) {
21+ authenticationMethod = 'Management Token' ;
22+ } else if ( isAuthenticated ( ) ) {
23+ // Check if user is authenticated via OAuth
24+ const isOAuthUser = configHandler . get ( 'authorisationType' ) === 'OAUTH' || false ;
25+ if ( isOAuthUser ) {
26+ authenticationMethod = 'OAuth' ;
27+ } else {
28+ authenticationMethod = 'Basic Auth' ;
29+ }
30+ } else {
31+ authenticationMethod = 'Basic Auth' ;
32+ }
33+
34+ return authenticationMethod ;
35+ }
36+
37+ /**
38+ * Create clone context object for logging
39+ */
40+ createCloneContext ( authenticationMethod ) {
41+ return {
42+ command : this . context ?. info ?. command || 'cm:stacks:clone' ,
43+ module : 'clone' ,
44+ email : configHandler . get ( 'email' ) || '' ,
45+ sessionId : this . context ?. sessionId || '' ,
46+ authenticationMethod : authenticationMethod || 'Basic Auth' ,
47+ } ;
48+ }
49+
1250 async run ( ) {
1351 try {
1452 let self = this ;
@@ -31,14 +69,27 @@ class StackCloneCommand extends Command {
3169
3270 const handleClone = async ( ) => {
3371 const listOfTokens = configHandler . get ( 'tokens' ) ;
72+ const authenticationMethod = this . determineAuthenticationMethod (
73+ sourceManagementTokenAlias ,
74+ destinationManagementTokenAlias ,
75+ ) ;
76+ const cloneContext = this . createCloneContext ( authenticationMethod ) ;
77+ log . debug ( 'Starting clone operation setup' , cloneContext ) ;
3478
3579 if ( externalConfigPath ) {
80+ log . debug ( `Loading external configuration from: ${ externalConfigPath } ` , cloneContext ) ;
3681 let externalConfig = readFileSync ( externalConfigPath , 'utf-8' ) ;
3782 externalConfig = JSON . parse ( externalConfig ) ;
3883 config = merge . recursive ( config , externalConfig ) ;
3984 }
4085 config . forceStopMarketplaceAppsPrompt = yes ;
4186 config . skipAudit = cloneCommandFlags [ 'skip-audit' ] ;
87+ log . debug ( 'Clone configuration prepared' , {
88+ ...cloneContext ,
89+ cloneType : config . cloneType ,
90+ skipAudit : config . skipAudit ,
91+ forceStopMarketplaceAppsPrompt : config . forceStopMarketplaceAppsPrompt
92+ } ) ;
4293
4394 if ( cloneType ) {
4495 config . cloneType = cloneType ;
@@ -67,34 +118,42 @@ class StackCloneCommand extends Command {
67118 if ( sourceManagementTokenAlias && listOfTokens [ sourceManagementTokenAlias ] ) {
68119 config . source_alias = sourceManagementTokenAlias ;
69120 config . source_stack = listOfTokens [ sourceManagementTokenAlias ] . apiKey ;
121+ log . debug ( `Using source token alias: ${ sourceManagementTokenAlias } ` , cloneContext ) ;
70122 } else if ( sourceManagementTokenAlias ) {
71- console . log ( `Provided source token alias (${ sourceManagementTokenAlias } ) not found in your config.!` ) ;
123+ log . warn ( `Provided source token alias (${ sourceManagementTokenAlias } ) not found in your config.!` , cloneContext ) ;
72124 }
73125 if ( destinationManagementTokenAlias && listOfTokens [ destinationManagementTokenAlias ] ) {
74126 config . destination_alias = destinationManagementTokenAlias ;
75127 config . target_stack = listOfTokens [ destinationManagementTokenAlias ] . apiKey ;
128+ log . debug ( `Using destination token alias: ${ destinationManagementTokenAlias } ` , cloneContext ) ;
76129 } else if ( destinationManagementTokenAlias ) {
77- console . log (
130+ log . warn (
78131 `Provided destination token alias (${ destinationManagementTokenAlias } ) not found in your config.!` ,
132+ cloneContext ,
79133 ) ;
80134 }
81135 if ( importWebhookStatus ) {
82136 config . importWebhookStatus = importWebhookStatus ;
83137 }
84138
85139 const managementAPIClient = await managementSDKClient ( config ) ;
140+ log . debug ( 'Management API client initialized successfully' , cloneContext ) ;
86141
87- await this . removeContentDirIfNotEmptyBeforeClone ( pathdir ) ; // NOTE remove if folder not empty before clone
88- this . registerCleanupOnInterrupt ( pathdir ) ;
142+ log . debug ( `Content directory path: ${ pathdir } ` , cloneContext ) ;
143+ await this . removeContentDirIfNotEmptyBeforeClone ( pathdir , cloneContext ) ; // NOTE remove if folder not empty before clone
144+ this . registerCleanupOnInterrupt ( pathdir , cloneContext ) ;
89145
90146 config . auth_token = configHandler . get ( 'authtoken' ) ;
91147 config . host = this . cmaHost ;
92148 config . cdn = this . cdaHost ;
93149 config . pathDir = pathdir ;
150+ config . cloneContext = cloneContext ;
151+ log . debug ( 'Clone configuration finalized' , cloneContext ) ;
94152 const cloneHandler = new CloneHandler ( config ) ;
95153 cloneHandler . setClient ( managementAPIClient ) ;
154+ log . debug ( 'Starting clone operation' , cloneContext ) ;
96155 cloneHandler . execute ( ) . catch ( ( error ) => {
97- console . log ( error ) ;
156+ handleAndLogError ( error , cloneContext ) ;
98157 } ) ;
99158 } ;
100159
@@ -103,7 +162,7 @@ class StackCloneCommand extends Command {
103162 if ( isAuthenticated ( ) ) {
104163 handleClone ( ) ;
105164 } else {
106- log . error ( 'Log in to execute this command,csdx auth:login' , cloneContext ) ;
165+ log . error ( 'Please login to execute this command, csdx auth:login' , cloneContext ) ;
107166 this . exit ( 1 ) ;
108167 }
109168 } else {
@@ -112,76 +171,76 @@ class StackCloneCommand extends Command {
112171 } else if ( isAuthenticated ( ) ) {
113172 handleClone ( ) ;
114173 } else {
115- console . log ( 'Please login to execute this command, csdx auth:login' ) ;
174+ log . error ( 'Please login to execute this command, csdx auth:login' , cloneContext ) ;
116175 this . exit ( 1 ) ;
117176 }
118177 } catch ( error ) {
119178 if ( error ) {
120- await this . cleanUp ( pathdir ) ;
121- // eslint-disable-next-line no-console
122- console . log ( error . message || error ) ;
179+ await this . cleanUp ( pathdir , null , cloneContext ) ;
180+ log . error ( 'Stack clone command failed' , { ...cloneContext , error : error ?. message || error } ) ;
123181 }
124182 }
125183 }
126184
127185
128186
129- async removeContentDirIfNotEmptyBeforeClone ( dir ) {
187+ async removeContentDirIfNotEmptyBeforeClone ( dir , cloneContext ) {
130188 try {
189+ log . debug ( 'Checking if content directory is empty' , { ...cloneContext , dir } ) ;
131190 const dirNotEmpty = readdirSync ( dir ) . length ;
132191
133192 if ( dirNotEmpty ) {
134- await this . cleanUp ( dir ) ;
193+ log . debug ( 'Content directory is not empty, cleaning up' , { ...cloneContext , dir } ) ;
194+ await this . cleanUp ( dir , null , cloneContext ) ;
135195 }
136196 } catch ( error ) {
137197 const omit = [ 'ENOENT' ] ; // NOTE add emittable error codes in the array
138198
139199 if ( ! omit . includes ( error . code ) ) {
140- console . log ( error . message ) ;
200+ log . error ( 'Error checking content directory' , { ... cloneContext , error : error ? .message , code : error . code } ) ;
141201 }
142202 }
143203 }
144204
145- async cleanUp ( pathDir , message ) {
205+ async cleanUp ( pathDir , message , cloneContext ) {
146206 try {
207+ log . debug ( 'Starting cleanup' , { ...cloneContext , pathDir } ) ;
147208 await rimraf ( pathDir ) ;
148209 if ( message ) {
149- // eslint-disable-next-line no-console
150- console . log ( message ) ;
210+ log . info ( message , cloneContext ) ;
151211 }
212+ log . debug ( 'Cleanup completed' , { ...cloneContext , pathDir } ) ;
152213 } catch ( err ) {
153214 if ( err ) {
154- console . log ( '\nCleaning up') ;
215+ log . debug ( 'Cleaning up', cloneContext ) ;
155216 const skipCodeArr = [ 'ENOENT' , 'EBUSY' , 'EPERM' , 'EMFILE' , 'ENOTEMPTY' ] ;
156217
157218 if ( skipCodeArr . includes ( err . code ) ) {
219+ log . debug ( 'Cleanup error code is in skip list, exiting' , { ...cloneContext , code : err ?. code } ) ;
158220 process . exit ( ) ;
159221 }
160222 }
161223 }
162224 }
163225
164- registerCleanupOnInterrupt ( pathDir ) {
226+ registerCleanupOnInterrupt ( pathDir , cloneContext ) {
165227 const interrupt = [ 'SIGINT' , 'SIGQUIT' , 'SIGTERM' ] ;
166228 const exceptions = [ 'unhandledRejection' , 'uncaughtException' ] ;
167229
168230 const cleanUp = async ( exitOrError ) => {
169231 if ( exitOrError ) {
170- // eslint-disable-next-line no-console
171- console . log ( '\nCleaning up' ) ;
172- await this . cleanUp ( pathDir ) ;
173- // eslint-disable-next-line no-console
174- console . log ( 'done' ) ;
175- // eslint-disable-next-line no-process-exit
232+ log . debug ( 'Cleaning up on interrupt' , cloneContext ) ;
233+ await this . cleanUp ( pathDir , null , cloneContext ) ;
234+ log . info ( 'Cleanup done' , cloneContext ) ;
176235
177236 if ( exitOrError instanceof Promise ) {
178237 exitOrError . catch ( ( error ) => {
179- console . log ( ( error && error . message ) || '' ) ;
238+ log . error ( 'Error during cleanup' , { ... cloneContext , error : ( error && error ? .message ) || '' } ) ;
180239 } ) ;
181240 } else if ( exitOrError . message ) {
182- console . log ( exitOrError . message ) ;
241+ log . error ( 'Cleanup error' , { ... cloneContext , error : exitOrError ? .message } ) ;
183242 } else if ( exitOrError . errorMessage ) {
184- console . log ( exitOrError . message ) ;
243+ log . error ( 'Cleanup error' , { ... cloneContext , error : exitOrError ?. errorMessage } ) ;
185244 }
186245
187246 if ( exitOrError === true ) process . exit ( ) ;
0 commit comments