@@ -49,49 +49,47 @@ const args = {
4949
5050config . ensureReportsDir ( ) ;
5151
52- async function finishReport ( datastore , usersReport , aclReport ) {
53- const report = {
54- mode : args . apply ? 'apply' : 'dry-run' ,
52+ function printDryRunNextSteps ( ) {
53+ console . log ( '\n=== DRY RUN MODE (default) ===' ) ;
54+ console . log ( 'No changes applied.' ) ;
55+ console . log ( '\nNext steps:' ) ;
56+ console . log ( ' 1. Create users backup (recommended):' ) ;
57+ console . log ( ' node scripts/migrate/backup-users.js' ) ;
58+ console . log ( ' 2. Prepare CSV mapping: username,email' ) ;
59+ console . log ( ' 3. Apply changes:' ) ;
60+ console . log ( ' node scripts/migrate/migrate-users.js --apply --csv mappings.csv' ) ;
61+ }
62+
63+ function buildUsersMigrationReport ( { mode, usersReport, aclReport, csv, apply } ) {
64+ return {
65+ mode,
5566 users : usersReport ,
5667 acl : aclReport ,
57- apply : null ,
58- csv : null ,
68+ apply : apply ?? null ,
69+ csv : csv ?? null ,
5970 } ;
71+ }
6072
61- if ( ! args . apply ) {
62- console . log ( '\n=== DRY RUN MODE (default) ===' ) ;
63- console . log ( 'No changes applied.' ) ;
64- console . log ( '\nNext steps:' ) ;
65- console . log ( ' 1. Create users backup (recommended):' ) ;
66- console . log ( ' node scripts/migrate/backup-users.js' ) ;
67- console . log ( ' 2. Prepare CSV mapping: username,email' ) ;
68- console . log ( ' 3. Apply changes:' ) ;
69- console . log ( ' node scripts/migrate/migrate-users.js --apply --csv mappings.csv' ) ;
70- } else {
71- console . log ( '\n=== APPLY MODE ===' ) ;
72- if ( ! args . csvPath ) {
73- throw new Error ( '--apply requires --csv <path>' ) ;
74- }
75-
76- const absCsvPath = path . isAbsolute ( args . csvPath )
77- ? args . csvPath
78- : path . join ( process . cwd ( ) , args . csvPath ) ;
79- const { mapping, errors, rows } = readUsernameEmailCsv ( absCsvPath ) ;
80- report . csv = { path : absCsvPath , rowCount : rows . length , errors } ;
81-
82- if ( errors . length > 0 ) {
83- console . error ( `CSV validation errors: ${ errors . length } ` ) ;
84- report . apply = { ok : false , reason : 'csv-errors' , changes : [ ] } ;
85- } else {
86- report . apply = await applyUserEmailsWithDatastore ( datastore , mapping , {
87- dryRun : false ,
88- } ) ;
89- }
73+ async function runEmailApplyFromCsv ( datastore , csvPath ) {
74+ const absCsvPath = path . isAbsolute ( csvPath ) ? csvPath : path . join ( process . cwd ( ) , csvPath ) ;
75+ const { mapping, errors, rows } = readUsernameEmailCsv ( absCsvPath ) ;
76+ const csv = { path : absCsvPath , rowCount : rows . length , errors } ;
77+
78+ if ( errors . length > 0 ) {
79+ console . error ( `CSV validation errors: ${ errors . length } ` ) ;
80+ return {
81+ csv,
82+ apply : { ok : false , reason : 'csv-errors' , changes : [ ] } ,
83+ } ;
9084 }
9185
92- const timestamp = Date . now ( ) ;
93- generateReports ( config . reportsDir , report , timestamp ) ;
86+ const apply = await applyUserEmailsWithDatastore ( datastore , mapping , {
87+ dryRun : false ,
88+ } ) ;
89+ return { csv, apply } ;
90+ }
9491
92+ function computeExitCode ( report ) {
9593 const blockingUsers = report . users ?. blockingIssueCount ?? 0 ;
9694 const aclOrphans = report . acl ?. orphanCount ?? 0 ;
9795 const applyOk = report . apply ? report . apply . ok : true ;
@@ -102,17 +100,33 @@ async function finishReport(datastore, usersReport, aclReport) {
102100 const shouldFail =
103101 blockingUsers > 0 || aclOrphans > 0 || ! applyOk || applyConflicts > 0 || csvErrors > 0 ;
104102
103+ return shouldFail ? 1 : 0 ;
104+ }
105+
106+ function printMigrationSummary ( report , applyMode ) {
107+ const blockingUsers = report . users ?. blockingIssueCount ?? 0 ;
108+ const aclOrphans = report . acl ?. orphanCount ?? 0 ;
109+ const applyOk = report . apply ? report . apply . ok : true ;
110+ const applyConflicts =
111+ report . apply && Array . isArray ( report . apply . conflicts ) ? report . apply . conflicts . length : 0 ;
112+ const csvErrors = report . csv && Array . isArray ( report . csv . errors ) ? report . csv . errors . length : 0 ;
113+
105114 console . log ( '\n=== SUMMARY ===' ) ;
106115 console . log ( `Mode: ${ report . mode } ` ) ;
107116 console . log ( `Users (blocking): ${ blockingUsers } ` ) ;
108117 console . log ( `ACL orphans: ${ aclOrphans } ` ) ;
109- if ( args . apply ) {
118+ if ( applyMode ) {
110119 console . log ( `CSV errors: ${ csvErrors } ` ) ;
111120 console . log ( `Apply ok: ${ applyOk } ` ) ;
112121 console . log ( `Apply conflicts: ${ applyConflicts } ` ) ;
113122 }
123+ }
114124
115- return shouldFail ? 1 : 0 ;
125+ function persistReportAndPrintSummary ( reportsDir , report , applyMode ) {
126+ const timestamp = Date . now ( ) ;
127+ generateReports ( reportsDir , report , timestamp ) ;
128+ printMigrationSummary ( report , applyMode ) ;
129+ return computeExitCode ( report ) ;
116130}
117131
118132async function main ( ) {
@@ -121,7 +135,29 @@ async function main() {
121135 ds = await createDatastoreFromArgv ( argv ) ;
122136 const { report : usersReport } = await analyzeUsersWithDatastore ( ds ) ;
123137 const { report : aclReport } = await analyzeAclWithDatastore ( ds ) ;
124- const exitCode = await finishReport ( ds , usersReport , aclReport ) ;
138+
139+ let csv = null ;
140+ let apply = null ;
141+
142+ if ( ! args . apply ) {
143+ printDryRunNextSteps ( ) ;
144+ } else {
145+ console . log ( '\n=== APPLY MODE ===' ) ;
146+ if ( ! args . csvPath ) {
147+ throw new Error ( '--apply requires --csv <path>' ) ;
148+ }
149+ ( { csv, apply } = await runEmailApplyFromCsv ( ds , args . csvPath ) ) ;
150+ }
151+
152+ const report = buildUsersMigrationReport ( {
153+ mode : args . apply ? 'apply' : 'dry-run' ,
154+ usersReport,
155+ aclReport,
156+ csv,
157+ apply,
158+ } ) ;
159+
160+ const exitCode = persistReportAndPrintSummary ( config . reportsDir , report , args . apply ) ;
125161 process . exit ( exitCode ) ;
126162 } catch ( error ) {
127163 console . error ( 'FATAL ERROR:' , error . message ) ;
0 commit comments