@joltdesign/scripts 0.21.0 → 0.22.1
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.
- package/dist/Command/AWS.js.map +1 -1
- package/dist/Command/Config.js +7 -2
- package/dist/Command/Config.js.map +1 -1
- package/dist/Command/DB.js +154 -21
- package/dist/Command/DB.js.map +1 -1
- package/dist/Command/Docker.js +4 -3
- package/dist/Command/Docker.js.map +1 -1
- package/dist/Command/JoltCommand.js +8 -6
- package/dist/Command/JoltCommand.js.map +1 -1
- package/dist/Command/Nexcess.js +8 -7
- package/dist/Command/Nexcess.js.map +1 -1
- package/dist/Command/Prepare.js.map +1 -1
- package/dist/Command/SSH.js +8 -6
- package/dist/Command/SSH.js.map +1 -1
- package/dist/Command/WP.js +376 -244
- package/dist/Command/WP.js.map +1 -1
- package/dist/Config.js +109 -18
- package/dist/Config.js.map +1 -1
- package/dist/schemas.js +7 -0
- package/dist/schemas.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/compose.js +2 -0
- package/dist/types/compose.js.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/package.js +2 -0
- package/dist/types/package.js.map +1 -0
- package/dist/utils.js.map +1 -1
- package/package.json +4 -7
- package/dist/AWSClient.js +0 -102
- package/dist/AWSClient.js.map +0 -1
package/dist/Command/WP.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
1
2
|
import { userInfo } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
2
4
|
import ansis from 'ansis';
|
|
3
5
|
import { Option } from 'clipanion';
|
|
4
6
|
import * as t from 'typanion';
|
|
5
|
-
import { execC, which } from '../utils.js';
|
|
7
|
+
import { execC, fileExists, which } from '../utils.js';
|
|
6
8
|
import JoltCommand from './JoltCommand.js';
|
|
7
9
|
// Possible sub-arguments for the CLI command as of WP-CLI v2.12.0
|
|
8
10
|
const possibleCliArgs = [
|
|
@@ -113,9 +115,10 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
113
115
|
this.skipCore = Option.Boolean('--skip-core', false, { description: 'Skip WordPress core updates' });
|
|
114
116
|
this.skipPlugins = Option.Boolean('--skip-plugins', false, { description: 'Skip plugin updates' });
|
|
115
117
|
this.skipThemes = Option.Boolean('--skip-themes', false, { description: 'Skip theme updates' });
|
|
118
|
+
this.skipLanguages = Option.Boolean('--skip-languages', false, { description: 'Skip language/translation updates' });
|
|
116
119
|
}
|
|
117
120
|
async command() {
|
|
118
|
-
const { config, context: { stdout, stderr }, skipCore, skipPlugins, skipThemes, logo, } = this;
|
|
121
|
+
const { config, context: { stdout, stderr }, skipCore, skipPlugins, skipThemes, skipLanguages, logo, } = this;
|
|
119
122
|
stdout.write(ansis.bold(`${logo} WordPress Updates\n\n`));
|
|
120
123
|
// Load configuration
|
|
121
124
|
const wpConfig = await config.loadWordPressConfig();
|
|
@@ -127,30 +130,19 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
127
130
|
let updatedPluginCount = 0;
|
|
128
131
|
let updatedThemeCount = 0;
|
|
129
132
|
let updatedCore = false;
|
|
133
|
+
// Track detailed update information for summary
|
|
134
|
+
const updateSummary = {
|
|
135
|
+
plugins: [],
|
|
136
|
+
themes: [],
|
|
137
|
+
core: null,
|
|
138
|
+
translations: false,
|
|
139
|
+
};
|
|
130
140
|
// Get current status
|
|
131
141
|
if (!skipPlugins) {
|
|
132
|
-
|
|
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
|
-
}
|
|
142
|
+
await this.getItems('plugin');
|
|
142
143
|
}
|
|
143
144
|
if (!skipThemes) {
|
|
144
|
-
|
|
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
|
-
}
|
|
145
|
+
await this.getItems('theme');
|
|
154
146
|
}
|
|
155
147
|
// We'll create the update branch only when we need to make our first commit
|
|
156
148
|
const branchRef = { branch: undefined, created: false };
|
|
@@ -158,45 +150,21 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
158
150
|
const originalHookPath = await this.disableGitHooks();
|
|
159
151
|
try {
|
|
160
152
|
// Update plugins
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
}
|
|
153
|
+
const pluginUpdates = await this.processItemUpdates('plugin', skipPlugins, wpConfig, branchRef);
|
|
154
|
+
updatedPluginCount = pluginUpdates.count;
|
|
155
|
+
updateSummary.plugins = pluginUpdates.details;
|
|
178
156
|
// Update themes
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
}
|
|
157
|
+
const themeUpdates = await this.processItemUpdates('theme', skipThemes, wpConfig, branchRef);
|
|
158
|
+
updatedThemeCount = themeUpdates.count;
|
|
159
|
+
updateSummary.themes = themeUpdates.details;
|
|
196
160
|
// Update core
|
|
197
161
|
if (!skipCore) {
|
|
198
162
|
stdout.write(ansis.cyan('📦 Checking WordPress core...\n'));
|
|
199
|
-
|
|
163
|
+
const coreResult = await this.maybeUpdateCore(wpConfig, branchRef);
|
|
164
|
+
updatedCore = coreResult.updated;
|
|
165
|
+
if (coreResult.updated && coreResult.details) {
|
|
166
|
+
updateSummary.core = coreResult.details;
|
|
167
|
+
}
|
|
200
168
|
}
|
|
201
169
|
}
|
|
202
170
|
finally {
|
|
@@ -210,218 +178,280 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
210
178
|
stdout.write(ansis.cyan('📦 Updated WordPress core\n'));
|
|
211
179
|
}
|
|
212
180
|
const totalUpdates = updatedPluginCount + updatedThemeCount + (updatedCore ? 1 : 0);
|
|
181
|
+
// Update translations if possible
|
|
182
|
+
let updatedTranslations = false;
|
|
183
|
+
if (!skipLanguages && (totalUpdates > 0 || !branchRef.created)) {
|
|
184
|
+
stdout.write(ansis.cyan('🌐 Updating translations...\n'));
|
|
185
|
+
updatedTranslations = await this.maybeUpdateTranslations(branchRef);
|
|
186
|
+
updateSummary.translations = updatedTranslations;
|
|
187
|
+
}
|
|
188
|
+
// Show detailed update summary
|
|
189
|
+
if (totalUpdates > 0 || updatedTranslations) {
|
|
190
|
+
stdout.write(ansis.bold('\n📋 Update Summary:\n'));
|
|
191
|
+
if (updateSummary.plugins.length > 0) {
|
|
192
|
+
stdout.write(ansis.green('🔌 Plugins updated:\n'));
|
|
193
|
+
for (const plugin of updateSummary.plugins) {
|
|
194
|
+
stdout.write(ansis.cyan(` • ${plugin.title} (${plugin.fromVersion} → ${plugin.toVersion})\n`));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (updateSummary.themes.length > 0) {
|
|
198
|
+
stdout.write(ansis.green('🎨 Themes updated:\n'));
|
|
199
|
+
for (const theme of updateSummary.themes) {
|
|
200
|
+
stdout.write(ansis.cyan(` • ${theme.title} (${theme.fromVersion} → ${theme.toVersion})\n`));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (updateSummary.core) {
|
|
204
|
+
stdout.write(ansis.green('📦 WordPress core updated:\n'));
|
|
205
|
+
stdout.write(ansis.cyan(` • WordPress (${updateSummary.core.fromVersion} → ${updateSummary.core.toVersion})\n`));
|
|
206
|
+
}
|
|
207
|
+
if (updateSummary.translations) {
|
|
208
|
+
stdout.write(ansis.green('🌐 Translations updated\n'));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
213
211
|
if (totalUpdates > 0 && branchRef.created) {
|
|
214
212
|
stdout.write(ansis.yellow('\nNext steps:\n'));
|
|
215
|
-
stdout.write(`• Review updates: ${ansis.dim('
|
|
213
|
+
stdout.write(`• Review updates: ${ansis.dim(await this.getUpdateCommand('modify'))}\n`);
|
|
216
214
|
// Use root config value directly
|
|
217
|
-
stdout.write(`• Merge to ${await config.get('branch')}: ${ansis.dim('
|
|
215
|
+
stdout.write(`• Merge to ${await config.get('branch')}: ${ansis.dim(await this.getUpdateCommand('merge'))}\n`);
|
|
218
216
|
}
|
|
219
|
-
else if (totalUpdates === 0) {
|
|
217
|
+
else if (totalUpdates === 0 && !updatedTranslations) {
|
|
220
218
|
stdout.write(ansis.green('\n✅ No updates available - staying on current branch\n'));
|
|
221
219
|
}
|
|
222
220
|
return 0;
|
|
223
221
|
}
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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 '';
|
|
222
|
+
// Helper method to get the appropriate command format (short or long form)
|
|
223
|
+
async getUpdateCommand(subCommand) {
|
|
224
|
+
var _a;
|
|
225
|
+
const { config } = this;
|
|
226
|
+
const yarnCommand = await config.command('yarn');
|
|
227
|
+
const packageJson = await config.getPackageJson();
|
|
228
|
+
// Check if there's an 'update' script that acts as a shortcut for 'jolt wp update'
|
|
229
|
+
const updateScript = (_a = packageJson === null || packageJson === void 0 ? void 0 : packageJson.scripts) === null || _a === void 0 ? void 0 : _a.update;
|
|
230
|
+
if (updateScript && (updateScript === 'jolt wp update' || updateScript.startsWith('jolt wp update '))) {
|
|
231
|
+
return `${yarnCommand} update ${subCommand}`;
|
|
266
232
|
}
|
|
233
|
+
// Fall back to the full command
|
|
234
|
+
return `${yarnCommand} jolt wp update ${subCommand}`;
|
|
267
235
|
}
|
|
268
|
-
|
|
236
|
+
// Helper method to execute WP CLI commands
|
|
237
|
+
async executeWpCli(args, options) {
|
|
269
238
|
const { config, context } = this;
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
239
|
+
const yarnCommand = await config.command('yarn');
|
|
240
|
+
// For silent operations, don't pass context to suppress output
|
|
241
|
+
const execOptions = {
|
|
242
|
+
reject: false,
|
|
243
|
+
...options,
|
|
244
|
+
...(!(options === null || options === void 0 ? void 0 : options.silent) ? { context } : {}),
|
|
245
|
+
};
|
|
246
|
+
const result = await execC(yarnCommand, ['jolt', 'wp', 'cli', ...args], execOptions);
|
|
247
|
+
return {
|
|
248
|
+
exitCode: result.exitCode || 0,
|
|
249
|
+
stdout: result.exitCode === 0 ? String(result.stdout || '') : null,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
// Configuration for different item types
|
|
253
|
+
getItemConfig(type) {
|
|
254
|
+
const configs = {
|
|
255
|
+
plugin: {
|
|
256
|
+
type: 'plugin',
|
|
257
|
+
icon: '🔌',
|
|
258
|
+
listCommand: ['plugin', 'list', '--json'],
|
|
259
|
+
updateCommand: (name) => ['plugin', 'update', name],
|
|
260
|
+
getFolder: (wpConfig) => wpConfig.pluginFolder,
|
|
261
|
+
commitPrefix: 'Update',
|
|
262
|
+
},
|
|
263
|
+
theme: {
|
|
264
|
+
type: 'theme',
|
|
265
|
+
icon: '🎨',
|
|
266
|
+
listCommand: ['theme', 'list', '--json'],
|
|
267
|
+
updateCommand: (name) => ['theme', 'update', name],
|
|
268
|
+
getFolder: (wpConfig) => wpConfig.themeFolder,
|
|
269
|
+
commitPrefix: 'Update theme',
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
return configs[type];
|
|
273
|
+
}
|
|
274
|
+
// Generic method to get and parse item lists
|
|
275
|
+
async getItems(type) {
|
|
279
276
|
const { context: { stdout }, } = this;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
277
|
+
const itemConfig = this.getItemConfig(type);
|
|
278
|
+
stdout.write(ansis.cyan(`${itemConfig.icon} Checking ${type}s...\n`));
|
|
279
|
+
const result = await this.executeWpCli(itemConfig.listCommand, { silent: true });
|
|
280
|
+
if (!result.stdout) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
const items = this.parseItemJson(result.stdout);
|
|
284
|
+
stdout.write(`Found ${items.length} ${type}s\n`);
|
|
285
|
+
return items;
|
|
286
|
+
}
|
|
287
|
+
// Generic method to parse item JSON (plugins/themes have same structure)
|
|
288
|
+
parseItemJson(itemJson) {
|
|
289
|
+
// Trim off any preceding warnings, try to look for the start of the actual JSON.
|
|
290
|
+
const trimmed = itemJson.substring(itemJson.indexOf('[{'));
|
|
291
|
+
const allItems = JSON.parse(trimmed);
|
|
292
|
+
// For plugins, filter out dropin and must-use plugins
|
|
293
|
+
return allItems.filter((item) => !['dropin', 'must-use'].includes(item.status));
|
|
294
|
+
}
|
|
295
|
+
// Generic method to handle updating items
|
|
296
|
+
async processItemUpdates(type, skip, wpConfig, branchRef) {
|
|
297
|
+
if (skip) {
|
|
298
|
+
return { count: 0, details: [] };
|
|
299
|
+
}
|
|
300
|
+
const itemConfig = this.getItemConfig(type);
|
|
301
|
+
const items = await this.getItems(type);
|
|
302
|
+
const updateDetails = [];
|
|
303
|
+
let count = 0;
|
|
304
|
+
if (items.length > 0) {
|
|
305
|
+
this.context.stdout.write(ansis.cyan(`${itemConfig.icon} Updating ${type}s...\n`));
|
|
306
|
+
for (const item of items) {
|
|
307
|
+
const result = await this.maybeUpdateItem(item, wpConfig, branchRef, itemConfig);
|
|
308
|
+
if (result.updated) {
|
|
309
|
+
count++;
|
|
310
|
+
if (result.details) {
|
|
311
|
+
updateDetails.push(result.details);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
297
315
|
}
|
|
298
|
-
return
|
|
316
|
+
return { count, details: updateDetails };
|
|
299
317
|
}
|
|
300
|
-
|
|
318
|
+
// Generic method to check and maybe update an item (plugin/theme)
|
|
319
|
+
async maybeUpdateItem(item, wpConfig, branchRef, itemConfig) {
|
|
301
320
|
const { context: { stdout }, } = this;
|
|
302
|
-
if (wpConfig.doNotUpdate.includes(
|
|
303
|
-
stdout.write(ansis.dim(` Skipping ${
|
|
304
|
-
return false;
|
|
321
|
+
if (wpConfig.doNotUpdate.includes(item.name)) {
|
|
322
|
+
stdout.write(ansis.dim(` Skipping ${item.name} (configured to skip)\n`));
|
|
323
|
+
return { updated: false };
|
|
305
324
|
}
|
|
306
|
-
stdout.write(` Checking ${
|
|
307
|
-
if (
|
|
325
|
+
stdout.write(` Checking ${item.name}...`);
|
|
326
|
+
if (item.update === 'available') {
|
|
308
327
|
stdout.write(ansis.green(' updating\n'));
|
|
309
|
-
return await this.
|
|
328
|
+
return await this.updateItem(item, wpConfig, branchRef, itemConfig);
|
|
310
329
|
}
|
|
311
|
-
if (
|
|
330
|
+
if (item.update === 'none') {
|
|
312
331
|
stdout.write(ansis.dim(' up to date\n'));
|
|
313
332
|
}
|
|
314
|
-
else if (
|
|
315
|
-
stdout.write(ansis.yellow(` local version ${
|
|
333
|
+
else if (item.update === 'version higher than expected') {
|
|
334
|
+
stdout.write(ansis.yellow(` local version ${item.version} is higher than remote\n`));
|
|
316
335
|
}
|
|
317
336
|
else {
|
|
318
|
-
stdout.write(ansis.red(` unknown status: ${
|
|
337
|
+
stdout.write(ansis.red(` unknown status: ${item.update}\n`));
|
|
319
338
|
}
|
|
320
|
-
return false;
|
|
339
|
+
return { updated: false };
|
|
321
340
|
}
|
|
322
|
-
|
|
323
|
-
|
|
341
|
+
// Generic method to update an item (plugin/theme)
|
|
342
|
+
async updateItem(item, wpConfig, branchRef, itemConfig) {
|
|
343
|
+
const { config, context: { stdout, stderr }, } = this;
|
|
324
344
|
try {
|
|
325
345
|
const gitCommand = await config.command('git');
|
|
326
|
-
const details = await this.
|
|
346
|
+
const details = await this.getDetails(item.name, itemConfig.type);
|
|
327
347
|
if (!details) {
|
|
328
|
-
return false;
|
|
348
|
+
return { updated: false };
|
|
329
349
|
}
|
|
330
350
|
const fromVersion = details.version;
|
|
331
351
|
const prettyTitle = this.cleanTitle(details.title);
|
|
332
|
-
const location = `${wpConfig
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
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;
|
|
352
|
+
const location = `${itemConfig.getFolder(wpConfig)}/${item.name}`;
|
|
353
|
+
const updateResult = await this.executeWpCli(itemConfig.updateCommand(item.name), { silent: true });
|
|
354
|
+
if (updateResult.exitCode !== 0) {
|
|
355
|
+
stderr.write(ansis.red(` Error updating ${item.name}\n`));
|
|
356
|
+
return { updated: false };
|
|
341
357
|
}
|
|
342
|
-
const newDetails = await this.
|
|
358
|
+
const newDetails = await this.getDetails(item.name, itemConfig.type);
|
|
343
359
|
if (!newDetails || newDetails.version === details.version) {
|
|
344
360
|
stderr.write(ansis.red(' Update failed!\n'));
|
|
345
|
-
return false;
|
|
361
|
+
return { updated: false };
|
|
346
362
|
}
|
|
347
363
|
// Ensure branch is created before making our first commit
|
|
348
364
|
await this.ensureBranchCreated(wpConfig, branchRef);
|
|
349
|
-
|
|
350
|
-
|
|
365
|
+
// Special case handling for Redis dropin
|
|
366
|
+
try {
|
|
367
|
+
const looksLikeRedis = /redis/i.test(String(item.name || details.title || ''));
|
|
368
|
+
if (looksLikeRedis) {
|
|
369
|
+
const cliResult = await this.executeWpCli(['redis', 'update-dropin'], { silent: true });
|
|
370
|
+
const wpContentDir = path.join(wpConfig.wpRoot, 'wp-content');
|
|
371
|
+
const dropinDest = path.join(wpContentDir, 'object-cache.php');
|
|
372
|
+
if (cliResult.exitCode === 0) {
|
|
373
|
+
const gitCommand = await config.command('git');
|
|
374
|
+
await execC(gitCommand, ['add', dropinDest]);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
// WP-CLI failed - fall back to copying the plugin-provided dropin
|
|
378
|
+
await this.updateRedisDropinFromPlugin(item, wpConfig, itemConfig);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (_a) {
|
|
383
|
+
// non-fatal - continue
|
|
384
|
+
}
|
|
385
|
+
const commitMessage = this.sanitizeCommitMessage(`${itemConfig.commitPrefix} ${prettyTitle} to ${newDetails.version}`);
|
|
386
|
+
await execC(gitCommand, ['add', location]);
|
|
351
387
|
await execC(gitCommand, ['commit', '-m', commitMessage], {
|
|
352
|
-
context,
|
|
353
388
|
shell: false,
|
|
354
389
|
env: { SKIP: 'prepare-commit-msg' },
|
|
355
390
|
});
|
|
356
391
|
stdout.write(ansis.green(` Updated ${prettyTitle} from ${fromVersion} to ${newDetails.version}\n`));
|
|
357
|
-
return
|
|
392
|
+
return {
|
|
393
|
+
updated: true,
|
|
394
|
+
details: {
|
|
395
|
+
name: item.name,
|
|
396
|
+
title: prettyTitle,
|
|
397
|
+
fromVersion,
|
|
398
|
+
toVersion: newDetails.version,
|
|
399
|
+
},
|
|
400
|
+
};
|
|
358
401
|
}
|
|
359
402
|
catch (error) {
|
|
360
|
-
stderr.write(ansis.red(` Error updating ${
|
|
361
|
-
return false;
|
|
403
|
+
stderr.write(ansis.red(` Error updating ${item.name}: ${error}\n`));
|
|
404
|
+
return { updated: false };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async createBranch() {
|
|
408
|
+
const { config } = this;
|
|
409
|
+
const isoDate = new Date().toISOString().replace(/[:.]/g, '-').replace(/T/, '_').slice(0, -5);
|
|
410
|
+
const branchName = `joltWpUpdate/${isoDate}`;
|
|
411
|
+
const gitCommand = await config.command('git');
|
|
412
|
+
await execC(gitCommand, ['checkout', '-b', branchName]);
|
|
413
|
+
return branchName;
|
|
414
|
+
}
|
|
415
|
+
async ensureBranchCreated(_wpConfig, branchRef) {
|
|
416
|
+
if (!branchRef.created) {
|
|
417
|
+
branchRef.branch = await this.createBranch();
|
|
418
|
+
branchRef.created = true;
|
|
419
|
+
const { context: { stdout }, } = this;
|
|
420
|
+
stdout.write(ansis.green(`📋 Created update branch ${branchRef.branch}\n`));
|
|
362
421
|
}
|
|
422
|
+
return branchRef.branch;
|
|
363
423
|
}
|
|
364
|
-
async
|
|
365
|
-
const { config, context, context: {
|
|
424
|
+
async disableGitHooks() {
|
|
425
|
+
const { config, context, context: { stderr }, } = this;
|
|
426
|
+
const gitCommand = await config.command('git');
|
|
366
427
|
try {
|
|
367
|
-
const
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
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;
|
|
428
|
+
const result = await execC(gitCommand, ['config', '--get', 'core.hooksPath'], { stderr, reject: false });
|
|
429
|
+
const originalHookPath = String(result.stdout || '').trim();
|
|
430
|
+
await execC(gitCommand, ['config', 'core.hooksPath', '/dev/null'], { context });
|
|
431
|
+
return originalHookPath;
|
|
400
432
|
}
|
|
401
|
-
catch (
|
|
402
|
-
|
|
403
|
-
return
|
|
433
|
+
catch (_a) {
|
|
434
|
+
await execC(gitCommand, ['config', 'core.hooksPath', '/dev/null'], { context });
|
|
435
|
+
return '';
|
|
404
436
|
}
|
|
405
437
|
}
|
|
406
|
-
async
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
438
|
+
async rollbackGitHooks(originalHookPath) {
|
|
439
|
+
const { config, context } = this;
|
|
440
|
+
const gitCommand = await config.command('git');
|
|
441
|
+
if (originalHookPath) {
|
|
442
|
+
await execC(gitCommand, ['config', 'core.hooksPath', originalHookPath], { context });
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
await execC(gitCommand, ['config', '--unset', 'core.hooksPath'], { context, reject: false });
|
|
446
|
+
}
|
|
411
447
|
}
|
|
412
448
|
async getDetails(name, type) {
|
|
413
|
-
const { config } = this;
|
|
414
|
-
const cmdType = type === 'plugin' ? 'plugin' : 'theme';
|
|
415
449
|
try {
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
reject: false,
|
|
419
|
-
});
|
|
420
|
-
const out = detailsResult.exitCode === 0 ? String(detailsResult.stdout || '') : null;
|
|
421
|
-
if (!out) {
|
|
450
|
+
const result = await this.executeWpCli([type, 'get', '--json', name], { silent: true });
|
|
451
|
+
if (!result.stdout) {
|
|
422
452
|
return null;
|
|
423
453
|
}
|
|
424
|
-
let output =
|
|
454
|
+
let output = result.stdout;
|
|
425
455
|
if (output.startsWith('$')) {
|
|
426
456
|
// Older Yarn versions include the script name in stdout so we need to trim the first line off
|
|
427
457
|
output = output.substring(1 + output.indexOf('\n'));
|
|
@@ -449,8 +479,10 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
449
479
|
const newVersion = await this.hasCoreUpdate();
|
|
450
480
|
if (!newVersion) {
|
|
451
481
|
stdout.write(ansis.dim(' WordPress core is up to date\n'));
|
|
452
|
-
return false;
|
|
482
|
+
return { updated: false };
|
|
453
483
|
}
|
|
484
|
+
// Get current version before updating
|
|
485
|
+
const currentVersion = await this.getCurrentCoreVersion();
|
|
454
486
|
stdout.write(ansis.green(` Updating WordPress core to ${newVersion}\n`));
|
|
455
487
|
const shouldStash = await this.hasGitChanges(wpConfig.wpRoot);
|
|
456
488
|
if (shouldStash) {
|
|
@@ -460,7 +492,13 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
460
492
|
try {
|
|
461
493
|
await this.doCoreUpdate(wpConfig.wpRoot, newVersion, wpConfig, branchRef);
|
|
462
494
|
stdout.write(ansis.green(` Updated WordPress core to ${newVersion}\n`));
|
|
463
|
-
return
|
|
495
|
+
return {
|
|
496
|
+
updated: true,
|
|
497
|
+
details: {
|
|
498
|
+
fromVersion: currentVersion || 'unknown',
|
|
499
|
+
toVersion: newVersion,
|
|
500
|
+
},
|
|
501
|
+
};
|
|
464
502
|
}
|
|
465
503
|
finally {
|
|
466
504
|
if (shouldStash) {
|
|
@@ -469,22 +507,23 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
469
507
|
}
|
|
470
508
|
}
|
|
471
509
|
}
|
|
510
|
+
async getCurrentCoreVersion() {
|
|
511
|
+
try {
|
|
512
|
+
const result = await this.executeWpCli(['core', 'version'], { silent: true });
|
|
513
|
+
return result.stdout ? result.stdout.trim() : null;
|
|
514
|
+
}
|
|
515
|
+
catch (_a) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
472
519
|
async hasCoreUpdate() {
|
|
473
520
|
var _a;
|
|
474
|
-
const { config } = this;
|
|
475
521
|
try {
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
reject: false,
|
|
479
|
-
});
|
|
480
|
-
if (!coreResult || coreResult.exitCode !== 0) {
|
|
481
|
-
return false;
|
|
482
|
-
}
|
|
483
|
-
const stdoutStr = String(coreResult.stdout || '');
|
|
484
|
-
if (!stdoutStr.trim() || stdoutStr.trim() === '[]') {
|
|
522
|
+
const result = await this.executeWpCli(['core', 'check-update', '--json'], { silent: true });
|
|
523
|
+
if (!result.stdout || !result.stdout.trim() || result.stdout.trim() === '[]') {
|
|
485
524
|
return false;
|
|
486
525
|
}
|
|
487
|
-
const trimmed =
|
|
526
|
+
const trimmed = result.stdout.substring(result.stdout.indexOf('[{'));
|
|
488
527
|
const parsed = JSON.parse(trimmed);
|
|
489
528
|
return ((_a = parsed[0]) === null || _a === void 0 ? void 0 : _a.version) || false;
|
|
490
529
|
}
|
|
@@ -493,11 +532,10 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
493
532
|
}
|
|
494
533
|
}
|
|
495
534
|
async hasGitChanges(path) {
|
|
496
|
-
const { config
|
|
535
|
+
const { config } = this;
|
|
497
536
|
try {
|
|
498
537
|
const gitCommand = await config.command('git');
|
|
499
538
|
const result = await execC(gitCommand, ['status', '--porcelain=v1', '--', path], {
|
|
500
|
-
context,
|
|
501
539
|
reject: false,
|
|
502
540
|
});
|
|
503
541
|
return String(result.stdout || '').trim() !== '';
|
|
@@ -507,36 +545,124 @@ export class WPUpdateCommand extends JoltCommand {
|
|
|
507
545
|
}
|
|
508
546
|
}
|
|
509
547
|
async stashChanges() {
|
|
510
|
-
const { config
|
|
548
|
+
const { config } = this;
|
|
511
549
|
const date = new Date().toISOString();
|
|
512
550
|
const gitCommand = await config.command('git');
|
|
513
|
-
await execC(gitCommand, ['add', '.']
|
|
551
|
+
await execC(gitCommand, ['add', '.']);
|
|
514
552
|
await execC(gitCommand, ['stash', 'save', '--', `Automated stash by Jolt WP Updater at ${date}`], {
|
|
515
|
-
context,
|
|
516
553
|
shell: false,
|
|
517
554
|
});
|
|
518
555
|
}
|
|
519
556
|
async unstashChanges() {
|
|
520
|
-
const { config
|
|
557
|
+
const { config } = this;
|
|
521
558
|
const gitCommand = await config.command('git');
|
|
522
|
-
await execC(gitCommand, ['stash', 'pop']
|
|
523
|
-
await execC(gitCommand, ['reset', 'HEAD', '--']
|
|
559
|
+
await execC(gitCommand, ['stash', 'pop']);
|
|
560
|
+
await execC(gitCommand, ['reset', 'HEAD', '--']);
|
|
524
561
|
}
|
|
525
562
|
async doCoreUpdate(path, version, wpConfig, branchRef) {
|
|
526
|
-
const { config
|
|
563
|
+
const { config } = this;
|
|
527
564
|
// Ensure branch is created before making our first commit
|
|
528
565
|
await this.ensureBranchCreated(wpConfig, branchRef);
|
|
529
566
|
const gitCommand = await config.command('git');
|
|
530
|
-
|
|
531
|
-
await execC(
|
|
532
|
-
await execC(gitCommand, ['add', path], { context });
|
|
567
|
+
await this.executeWpCli(['core', 'update'], { silent: true });
|
|
568
|
+
await execC(gitCommand, ['add', path]);
|
|
533
569
|
const commitMessage = this.sanitizeCommitMessage(`Update WordPress to ${version}`);
|
|
534
570
|
await execC(gitCommand, ['commit', '-m', commitMessage], {
|
|
535
|
-
context,
|
|
536
571
|
shell: false,
|
|
537
572
|
env: { SKIP: 'prepare-commit-msg' },
|
|
538
573
|
});
|
|
539
574
|
}
|
|
575
|
+
async maybeUpdateTranslations(branchRef) {
|
|
576
|
+
const { config, context: { stdout, stderr }, } = this;
|
|
577
|
+
try {
|
|
578
|
+
let hasUpdates = false;
|
|
579
|
+
// Helper to check and update translations for a specific type
|
|
580
|
+
const updateTranslationType = async (type, command) => {
|
|
581
|
+
stdout.write(` Checking ${type} translations...`);
|
|
582
|
+
const result = await this.executeWpCli(command, { silent: true });
|
|
583
|
+
if (result.exitCode === 0 && result.stdout) {
|
|
584
|
+
if (result.stdout.includes('Updated') || result.stdout.includes('updated')) {
|
|
585
|
+
hasUpdates = true;
|
|
586
|
+
stdout.write(ansis.green(' updated\n'));
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
stdout.write(ansis.dim(' up to date\n'));
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
stdout.write(ansis.dim(' skipped (not available)\n'));
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
// Update different translation types
|
|
597
|
+
await updateTranslationType('core', ['language', 'core', 'update']);
|
|
598
|
+
await updateTranslationType('plugin', ['language', 'plugin', 'update', '--all']);
|
|
599
|
+
await updateTranslationType('theme', ['language', 'theme', 'update', '--all']);
|
|
600
|
+
// If we have translation updates, commit them
|
|
601
|
+
if (hasUpdates) {
|
|
602
|
+
const wpConfig = await config.loadWordPressConfig();
|
|
603
|
+
if (!wpConfig) {
|
|
604
|
+
stderr.write(ansis.red('Failed to load WordPress configuration\n'));
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
// Create or ensure we're on the update branch
|
|
608
|
+
await this.ensureBranchCreated(wpConfig, branchRef);
|
|
609
|
+
const gitCommand = await config.command('git');
|
|
610
|
+
// Add all language files that might have been updated
|
|
611
|
+
await execC(gitCommand, ['add', wpConfig.wpRoot]);
|
|
612
|
+
// Check if there are any changes to commit
|
|
613
|
+
const statusResult = await execC(gitCommand, ['diff', '--cached', '--exit-code'], {
|
|
614
|
+
reject: false,
|
|
615
|
+
});
|
|
616
|
+
if (statusResult.exitCode !== 0) {
|
|
617
|
+
// There are changes to commit
|
|
618
|
+
await execC(gitCommand, ['commit', '-m', 'Update translations'], {
|
|
619
|
+
shell: false,
|
|
620
|
+
env: { SKIP: 'prepare-commit-msg' },
|
|
621
|
+
});
|
|
622
|
+
stdout.write(ansis.green(' Committed translation updates\n'));
|
|
623
|
+
return true;
|
|
624
|
+
}
|
|
625
|
+
stdout.write(ansis.dim(' No translation files changed\n'));
|
|
626
|
+
}
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
stderr.write(ansis.red(`Error updating translations: ${error}\n`));
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Update Redis drop-in from plugin files if WP-CLI fails
|
|
636
|
+
*/
|
|
637
|
+
async updateRedisDropinFromPlugin(item, wpConfig, itemConfig) {
|
|
638
|
+
const pluginBase = path.join(itemConfig.getFolder(wpConfig), item.name);
|
|
639
|
+
const candidatePaths = [
|
|
640
|
+
path.join(pluginBase, 'object-cache.php'),
|
|
641
|
+
path.join(pluginBase, 'includes', 'object-cache.php'),
|
|
642
|
+
path.join(pluginBase, 'drop-in', 'object-cache.php'),
|
|
643
|
+
];
|
|
644
|
+
const wpContentDir = path.join(wpConfig.wpRoot, 'wp-content');
|
|
645
|
+
const dropinDestLocal = path.join(wpContentDir, 'object-cache.php');
|
|
646
|
+
const { config } = this;
|
|
647
|
+
for (const candidate of candidatePaths) {
|
|
648
|
+
if (!(await fileExists(candidate))) {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
try {
|
|
652
|
+
const contents = String(await fs.readFile(candidate));
|
|
653
|
+
if (!/Redis Object Cache|Redis\b/i.test(contents)) {
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
await fs.copyFile(candidate, dropinDestLocal);
|
|
657
|
+
const gitCommand = await config.command('git');
|
|
658
|
+
await execC(gitCommand, ['add', dropinDestLocal]);
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
catch (_a) {
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
540
666
|
}
|
|
541
667
|
WPUpdateCommand.paths = [['wp', 'update']];
|
|
542
668
|
export class WPUpdateMergeCommand extends JoltCommand {
|
|
@@ -720,6 +846,9 @@ export class WPUpdateModifyCommand extends JoltCommand {
|
|
|
720
846
|
constructor() {
|
|
721
847
|
super(...arguments);
|
|
722
848
|
this.requiredCommands = ['git'];
|
|
849
|
+
this.autostash = Option.Boolean('--autostash', false, {
|
|
850
|
+
description: 'Automatically stash and unstash changes before and after the rebase',
|
|
851
|
+
});
|
|
723
852
|
}
|
|
724
853
|
async command() {
|
|
725
854
|
const { config, context, context: { stdout, stderr }, logo, } = this;
|
|
@@ -732,7 +861,7 @@ export class WPUpdateModifyCommand extends JoltCommand {
|
|
|
732
861
|
try {
|
|
733
862
|
const gitCommand = await config.command('git');
|
|
734
863
|
// Get current branch
|
|
735
|
-
const currentBranchResult = await execC(gitCommand, ['branch', '--show-current']
|
|
864
|
+
const currentBranchResult = await execC(gitCommand, ['branch', '--show-current']);
|
|
736
865
|
const currentBranch = String(currentBranchResult.stdout || '').trim();
|
|
737
866
|
if (!currentBranch.startsWith('joltWpUpdate/')) {
|
|
738
867
|
stderr.write(ansis.red('Not currently on a WordPress update branch\n'));
|
|
@@ -741,7 +870,6 @@ export class WPUpdateModifyCommand extends JoltCommand {
|
|
|
741
870
|
// Count commits since branching from main
|
|
742
871
|
const branchName = (await config.get('branch')) || 'master';
|
|
743
872
|
const commitCountResult = await execC(gitCommand, ['rev-list', '--count', `${branchName}..HEAD`], {
|
|
744
|
-
context,
|
|
745
873
|
reject: false,
|
|
746
874
|
});
|
|
747
875
|
if (commitCountResult.exitCode !== 0) {
|
|
@@ -755,7 +883,11 @@ export class WPUpdateModifyCommand extends JoltCommand {
|
|
|
755
883
|
}
|
|
756
884
|
stdout.write(ansis.cyan(`📋 Starting interactive rebase for ${commitCount} commits...\n`));
|
|
757
885
|
// Run interactive rebase
|
|
758
|
-
const
|
|
886
|
+
const rebaseArgs = ['rebase', '-i', `HEAD~${commitCount}`];
|
|
887
|
+
if (this.autostash) {
|
|
888
|
+
rebaseArgs.push('--autostash');
|
|
889
|
+
}
|
|
890
|
+
const rebaseResult = await execC(gitCommand, rebaseArgs, {
|
|
759
891
|
context,
|
|
760
892
|
reject: false,
|
|
761
893
|
});
|