@atomicsmash/cli 9.0.1-beta.1 → 9.0.1-beta.3
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull-database.d.ts","sourceRoot":"","sources":["../../src/commands/pull-database.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pull-database.d.ts","sourceRoot":"","sources":["../../src/commands/pull-database.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,OAAO,kBAAkB,CAAC;AACvC,eAAO,MAAM,QAAQ,oEAC6C,CAAC;AACnE,wBAAsB,OAAO,kBA4I5B"}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
|
-
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
2
|
import { unlink as deleteFile } from "node:fs/promises";
|
|
4
3
|
import { performance } from "node:perf_hooks";
|
|
5
|
-
import readline from "node:readline";
|
|
6
4
|
import { promisify } from "node:util";
|
|
7
5
|
import { convertMeasureToPrettyString, getSmashConfig, startRunningMessage, } from "../utils.js";
|
|
8
6
|
export const command = "pull-database";
|
|
@@ -40,7 +38,6 @@ export async function handler() {
|
|
|
40
38
|
await (async () => {
|
|
41
39
|
const tmpFile = "/tmp/staging-database.sql";
|
|
42
40
|
const dbPrefixFile = "/tmp/db-prefix.txt";
|
|
43
|
-
const tmpFileProcessed = "/tmp/staging-database.processed.sql";
|
|
44
41
|
const tablesToExclude = [
|
|
45
42
|
// WordFence
|
|
46
43
|
"wfauditevents",
|
|
@@ -81,26 +78,11 @@ export async function handler() {
|
|
|
81
78
|
.then(async () => {
|
|
82
79
|
await stopRunningMessage();
|
|
83
80
|
console.log("Database downloaded.");
|
|
84
|
-
const stopRunningMessage2 = startRunningMessage("
|
|
85
|
-
await
|
|
86
|
-
const readStream = createReadStream(tmpFile, { encoding: "utf8" });
|
|
87
|
-
const writeStream = createWriteStream(tmpFileProcessed, {
|
|
88
|
-
encoding: "utf8",
|
|
89
|
-
});
|
|
90
|
-
const rl = readline.createInterface({ input: readStream });
|
|
91
|
-
rl.on("line", (line) => {
|
|
92
|
-
writeStream.write(line.replaceAll(stagingUrl, `//${projectName}.test`) + "\n");
|
|
93
|
-
});
|
|
94
|
-
rl.on("close", () => {
|
|
95
|
-
writeStream.end();
|
|
96
|
-
});
|
|
97
|
-
writeStream.on("finish", resolve);
|
|
98
|
-
writeStream.on("error", reject);
|
|
99
|
-
readStream.on("error", reject);
|
|
100
|
-
})
|
|
81
|
+
const stopRunningMessage2 = startRunningMessage("Importing database");
|
|
82
|
+
await execute(`wp db query < ${tmpFile}`)
|
|
101
83
|
.then(async () => {
|
|
102
84
|
await stopRunningMessage2();
|
|
103
|
-
console.log("
|
|
85
|
+
console.log("Database imported.");
|
|
104
86
|
})
|
|
105
87
|
.catch(async (error) => {
|
|
106
88
|
await stopRunningMessage2();
|
|
@@ -108,20 +90,22 @@ export async function handler() {
|
|
|
108
90
|
});
|
|
109
91
|
})
|
|
110
92
|
.then(async () => {
|
|
111
|
-
const stopRunningMessage3 = startRunningMessage("
|
|
112
|
-
await execute(`wp
|
|
93
|
+
const stopRunningMessage3 = startRunningMessage("Running search and replace");
|
|
94
|
+
await execute(`wp search-replace --url=${projectName}.test ${stagingUrl} '//${projectName}.test' --skip-columns=guid`)
|
|
113
95
|
.then(async () => {
|
|
114
96
|
await stopRunningMessage3();
|
|
97
|
+
console.log("Search and replace completed.");
|
|
115
98
|
})
|
|
116
99
|
.catch(async (error) => {
|
|
117
100
|
await stopRunningMessage3();
|
|
118
101
|
throw error;
|
|
119
102
|
});
|
|
103
|
+
})
|
|
104
|
+
.then(async () => {
|
|
120
105
|
const stopRunningMessage4 = startRunningMessage("Cleaning up");
|
|
121
106
|
await Promise.allSettled([
|
|
122
107
|
deleteFile(tmpFile),
|
|
123
108
|
deleteFile(dbPrefixFile),
|
|
124
|
-
deleteFile(tmpFileProcessed),
|
|
125
109
|
]).then(async () => {
|
|
126
110
|
await stopRunningMessage4();
|
|
127
111
|
});
|
|
@@ -134,7 +118,6 @@ export async function handler() {
|
|
|
134
118
|
const cleanupResults = await Promise.allSettled([
|
|
135
119
|
deleteFile(tmpFile),
|
|
136
120
|
deleteFile(dbPrefixFile),
|
|
137
|
-
deleteFile(tmpFileProcessed),
|
|
138
121
|
]);
|
|
139
122
|
const failedCleanups = cleanupResults.filter((result) => result.status === "rejected");
|
|
140
123
|
if (failedCleanups.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-database.d.ts","sourceRoot":"","sources":["../../src/commands/setup-database.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup-database.d.ts","sourceRoot":"","sources":["../../src/commands/setup-database.ts"],"names":[],"mappings":"AAQA,OAAO,eAAe,CAAC;AAiEvB,eAAO,MAAM,OAAO,mBAAmB,CAAC;AACxC,eAAO,MAAM,QAAQ,mEAC4C,CAAC;AAClE,wBAAsB,OAAO,kBAkG5B"}
|
|
@@ -1,112 +1,61 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
|
-
import { readFileSync, readdirSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
2
|
import { performance } from "node:perf_hooks";
|
|
5
3
|
import { promisify } from "node:util";
|
|
6
4
|
import { convertMeasureToPrettyString, getSmashConfig, startRunningMessage, } from "../utils.js";
|
|
7
5
|
import "dotenv/config";
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const content = readFileSync(join(pluginPath, file), 'utf8');
|
|
16
|
-
// Check for the standard plugin header (Plugin Name: field)
|
|
17
|
-
if (/^\s*\/\*\*[\s\S]*?Plugin Name:/m.test(content)) {
|
|
18
|
-
mainFile = file;
|
|
19
|
-
break;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
if (!mainFile)
|
|
23
|
-
return null;
|
|
24
|
-
const content = readFileSync(join(pluginPath, mainFile), 'utf8');
|
|
25
|
-
// Match the plugin header block
|
|
26
|
-
const headerPattern = /\/\*\*\s*([\s\S]*?)\s*\*\//;
|
|
27
|
-
const match = headerPattern.exec(content);
|
|
28
|
-
if (!match)
|
|
29
|
-
return null;
|
|
30
|
-
const headers = {};
|
|
31
|
-
const headerContent = match[1];
|
|
32
|
-
// Parse each header line
|
|
33
|
-
headerContent?.split('\n').forEach(line => {
|
|
34
|
-
const colonIndex = line.indexOf(':');
|
|
35
|
-
if (colonIndex > 0) {
|
|
36
|
-
const key = line.substring(0, colonIndex).trim().replace(/\*/g, '');
|
|
37
|
-
const value = line.substring(colonIndex + 1).trim();
|
|
38
|
-
if (key && value) {
|
|
39
|
-
headers[key] = value;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
return headers;
|
|
44
|
-
}
|
|
45
|
-
catch (_error) {
|
|
46
|
-
return null;
|
|
6
|
+
function extractDependenciesFromError(errorMessage) {
|
|
7
|
+
// Extract plugin names from error messages like:
|
|
8
|
+
// "Warning: Failed to activate plugin. Discount Rules for WooCommerce requires 1 plugin to be installed and activated: WooCommerce."
|
|
9
|
+
const regex = /requires \d+ plugin[s]? to be installed and activated: (.+)\./;
|
|
10
|
+
const match = regex.exec(errorMessage);
|
|
11
|
+
if (match?.[1]) {
|
|
12
|
+
return match[1].split(',').map(dep => dep.trim());
|
|
47
13
|
}
|
|
14
|
+
return [];
|
|
48
15
|
}
|
|
49
|
-
async function
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (temp.has(node)) {
|
|
86
|
-
throw new Error(`Circular dependency detected involving ${node}`);
|
|
87
|
-
}
|
|
88
|
-
if (visited.has(node)) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
temp.add(node);
|
|
92
|
-
// Visit all dependencies first
|
|
93
|
-
const dependencies = graph.get(node) ?? [];
|
|
94
|
-
for (const dep of dependencies) {
|
|
95
|
-
if (graph.has(dep)) {
|
|
96
|
-
visit(dep);
|
|
16
|
+
async function activatePluginsWithRetry(execute, pluginsToActivate, excludedPlugins) {
|
|
17
|
+
const maxRetries = 10; // Prevent infinite loops
|
|
18
|
+
let retryCount = 0;
|
|
19
|
+
const remainingPlugins = [...pluginsToActivate];
|
|
20
|
+
while (remainingPlugins.length > 0 && retryCount < maxRetries) {
|
|
21
|
+
const pluginsToTry = [...remainingPlugins];
|
|
22
|
+
remainingPlugins.length = 0; // Clear the array
|
|
23
|
+
for (const pluginName of pluginsToTry) {
|
|
24
|
+
if (excludedPlugins.includes(pluginName)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
await execute(`wp plugin activate ${pluginName}`);
|
|
29
|
+
console.log(`✓ Activated ${pluginName}`);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
33
|
+
// Check if this is a dependency error
|
|
34
|
+
const dependencies = extractDependenciesFromError(errorMessage);
|
|
35
|
+
if (dependencies.length > 0) {
|
|
36
|
+
console.log(`⚠ ${pluginName} requires dependencies: ${dependencies.join(', ')}`);
|
|
37
|
+
// Add dependencies to the remaining plugins list if they're not already there
|
|
38
|
+
for (const dep of dependencies) {
|
|
39
|
+
if (!pluginsToActivate.includes(dep) && !excludedPlugins.includes(dep)) {
|
|
40
|
+
pluginsToActivate.push(dep);
|
|
41
|
+
}
|
|
42
|
+
if (!remainingPlugins.includes(dep)) {
|
|
43
|
+
remainingPlugins.push(dep);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Add the current plugin back to try again later
|
|
47
|
+
remainingPlugins.push(pluginName);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.warn(`⚠ Warning: Could not activate ${pluginName}: ${errorMessage}`);
|
|
51
|
+
}
|
|
97
52
|
}
|
|
98
53
|
}
|
|
99
|
-
|
|
100
|
-
visited.add(node);
|
|
101
|
-
result.push(node);
|
|
54
|
+
retryCount++;
|
|
102
55
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (!visited.has(node)) {
|
|
106
|
-
visit(node);
|
|
107
|
-
}
|
|
56
|
+
if (remainingPlugins.length > 0) {
|
|
57
|
+
console.warn(`⚠ Warning: Could not activate all plugins after ${maxRetries} attempts. Remaining: ${remainingPlugins.join(', ')}`);
|
|
108
58
|
}
|
|
109
|
-
return result;
|
|
110
59
|
}
|
|
111
60
|
export const command = "setup-database";
|
|
112
61
|
export const describe = "Create a new database and initialise the site with no content.";
|
|
@@ -140,31 +89,15 @@ export async function handler() {
|
|
|
140
89
|
performance.mark("add-custom-user");
|
|
141
90
|
console.log(`Custom user ${process.env.WORDPRESS_USER} added. (${convertMeasureToPrettyString(performance.measure("add-custom-user", "wordpress-tables"))})`);
|
|
142
91
|
}
|
|
143
|
-
const dependencyGraph = await buildDependencyGraph(execute);
|
|
144
92
|
const excludedPlugins = ['stream', 'shortpixel-image-optimiser', 'wordfence'];
|
|
145
93
|
const { stdout: pluginList } = await execute('wp plugin list --format=json');
|
|
146
94
|
const allPlugins = JSON.parse(pluginList);
|
|
147
95
|
// Filter out excluded plugins and already active plugins
|
|
148
|
-
const pluginsToActivate = allPlugins
|
|
149
|
-
plugin
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// Only include dependencies that are also in our activation list
|
|
154
|
-
const filteredDependencies = dependencies.filter(dep => pluginsToActivate.some(p => p.name === dep));
|
|
155
|
-
filteredGraph.set(plugin.name, filteredDependencies);
|
|
156
|
-
}
|
|
157
|
-
const activationOrder = topologicalSort(filteredGraph);
|
|
158
|
-
for (const pluginName of activationOrder) {
|
|
159
|
-
try {
|
|
160
|
-
await execute(`wp plugin activate ${pluginName}`);
|
|
161
|
-
console.log(`✓ Activated ${pluginName}`);
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
165
|
-
console.warn(`⚠ Warning: Could not activate ${pluginName}: ${errorMessage}`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
96
|
+
const pluginsToActivate = allPlugins
|
|
97
|
+
.filter(plugin => !excludedPlugins.includes(plugin.name) &&
|
|
98
|
+
plugin.status !== 'active')
|
|
99
|
+
.map(plugin => plugin.name);
|
|
100
|
+
await activatePluginsWithRetry(execute, pluginsToActivate, excludedPlugins);
|
|
168
101
|
})
|
|
169
102
|
.then(() => {
|
|
170
103
|
performance.mark("plugins");
|