Skip to content

Commit 0afa13e

Browse files
committed
refactor(migrate): split migrate-users report flow from email apply
1 parent 4744a7f commit 0afa13e

1 file changed

Lines changed: 75 additions & 39 deletions

File tree

scripts/migrate/migrate-users.js

Lines changed: 75 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -49,49 +49,47 @@ const args = {
4949

5050
config.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

118132
async 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

Comments
 (0)