@joltdesign/scripts 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -113,9 +113,10 @@ export class WPUpdateCommand extends JoltCommand {
113
113
  this.skipCore = Option.Boolean('--skip-core', false, { description: 'Skip WordPress core updates' });
114
114
  this.skipPlugins = Option.Boolean('--skip-plugins', false, { description: 'Skip plugin updates' });
115
115
  this.skipThemes = Option.Boolean('--skip-themes', false, { description: 'Skip theme updates' });
116
+ this.skipLanguages = Option.Boolean('--skip-languages', false, { description: 'Skip language/translation updates' });
116
117
  }
117
118
  async command() {
118
- const { config, context: { stdout, stderr }, skipCore, skipPlugins, skipThemes, logo, } = this;
119
+ const { config, context: { stdout, stderr }, skipCore, skipPlugins, skipThemes, skipLanguages, logo, } = this;
119
120
  stdout.write(ansis.bold(`${logo} WordPress Updates\n\n`));
120
121
  // Load configuration
121
122
  const wpConfig = await config.loadWordPressConfig();
@@ -127,30 +128,19 @@ export class WPUpdateCommand extends JoltCommand {
127
128
  let updatedPluginCount = 0;
128
129
  let updatedThemeCount = 0;
129
130
  let updatedCore = false;
131
+ // Track detailed update information for summary
132
+ const updateSummary = {
133
+ plugins: [],
134
+ themes: [],
135
+ core: null,
136
+ translations: false,
137
+ };
130
138
  // Get current status
131
139
  if (!skipPlugins) {
132
- stdout.write(ansis.cyan('🔌 Checking plugins...\n'));
133
- const yarnCommand = await config.command('yarn');
134
- const pluginsResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', 'plugin', 'list', '--json'], {
135
- reject: false,
136
- });
137
- const pluginsJson = pluginsResult.exitCode === 0 ? String(pluginsResult.stdout || '') : null;
138
- if (pluginsJson) {
139
- const plugins = this.parsePluginJson(pluginsJson);
140
- stdout.write(`Found ${plugins.length} plugins\n`);
141
- }
140
+ await this.getItems('plugin');
142
141
  }
143
142
  if (!skipThemes) {
144
- stdout.write(ansis.cyan('🎨 Checking themes...\n'));
145
- const yarnCommand = await config.command('yarn');
146
- const themesResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', 'theme', 'list', '--json'], {
147
- reject: false,
148
- });
149
- const themesJson = themesResult.exitCode === 0 ? String(themesResult.stdout || '') : null;
150
- if (themesJson) {
151
- const themes = this.parseThemeJson(themesJson);
152
- stdout.write(`Found ${themes.length} themes\n`);
153
- }
143
+ await this.getItems('theme');
154
144
  }
155
145
  // We'll create the update branch only when we need to make our first commit
156
146
  const branchRef = { branch: undefined, created: false };
@@ -158,45 +148,21 @@ export class WPUpdateCommand extends JoltCommand {
158
148
  const originalHookPath = await this.disableGitHooks();
159
149
  try {
160
150
  // Update plugins
161
- if (!skipPlugins) {
162
- stdout.write(ansis.cyan('🔌 Updating plugins...\n'));
163
- const yarnCommand = await config.command('yarn');
164
- const pluginsResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', 'plugin', 'list', '--json'], {
165
- reject: false,
166
- });
167
- const pluginsJson = pluginsResult.exitCode === 0 ? String(pluginsResult.stdout || '') : null;
168
- if (pluginsJson) {
169
- const plugins = this.parsePluginJson(pluginsJson);
170
- for (const plugin of plugins) {
171
- const didUpdate = await this.maybeUpdatePlugin(plugin, wpConfig, branchRef);
172
- if (didUpdate) {
173
- updatedPluginCount++;
174
- }
175
- }
176
- }
177
- }
151
+ const pluginUpdates = await this.processItemUpdates('plugin', skipPlugins, wpConfig, branchRef);
152
+ updatedPluginCount = pluginUpdates.count;
153
+ updateSummary.plugins = pluginUpdates.details;
178
154
  // Update themes
179
- if (!skipThemes) {
180
- stdout.write(ansis.cyan('🎨 Updating themes...\n'));
181
- const yarnCommand = await config.command('yarn');
182
- const themesResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', 'theme', 'list', '--json'], {
183
- reject: false,
184
- });
185
- const themesJson = themesResult.exitCode === 0 ? String(themesResult.stdout || '') : null;
186
- if (themesJson) {
187
- const themes = this.parseThemeJson(themesJson);
188
- for (const theme of themes) {
189
- const didUpdate = await this.maybeUpdateTheme(theme, wpConfig, branchRef);
190
- if (didUpdate) {
191
- updatedThemeCount++;
192
- }
193
- }
194
- }
195
- }
155
+ const themeUpdates = await this.processItemUpdates('theme', skipThemes, wpConfig, branchRef);
156
+ updatedThemeCount = themeUpdates.count;
157
+ updateSummary.themes = themeUpdates.details;
196
158
  // Update core
197
159
  if (!skipCore) {
198
160
  stdout.write(ansis.cyan('📦 Checking WordPress core...\n'));
199
- updatedCore = await this.maybeUpdateCore(wpConfig, branchRef);
161
+ const coreResult = await this.maybeUpdateCore(wpConfig, branchRef);
162
+ updatedCore = coreResult.updated;
163
+ if (coreResult.updated && coreResult.details) {
164
+ updateSummary.core = coreResult.details;
165
+ }
200
166
  }
201
167
  }
202
168
  finally {
@@ -210,218 +176,260 @@ export class WPUpdateCommand extends JoltCommand {
210
176
  stdout.write(ansis.cyan('📦 Updated WordPress core\n'));
211
177
  }
212
178
  const totalUpdates = updatedPluginCount + updatedThemeCount + (updatedCore ? 1 : 0);
179
+ // Update translations if possible
180
+ let updatedTranslations = false;
181
+ if (!skipLanguages && (totalUpdates > 0 || !branchRef.created)) {
182
+ stdout.write(ansis.cyan('🌐 Updating translations...\n'));
183
+ updatedTranslations = await this.maybeUpdateTranslations(branchRef);
184
+ updateSummary.translations = updatedTranslations;
185
+ }
186
+ // Show detailed update summary
187
+ if (totalUpdates > 0 || updatedTranslations) {
188
+ stdout.write(ansis.bold('\n📋 Update Summary:\n'));
189
+ if (updateSummary.plugins.length > 0) {
190
+ stdout.write(ansis.green('🔌 Plugins updated:\n'));
191
+ for (const plugin of updateSummary.plugins) {
192
+ stdout.write(ansis.cyan(` • ${plugin.title} (${plugin.fromVersion} → ${plugin.toVersion})\n`));
193
+ }
194
+ }
195
+ if (updateSummary.themes.length > 0) {
196
+ stdout.write(ansis.green('🎨 Themes updated:\n'));
197
+ for (const theme of updateSummary.themes) {
198
+ stdout.write(ansis.cyan(` • ${theme.title} (${theme.fromVersion} → ${theme.toVersion})\n`));
199
+ }
200
+ }
201
+ if (updateSummary.core) {
202
+ stdout.write(ansis.green('📦 WordPress core updated:\n'));
203
+ stdout.write(ansis.cyan(` • WordPress (${updateSummary.core.fromVersion} → ${updateSummary.core.toVersion})\n`));
204
+ }
205
+ if (updateSummary.translations) {
206
+ stdout.write(ansis.green('🌐 Translations updated\n'));
207
+ }
208
+ }
213
209
  if (totalUpdates > 0 && branchRef.created) {
214
210
  stdout.write(ansis.yellow('\nNext steps:\n'));
215
- stdout.write(`• Review updates: ${ansis.dim('jolt wp update modify')}\n`);
211
+ stdout.write(`• Review updates: ${ansis.dim(await this.getUpdateCommand('modify'))}\n`);
216
212
  // Use root config value directly
217
- stdout.write(`• Merge to ${await config.get('branch')}: ${ansis.dim('jolt wp update merge')}\n`);
213
+ stdout.write(`• Merge to ${await config.get('branch')}: ${ansis.dim(await this.getUpdateCommand('merge'))}\n`);
218
214
  }
219
- else if (totalUpdates === 0) {
215
+ else if (totalUpdates === 0 && !updatedTranslations) {
220
216
  stdout.write(ansis.green('\n✅ No updates available - staying on current branch\n'));
221
217
  }
222
218
  return 0;
223
219
  }
224
- // Note: WP CLI invocations run via `yarn jolt wp cli ...` using execC directly.
225
- parsePluginJson(pluginsJson) {
226
- // Trim off any preceding warnings, try to look for the start of the actual JSON.
227
- const trimmed = pluginsJson.substring(pluginsJson.indexOf('[{'));
228
- const allPlugins = JSON.parse(trimmed);
229
- return allPlugins.filter((plugin) => !['dropin', 'must-use'].includes(plugin.status));
230
- }
231
- parseThemeJson(themeJson) {
232
- // Trim off any preceding warnings, try to look for the start of the actual JSON.
233
- const trimmed = themeJson.substring(themeJson.indexOf('[{'));
234
- return JSON.parse(trimmed);
235
- }
236
- // (No helper) obtain the package runner directly via config.command('yarn') where needed
237
- async createBranch() {
238
- const { config, context } = this;
239
- const isoDate = new Date().toISOString().replace(/[:.]/g, '-').replace(/T/, '_').slice(0, -5);
240
- const branchName = `joltWpUpdate/${isoDate}`;
241
- const gitCommand = await config.command('git');
242
- await execC(gitCommand, ['checkout', '-b', branchName], { context });
243
- return branchName;
244
- }
245
- async ensureBranchCreated(_wpConfig, branchRef) {
246
- if (!branchRef.created) {
247
- branchRef.branch = await this.createBranch();
248
- branchRef.created = true;
249
- const { context: { stdout }, } = this;
250
- stdout.write(ansis.green(`📋 Created update branch ${branchRef.branch}\n`));
251
- }
252
- return branchRef.branch;
253
- }
254
- async disableGitHooks() {
255
- const { config, context } = this;
256
- const gitCommand = await config.command('git');
257
- try {
258
- const result = await execC(gitCommand, ['config', '--get', 'core.hooksPath'], { context, reject: false });
259
- const originalHookPath = String(result.stdout || '').trim();
260
- await execC(gitCommand, ['config', 'core.hooksPath', '/dev/null'], { context });
261
- return originalHookPath;
262
- }
263
- catch (_a) {
264
- await execC(gitCommand, ['config', 'core.hooksPath', '/dev/null'], { context });
265
- return '';
220
+ // Helper method to get the appropriate command format (short or long form)
221
+ async getUpdateCommand(subCommand) {
222
+ var _a;
223
+ const { config } = this;
224
+ const yarnCommand = await config.command('yarn');
225
+ const packageJson = await config.getPackageJson();
226
+ // Check if there's an 'update' script that acts as a shortcut for 'jolt wp update'
227
+ const updateScript = (_a = packageJson === null || packageJson === void 0 ? void 0 : packageJson.scripts) === null || _a === void 0 ? void 0 : _a.update;
228
+ if (updateScript && (updateScript === 'jolt wp update' || updateScript.startsWith('jolt wp update '))) {
229
+ return `${yarnCommand} update ${subCommand}`;
266
230
  }
231
+ // Fall back to the full command
232
+ return `${yarnCommand} jolt wp update ${subCommand}`;
267
233
  }
268
- async rollbackGitHooks(originalHookPath) {
234
+ // Helper method to execute WP CLI commands
235
+ async executeWpCli(args, options) {
269
236
  const { config, context } = this;
270
- const gitCommand = await config.command('git');
271
- if (originalHookPath) {
272
- await execC(gitCommand, ['config', 'core.hooksPath', originalHookPath], { context });
273
- }
274
- else {
275
- await execC(gitCommand, ['config', '--unset', 'core.hooksPath'], { context, reject: false });
276
- }
277
- }
278
- async maybeUpdatePlugin(plugin, wpConfig, branchRef) {
237
+ const yarnCommand = await config.command('yarn');
238
+ // For silent operations, don't pass context to suppress output
239
+ const execOptions = {
240
+ reject: false,
241
+ ...options,
242
+ ...(!(options === null || options === void 0 ? void 0 : options.silent) ? { context } : {}),
243
+ };
244
+ const result = await execC(yarnCommand, ['jolt', 'wp', 'cli', ...args], execOptions);
245
+ return {
246
+ exitCode: result.exitCode || 0,
247
+ stdout: result.exitCode === 0 ? String(result.stdout || '') : null,
248
+ };
249
+ }
250
+ // Configuration for different item types
251
+ getItemConfig(type) {
252
+ const configs = {
253
+ plugin: {
254
+ type: 'plugin',
255
+ icon: '🔌',
256
+ listCommand: ['plugin', 'list', '--json'],
257
+ updateCommand: (name) => ['plugin', 'update', name],
258
+ getFolder: (wpConfig) => wpConfig.pluginFolder,
259
+ commitPrefix: 'Update',
260
+ },
261
+ theme: {
262
+ type: 'theme',
263
+ icon: '🎨',
264
+ listCommand: ['theme', 'list', '--json'],
265
+ updateCommand: (name) => ['theme', 'update', name],
266
+ getFolder: (wpConfig) => wpConfig.themeFolder,
267
+ commitPrefix: 'Update theme',
268
+ },
269
+ };
270
+ return configs[type];
271
+ }
272
+ // Generic method to get and parse item lists
273
+ async getItems(type) {
279
274
  const { context: { stdout }, } = this;
280
- if (wpConfig.doNotUpdate.includes(plugin.name)) {
281
- stdout.write(ansis.dim(` Skipping ${plugin.name} (configured to skip)\n`));
282
- return false;
283
- }
284
- stdout.write(` Checking ${plugin.name}...`);
285
- if (plugin.update === 'available') {
286
- stdout.write(ansis.green(' updating\n'));
287
- return await this.updatePlugin(plugin, wpConfig, branchRef);
288
- }
289
- if (plugin.update === 'none') {
290
- stdout.write(ansis.dim(' up to date\n'));
291
- }
292
- else if (plugin.update === 'version higher than expected') {
293
- stdout.write(ansis.yellow(` local version ${plugin.version} is higher than remote\n`));
294
- }
295
- else {
296
- stdout.write(ansis.red(` unknown status: ${plugin.update}\n`));
275
+ const itemConfig = this.getItemConfig(type);
276
+ stdout.write(ansis.cyan(`${itemConfig.icon} Checking ${type}s...\n`));
277
+ const result = await this.executeWpCli(itemConfig.listCommand, { silent: true });
278
+ if (!result.stdout) {
279
+ return [];
280
+ }
281
+ const items = this.parseItemJson(result.stdout);
282
+ stdout.write(`Found ${items.length} ${type}s\n`);
283
+ return items;
284
+ }
285
+ // Generic method to parse item JSON (plugins/themes have same structure)
286
+ parseItemJson(itemJson) {
287
+ // Trim off any preceding warnings, try to look for the start of the actual JSON.
288
+ const trimmed = itemJson.substring(itemJson.indexOf('[{'));
289
+ const allItems = JSON.parse(trimmed);
290
+ // For plugins, filter out dropin and must-use plugins
291
+ return allItems.filter((item) => !['dropin', 'must-use'].includes(item.status));
292
+ }
293
+ // Generic method to handle updating items
294
+ async processItemUpdates(type, skip, wpConfig, branchRef) {
295
+ if (skip) {
296
+ return { count: 0, details: [] };
297
+ }
298
+ const itemConfig = this.getItemConfig(type);
299
+ const items = await this.getItems(type);
300
+ const updateDetails = [];
301
+ let count = 0;
302
+ if (items.length > 0) {
303
+ this.context.stdout.write(ansis.cyan(`${itemConfig.icon} Updating ${type}s...\n`));
304
+ for (const item of items) {
305
+ const result = await this.maybeUpdateItem(item, wpConfig, branchRef, itemConfig);
306
+ if (result.updated) {
307
+ count++;
308
+ if (result.details) {
309
+ updateDetails.push(result.details);
310
+ }
311
+ }
312
+ }
297
313
  }
298
- return false;
314
+ return { count, details: updateDetails };
299
315
  }
300
- async maybeUpdateTheme(theme, wpConfig, branchRef) {
316
+ // Generic method to check and maybe update an item (plugin/theme)
317
+ async maybeUpdateItem(item, wpConfig, branchRef, itemConfig) {
301
318
  const { context: { stdout }, } = this;
302
- if (wpConfig.doNotUpdate.includes(theme.name)) {
303
- stdout.write(ansis.dim(` Skipping ${theme.name} (configured to skip)\n`));
304
- return false;
319
+ if (wpConfig.doNotUpdate.includes(item.name)) {
320
+ stdout.write(ansis.dim(` Skipping ${item.name} (configured to skip)\n`));
321
+ return { updated: false };
305
322
  }
306
- stdout.write(` Checking ${theme.name}...`);
307
- if (theme.update === 'available') {
323
+ stdout.write(` Checking ${item.name}...`);
324
+ if (item.update === 'available') {
308
325
  stdout.write(ansis.green(' updating\n'));
309
- return await this.updateTheme(theme, wpConfig, branchRef);
326
+ return await this.updateItem(item, wpConfig, branchRef, itemConfig);
310
327
  }
311
- if (theme.update === 'none') {
328
+ if (item.update === 'none') {
312
329
  stdout.write(ansis.dim(' up to date\n'));
313
330
  }
314
- else if (theme.update === 'version higher than expected') {
315
- stdout.write(ansis.yellow(` local version ${theme.version} is higher than remote\n`));
331
+ else if (item.update === 'version higher than expected') {
332
+ stdout.write(ansis.yellow(` local version ${item.version} is higher than remote\n`));
316
333
  }
317
334
  else {
318
- stdout.write(ansis.red(` unknown status: ${theme.update}\n`));
335
+ stdout.write(ansis.red(` unknown status: ${item.update}\n`));
319
336
  }
320
- return false;
337
+ return { updated: false };
321
338
  }
322
- async updatePlugin(plugin, wpConfig, branchRef) {
323
- const { config, context, context: { stdout, stderr }, } = this;
339
+ // Generic method to update an item (plugin/theme)
340
+ async updateItem(item, wpConfig, branchRef, itemConfig) {
341
+ const { config, context: { stdout, stderr }, } = this;
324
342
  try {
325
343
  const gitCommand = await config.command('git');
326
- const details = await this.getPluginDetails(plugin.name);
344
+ const details = await this.getDetails(item.name, itemConfig.type);
327
345
  if (!details) {
328
- return false;
346
+ return { updated: false };
329
347
  }
330
348
  const fromVersion = details.version;
331
349
  const prettyTitle = this.cleanTitle(details.title);
332
- const location = `${wpConfig.pluginFolder}/${plugin.name}`;
333
- const yarnCommand = await config.command('yarn');
334
- const updateResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', 'plugin', 'update', plugin.name], {
335
- reject: false,
336
- });
337
- const updateResultStr = updateResult.exitCode === 0 ? String(updateResult.stdout || '') : null;
338
- if (!updateResultStr) {
339
- stderr.write(ansis.red(` Error updating ${plugin.name}\n`));
340
- return false;
350
+ const location = `${itemConfig.getFolder(wpConfig)}/${item.name}`;
351
+ const updateResult = await this.executeWpCli(itemConfig.updateCommand(item.name), { silent: true });
352
+ if (updateResult.exitCode !== 0) {
353
+ stderr.write(ansis.red(` Error updating ${item.name}\n`));
354
+ return { updated: false };
341
355
  }
342
- const newDetails = await this.getPluginDetails(plugin.name);
356
+ const newDetails = await this.getDetails(item.name, itemConfig.type);
343
357
  if (!newDetails || newDetails.version === details.version) {
344
358
  stderr.write(ansis.red(' Update failed!\n'));
345
- return false;
359
+ return { updated: false };
346
360
  }
347
361
  // Ensure branch is created before making our first commit
348
362
  await this.ensureBranchCreated(wpConfig, branchRef);
349
- const commitMessage = this.sanitizeCommitMessage(`Update ${prettyTitle} to ${newDetails.version}`);
350
- await execC(gitCommand, ['add', location], { context });
363
+ const commitMessage = this.sanitizeCommitMessage(`${itemConfig.commitPrefix} ${prettyTitle} to ${newDetails.version}`);
364
+ await execC(gitCommand, ['add', location]);
351
365
  await execC(gitCommand, ['commit', '-m', commitMessage], {
352
- context,
353
366
  shell: false,
354
367
  env: { SKIP: 'prepare-commit-msg' },
355
368
  });
356
369
  stdout.write(ansis.green(` Updated ${prettyTitle} from ${fromVersion} to ${newDetails.version}\n`));
357
- return true;
370
+ return {
371
+ updated: true,
372
+ details: {
373
+ name: item.name,
374
+ title: prettyTitle,
375
+ fromVersion,
376
+ toVersion: newDetails.version,
377
+ },
378
+ };
358
379
  }
359
380
  catch (error) {
360
- stderr.write(ansis.red(` Error updating ${plugin.name}: ${error}\n`));
361
- return false;
381
+ stderr.write(ansis.red(` Error updating ${item.name}: ${error}\n`));
382
+ return { updated: false };
362
383
  }
363
384
  }
364
- async updateTheme(theme, wpConfig, branchRef) {
365
- const { config, context, context: { stdout, stderr }, } = this;
385
+ async createBranch() {
386
+ const { config } = this;
387
+ const isoDate = new Date().toISOString().replace(/[:.]/g, '-').replace(/T/, '_').slice(0, -5);
388
+ const branchName = `joltWpUpdate/${isoDate}`;
389
+ const gitCommand = await config.command('git');
390
+ await execC(gitCommand, ['checkout', '-b', branchName]);
391
+ return branchName;
392
+ }
393
+ async ensureBranchCreated(_wpConfig, branchRef) {
394
+ if (!branchRef.created) {
395
+ branchRef.branch = await this.createBranch();
396
+ branchRef.created = true;
397
+ const { context: { stdout }, } = this;
398
+ stdout.write(ansis.green(`📋 Created update branch ${branchRef.branch}\n`));
399
+ }
400
+ return branchRef.branch;
401
+ }
402
+ async disableGitHooks() {
403
+ const { config, context } = this;
404
+ const gitCommand = await config.command('git');
366
405
  try {
367
- const gitCommand = await config.command('git');
368
- const details = await this.getThemeDetails(theme.name);
369
- if (!details) {
370
- return false;
371
- }
372
- const fromVersion = details.version;
373
- const prettyTitle = this.cleanTitle(details.title);
374
- const location = `${wpConfig.themeFolder}/${theme.name}`;
375
- const yarnCommand = await config.command('yarn');
376
- const updateResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', 'theme', 'update', theme.name], {
377
- reject: false,
378
- });
379
- const updateResultStr = updateResult.exitCode === 0 ? String(updateResult.stdout || '') : null;
380
- if (!updateResultStr) {
381
- stderr.write(ansis.red(` Error updating ${theme.name}\n`));
382
- return false;
383
- }
384
- const newDetails = await this.getThemeDetails(theme.name);
385
- if (!newDetails || newDetails.version === details.version) {
386
- stderr.write(ansis.red(' Update failed!\n'));
387
- return false;
388
- }
389
- // Ensure branch is created before making our first commit
390
- await this.ensureBranchCreated(wpConfig, branchRef);
391
- const commitMessage = this.sanitizeCommitMessage(`Update theme ${prettyTitle} to ${newDetails.version}`);
392
- await execC(gitCommand, ['add', location], { context });
393
- await execC(gitCommand, ['commit', '-m', commitMessage], {
394
- context,
395
- shell: false,
396
- env: { SKIP: 'prepare-commit-msg' },
397
- });
398
- stdout.write(ansis.green(` Updated theme ${prettyTitle} from ${fromVersion} to ${newDetails.version}\n`));
399
- return true;
406
+ const result = await execC(gitCommand, ['config', '--get', 'core.hooksPath'], { context, reject: false });
407
+ const originalHookPath = String(result.stdout || '').trim();
408
+ await execC(gitCommand, ['config', 'core.hooksPath', '/dev/null'], { context });
409
+ return originalHookPath;
400
410
  }
401
- catch (error) {
402
- stderr.write(ansis.red(` Error updating ${theme.name}: ${error}\n`));
403
- return false;
411
+ catch (_a) {
412
+ await execC(gitCommand, ['config', 'core.hooksPath', '/dev/null'], { context });
413
+ return '';
404
414
  }
405
415
  }
406
- async getPluginDetails(pluginName) {
407
- return await this.getDetails(pluginName, 'plugin');
408
- }
409
- async getThemeDetails(themeName) {
410
- return await this.getDetails(themeName, 'theme');
416
+ async rollbackGitHooks(originalHookPath) {
417
+ const { config, context } = this;
418
+ const gitCommand = await config.command('git');
419
+ if (originalHookPath) {
420
+ await execC(gitCommand, ['config', 'core.hooksPath', originalHookPath], { context });
421
+ }
422
+ else {
423
+ await execC(gitCommand, ['config', '--unset', 'core.hooksPath'], { context, reject: false });
424
+ }
411
425
  }
412
426
  async getDetails(name, type) {
413
- const { config } = this;
414
- const cmdType = type === 'plugin' ? 'plugin' : 'theme';
415
427
  try {
416
- const yarnCommand = await config.command('yarn');
417
- const detailsResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', cmdType, 'get', '--json', name], {
418
- reject: false,
419
- });
420
- const out = detailsResult.exitCode === 0 ? String(detailsResult.stdout || '') : null;
421
- if (!out) {
428
+ const result = await this.executeWpCli([type, 'get', '--json', name], { silent: true });
429
+ if (!result.stdout) {
422
430
  return null;
423
431
  }
424
- let output = String(out || '');
432
+ let output = result.stdout;
425
433
  if (output.startsWith('$')) {
426
434
  // Older Yarn versions include the script name in stdout so we need to trim the first line off
427
435
  output = output.substring(1 + output.indexOf('\n'));
@@ -449,8 +457,10 @@ export class WPUpdateCommand extends JoltCommand {
449
457
  const newVersion = await this.hasCoreUpdate();
450
458
  if (!newVersion) {
451
459
  stdout.write(ansis.dim(' WordPress core is up to date\n'));
452
- return false;
460
+ return { updated: false };
453
461
  }
462
+ // Get current version before updating
463
+ const currentVersion = await this.getCurrentCoreVersion();
454
464
  stdout.write(ansis.green(` Updating WordPress core to ${newVersion}\n`));
455
465
  const shouldStash = await this.hasGitChanges(wpConfig.wpRoot);
456
466
  if (shouldStash) {
@@ -460,7 +470,13 @@ export class WPUpdateCommand extends JoltCommand {
460
470
  try {
461
471
  await this.doCoreUpdate(wpConfig.wpRoot, newVersion, wpConfig, branchRef);
462
472
  stdout.write(ansis.green(` Updated WordPress core to ${newVersion}\n`));
463
- return true;
473
+ return {
474
+ updated: true,
475
+ details: {
476
+ fromVersion: currentVersion || 'unknown',
477
+ toVersion: newVersion,
478
+ },
479
+ };
464
480
  }
465
481
  finally {
466
482
  if (shouldStash) {
@@ -469,22 +485,23 @@ export class WPUpdateCommand extends JoltCommand {
469
485
  }
470
486
  }
471
487
  }
488
+ async getCurrentCoreVersion() {
489
+ try {
490
+ const result = await this.executeWpCli(['core', 'version'], { silent: true });
491
+ return result.stdout ? result.stdout.trim() : null;
492
+ }
493
+ catch (_a) {
494
+ return null;
495
+ }
496
+ }
472
497
  async hasCoreUpdate() {
473
498
  var _a;
474
- const { config } = this;
475
499
  try {
476
- const yarnCommand = await config.command('yarn');
477
- const coreResult = await execC(yarnCommand, ['jolt', 'wp', 'cli', 'core', 'check-update', '--json'], {
478
- reject: false,
479
- });
480
- if (!coreResult || coreResult.exitCode !== 0) {
500
+ const result = await this.executeWpCli(['core', 'check-update', '--json'], { silent: true });
501
+ if (!result.stdout || !result.stdout.trim() || result.stdout.trim() === '[]') {
481
502
  return false;
482
503
  }
483
- const stdoutStr = String(coreResult.stdout || '');
484
- if (!stdoutStr.trim() || stdoutStr.trim() === '[]') {
485
- return false;
486
- }
487
- const trimmed = stdoutStr.substring(stdoutStr.indexOf('[{'));
504
+ const trimmed = result.stdout.substring(result.stdout.indexOf('[{'));
488
505
  const parsed = JSON.parse(trimmed);
489
506
  return ((_a = parsed[0]) === null || _a === void 0 ? void 0 : _a.version) || false;
490
507
  }
@@ -493,11 +510,10 @@ export class WPUpdateCommand extends JoltCommand {
493
510
  }
494
511
  }
495
512
  async hasGitChanges(path) {
496
- const { config, context } = this;
513
+ const { config } = this;
497
514
  try {
498
515
  const gitCommand = await config.command('git');
499
516
  const result = await execC(gitCommand, ['status', '--porcelain=v1', '--', path], {
500
- context,
501
517
  reject: false,
502
518
  });
503
519
  return String(result.stdout || '').trim() !== '';
@@ -507,36 +523,92 @@ export class WPUpdateCommand extends JoltCommand {
507
523
  }
508
524
  }
509
525
  async stashChanges() {
510
- const { config, context } = this;
526
+ const { config } = this;
511
527
  const date = new Date().toISOString();
512
528
  const gitCommand = await config.command('git');
513
- await execC(gitCommand, ['add', '.'], { context });
529
+ await execC(gitCommand, ['add', '.']);
514
530
  await execC(gitCommand, ['stash', 'save', '--', `Automated stash by Jolt WP Updater at ${date}`], {
515
- context,
516
531
  shell: false,
517
532
  });
518
533
  }
519
534
  async unstashChanges() {
520
- const { config, context } = this;
535
+ const { config } = this;
521
536
  const gitCommand = await config.command('git');
522
- await execC(gitCommand, ['stash', 'pop'], { context });
523
- await execC(gitCommand, ['reset', 'HEAD', '--'], { context });
537
+ await execC(gitCommand, ['stash', 'pop']);
538
+ await execC(gitCommand, ['reset', 'HEAD', '--']);
524
539
  }
525
540
  async doCoreUpdate(path, version, wpConfig, branchRef) {
526
- const { config, context } = this;
541
+ const { config } = this;
527
542
  // Ensure branch is created before making our first commit
528
543
  await this.ensureBranchCreated(wpConfig, branchRef);
529
544
  const gitCommand = await config.command('git');
530
- const yarnCommand = await config.command('yarn');
531
- await execC(yarnCommand, ['jolt', 'wp', 'cli', 'core', 'update'], { context, reject: false });
532
- await execC(gitCommand, ['add', path], { context });
545
+ await this.executeWpCli(['core', 'update'], { silent: true });
546
+ await execC(gitCommand, ['add', path]);
533
547
  const commitMessage = this.sanitizeCommitMessage(`Update WordPress to ${version}`);
534
548
  await execC(gitCommand, ['commit', '-m', commitMessage], {
535
- context,
536
549
  shell: false,
537
550
  env: { SKIP: 'prepare-commit-msg' },
538
551
  });
539
552
  }
553
+ async maybeUpdateTranslations(branchRef) {
554
+ const { config, context: { stdout, stderr }, } = this;
555
+ try {
556
+ let hasUpdates = false;
557
+ // Helper to check and update translations for a specific type
558
+ const updateTranslationType = async (type, command) => {
559
+ stdout.write(` Checking ${type} translations...`);
560
+ const result = await this.executeWpCli(command, { silent: true });
561
+ if (result.exitCode === 0 && result.stdout) {
562
+ if (result.stdout.includes('Updated') || result.stdout.includes('updated')) {
563
+ hasUpdates = true;
564
+ stdout.write(ansis.green(' updated\n'));
565
+ }
566
+ else {
567
+ stdout.write(ansis.dim(' up to date\n'));
568
+ }
569
+ }
570
+ else {
571
+ stdout.write(ansis.dim(' skipped (not available)\n'));
572
+ }
573
+ };
574
+ // Update different translation types
575
+ await updateTranslationType('core', ['language', 'core', 'update']);
576
+ await updateTranslationType('plugin', ['language', 'plugin', 'update', '--all']);
577
+ await updateTranslationType('theme', ['language', 'theme', 'update', '--all']);
578
+ // If we have translation updates, commit them
579
+ if (hasUpdates) {
580
+ const wpConfig = await config.loadWordPressConfig();
581
+ if (!wpConfig) {
582
+ stderr.write(ansis.red('Failed to load WordPress configuration\n'));
583
+ return false;
584
+ }
585
+ // Create or ensure we're on the update branch
586
+ await this.ensureBranchCreated(wpConfig, branchRef);
587
+ const gitCommand = await config.command('git');
588
+ // Add all language files that might have been updated
589
+ await execC(gitCommand, ['add', wpConfig.wpRoot]);
590
+ // Check if there are any changes to commit
591
+ const statusResult = await execC(gitCommand, ['diff', '--cached', '--exit-code'], {
592
+ reject: false,
593
+ });
594
+ if (statusResult.exitCode !== 0) {
595
+ // There are changes to commit
596
+ await execC(gitCommand, ['commit', '-m', 'Update translations'], {
597
+ shell: false,
598
+ env: { SKIP: 'prepare-commit-msg' },
599
+ });
600
+ stdout.write(ansis.green(' Committed translation updates\n'));
601
+ return true;
602
+ }
603
+ stdout.write(ansis.dim(' No translation files changed\n'));
604
+ }
605
+ return false;
606
+ }
607
+ catch (error) {
608
+ stderr.write(ansis.red(`Error updating translations: ${error}\n`));
609
+ return false;
610
+ }
611
+ }
540
612
  }
541
613
  WPUpdateCommand.paths = [['wp', 'update']];
542
614
  export class WPUpdateMergeCommand extends JoltCommand {
@@ -720,6 +792,9 @@ export class WPUpdateModifyCommand extends JoltCommand {
720
792
  constructor() {
721
793
  super(...arguments);
722
794
  this.requiredCommands = ['git'];
795
+ this.autostash = Option.Boolean('--autostash', false, {
796
+ description: 'Automatically stash and unstash changes before and after the rebase',
797
+ });
723
798
  }
724
799
  async command() {
725
800
  const { config, context, context: { stdout, stderr }, logo, } = this;
@@ -732,7 +807,7 @@ export class WPUpdateModifyCommand extends JoltCommand {
732
807
  try {
733
808
  const gitCommand = await config.command('git');
734
809
  // Get current branch
735
- const currentBranchResult = await execC(gitCommand, ['branch', '--show-current'], { context });
810
+ const currentBranchResult = await execC(gitCommand, ['branch', '--show-current']);
736
811
  const currentBranch = String(currentBranchResult.stdout || '').trim();
737
812
  if (!currentBranch.startsWith('joltWpUpdate/')) {
738
813
  stderr.write(ansis.red('Not currently on a WordPress update branch\n'));
@@ -741,7 +816,6 @@ export class WPUpdateModifyCommand extends JoltCommand {
741
816
  // Count commits since branching from main
742
817
  const branchName = (await config.get('branch')) || 'master';
743
818
  const commitCountResult = await execC(gitCommand, ['rev-list', '--count', `${branchName}..HEAD`], {
744
- context,
745
819
  reject: false,
746
820
  });
747
821
  if (commitCountResult.exitCode !== 0) {
@@ -755,7 +829,11 @@ export class WPUpdateModifyCommand extends JoltCommand {
755
829
  }
756
830
  stdout.write(ansis.cyan(`📋 Starting interactive rebase for ${commitCount} commits...\n`));
757
831
  // Run interactive rebase
758
- const rebaseResult = await execC(gitCommand, ['rebase', '-i', `HEAD~${commitCount}`], {
832
+ const rebaseArgs = ['rebase', '-i', `HEAD~${commitCount}`];
833
+ if (this.autostash) {
834
+ rebaseArgs.push('--autostash');
835
+ }
836
+ const rebaseResult = await execC(gitCommand, rebaseArgs, {
759
837
  context,
760
838
  reject: false,
761
839
  });