@wordpress-flow/cli 1.2.12 → 1.2.13
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/index.js +595 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -80975,6 +80975,16 @@ class ConfigManager {
|
|
|
80975
80975
|
}
|
|
80976
80976
|
return result2;
|
|
80977
80977
|
}
|
|
80978
|
+
getRawConfig() {
|
|
80979
|
+
if (!this.rawConfig) {
|
|
80980
|
+
throw new Error("Configuration not loaded. Call loadConfig() first.");
|
|
80981
|
+
}
|
|
80982
|
+
return this.rawConfig;
|
|
80983
|
+
}
|
|
80984
|
+
getEnvironments() {
|
|
80985
|
+
const raw = this.getRawConfig();
|
|
80986
|
+
return raw.environments || [];
|
|
80987
|
+
}
|
|
80978
80988
|
getConfig() {
|
|
80979
80989
|
if (!this.config) {
|
|
80980
80990
|
throw new Error("Configuration not loaded. Call loadConfig() first.");
|
|
@@ -81126,6 +81136,9 @@ class Logger {
|
|
|
81126
81136
|
step(step, total, message) {
|
|
81127
81137
|
console.log(import_chalk2.default.cyan(`[${step}/${total}] ${message}`));
|
|
81128
81138
|
}
|
|
81139
|
+
substep(message) {
|
|
81140
|
+
console.log(import_chalk2.default.gray(` ↳ ${message}`));
|
|
81141
|
+
}
|
|
81129
81142
|
}
|
|
81130
81143
|
var logger = Logger.getInstance();
|
|
81131
81144
|
// src/commands/pull-command.ts
|
|
@@ -114937,10 +114950,6 @@ class BlockHashManager {
|
|
|
114937
114950
|
logger.debug(`${blockName}: No stored hash, needs rebuild`);
|
|
114938
114951
|
return true;
|
|
114939
114952
|
}
|
|
114940
|
-
if (storedHash.cliVersion !== this.cliVersion) {
|
|
114941
|
-
logger.debug(`${blockName}: CLI version changed, needs rebuild`);
|
|
114942
|
-
return true;
|
|
114943
|
-
}
|
|
114944
114953
|
const currentHash = this.calculateSourceHash(sourcePath);
|
|
114945
114954
|
if (!currentHash) {
|
|
114946
114955
|
logger.debug(`${blockName}: Source file missing, needs rebuild`);
|
|
@@ -116186,7 +116195,7 @@ class DockerEnvManager {
|
|
|
116186
116195
|
// package.json
|
|
116187
116196
|
var package_default = {
|
|
116188
116197
|
name: "@wordpress-flow/cli",
|
|
116189
|
-
version: "1.2.
|
|
116198
|
+
version: "1.2.13",
|
|
116190
116199
|
type: "module",
|
|
116191
116200
|
description: "TypeScript-based WordPress block creation system",
|
|
116192
116201
|
main: "dist/index.js",
|
|
@@ -117159,6 +117168,7 @@ class DevCommand {
|
|
|
117159
117168
|
|
|
117160
117169
|
// src/commands/build-command.ts
|
|
117161
117170
|
import * as path22 from "path";
|
|
117171
|
+
import * as fs19 from "fs";
|
|
117162
117172
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
117163
117173
|
|
|
117164
117174
|
// src/build/block-builder.ts
|
|
@@ -117751,6 +117761,12 @@ class WorkerPool {
|
|
|
117751
117761
|
// src/commands/build-command.ts
|
|
117752
117762
|
var __filename3 = fileURLToPath5(import.meta.url);
|
|
117753
117763
|
var __dirname3 = path22.dirname(__filename3);
|
|
117764
|
+
var packageJsonPath = path22.join(__dirname3, "..", "..", "..", "package.json");
|
|
117765
|
+
var packageVersion = "1.2.12";
|
|
117766
|
+
try {
|
|
117767
|
+
const packageJson = JSON.parse(fs19.readFileSync(packageJsonPath, "utf8"));
|
|
117768
|
+
packageVersion = packageJson.version;
|
|
117769
|
+
} catch {}
|
|
117754
117770
|
|
|
117755
117771
|
class BuildCommand {
|
|
117756
117772
|
configManager;
|
|
@@ -117759,6 +117775,7 @@ class BuildCommand {
|
|
|
117759
117775
|
typeGenerator;
|
|
117760
117776
|
phpGenerator;
|
|
117761
117777
|
blockScripts;
|
|
117778
|
+
hashManager = null;
|
|
117762
117779
|
constructor() {
|
|
117763
117780
|
this.configManager = ConfigManager.getInstance();
|
|
117764
117781
|
this.blockScanner = new BlockScanner;
|
|
@@ -117809,6 +117826,27 @@ class BuildCommand {
|
|
|
117809
117826
|
} else {
|
|
117810
117827
|
logger.info(`Found ${blocks.length} block(s): ${blocks.map((b) => b.name).join(", ")}`);
|
|
117811
117828
|
}
|
|
117829
|
+
this.hashManager = new BlockHashManager(outputDir, packageVersion);
|
|
117830
|
+
let blocksToRebuild = blocks;
|
|
117831
|
+
if (!options.noCache) {
|
|
117832
|
+
const stats = this.hashManager.getStats(blocks);
|
|
117833
|
+
if (stats.cached > 0) {
|
|
117834
|
+
logger.info(`\uD83D\uDCE6 Cache: ${stats.cached} block(s) unchanged, ${stats.needsRebuild} need(s) rebuild`);
|
|
117835
|
+
blocksToRebuild = this.hashManager.filterBlocksNeedingRebuild(blocks);
|
|
117836
|
+
const skippedBlocks = blocks.filter((b) => !blocksToRebuild.find((rb) => rb.name === b.name));
|
|
117837
|
+
if (skippedBlocks.length > 0) {
|
|
117838
|
+
logger.info(`⏭️ Skipping: ${skippedBlocks.map((b) => b.name).join(", ")}`);
|
|
117839
|
+
}
|
|
117840
|
+
}
|
|
117841
|
+
} else {
|
|
117842
|
+
logger.info("\uD83D\uDD04 Cache disabled (--no-cache), rebuilding all blocks");
|
|
117843
|
+
this.hashManager.clearAllHashes();
|
|
117844
|
+
}
|
|
117845
|
+
if (blocksToRebuild.length === 0) {
|
|
117846
|
+
logger.success("✨ All blocks are up to date! Nothing to build.");
|
|
117847
|
+
return;
|
|
117848
|
+
}
|
|
117849
|
+
logger.info(`\uD83D\uDD28 Building ${blocksToRebuild.length} block(s): ${blocksToRebuild.map((b) => b.name).join(", ")}`);
|
|
117812
117850
|
const scriptsOutputDir = this.configManager.resolvePath(config.paths.scriptsDist);
|
|
117813
117851
|
const workerPool = new WorkerPool({
|
|
117814
117852
|
concurrency: options.concurrency,
|
|
@@ -117817,9 +117855,14 @@ class BuildCommand {
|
|
|
117817
117855
|
webpackConfigPath: webpackConfig,
|
|
117818
117856
|
scriptsPath
|
|
117819
117857
|
});
|
|
117820
|
-
const results = await workerPool.buildAll(
|
|
117858
|
+
const results = await workerPool.buildAll(blocksToRebuild, (completed, total, blockName, success, error) => {
|
|
117821
117859
|
if (success) {
|
|
117822
117860
|
logger.success(`✅ Built: ${blockName} [${completed}/${total}]`);
|
|
117861
|
+
const block = blocksToRebuild.find((b) => b.name === blockName);
|
|
117862
|
+
if (block && this.hashManager) {
|
|
117863
|
+
const sourceHash = this.hashManager.calculateSourceHash(block.filePath);
|
|
117864
|
+
this.hashManager.storeHash(blockName, sourceHash);
|
|
117865
|
+
}
|
|
117823
117866
|
} else {
|
|
117824
117867
|
logger.error(`❌ Failed: ${blockName} [${completed}/${total}]: ${error || "unknown error"}`);
|
|
117825
117868
|
}
|
|
@@ -117848,11 +117891,17 @@ Failed to build ${failures.length} block(s):`);
|
|
|
117848
117891
|
}
|
|
117849
117892
|
await this.generateTypeDefinitions(outputDir);
|
|
117850
117893
|
const successCount = results.filter((r) => r.success).length;
|
|
117894
|
+
const totalBlocks = blocks.length;
|
|
117895
|
+
const cachedCount = totalBlocks - blocksToRebuild.length;
|
|
117851
117896
|
if (failures.length > 0) {
|
|
117852
|
-
logger.warn(`Build completed with errors. Built ${successCount}/${
|
|
117897
|
+
logger.warn(`Build completed with errors. Built ${successCount}/${blocksToRebuild.length} block(s)${cachedCount > 0 ? `, ${cachedCount} cached` : ""}.`);
|
|
117853
117898
|
process.exit(1);
|
|
117854
117899
|
} else {
|
|
117855
|
-
|
|
117900
|
+
if (cachedCount > 0) {
|
|
117901
|
+
logger.success(`Build completed successfully! Built ${successCount} block(s), ${cachedCount} cached.`);
|
|
117902
|
+
} else {
|
|
117903
|
+
logger.success(`Build completed successfully! Built ${successCount} block(s).`);
|
|
117904
|
+
}
|
|
117856
117905
|
}
|
|
117857
117906
|
} catch (error) {
|
|
117858
117907
|
logger.error("Build operation failed:");
|
|
@@ -117881,7 +117930,7 @@ Failed to build ${failures.length} block(s):`);
|
|
|
117881
117930
|
}
|
|
117882
117931
|
|
|
117883
117932
|
// src/commands/build-templates-command.ts
|
|
117884
|
-
import * as
|
|
117933
|
+
import * as fs20 from "fs";
|
|
117885
117934
|
import * as path23 from "path";
|
|
117886
117935
|
class BuildTemplatesCommand {
|
|
117887
117936
|
configManager;
|
|
@@ -117893,11 +117942,11 @@ class BuildTemplatesCommand {
|
|
|
117893
117942
|
const config = this.configManager.getConfig();
|
|
117894
117943
|
const templatesDir = options.templatesDir || this.configManager.resolvePath(config.paths.templates);
|
|
117895
117944
|
const outputDir = options.outputDir || this.configManager.resolvePath(config.paths.templatesOutput);
|
|
117896
|
-
if (!
|
|
117945
|
+
if (!fs20.existsSync(templatesDir)) {
|
|
117897
117946
|
logger.error(`Templates directory not found: ${templatesDir}`);
|
|
117898
117947
|
return;
|
|
117899
117948
|
}
|
|
117900
|
-
|
|
117949
|
+
fs20.mkdirSync(outputDir, { recursive: true });
|
|
117901
117950
|
const blocksOutputDir = this.configManager.resolvePath(config.paths.blocksDist);
|
|
117902
117951
|
const blockRegistry = new BlockRegistry(blocksOutputDir);
|
|
117903
117952
|
await blockRegistry.loadBuiltBlocks();
|
|
@@ -117935,17 +117984,17 @@ class BuildTemplatesCommand {
|
|
|
117935
117984
|
}
|
|
117936
117985
|
}
|
|
117937
117986
|
async buildTemplate(templatePath, templateName, outputDir, renderer) {
|
|
117938
|
-
const mdxContent =
|
|
117987
|
+
const mdxContent = fs20.readFileSync(templatePath, "utf8");
|
|
117939
117988
|
const cleanContent = this.removeFrontmatter(mdxContent);
|
|
117940
117989
|
const html5 = await renderer.renderMDXToHTML(cleanContent);
|
|
117941
117990
|
const cleanHTML = renderer.postProcessHTML(html5);
|
|
117942
117991
|
const outputPath = path23.join(outputDir, `${templateName}.html`);
|
|
117943
|
-
|
|
117992
|
+
fs20.writeFileSync(outputPath, cleanHTML, "utf8");
|
|
117944
117993
|
logger.info(`✓ Generated: ${path23.relative(process.cwd(), outputPath)}`);
|
|
117945
117994
|
}
|
|
117946
117995
|
findMDXFiles(dir) {
|
|
117947
117996
|
const files = [];
|
|
117948
|
-
const entries =
|
|
117997
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
117949
117998
|
for (const entry of entries) {
|
|
117950
117999
|
const fullPath = path23.join(dir, entry.name);
|
|
117951
118000
|
if (entry.isDirectory()) {
|
|
@@ -117963,7 +118012,7 @@ class BuildTemplatesCommand {
|
|
|
117963
118012
|
}
|
|
117964
118013
|
|
|
117965
118014
|
// src/commands/env-command.ts
|
|
117966
|
-
import * as
|
|
118015
|
+
import * as fs21 from "fs";
|
|
117967
118016
|
import * as path24 from "path";
|
|
117968
118017
|
class EnvCommand {
|
|
117969
118018
|
configManager;
|
|
@@ -117972,11 +118021,11 @@ class EnvCommand {
|
|
|
117972
118021
|
}
|
|
117973
118022
|
getDockerEnvManager() {
|
|
117974
118023
|
const config = this.configManager.getConfig();
|
|
117975
|
-
const
|
|
118024
|
+
const packageJsonPath2 = path24.join(this.configManager.getConfigDir(), "package.json");
|
|
117976
118025
|
let projectName = "wordpress-project";
|
|
117977
|
-
if (
|
|
118026
|
+
if (fs21.existsSync(packageJsonPath2)) {
|
|
117978
118027
|
try {
|
|
117979
|
-
const pkg = JSON.parse(
|
|
118028
|
+
const pkg = JSON.parse(fs21.readFileSync(packageJsonPath2, "utf8"));
|
|
117980
118029
|
projectName = pkg.name || projectName;
|
|
117981
118030
|
} catch {}
|
|
117982
118031
|
}
|
|
@@ -118025,6 +118074,501 @@ class EnvCommand {
|
|
|
118025
118074
|
}
|
|
118026
118075
|
}
|
|
118027
118076
|
|
|
118077
|
+
// src/commands/deploy-command.ts
|
|
118078
|
+
import * as fs23 from "fs";
|
|
118079
|
+
import * as path26 from "path";
|
|
118080
|
+
|
|
118081
|
+
// src/wordpress/ssh-tunnel.ts
|
|
118082
|
+
import * as fs22 from "fs";
|
|
118083
|
+
import * as path25 from "path";
|
|
118084
|
+
import { spawn } from "child_process";
|
|
118085
|
+
class SSHTunnel {
|
|
118086
|
+
configManager;
|
|
118087
|
+
secrets = new Map;
|
|
118088
|
+
tunnelProcess = null;
|
|
118089
|
+
constructor() {
|
|
118090
|
+
this.configManager = ConfigManager.getInstance();
|
|
118091
|
+
this.loadSecrets();
|
|
118092
|
+
}
|
|
118093
|
+
loadSecrets() {
|
|
118094
|
+
const secretsPath = path25.join(process.cwd(), "wordpress-flow.secrets");
|
|
118095
|
+
if (!fs22.existsSync(secretsPath))
|
|
118096
|
+
return;
|
|
118097
|
+
try {
|
|
118098
|
+
const content4 = fs22.readFileSync(secretsPath, "utf8");
|
|
118099
|
+
for (const line of content4.split(`
|
|
118100
|
+
`)) {
|
|
118101
|
+
const trimmed = line.trim();
|
|
118102
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
118103
|
+
continue;
|
|
118104
|
+
const eqIndex = trimmed.indexOf("=");
|
|
118105
|
+
if (eqIndex === -1)
|
|
118106
|
+
continue;
|
|
118107
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
118108
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
118109
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
118110
|
+
value = value.slice(1, -1);
|
|
118111
|
+
}
|
|
118112
|
+
this.secrets.set(key, value);
|
|
118113
|
+
}
|
|
118114
|
+
} catch {}
|
|
118115
|
+
}
|
|
118116
|
+
resolveVariables(value) {
|
|
118117
|
+
return value.replace(/\$\{([^}]+)\}/g, (match2, varName) => {
|
|
118118
|
+
const secret = this.secrets.get(varName);
|
|
118119
|
+
if (secret !== undefined)
|
|
118120
|
+
return secret;
|
|
118121
|
+
const envVar = process.env[varName];
|
|
118122
|
+
if (envVar !== undefined)
|
|
118123
|
+
return envVar;
|
|
118124
|
+
return match2;
|
|
118125
|
+
});
|
|
118126
|
+
}
|
|
118127
|
+
getEnvironment(name2) {
|
|
118128
|
+
const environments = this.configManager.getEnvironments();
|
|
118129
|
+
const env2 = environments.find((e) => e.name === name2);
|
|
118130
|
+
if (!env2) {
|
|
118131
|
+
throw new Error(`Environment "${name2}" not found. Available: ${environments.map((e) => e.name).join(", ")}`);
|
|
118132
|
+
}
|
|
118133
|
+
return env2;
|
|
118134
|
+
}
|
|
118135
|
+
async connect(envName) {
|
|
118136
|
+
const env2 = this.getEnvironment(envName);
|
|
118137
|
+
if (!env2.database) {
|
|
118138
|
+
throw new Error(`No database config for environment "${envName}"`);
|
|
118139
|
+
}
|
|
118140
|
+
const db = env2.database;
|
|
118141
|
+
const dbHost = this.resolveVariables(db.host);
|
|
118142
|
+
const dbPort = db.port || 3306;
|
|
118143
|
+
const dbUser = this.resolveVariables(db.username);
|
|
118144
|
+
const dbPass = this.resolveVariables(db.password);
|
|
118145
|
+
const dbName = this.resolveVariables(db.database);
|
|
118146
|
+
const localPort = 13306 + Math.floor(Math.random() * 1000);
|
|
118147
|
+
const sshPort = env2.ssh.port || 22;
|
|
118148
|
+
const target = `${env2.ssh.user}@${env2.ssh.host}`;
|
|
118149
|
+
let sshFlags = `-p ${sshPort} -o ServerAliveInterval=30 -o StrictHostKeyChecking=no`;
|
|
118150
|
+
let prefix = "";
|
|
118151
|
+
if (env2.ssh.privateKeyPath) {
|
|
118152
|
+
sshFlags += ` -i ${this.resolveVariables(env2.ssh.privateKeyPath)}`;
|
|
118153
|
+
} else if (env2.ssh.password) {
|
|
118154
|
+
const password = this.resolveVariables(env2.ssh.password);
|
|
118155
|
+
const escaped = password.replace(/'/g, "'\\''");
|
|
118156
|
+
prefix = `sshpass -p '${escaped}' `;
|
|
118157
|
+
sshFlags += ` -o PreferredAuthentications=password -o PubkeyAuthentication=no`;
|
|
118158
|
+
}
|
|
118159
|
+
const tunnelCmd = `${prefix}ssh ${sshFlags} -N -L ${localPort}:${dbHost}:${dbPort} ${target}`;
|
|
118160
|
+
logger.substep(`SSH tunnel: localhost:${localPort} → ${envName} (${dbHost}:${dbPort})`);
|
|
118161
|
+
this.tunnelProcess = spawn("bash", ["-c", tunnelCmd], {
|
|
118162
|
+
stdio: "pipe",
|
|
118163
|
+
detached: false
|
|
118164
|
+
});
|
|
118165
|
+
await this.waitForPort(localPort, 15000);
|
|
118166
|
+
this.configManager.setDatabaseConnection({
|
|
118167
|
+
host: "127.0.0.1",
|
|
118168
|
+
port: localPort,
|
|
118169
|
+
database: dbName,
|
|
118170
|
+
username: dbUser,
|
|
118171
|
+
password: dbPass
|
|
118172
|
+
});
|
|
118173
|
+
return () => {
|
|
118174
|
+
this.tunnelProcess?.kill();
|
|
118175
|
+
this.tunnelProcess = null;
|
|
118176
|
+
};
|
|
118177
|
+
}
|
|
118178
|
+
waitForPort(port, timeoutMs) {
|
|
118179
|
+
const net = __require("net");
|
|
118180
|
+
const start2 = Date.now();
|
|
118181
|
+
return new Promise((resolve7, reject) => {
|
|
118182
|
+
const tryConnect = () => {
|
|
118183
|
+
if (Date.now() - start2 > timeoutMs) {
|
|
118184
|
+
return reject(new Error(`SSH tunnel timed out — port ${port} not reachable after ${timeoutMs / 1000}s`));
|
|
118185
|
+
}
|
|
118186
|
+
const socket = net.createConnection({ port, host: "127.0.0.1" });
|
|
118187
|
+
socket.setTimeout(500);
|
|
118188
|
+
socket.on("connect", () => {
|
|
118189
|
+
socket.destroy();
|
|
118190
|
+
resolve7();
|
|
118191
|
+
});
|
|
118192
|
+
socket.on("error", () => {
|
|
118193
|
+
socket.destroy();
|
|
118194
|
+
setTimeout(tryConnect, 300);
|
|
118195
|
+
});
|
|
118196
|
+
socket.on("timeout", () => {
|
|
118197
|
+
socket.destroy();
|
|
118198
|
+
setTimeout(tryConnect, 300);
|
|
118199
|
+
});
|
|
118200
|
+
};
|
|
118201
|
+
tryConnect();
|
|
118202
|
+
});
|
|
118203
|
+
}
|
|
118204
|
+
}
|
|
118205
|
+
|
|
118206
|
+
// src/commands/deploy-command.ts
|
|
118207
|
+
import { execSync as execSync3 } from "child_process";
|
|
118208
|
+
|
|
118209
|
+
class DeployCommand {
|
|
118210
|
+
configManager;
|
|
118211
|
+
secrets = new Map;
|
|
118212
|
+
constructor() {
|
|
118213
|
+
this.configManager = ConfigManager.getInstance();
|
|
118214
|
+
this.loadSecrets();
|
|
118215
|
+
}
|
|
118216
|
+
loadSecrets() {
|
|
118217
|
+
const secretsPath = path26.join(process.cwd(), "wordpress-flow.secrets");
|
|
118218
|
+
if (!fs23.existsSync(secretsPath)) {
|
|
118219
|
+
logger.debug("No secrets file found at wordpress-flow.secrets");
|
|
118220
|
+
return;
|
|
118221
|
+
}
|
|
118222
|
+
try {
|
|
118223
|
+
const content4 = fs23.readFileSync(secretsPath, "utf8");
|
|
118224
|
+
for (const line of content4.split(`
|
|
118225
|
+
`)) {
|
|
118226
|
+
const trimmed = line.trim();
|
|
118227
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
118228
|
+
continue;
|
|
118229
|
+
const eqIndex = trimmed.indexOf("=");
|
|
118230
|
+
if (eqIndex === -1)
|
|
118231
|
+
continue;
|
|
118232
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
118233
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
118234
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
118235
|
+
value = value.slice(1, -1);
|
|
118236
|
+
}
|
|
118237
|
+
this.secrets.set(key, value);
|
|
118238
|
+
}
|
|
118239
|
+
logger.debug(`Loaded ${this.secrets.size} secrets`);
|
|
118240
|
+
} catch (error) {
|
|
118241
|
+
logger.warn(`Failed to load secrets: ${error.message}`);
|
|
118242
|
+
}
|
|
118243
|
+
}
|
|
118244
|
+
resolveVariables(value) {
|
|
118245
|
+
return value.replace(/\$\{([^}]+)\}/g, (match2, varName) => {
|
|
118246
|
+
const secret = this.secrets.get(varName);
|
|
118247
|
+
if (secret !== undefined)
|
|
118248
|
+
return secret;
|
|
118249
|
+
const envVar = process.env[varName];
|
|
118250
|
+
if (envVar !== undefined)
|
|
118251
|
+
return envVar;
|
|
118252
|
+
logger.warn(`Variable ${varName} not found in secrets or environment`);
|
|
118253
|
+
return match2;
|
|
118254
|
+
});
|
|
118255
|
+
}
|
|
118256
|
+
buildSSHConfig(env2) {
|
|
118257
|
+
return `${env2.ssh.user}@${env2.ssh.host}`;
|
|
118258
|
+
}
|
|
118259
|
+
buildSSHParts(env2) {
|
|
118260
|
+
const port = env2.ssh.port || 22;
|
|
118261
|
+
let sshFlags = `-p ${port} -o ServerAliveInterval=30 -o StrictHostKeyChecking=no`;
|
|
118262
|
+
let prefix = "";
|
|
118263
|
+
if (env2.ssh.privateKeyPath) {
|
|
118264
|
+
const keyPath = this.resolveVariables(env2.ssh.privateKeyPath);
|
|
118265
|
+
sshFlags += ` -i ${keyPath}`;
|
|
118266
|
+
} else if (env2.ssh.password) {
|
|
118267
|
+
const password = this.resolveVariables(env2.ssh.password);
|
|
118268
|
+
const escaped = password.replace(/'/g, "'\\''");
|
|
118269
|
+
prefix = `sshpass -p '${escaped}' `;
|
|
118270
|
+
sshFlags += ` -o PreferredAuthentications=password -o PubkeyAuthentication=no`;
|
|
118271
|
+
}
|
|
118272
|
+
return { prefix, sshFlags };
|
|
118273
|
+
}
|
|
118274
|
+
sshExec(env2, remoteCmd, opts) {
|
|
118275
|
+
const { prefix, sshFlags } = this.buildSSHParts(env2);
|
|
118276
|
+
const target = this.buildSSHConfig(env2);
|
|
118277
|
+
const fullCmd = `${prefix}ssh ${sshFlags} ${target} "${remoteCmd.replace(/"/g, "\\\"")}"`;
|
|
118278
|
+
if (!opts?.silent)
|
|
118279
|
+
logger.debug(`SSH: ${remoteCmd}`);
|
|
118280
|
+
try {
|
|
118281
|
+
return execSync3(fullCmd, { encoding: "utf8", stdio: "pipe" }).trim();
|
|
118282
|
+
} catch (error) {
|
|
118283
|
+
const stderr = error.stderr?.toString() || "";
|
|
118284
|
+
const msg = error.message || String(error);
|
|
118285
|
+
if (msg.includes("sshpass: command not found") || msg.includes("sshpass: not found")) {
|
|
118286
|
+
throw new Error(`sshpass is required for password authentication.
|
|
118287
|
+
` + `Install: brew install sshpass (macOS) / apt install sshpass (Linux)
|
|
118288
|
+
` + `Or use SSH key authentication instead.`);
|
|
118289
|
+
}
|
|
118290
|
+
throw new Error(`SSH command failed: ${remoteCmd}
|
|
118291
|
+
${stderr || msg}`);
|
|
118292
|
+
}
|
|
118293
|
+
}
|
|
118294
|
+
scpUpload(env2, localPath, remotePath) {
|
|
118295
|
+
const { prefix, sshFlags } = this.buildSSHParts(env2);
|
|
118296
|
+
const target = this.buildSSHConfig(env2);
|
|
118297
|
+
const port = env2.ssh.port || 22;
|
|
118298
|
+
const scpFlags = sshFlags.replace(`-p ${port}`, "");
|
|
118299
|
+
const fullCmd = `${prefix}scp -P ${port} ${scpFlags} "${localPath}" "${target}:${remotePath}"`;
|
|
118300
|
+
logger.debug(`SCP upload: ${localPath} → ${remotePath}`);
|
|
118301
|
+
try {
|
|
118302
|
+
execSync3(fullCmd, { encoding: "utf8", stdio: "pipe" });
|
|
118303
|
+
} catch (error) {
|
|
118304
|
+
const stderr = error.stderr?.toString() || "";
|
|
118305
|
+
throw new Error(`SCP upload failed: ${stderr || error.message}`);
|
|
118306
|
+
}
|
|
118307
|
+
}
|
|
118308
|
+
async checkWordPressInstalled(env2) {
|
|
118309
|
+
const webroot = this.deriveWebroot(env2);
|
|
118310
|
+
logger.debug(`Checking WordPress at ${webroot}...`);
|
|
118311
|
+
try {
|
|
118312
|
+
const result2 = this.sshExec(env2, `test -f ${webroot}/wp-config.php && echo 'yes' || echo 'no'`, { silent: true });
|
|
118313
|
+
return { installed: result2 === "yes", webroot };
|
|
118314
|
+
} catch {
|
|
118315
|
+
return { installed: false, webroot };
|
|
118316
|
+
}
|
|
118317
|
+
}
|
|
118318
|
+
deriveWebroot(env2) {
|
|
118319
|
+
const themePath = env2.remotePaths.theme;
|
|
118320
|
+
const wpContentIdx = themePath.indexOf("/wp-content/");
|
|
118321
|
+
if (wpContentIdx !== -1) {
|
|
118322
|
+
return themePath.slice(0, wpContentIdx);
|
|
118323
|
+
}
|
|
118324
|
+
return path26.posix.resolve(themePath, "../../..");
|
|
118325
|
+
}
|
|
118326
|
+
askYesNo(question) {
|
|
118327
|
+
return new Promise((resolve7) => {
|
|
118328
|
+
const readline2 = __require("readline");
|
|
118329
|
+
const rl = readline2.createInterface({
|
|
118330
|
+
input: process.stdin,
|
|
118331
|
+
output: process.stdout
|
|
118332
|
+
});
|
|
118333
|
+
rl.question(`${question} (Y/n): `, (answer) => {
|
|
118334
|
+
rl.close();
|
|
118335
|
+
const n = answer.trim().toLowerCase();
|
|
118336
|
+
resolve7(n === "" || n === "y" || n === "yes");
|
|
118337
|
+
});
|
|
118338
|
+
});
|
|
118339
|
+
}
|
|
118340
|
+
askInput(question, defaultValue) {
|
|
118341
|
+
return new Promise((resolve7) => {
|
|
118342
|
+
const readline2 = __require("readline");
|
|
118343
|
+
const rl = readline2.createInterface({
|
|
118344
|
+
input: process.stdin,
|
|
118345
|
+
output: process.stdout
|
|
118346
|
+
});
|
|
118347
|
+
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
118348
|
+
rl.question(prompt, (answer) => {
|
|
118349
|
+
rl.close();
|
|
118350
|
+
resolve7(answer.trim() || defaultValue || "");
|
|
118351
|
+
});
|
|
118352
|
+
});
|
|
118353
|
+
}
|
|
118354
|
+
async installWordPress(env2, webroot, sqlDump) {
|
|
118355
|
+
logger.progress("Installing WordPress on remote server...");
|
|
118356
|
+
logger.info("Checking for WP-CLI...");
|
|
118357
|
+
try {
|
|
118358
|
+
this.sshExec(env2, "wp --version", { silent: true });
|
|
118359
|
+
logger.debug("WP-CLI already available");
|
|
118360
|
+
} catch {
|
|
118361
|
+
logger.info("Installing WP-CLI...");
|
|
118362
|
+
this.sshExec(env2, `curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mkdir -p ~/bin && mv wp-cli.phar ~/bin/wp`);
|
|
118363
|
+
logger.success("WP-CLI installed to ~/bin/wp");
|
|
118364
|
+
}
|
|
118365
|
+
const wp = "~/bin/wp";
|
|
118366
|
+
this.sshExec(env2, `mkdir -p ${webroot}`);
|
|
118367
|
+
logger.info("Downloading WordPress core...");
|
|
118368
|
+
this.sshExec(env2, `${wp} core download --path=${webroot} --locale=pl_PL --skip-content`);
|
|
118369
|
+
logger.success("WordPress core downloaded");
|
|
118370
|
+
if (env2.database) {
|
|
118371
|
+
const db = env2.database;
|
|
118372
|
+
const dbHost = this.resolveVariables(db.host);
|
|
118373
|
+
const dbPort = db.port || 3306;
|
|
118374
|
+
const dbName = this.resolveVariables(db.database);
|
|
118375
|
+
const dbUser = this.resolveVariables(db.username);
|
|
118376
|
+
const dbPass = this.resolveVariables(db.password);
|
|
118377
|
+
logger.info("Creating wp-config.php...");
|
|
118378
|
+
this.sshExec(env2, `${wp} config create --path=${webroot} --dbname=${dbName} --dbuser=${dbUser} --dbpass='${dbPass.replace(/'/g, "'\\''")}' --dbhost=${dbHost}:${dbPort} --skip-check`);
|
|
118379
|
+
logger.success("wp-config.php created");
|
|
118380
|
+
if (sqlDump && fs23.existsSync(sqlDump)) {
|
|
118381
|
+
logger.info(`Loading database dump from ${sqlDump}...`);
|
|
118382
|
+
await this.loadDump(env2, sqlDump);
|
|
118383
|
+
logger.success("Database imported from dump");
|
|
118384
|
+
} else {
|
|
118385
|
+
if (sqlDump) {
|
|
118386
|
+
logger.warn(`Dump file not found at ${sqlDump}, falling back to fresh install`);
|
|
118387
|
+
}
|
|
118388
|
+
logger.info("Installing WordPress database...");
|
|
118389
|
+
const siteUrl = env2.url || `https://${env2.ssh.host}`;
|
|
118390
|
+
const adminUser = await this.askInput("Admin username", "admin");
|
|
118391
|
+
const adminPassword = await this.askInput("Admin password");
|
|
118392
|
+
const adminEmail = await this.askInput("Admin email");
|
|
118393
|
+
const siteTitle = await this.askInput("Site title", env2.name);
|
|
118394
|
+
this.sshExec(env2, `${wp} core install --path=${webroot} --url='${siteUrl}' --title='${siteTitle.replace(/'/g, "'\\''")}' --admin_user='${adminUser}' --admin_password='${adminPassword.replace(/'/g, "'\\''")}' --admin_email='${adminEmail}' --skip-email`);
|
|
118395
|
+
logger.success("WordPress database installed");
|
|
118396
|
+
}
|
|
118397
|
+
} else {
|
|
118398
|
+
logger.warn(`No database config in environment — skipping wp-config.php creation.
|
|
118399
|
+
` + "You'll need to create it manually on the server.");
|
|
118400
|
+
}
|
|
118401
|
+
this.sshExec(env2, `mkdir -p ${webroot}/wp-content/themes ${webroot}/wp-content/plugins`);
|
|
118402
|
+
logger.success("WordPress installed on remote server");
|
|
118403
|
+
}
|
|
118404
|
+
async execute(options) {
|
|
118405
|
+
const config = this.configManager.getConfig();
|
|
118406
|
+
const environments = this.configManager.getEnvironments();
|
|
118407
|
+
const env2 = environments.find((e) => e.name === options.environment);
|
|
118408
|
+
if (!env2) {
|
|
118409
|
+
throw new Error(`Environment "${options.environment}" not found in config. ` + `Available: ${environments.map((e) => e.name).join(", ")}`);
|
|
118410
|
+
}
|
|
118411
|
+
console.log("");
|
|
118412
|
+
logger.progress(`Deploying to ${env2.name} (${env2.ssh.host})`);
|
|
118413
|
+
console.log("");
|
|
118414
|
+
try {
|
|
118415
|
+
const { installed, webroot } = await this.checkWordPressInstalled(env2);
|
|
118416
|
+
if (!installed) {
|
|
118417
|
+
logger.warn(`WordPress not found at ${webroot}`);
|
|
118418
|
+
const shouldInstall = await this.askYesNo("WordPress is not installed on the remote server. Install it now?");
|
|
118419
|
+
if (shouldInstall) {
|
|
118420
|
+
await this.installWordPress(env2, webroot, config.dev.sqlDump);
|
|
118421
|
+
} else {
|
|
118422
|
+
throw new Error(`WordPress is required on the remote server before deploying.
|
|
118423
|
+
` + "Install it manually or re-run deploy and choose to install.");
|
|
118424
|
+
}
|
|
118425
|
+
}
|
|
118426
|
+
const hasDump = options.dump !== undefined;
|
|
118427
|
+
let dumpPath;
|
|
118428
|
+
if (hasDump) {
|
|
118429
|
+
dumpPath = typeof options.dump === "string" ? options.dump : config.dev.sqlDump;
|
|
118430
|
+
if (!dumpPath) {
|
|
118431
|
+
throw new Error("No dump file specified. Use --dump <path> or set dev.sqlDump in config");
|
|
118432
|
+
}
|
|
118433
|
+
}
|
|
118434
|
+
const steps = [];
|
|
118435
|
+
if (!options.skipBuild)
|
|
118436
|
+
steps.push("build");
|
|
118437
|
+
steps.push("sync");
|
|
118438
|
+
if (hasDump) {
|
|
118439
|
+
steps.push("database");
|
|
118440
|
+
} else if (env2.database) {
|
|
118441
|
+
steps.push("content");
|
|
118442
|
+
}
|
|
118443
|
+
let step = 0;
|
|
118444
|
+
const logStep = (msg) => {
|
|
118445
|
+
step++;
|
|
118446
|
+
logger.step(step, steps.length, msg);
|
|
118447
|
+
};
|
|
118448
|
+
if (!options.skipBuild) {
|
|
118449
|
+
logStep("Building blocks and assets...");
|
|
118450
|
+
const buildCommand = new BuildCommand;
|
|
118451
|
+
await buildCommand.execute({ noCache: false });
|
|
118452
|
+
logger.success("Build completed");
|
|
118453
|
+
}
|
|
118454
|
+
logStep("Syncing files to remote server...");
|
|
118455
|
+
await this.syncFiles(env2, config.paths);
|
|
118456
|
+
if (hasDump && dumpPath) {
|
|
118457
|
+
logStep("Importing database...");
|
|
118458
|
+
await this.loadDump(env2, dumpPath);
|
|
118459
|
+
} else if (env2.database) {
|
|
118460
|
+
logStep("Pushing content to remote database...");
|
|
118461
|
+
await this.pushContent(env2);
|
|
118462
|
+
}
|
|
118463
|
+
console.log("");
|
|
118464
|
+
logger.success(`\uD83D\uDE80 Deployment to ${env2.name} completed!`);
|
|
118465
|
+
console.log("");
|
|
118466
|
+
} catch (error) {
|
|
118467
|
+
console.log("");
|
|
118468
|
+
logger.error("Deployment failed:", error);
|
|
118469
|
+
process.exit(1);
|
|
118470
|
+
}
|
|
118471
|
+
}
|
|
118472
|
+
async syncFiles(env2, paths) {
|
|
118473
|
+
const target = this.buildSSHConfig(env2);
|
|
118474
|
+
try {
|
|
118475
|
+
this.sshExec(env2, `mkdir -p ${env2.remotePaths.theme} ${env2.remotePaths.plugins}`, { silent: true });
|
|
118476
|
+
} catch {}
|
|
118477
|
+
logger.substep("theme");
|
|
118478
|
+
this.rsync(paths.theme + "/", `${target}:${env2.remotePaths.theme}/`, env2);
|
|
118479
|
+
logger.substep("plugins");
|
|
118480
|
+
this.rsync(paths.plugins + "/", `${target}:${env2.remotePaths.plugins}/`, env2);
|
|
118481
|
+
logger.success("Files synced");
|
|
118482
|
+
}
|
|
118483
|
+
rsync(source, destination, env2) {
|
|
118484
|
+
const { prefix, sshFlags } = this.buildSSHParts(env2);
|
|
118485
|
+
const rsyncCmd = `${prefix}rsync -avz --delete ` + `--exclude 'node_modules' --exclude '.git' --exclude '.DS_Store' --exclude '*.log' ` + `-e "ssh ${sshFlags}" ` + `"${source}" "${destination}"`;
|
|
118486
|
+
try {
|
|
118487
|
+
execSync3(rsyncCmd, { encoding: "utf8", stdio: "pipe" });
|
|
118488
|
+
} catch (error) {
|
|
118489
|
+
const msg = error.message || String(error);
|
|
118490
|
+
const stderr = error.stderr?.toString() || "";
|
|
118491
|
+
if (msg.includes("Disk quota exceeded") || stderr.includes("Disk quota exceeded")) {
|
|
118492
|
+
throw new Error("Server disk quota exceeded. Free up space and retry.");
|
|
118493
|
+
}
|
|
118494
|
+
if (msg.includes("Permission denied") || stderr.includes("Permission denied")) {
|
|
118495
|
+
throw new Error(`Permission denied writing to ${destination}.`);
|
|
118496
|
+
}
|
|
118497
|
+
if (msg.includes("Connection refused") || msg.includes("Connection timed out")) {
|
|
118498
|
+
throw new Error(`Cannot connect to ${env2.ssh.host}.`);
|
|
118499
|
+
}
|
|
118500
|
+
if (msg.includes("sshpass: command not found") || msg.includes("sshpass: not found")) {
|
|
118501
|
+
throw new Error("sshpass is required for password auth. Install: brew install sshpass");
|
|
118502
|
+
}
|
|
118503
|
+
if (msg.includes("child exited with status 11") || msg.includes("unexpected end of file")) {
|
|
118504
|
+
throw new Error("Connection interrupted. Check disk quota and try again.");
|
|
118505
|
+
}
|
|
118506
|
+
throw new Error(`Rsync failed:
|
|
118507
|
+
${stderr || msg}`);
|
|
118508
|
+
}
|
|
118509
|
+
}
|
|
118510
|
+
async pushContent(env2) {
|
|
118511
|
+
if (!env2.database) {
|
|
118512
|
+
throw new Error(`Database configuration not found for environment ${env2.name}`);
|
|
118513
|
+
}
|
|
118514
|
+
const tunnel = new SSHTunnel;
|
|
118515
|
+
const closeTunnel = await tunnel.connect(env2.name);
|
|
118516
|
+
try {
|
|
118517
|
+
logger.substep("Pushing MDX content to remote database");
|
|
118518
|
+
const pushCommand = new PushCommand;
|
|
118519
|
+
const result2 = await pushCommand.execute({ force: true });
|
|
118520
|
+
if (!result2.success) {
|
|
118521
|
+
const errors = result2.errors.map((e) => e.error).join(", ");
|
|
118522
|
+
throw new Error(`Failed to push content: ${errors}`);
|
|
118523
|
+
}
|
|
118524
|
+
logger.success(`Content pushed (${result2.filesProcessed} files)`);
|
|
118525
|
+
} finally {
|
|
118526
|
+
closeTunnel();
|
|
118527
|
+
}
|
|
118528
|
+
}
|
|
118529
|
+
async loadDump(env2, dumpPath) {
|
|
118530
|
+
if (!env2.database) {
|
|
118531
|
+
throw new Error(`Database configuration not found for environment ${env2.name}`);
|
|
118532
|
+
}
|
|
118533
|
+
if (!fs23.existsSync(dumpPath)) {
|
|
118534
|
+
throw new Error(`Dump file not found: ${dumpPath}`);
|
|
118535
|
+
}
|
|
118536
|
+
const db = env2.database;
|
|
118537
|
+
const dbHost = this.resolveVariables(db.host);
|
|
118538
|
+
const dbPort = db.port || 3306;
|
|
118539
|
+
const dbName = this.resolveVariables(db.database);
|
|
118540
|
+
const dbUser = this.resolveVariables(db.username);
|
|
118541
|
+
const dbPass = this.resolveVariables(db.password);
|
|
118542
|
+
logger.substep(`Uploading ${dumpPath}`);
|
|
118543
|
+
const remoteDumpPath = `/tmp/wordpress-flow-dump-${Date.now()}.sql`;
|
|
118544
|
+
this.scpUpload(env2, dumpPath, remoteDumpPath);
|
|
118545
|
+
logger.substep("Importing into database");
|
|
118546
|
+
const escapedPass = dbPass.replace(/'/g, "'\\''");
|
|
118547
|
+
const mysqlCmd = `grep -v '^mysqldump:' ${remoteDumpPath} | mysql -h ${dbHost} -P ${dbPort} -u ${dbUser} -p'${escapedPass}' ${dbName} && rm ${remoteDumpPath}`;
|
|
118548
|
+
this.sshExec(env2, mysqlCmd);
|
|
118549
|
+
const webroot = this.deriveWebroot(env2);
|
|
118550
|
+
const wp = "~/bin/wp";
|
|
118551
|
+
if (env2.url) {
|
|
118552
|
+
const config = this.configManager.getConfig();
|
|
118553
|
+
const devPort = config.dev.port || 8888;
|
|
118554
|
+
const oldUrl = `http://localhost:${devPort}`;
|
|
118555
|
+
const newUrl = env2.url.replace(/\/$/, "");
|
|
118556
|
+
logger.substep(`Rewriting URLs: ${oldUrl} → ${newUrl}`);
|
|
118557
|
+
this.sshExec(env2, `${wp} search-replace '${oldUrl}' '${newUrl}' --path=${webroot} --all-tables --skip-columns=guid`);
|
|
118558
|
+
} else {
|
|
118559
|
+
logger.warn("No 'url' in environment config — skipping URL rewrite");
|
|
118560
|
+
}
|
|
118561
|
+
const themeSlug = path26.posix.basename(env2.remotePaths.theme);
|
|
118562
|
+
logger.substep(`Activating theme: ${themeSlug}`);
|
|
118563
|
+
try {
|
|
118564
|
+
this.sshExec(env2, `${wp} theme activate ${themeSlug} --path=${webroot}`);
|
|
118565
|
+
} catch {
|
|
118566
|
+
logger.warn(`Could not activate theme "${themeSlug}"`);
|
|
118567
|
+
}
|
|
118568
|
+
logger.success("Database imported");
|
|
118569
|
+
}
|
|
118570
|
+
}
|
|
118571
|
+
|
|
118028
118572
|
// src/index.ts
|
|
118029
118573
|
var program2 = new Command;
|
|
118030
118574
|
program2.name("wordpress-flow").description("Build WordPress sites with React, TypeScript, and MDX - A content-first development framework").version(package_default.version);
|
|
@@ -118045,7 +118589,7 @@ program2.command("setup").description("Run the configuration wizard").action(asy
|
|
|
118045
118589
|
process.exit(1);
|
|
118046
118590
|
}
|
|
118047
118591
|
});
|
|
118048
|
-
program2.command("build").description("Build WordPress blocks for Gutenberg").option("--blocks-dir <dir>", "Source directory for blocks").option("--output-dir <dir>", "Output directory for built blocks").option("--webpack-config <path>", "Path to webpack config file").option("--watch", "Watch mode for development").option("-b, --blocks <names...>", "Specific block names to build").option("-c, --concurrency <number>", "Number of parallel workers (default: CPU cores - 1)").action(async (options) => {
|
|
118592
|
+
program2.command("build").description("Build WordPress blocks for Gutenberg").option("--blocks-dir <dir>", "Source directory for blocks").option("--output-dir <dir>", "Output directory for built blocks").option("--webpack-config <path>", "Path to webpack config file").option("--watch", "Watch mode for development").option("-b, --blocks <names...>", "Specific block names to build").option("-c, --concurrency <number>", "Number of parallel workers (default: CPU cores - 1)").option("--no-cache", "Disable cache and rebuild all blocks").action(async (options) => {
|
|
118049
118593
|
try {
|
|
118050
118594
|
await ensureConfiguration();
|
|
118051
118595
|
const command = new BuildCommand;
|
|
@@ -118055,7 +118599,8 @@ program2.command("build").description("Build WordPress blocks for Gutenberg").op
|
|
|
118055
118599
|
webpackConfig: options.webpackConfig,
|
|
118056
118600
|
watch: options.watch || false,
|
|
118057
118601
|
blocks: options.blocks,
|
|
118058
|
-
concurrency: options.concurrency ? parseInt(options.concurrency, 10) : undefined
|
|
118602
|
+
concurrency: options.concurrency ? parseInt(options.concurrency, 10) : undefined,
|
|
118603
|
+
noCache: !options.cache
|
|
118059
118604
|
});
|
|
118060
118605
|
logger.info("Build command completed successfully");
|
|
118061
118606
|
} catch (error) {
|
|
@@ -118066,9 +118611,14 @@ program2.command("build").description("Build WordPress blocks for Gutenberg").op
|
|
|
118066
118611
|
program2.command("pull").description("Pull content from WordPress and convert to MDX").option("-f, --force", "Force overwrite existing files").option("--post-types <types...>", "Specific post types to pull").option("--post-ids <ids...>", "Specific post IDs to pull").option("--status <statuses...>", "Post statuses to include", [
|
|
118067
118612
|
"publish",
|
|
118068
118613
|
"draft"
|
|
118069
|
-
]).option("--output-dir <dir>", "Output directory for MDX files").action(async (options) => {
|
|
118614
|
+
]).option("--output-dir <dir>", "Output directory for MDX files").option("--env <environment>", "Pull from a remote environment via SSH tunnel").action(async (options) => {
|
|
118615
|
+
let closeTunnel;
|
|
118070
118616
|
try {
|
|
118071
118617
|
await ensureConfiguration();
|
|
118618
|
+
if (options.env) {
|
|
118619
|
+
const tunnel = new SSHTunnel;
|
|
118620
|
+
closeTunnel = await tunnel.connect(options.env);
|
|
118621
|
+
}
|
|
118072
118622
|
const command = new PullCommand;
|
|
118073
118623
|
await command.execute({
|
|
118074
118624
|
force: options.force || false,
|
|
@@ -118081,17 +118631,26 @@ program2.command("pull").description("Pull content from WordPress and convert to
|
|
|
118081
118631
|
} catch (error) {
|
|
118082
118632
|
logger.error("Pull command failed:", error);
|
|
118083
118633
|
process.exit(1);
|
|
118634
|
+
} finally {
|
|
118635
|
+
closeTunnel?.();
|
|
118084
118636
|
}
|
|
118085
118637
|
});
|
|
118086
|
-
program2.command("push").description("Convert MDX files and push to WordPress").option("-f, --files <files...>", "Specific files to push").option("--force", "Force push, overriding conflicts").option("--dry-run", "Preview changes without pushing").action(async (options) => {
|
|
118638
|
+
program2.command("push").description("Convert MDX files and push to WordPress").option("-f, --files <files...>", "Specific files to push").option("--force", "Force push, overriding conflicts").option("--dry-run", "Preview changes without pushing").option("--env <environment>", "Push to a remote environment via SSH tunnel").action(async (options) => {
|
|
118639
|
+
let closeTunnel;
|
|
118087
118640
|
try {
|
|
118088
118641
|
await ensureConfiguration();
|
|
118642
|
+
if (options.env) {
|
|
118643
|
+
const tunnel = new SSHTunnel;
|
|
118644
|
+
closeTunnel = await tunnel.connect(options.env);
|
|
118645
|
+
}
|
|
118089
118646
|
const command = new PushCommand;
|
|
118090
118647
|
await command.execute(options);
|
|
118091
118648
|
logger.info("Push command completed successfully");
|
|
118092
118649
|
} catch (error) {
|
|
118093
118650
|
logger.error("Push command failed:", error);
|
|
118094
118651
|
process.exit(1);
|
|
118652
|
+
} finally {
|
|
118653
|
+
closeTunnel?.();
|
|
118095
118654
|
}
|
|
118096
118655
|
});
|
|
118097
118656
|
program2.command("build-templates").description("Build WordPress theme templates from MDX files").option("--templates-dir <dir>", "Source directory for template MDX files").option("--output-dir <dir>", "Output directory for HTML templates").action(async (options) => {
|
|
@@ -118137,6 +118696,20 @@ program2.command("destroy").description("Remove Docker containers and volumes fo
|
|
|
118137
118696
|
process.exit(1);
|
|
118138
118697
|
}
|
|
118139
118698
|
});
|
|
118699
|
+
program2.command("deploy <environment>").description("Deploy to a remote environment (staging, production, etc.)").option("--skip-build", "Skip the build step").option("--dump [path]", "Load SQL dump instead of pushing MDX (uses config sqlDump if no path provided)").action(async (environment, options) => {
|
|
118700
|
+
try {
|
|
118701
|
+
await ensureConfiguration();
|
|
118702
|
+
const command = new DeployCommand;
|
|
118703
|
+
await command.execute({
|
|
118704
|
+
environment,
|
|
118705
|
+
skipBuild: options.skipBuild || false,
|
|
118706
|
+
dump: options.dump
|
|
118707
|
+
});
|
|
118708
|
+
} catch (error) {
|
|
118709
|
+
logger.error("Deploy command failed:", error);
|
|
118710
|
+
process.exit(1);
|
|
118711
|
+
}
|
|
118712
|
+
});
|
|
118140
118713
|
var databaseCmd = program2.command("database").description("Database management commands");
|
|
118141
118714
|
databaseCmd.command("import").description("Import SQL dump into Docker database").option("-f, --file <path>", "Path to SQL file (defaults to config sqlDump)").action(async (options) => {
|
|
118142
118715
|
try {
|