@joltdesign/scripts 0.20.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.
- package/README.md +85 -0
- package/dist/AWSClient.js +102 -0
- package/dist/AWSClient.js.map +1 -0
- package/dist/Command/AWS.js +101 -15
- package/dist/Command/AWS.js.map +1 -1
- package/dist/Command/Build.js +1 -1
- package/dist/Command/Build.js.map +1 -1
- package/dist/Command/Cmd.js +2 -2
- package/dist/Command/Cmd.js.map +1 -1
- package/dist/Command/Config.js +161 -3
- package/dist/Command/Config.js.map +1 -1
- package/dist/Command/DB.js +3 -2
- package/dist/Command/DB.js.map +1 -1
- package/dist/Command/Docker.js +9 -7
- package/dist/Command/Docker.js.map +1 -1
- package/dist/Command/JoltCommand.js +1 -1
- package/dist/Command/JoltCommand.js.map +1 -1
- package/dist/Command/Nexcess.js +58 -3
- package/dist/Command/Nexcess.js.map +1 -1
- package/dist/Command/Prepare.js +5 -4
- package/dist/Command/Prepare.js.map +1 -1
- package/dist/Command/SSH.js +3 -3
- package/dist/Command/SSH.js.map +1 -1
- package/dist/Command/WP.js +801 -4
- package/dist/Command/WP.js.map +1 -1
- package/dist/Config.js +123 -13
- package/dist/Config.js.map +1 -1
- package/dist/cli.js +10 -3
- package/dist/cli.js.map +1 -1
- package/dist/schemas.js +111 -6
- package/dist/schemas.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- 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/jolt-config.schema.json +308 -0
- package/package.json +17 -10
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
package/dist/Command/WP.js
CHANGED
|
@@ -1,17 +1,67 @@
|
|
|
1
1
|
import { userInfo } from 'node:os';
|
|
2
2
|
import ansis from 'ansis';
|
|
3
3
|
import { Option } from 'clipanion';
|
|
4
|
-
import
|
|
4
|
+
import * as t from 'typanion';
|
|
5
|
+
import { execC, which } from '../utils.js';
|
|
5
6
|
import JoltCommand from './JoltCommand.js';
|
|
7
|
+
// Possible sub-arguments for the CLI command as of WP-CLI v2.12.0
|
|
8
|
+
const possibleCliArgs = [
|
|
9
|
+
'alias',
|
|
10
|
+
'cache',
|
|
11
|
+
'check-update',
|
|
12
|
+
'cmd-dump',
|
|
13
|
+
'completions',
|
|
14
|
+
'has-command',
|
|
15
|
+
'info',
|
|
16
|
+
'param-dump',
|
|
17
|
+
'update',
|
|
18
|
+
'version',
|
|
19
|
+
];
|
|
20
|
+
let maybeAddCliArg = true;
|
|
6
21
|
export class WPCommand extends JoltCommand {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(...arguments);
|
|
24
|
+
this.wpArgs = Option.Proxy();
|
|
25
|
+
}
|
|
26
|
+
async command() {
|
|
27
|
+
const { cli, wpArgs } = this;
|
|
28
|
+
// Proxy to wp-cli for backwards compatibility
|
|
29
|
+
maybeAddCliArg = false;
|
|
30
|
+
const result = await cli.run(['wp-cli', ...wpArgs]);
|
|
31
|
+
maybeAddCliArg = true;
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
WPCommand.paths = [['wp']];
|
|
36
|
+
export class WPCLICommand extends JoltCommand {
|
|
7
37
|
constructor() {
|
|
8
38
|
super(...arguments);
|
|
9
39
|
this.requiredCommands = ['docker', 'compose'];
|
|
10
40
|
this.wpArgs = Option.Proxy();
|
|
11
41
|
}
|
|
12
42
|
async command() {
|
|
13
|
-
|
|
43
|
+
var _a;
|
|
44
|
+
const { config, context, context: { stderr }, wpArgs, } = this;
|
|
45
|
+
// Check if wp executable exists and no wp script in package.json
|
|
46
|
+
const wpExecutable = await which('wp');
|
|
47
|
+
const packageJson = await config.getPackageJson();
|
|
48
|
+
const hasWpScript = (_a = packageJson === null || packageJson === void 0 ? void 0 : packageJson.scripts) === null || _a === void 0 ? void 0 : _a.wp;
|
|
49
|
+
if (wpExecutable && !hasWpScript) {
|
|
50
|
+
// Use wp executable directly
|
|
51
|
+
let realArgs = wpArgs;
|
|
52
|
+
if (maybeAddCliArg && possibleCliArgs.includes(realArgs[0])) {
|
|
53
|
+
realArgs = ['cli', ...realArgs];
|
|
54
|
+
}
|
|
55
|
+
const parsedArgs = await Promise.all(realArgs.map((x) => config.parseArg(x)));
|
|
56
|
+
const result = await execC('wp', parsedArgs, { context, reject: false });
|
|
57
|
+
return result.exitCode;
|
|
58
|
+
}
|
|
59
|
+
// Fall back to container-based approach
|
|
14
60
|
const containerName = await this.getContainerName();
|
|
61
|
+
let realArgs = wpArgs;
|
|
62
|
+
if (maybeAddCliArg && possibleCliArgs.includes(realArgs[0])) {
|
|
63
|
+
realArgs = ['cli', ...realArgs];
|
|
64
|
+
}
|
|
15
65
|
if (!containerName) {
|
|
16
66
|
stderr.write(ansis.red(`Couldn't find a WP CLI container. Set it with the 'wpCliContainer' config key.\n`));
|
|
17
67
|
return 1;
|
|
@@ -21,7 +71,7 @@ export class WPCommand extends JoltCommand {
|
|
|
21
71
|
const userArg = uid !== undefined && uid !== -1 && `--user='${uid}:${gid}'`;
|
|
22
72
|
const profile = await this.getContainerProfile(containerName);
|
|
23
73
|
const [composeCommand, args] = await config.getComposeCommand();
|
|
24
|
-
args.push(profile ? `--profile=${profile}` : '', 'run', '--rm', userArg || '', containerName, 'wp', ...
|
|
74
|
+
args.push(profile ? `--profile=${profile}` : '', 'run', '--rm', userArg || '', containerName, 'wp', ...realArgs);
|
|
25
75
|
const parsedArgs = await Promise.all(args.map((x) => config.parseArg(x)));
|
|
26
76
|
const result = await execC(composeCommand, parsedArgs, { context, reject: false });
|
|
27
77
|
return result.exitCode;
|
|
@@ -55,5 +105,752 @@ export class WPCommand extends JoltCommand {
|
|
|
55
105
|
return service.profiles ? service.profiles[0] : undefined;
|
|
56
106
|
}
|
|
57
107
|
}
|
|
58
|
-
|
|
108
|
+
WPCLICommand.paths = [['wp', 'cli'], ['wp-cli']];
|
|
109
|
+
export class WPUpdateCommand extends JoltCommand {
|
|
110
|
+
constructor() {
|
|
111
|
+
super(...arguments);
|
|
112
|
+
this.requiredCommands = ['git'];
|
|
113
|
+
this.skipCore = Option.Boolean('--skip-core', false, { description: 'Skip WordPress core updates' });
|
|
114
|
+
this.skipPlugins = Option.Boolean('--skip-plugins', false, { description: 'Skip plugin updates' });
|
|
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' });
|
|
117
|
+
}
|
|
118
|
+
async command() {
|
|
119
|
+
const { config, context: { stdout, stderr }, skipCore, skipPlugins, skipThemes, skipLanguages, logo, } = this;
|
|
120
|
+
stdout.write(ansis.bold(`${logo} WordPress Updates\n\n`));
|
|
121
|
+
// Load configuration
|
|
122
|
+
const wpConfig = await config.loadWordPressConfig();
|
|
123
|
+
if (!wpConfig) {
|
|
124
|
+
stderr.write(ansis.red('Failed to load WordPress configuration\n'));
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
// Use `jolt wp cli ...` via execC with the configured package runner
|
|
128
|
+
let updatedPluginCount = 0;
|
|
129
|
+
let updatedThemeCount = 0;
|
|
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
|
+
};
|
|
138
|
+
// Get current status
|
|
139
|
+
if (!skipPlugins) {
|
|
140
|
+
await this.getItems('plugin');
|
|
141
|
+
}
|
|
142
|
+
if (!skipThemes) {
|
|
143
|
+
await this.getItems('theme');
|
|
144
|
+
}
|
|
145
|
+
// We'll create the update branch only when we need to make our first commit
|
|
146
|
+
const branchRef = { branch: undefined, created: false };
|
|
147
|
+
// Disable git hooks temporarily
|
|
148
|
+
const originalHookPath = await this.disableGitHooks();
|
|
149
|
+
try {
|
|
150
|
+
// Update plugins
|
|
151
|
+
const pluginUpdates = await this.processItemUpdates('plugin', skipPlugins, wpConfig, branchRef);
|
|
152
|
+
updatedPluginCount = pluginUpdates.count;
|
|
153
|
+
updateSummary.plugins = pluginUpdates.details;
|
|
154
|
+
// Update themes
|
|
155
|
+
const themeUpdates = await this.processItemUpdates('theme', skipThemes, wpConfig, branchRef);
|
|
156
|
+
updatedThemeCount = themeUpdates.count;
|
|
157
|
+
updateSummary.themes = themeUpdates.details;
|
|
158
|
+
// Update core
|
|
159
|
+
if (!skipCore) {
|
|
160
|
+
stdout.write(ansis.cyan('📦 Checking WordPress core...\n'));
|
|
161
|
+
const coreResult = await this.maybeUpdateCore(wpConfig, branchRef);
|
|
162
|
+
updatedCore = coreResult.updated;
|
|
163
|
+
if (coreResult.updated && coreResult.details) {
|
|
164
|
+
updateSummary.core = coreResult.details;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
await this.rollbackGitHooks(originalHookPath);
|
|
170
|
+
}
|
|
171
|
+
// Summary
|
|
172
|
+
stdout.write(ansis.green('\n✅ Update complete!\n'));
|
|
173
|
+
stdout.write(ansis.cyan(`🔌 Updated ${updatedPluginCount} plugins\n`));
|
|
174
|
+
stdout.write(ansis.cyan(`🎨 Updated ${updatedThemeCount} themes\n`));
|
|
175
|
+
if (updatedCore) {
|
|
176
|
+
stdout.write(ansis.cyan('📦 Updated WordPress core\n'));
|
|
177
|
+
}
|
|
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
|
+
}
|
|
209
|
+
if (totalUpdates > 0 && branchRef.created) {
|
|
210
|
+
stdout.write(ansis.yellow('\nNext steps:\n'));
|
|
211
|
+
stdout.write(`• Review updates: ${ansis.dim(await this.getUpdateCommand('modify'))}\n`);
|
|
212
|
+
// Use root config value directly
|
|
213
|
+
stdout.write(`• Merge to ${await config.get('branch')}: ${ansis.dim(await this.getUpdateCommand('merge'))}\n`);
|
|
214
|
+
}
|
|
215
|
+
else if (totalUpdates === 0 && !updatedTranslations) {
|
|
216
|
+
stdout.write(ansis.green('\n✅ No updates available - staying on current branch\n'));
|
|
217
|
+
}
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
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}`;
|
|
230
|
+
}
|
|
231
|
+
// Fall back to the full command
|
|
232
|
+
return `${yarnCommand} jolt wp update ${subCommand}`;
|
|
233
|
+
}
|
|
234
|
+
// Helper method to execute WP CLI commands
|
|
235
|
+
async executeWpCli(args, options) {
|
|
236
|
+
const { config, context } = this;
|
|
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) {
|
|
274
|
+
const { context: { stdout }, } = this;
|
|
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
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return { count, details: updateDetails };
|
|
315
|
+
}
|
|
316
|
+
// Generic method to check and maybe update an item (plugin/theme)
|
|
317
|
+
async maybeUpdateItem(item, wpConfig, branchRef, itemConfig) {
|
|
318
|
+
const { context: { stdout }, } = this;
|
|
319
|
+
if (wpConfig.doNotUpdate.includes(item.name)) {
|
|
320
|
+
stdout.write(ansis.dim(` Skipping ${item.name} (configured to skip)\n`));
|
|
321
|
+
return { updated: false };
|
|
322
|
+
}
|
|
323
|
+
stdout.write(` Checking ${item.name}...`);
|
|
324
|
+
if (item.update === 'available') {
|
|
325
|
+
stdout.write(ansis.green(' updating\n'));
|
|
326
|
+
return await this.updateItem(item, wpConfig, branchRef, itemConfig);
|
|
327
|
+
}
|
|
328
|
+
if (item.update === 'none') {
|
|
329
|
+
stdout.write(ansis.dim(' up to date\n'));
|
|
330
|
+
}
|
|
331
|
+
else if (item.update === 'version higher than expected') {
|
|
332
|
+
stdout.write(ansis.yellow(` local version ${item.version} is higher than remote\n`));
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
stdout.write(ansis.red(` unknown status: ${item.update}\n`));
|
|
336
|
+
}
|
|
337
|
+
return { updated: false };
|
|
338
|
+
}
|
|
339
|
+
// Generic method to update an item (plugin/theme)
|
|
340
|
+
async updateItem(item, wpConfig, branchRef, itemConfig) {
|
|
341
|
+
const { config, context: { stdout, stderr }, } = this;
|
|
342
|
+
try {
|
|
343
|
+
const gitCommand = await config.command('git');
|
|
344
|
+
const details = await this.getDetails(item.name, itemConfig.type);
|
|
345
|
+
if (!details) {
|
|
346
|
+
return { updated: false };
|
|
347
|
+
}
|
|
348
|
+
const fromVersion = details.version;
|
|
349
|
+
const prettyTitle = this.cleanTitle(details.title);
|
|
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 };
|
|
355
|
+
}
|
|
356
|
+
const newDetails = await this.getDetails(item.name, itemConfig.type);
|
|
357
|
+
if (!newDetails || newDetails.version === details.version) {
|
|
358
|
+
stderr.write(ansis.red(' Update failed!\n'));
|
|
359
|
+
return { updated: false };
|
|
360
|
+
}
|
|
361
|
+
// Ensure branch is created before making our first commit
|
|
362
|
+
await this.ensureBranchCreated(wpConfig, branchRef);
|
|
363
|
+
const commitMessage = this.sanitizeCommitMessage(`${itemConfig.commitPrefix} ${prettyTitle} to ${newDetails.version}`);
|
|
364
|
+
await execC(gitCommand, ['add', location]);
|
|
365
|
+
await execC(gitCommand, ['commit', '-m', commitMessage], {
|
|
366
|
+
shell: false,
|
|
367
|
+
env: { SKIP: 'prepare-commit-msg' },
|
|
368
|
+
});
|
|
369
|
+
stdout.write(ansis.green(` Updated ${prettyTitle} from ${fromVersion} to ${newDetails.version}\n`));
|
|
370
|
+
return {
|
|
371
|
+
updated: true,
|
|
372
|
+
details: {
|
|
373
|
+
name: item.name,
|
|
374
|
+
title: prettyTitle,
|
|
375
|
+
fromVersion,
|
|
376
|
+
toVersion: newDetails.version,
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
stderr.write(ansis.red(` Error updating ${item.name}: ${error}\n`));
|
|
382
|
+
return { updated: false };
|
|
383
|
+
}
|
|
384
|
+
}
|
|
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');
|
|
405
|
+
try {
|
|
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;
|
|
410
|
+
}
|
|
411
|
+
catch (_a) {
|
|
412
|
+
await execC(gitCommand, ['config', 'core.hooksPath', '/dev/null'], { context });
|
|
413
|
+
return '';
|
|
414
|
+
}
|
|
415
|
+
}
|
|
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
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async getDetails(name, type) {
|
|
427
|
+
try {
|
|
428
|
+
const result = await this.executeWpCli([type, 'get', '--json', name], { silent: true });
|
|
429
|
+
if (!result.stdout) {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
let output = result.stdout;
|
|
433
|
+
if (output.startsWith('$')) {
|
|
434
|
+
// Older Yarn versions include the script name in stdout so we need to trim the first line off
|
|
435
|
+
output = output.substring(1 + output.indexOf('\n'));
|
|
436
|
+
}
|
|
437
|
+
// Look for the start of the JSON to trim off PHP warnings
|
|
438
|
+
const trimmed = output.substring(output.indexOf('{"')).trim();
|
|
439
|
+
return JSON.parse(trimmed);
|
|
440
|
+
}
|
|
441
|
+
catch (_a) {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
cleanTitle(name) {
|
|
446
|
+
return name.split(/[|:]/i)[0].trim();
|
|
447
|
+
}
|
|
448
|
+
sanitizeCommitMessage(message) {
|
|
449
|
+
// Replace any problematic characters that might cause issues in commit messages
|
|
450
|
+
return message
|
|
451
|
+
.replace(/[""'']/g, '"') // Normalize quotes
|
|
452
|
+
.replace(/[^\x20-\x7E]/g, '') // Remove non-ASCII characters
|
|
453
|
+
.trim();
|
|
454
|
+
}
|
|
455
|
+
async maybeUpdateCore(wpConfig, branchRef) {
|
|
456
|
+
const { context: { stdout }, } = this;
|
|
457
|
+
const newVersion = await this.hasCoreUpdate();
|
|
458
|
+
if (!newVersion) {
|
|
459
|
+
stdout.write(ansis.dim(' WordPress core is up to date\n'));
|
|
460
|
+
return { updated: false };
|
|
461
|
+
}
|
|
462
|
+
// Get current version before updating
|
|
463
|
+
const currentVersion = await this.getCurrentCoreVersion();
|
|
464
|
+
stdout.write(ansis.green(` Updating WordPress core to ${newVersion}\n`));
|
|
465
|
+
const shouldStash = await this.hasGitChanges(wpConfig.wpRoot);
|
|
466
|
+
if (shouldStash) {
|
|
467
|
+
stdout.write(ansis.yellow(' Stashing changes temporarily...\n'));
|
|
468
|
+
await this.stashChanges();
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
await this.doCoreUpdate(wpConfig.wpRoot, newVersion, wpConfig, branchRef);
|
|
472
|
+
stdout.write(ansis.green(` Updated WordPress core to ${newVersion}\n`));
|
|
473
|
+
return {
|
|
474
|
+
updated: true,
|
|
475
|
+
details: {
|
|
476
|
+
fromVersion: currentVersion || 'unknown',
|
|
477
|
+
toVersion: newVersion,
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
finally {
|
|
482
|
+
if (shouldStash) {
|
|
483
|
+
stdout.write(ansis.yellow(' Restoring stashed changes...\n'));
|
|
484
|
+
await this.unstashChanges();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
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
|
+
}
|
|
497
|
+
async hasCoreUpdate() {
|
|
498
|
+
var _a;
|
|
499
|
+
try {
|
|
500
|
+
const result = await this.executeWpCli(['core', 'check-update', '--json'], { silent: true });
|
|
501
|
+
if (!result.stdout || !result.stdout.trim() || result.stdout.trim() === '[]') {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
const trimmed = result.stdout.substring(result.stdout.indexOf('[{'));
|
|
505
|
+
const parsed = JSON.parse(trimmed);
|
|
506
|
+
return ((_a = parsed[0]) === null || _a === void 0 ? void 0 : _a.version) || false;
|
|
507
|
+
}
|
|
508
|
+
catch (_b) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async hasGitChanges(path) {
|
|
513
|
+
const { config } = this;
|
|
514
|
+
try {
|
|
515
|
+
const gitCommand = await config.command('git');
|
|
516
|
+
const result = await execC(gitCommand, ['status', '--porcelain=v1', '--', path], {
|
|
517
|
+
reject: false,
|
|
518
|
+
});
|
|
519
|
+
return String(result.stdout || '').trim() !== '';
|
|
520
|
+
}
|
|
521
|
+
catch (_a) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async stashChanges() {
|
|
526
|
+
const { config } = this;
|
|
527
|
+
const date = new Date().toISOString();
|
|
528
|
+
const gitCommand = await config.command('git');
|
|
529
|
+
await execC(gitCommand, ['add', '.']);
|
|
530
|
+
await execC(gitCommand, ['stash', 'save', '--', `Automated stash by Jolt WP Updater at ${date}`], {
|
|
531
|
+
shell: false,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
async unstashChanges() {
|
|
535
|
+
const { config } = this;
|
|
536
|
+
const gitCommand = await config.command('git');
|
|
537
|
+
await execC(gitCommand, ['stash', 'pop']);
|
|
538
|
+
await execC(gitCommand, ['reset', 'HEAD', '--']);
|
|
539
|
+
}
|
|
540
|
+
async doCoreUpdate(path, version, wpConfig, branchRef) {
|
|
541
|
+
const { config } = this;
|
|
542
|
+
// Ensure branch is created before making our first commit
|
|
543
|
+
await this.ensureBranchCreated(wpConfig, branchRef);
|
|
544
|
+
const gitCommand = await config.command('git');
|
|
545
|
+
await this.executeWpCli(['core', 'update'], { silent: true });
|
|
546
|
+
await execC(gitCommand, ['add', path]);
|
|
547
|
+
const commitMessage = this.sanitizeCommitMessage(`Update WordPress to ${version}`);
|
|
548
|
+
await execC(gitCommand, ['commit', '-m', commitMessage], {
|
|
549
|
+
shell: false,
|
|
550
|
+
env: { SKIP: 'prepare-commit-msg' },
|
|
551
|
+
});
|
|
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
|
+
}
|
|
612
|
+
}
|
|
613
|
+
WPUpdateCommand.paths = [['wp', 'update']];
|
|
614
|
+
export class WPUpdateMergeCommand extends JoltCommand {
|
|
615
|
+
constructor() {
|
|
616
|
+
super(...arguments);
|
|
617
|
+
this.requiredCommands = ['git'];
|
|
618
|
+
this.rebase = Option.Boolean('--rebase', false, {
|
|
619
|
+
description: 'Use rebase instead of merge',
|
|
620
|
+
});
|
|
621
|
+
this.ffOnly = Option.Boolean('--ff-only', false, {
|
|
622
|
+
description: 'Only allow fast-forward merges',
|
|
623
|
+
});
|
|
624
|
+
this.noFf = Option.Boolean('--no-ff', false, {
|
|
625
|
+
description: 'Create a merge commit even when the merge resolves as a fast-forward',
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
async command() {
|
|
629
|
+
const { config, context, context: { stdout, stderr }, logo, } = this;
|
|
630
|
+
const operationTitle = this.rebase ? 'WordPress Update Rebase' : 'WordPress Update Merge';
|
|
631
|
+
stdout.write(`${logo} ${ansis.bold(operationTitle)}\n\n`);
|
|
632
|
+
const wpConfig = await config.loadWordPressConfig();
|
|
633
|
+
if (!wpConfig) {
|
|
634
|
+
stderr.write(ansis.red('Failed to load WordPress configuration\n'));
|
|
635
|
+
return 1;
|
|
636
|
+
}
|
|
637
|
+
try {
|
|
638
|
+
const gitCommand = await config.command('git');
|
|
639
|
+
// Get current branch
|
|
640
|
+
const currentBranchResult = await execC(gitCommand, ['branch', '--show-current']);
|
|
641
|
+
const currentBranch = String(currentBranchResult.stdout || '').trim();
|
|
642
|
+
if (!currentBranch.startsWith('joltWpUpdate/')) {
|
|
643
|
+
stderr.write(ansis.red('Not currently on a WordPress update branch\n'));
|
|
644
|
+
return 1;
|
|
645
|
+
}
|
|
646
|
+
const targetBranch = (await config.get('branch')) || 'master';
|
|
647
|
+
stdout.write(ansis.cyan(`📋 Switching to ${targetBranch}...\n`));
|
|
648
|
+
await execC(gitCommand, ['switch', targetBranch], { context });
|
|
649
|
+
if (this.rebase) {
|
|
650
|
+
stdout.write(ansis.cyan(`📋 Rebasing ${currentBranch}...\n`));
|
|
651
|
+
await execC(gitCommand, ['rebase', currentBranch], { context });
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
const mergeArgs = ['merge'];
|
|
655
|
+
if (this.ffOnly) {
|
|
656
|
+
mergeArgs.push('--ff-only');
|
|
657
|
+
}
|
|
658
|
+
else if (this.noFf) {
|
|
659
|
+
mergeArgs.push('--no-ff');
|
|
660
|
+
}
|
|
661
|
+
mergeArgs.push(currentBranch);
|
|
662
|
+
const mergeStrategy = this.ffOnly ? ' (fast-forward only)' : this.noFf ? ' (no fast-forward)' : '';
|
|
663
|
+
stdout.write(ansis.cyan(`📋 Merging ${currentBranch}${mergeStrategy}...\n`));
|
|
664
|
+
await execC(gitCommand, mergeArgs, { context });
|
|
665
|
+
}
|
|
666
|
+
const operation = this.rebase ? 'rebased' : 'merged';
|
|
667
|
+
stdout.write(ansis.green(`✅ Successfully ${operation} WordPress updates!\n`));
|
|
668
|
+
return 0;
|
|
669
|
+
}
|
|
670
|
+
catch (error) {
|
|
671
|
+
stderr.write(ansis.red(`Error during merge: ${error}\n`));
|
|
672
|
+
return 1;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
WPUpdateMergeCommand.paths = [['wp', 'update', 'merge']];
|
|
677
|
+
WPUpdateMergeCommand.schema = [
|
|
678
|
+
t.hasMutuallyExclusiveKeys(['rebase', 'ffOnly', 'noFf'], {
|
|
679
|
+
missingIf: 'falsy',
|
|
680
|
+
}),
|
|
681
|
+
];
|
|
682
|
+
export class WPUpdateCleanCommand extends JoltCommand {
|
|
683
|
+
constructor() {
|
|
684
|
+
super(...arguments);
|
|
685
|
+
this.requiredCommands = ['git'];
|
|
686
|
+
this.dryRun = Option.Boolean('--dry-run', false, { description: 'Show what would be deleted without actually deleting' });
|
|
687
|
+
this.deleteUnmerged = Option.Boolean('--delete-unmerged', false, {
|
|
688
|
+
description: 'Delete unmerged branches (default: only merged branches)',
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
async command() {
|
|
692
|
+
const { config, context: { stdout, stderr }, logo, dryRun, deleteUnmerged, } = this;
|
|
693
|
+
const operationTitle = dryRun ? 'WordPress Update Branch Cleanup (Dry Run)' : 'WordPress Update Branch Cleanup';
|
|
694
|
+
stdout.write(`${logo} ${ansis.bold(operationTitle)}\n\n`);
|
|
695
|
+
const wpConfig = await config.loadWordPressConfig();
|
|
696
|
+
if (!wpConfig) {
|
|
697
|
+
stderr.write(ansis.red('Failed to load WordPress configuration\n'));
|
|
698
|
+
return 1;
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
const gitCommand = await config.command('git');
|
|
702
|
+
// Get current branch to avoid deleting it
|
|
703
|
+
const currentBranchResult = await execC(gitCommand, ['branch', '--show-current'], {
|
|
704
|
+
reject: false,
|
|
705
|
+
});
|
|
706
|
+
const currentBranch = String(currentBranchResult.stdout || '').trim();
|
|
707
|
+
// List all branches with our prefix
|
|
708
|
+
const branchListResult = await execC(gitCommand, ['branch', '--list', 'joltWpUpdate/*'], {
|
|
709
|
+
reject: false,
|
|
710
|
+
});
|
|
711
|
+
if (branchListResult.exitCode !== 0) {
|
|
712
|
+
stderr.write(ansis.red('Failed to list branches\n'));
|
|
713
|
+
return 1;
|
|
714
|
+
}
|
|
715
|
+
const branchOutput = String(branchListResult.stdout || '').trim();
|
|
716
|
+
if (!branchOutput) {
|
|
717
|
+
stdout.write(ansis.green('✅ No WordPress update branches found to clean\n'));
|
|
718
|
+
return 0;
|
|
719
|
+
}
|
|
720
|
+
// Parse branch names (remove leading spaces and asterisks)
|
|
721
|
+
const branches = branchOutput
|
|
722
|
+
.split('\n')
|
|
723
|
+
.map((line) => line.trim().replace(/^\*\s*/, ''))
|
|
724
|
+
.filter((branch) => branch.startsWith('joltWpUpdate/') && branch !== currentBranch);
|
|
725
|
+
if (branches.length === 0) {
|
|
726
|
+
if (currentBranch.startsWith('joltWpUpdate/')) {
|
|
727
|
+
stdout.write(ansis.yellow('⚠️ Currently on a WordPress update branch. Switch to another branch first to clean it.\n'));
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
stdout.write(ansis.green('✅ No WordPress update branches found to clean\n'));
|
|
731
|
+
}
|
|
732
|
+
return 0;
|
|
733
|
+
}
|
|
734
|
+
if (dryRun) {
|
|
735
|
+
// Dry run - show what would be deleted without actually deleting
|
|
736
|
+
const deleteMode = deleteUnmerged ? 'force delete' : 'delete merged';
|
|
737
|
+
for (const branch of branches) {
|
|
738
|
+
stdout.write(ansis.cyan(`🔍 Would ${deleteMode} ${branch}\n`));
|
|
739
|
+
}
|
|
740
|
+
const modeNote = deleteUnmerged
|
|
741
|
+
? ' (including unmerged branches)'
|
|
742
|
+
: ' (merged branches only, unmerged will be skipped)';
|
|
743
|
+
stdout.write(ansis.cyan(`\n🔍 Dry run complete. Would process ${branches.length} WordPress update branch${branches.length === 1 ? '' : 'es'}${modeNote}\n`));
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
// Actually delete branches
|
|
747
|
+
const deleteFlag = deleteUnmerged ? '-D' : '-d';
|
|
748
|
+
let deletedCount = 0;
|
|
749
|
+
let skippedCount = 0;
|
|
750
|
+
for (const branch of branches) {
|
|
751
|
+
try {
|
|
752
|
+
const deleteResult = await execC(gitCommand, ['branch', deleteFlag, branch], { reject: false });
|
|
753
|
+
if (deleteResult.exitCode === 0) {
|
|
754
|
+
stdout.write(ansis.green(`✅ Deleted ${branch}\n`));
|
|
755
|
+
deletedCount++;
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
if (!deleteUnmerged && String(deleteResult.stderr || '').includes('not fully merged')) {
|
|
759
|
+
stdout.write(ansis.yellow(`⚠️ Skipped ${branch} (unmerged - use --delete-unmerged to force)\n`));
|
|
760
|
+
skippedCount++;
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
stderr.write(ansis.red(`❌ Failed to delete ${branch}\n`));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
catch (error) {
|
|
768
|
+
stderr.write(ansis.red(`❌ Error deleting ${branch}: ${error}\n`));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (deletedCount > 0 || skippedCount > 0) {
|
|
772
|
+
const messages = [];
|
|
773
|
+
if (deletedCount > 0) {
|
|
774
|
+
messages.push(`deleted ${deletedCount} branch${deletedCount === 1 ? '' : 'es'}`);
|
|
775
|
+
}
|
|
776
|
+
if (skippedCount > 0) {
|
|
777
|
+
messages.push(`skipped ${skippedCount} unmerged branch${skippedCount === 1 ? '' : 'es'}`);
|
|
778
|
+
}
|
|
779
|
+
stdout.write(ansis.green(`\n🎉 Successfully ${messages.join(', ')}\n`));
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return 0;
|
|
783
|
+
}
|
|
784
|
+
catch (error) {
|
|
785
|
+
stderr.write(ansis.red(`Error during cleanup: ${error}\n`));
|
|
786
|
+
return 1;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
WPUpdateCleanCommand.paths = [['wp', 'update', 'clean']];
|
|
791
|
+
export class WPUpdateModifyCommand extends JoltCommand {
|
|
792
|
+
constructor() {
|
|
793
|
+
super(...arguments);
|
|
794
|
+
this.requiredCommands = ['git'];
|
|
795
|
+
this.autostash = Option.Boolean('--autostash', false, {
|
|
796
|
+
description: 'Automatically stash and unstash changes before and after the rebase',
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
async command() {
|
|
800
|
+
const { config, context, context: { stdout, stderr }, logo, } = this;
|
|
801
|
+
stdout.write(`${logo} ${ansis.bold('WordPress Update Interactive Rebase')}\n\n`);
|
|
802
|
+
const wpConfig = await config.loadWordPressConfig();
|
|
803
|
+
if (!wpConfig) {
|
|
804
|
+
stderr.write(ansis.red('Failed to load WordPress configuration\n'));
|
|
805
|
+
return 1;
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
const gitCommand = await config.command('git');
|
|
809
|
+
// Get current branch
|
|
810
|
+
const currentBranchResult = await execC(gitCommand, ['branch', '--show-current']);
|
|
811
|
+
const currentBranch = String(currentBranchResult.stdout || '').trim();
|
|
812
|
+
if (!currentBranch.startsWith('joltWpUpdate/')) {
|
|
813
|
+
stderr.write(ansis.red('Not currently on a WordPress update branch\n'));
|
|
814
|
+
return 1;
|
|
815
|
+
}
|
|
816
|
+
// Count commits since branching from main
|
|
817
|
+
const branchName = (await config.get('branch')) || 'master';
|
|
818
|
+
const commitCountResult = await execC(gitCommand, ['rev-list', '--count', `${branchName}..HEAD`], {
|
|
819
|
+
reject: false,
|
|
820
|
+
});
|
|
821
|
+
if (commitCountResult.exitCode !== 0) {
|
|
822
|
+
stderr.write(ansis.red('Could not determine commit count\n'));
|
|
823
|
+
return 1;
|
|
824
|
+
}
|
|
825
|
+
const commitCount = Number.parseInt(String(commitCountResult.stdout || '').trim(), 10);
|
|
826
|
+
if (commitCount === 0) {
|
|
827
|
+
stdout.write(ansis.yellow('No commits to rebase\n'));
|
|
828
|
+
return 0;
|
|
829
|
+
}
|
|
830
|
+
stdout.write(ansis.cyan(`📋 Starting interactive rebase for ${commitCount} commits...\n`));
|
|
831
|
+
// Run interactive rebase
|
|
832
|
+
const rebaseArgs = ['rebase', '-i', `HEAD~${commitCount}`];
|
|
833
|
+
if (this.autostash) {
|
|
834
|
+
rebaseArgs.push('--autostash');
|
|
835
|
+
}
|
|
836
|
+
const rebaseResult = await execC(gitCommand, rebaseArgs, {
|
|
837
|
+
context,
|
|
838
|
+
reject: false,
|
|
839
|
+
});
|
|
840
|
+
if (rebaseResult.exitCode === 0) {
|
|
841
|
+
stdout.write(ansis.green('✅ Interactive rebase completed!\n'));
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
stderr.write(ansis.red('Interactive rebase was cancelled or failed\n'));
|
|
845
|
+
return rebaseResult.exitCode;
|
|
846
|
+
}
|
|
847
|
+
return 0;
|
|
848
|
+
}
|
|
849
|
+
catch (error) {
|
|
850
|
+
stderr.write(ansis.red(`Error during rebase: ${error}\n`));
|
|
851
|
+
return 1;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
WPUpdateModifyCommand.paths = [['wp', 'update', 'modify']];
|
|
59
856
|
//# sourceMappingURL=WP.js.map
|