@bootmap/wpep-cli 1.0.8 → 1.1.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/bin/wpep.js CHANGED
@@ -3,12 +3,13 @@ import { Command } from 'commander';
3
3
  import chalk from 'chalk';
4
4
  import initCommand from '../src/commands/init.js';
5
5
  import deployCommand from '../src/commands/deploy.js';
6
+ import githubCommand from '../src/commands/github.js';
6
7
 
7
8
  const program = new Command();
8
9
 
9
10
  program
10
11
  .name('wpep')
11
- .description('Deploy Next.js static exports to WordPress via WP Elementor Publisher')
12
+ .description('Deploy static sites (Next.js, Astro, React) to WordPress via WPEP')
12
13
  .version('1.0.0');
13
14
 
14
15
  program
@@ -39,6 +40,18 @@ program
39
40
  }
40
41
  });
41
42
 
43
+ program
44
+ .command('github')
45
+ .description('Generate a GitHub Actions workflow for auto-deploy on git push')
46
+ .action(async () => {
47
+ try {
48
+ await githubCommand();
49
+ } catch (err) {
50
+ console.error(chalk.red(`Error: ${err.message}`));
51
+ process.exit(1);
52
+ }
53
+ });
54
+
42
55
  program.parse(process.argv);
43
56
 
44
57
  if (!process.argv.slice(2).length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bootmap/wpep-cli",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool for deploying Next.js static exports to WordPress via WP Elementor Publisher",
5
5
  "main": "bin/wpep.js",
6
6
  "type": "module",
@@ -101,23 +101,26 @@ export default async function deployCommand(options) {
101
101
  let outputDir = options.dir || 'wpep-build';
102
102
  const targetDir = path.join(process.cwd(), 'wpep-build');
103
103
 
104
- if (!fs.existsSync(targetDir)) {
105
- // Try to auto-detect and copy to wpep-build
106
- const possibleDirs = ['out', 'dist', 'build'];
107
- let foundDir = null;
108
- for (const d of possibleDirs) {
109
- if (fs.existsSync(path.join(process.cwd(), d))) {
110
- foundDir = d;
111
- break;
112
- }
113
- }
104
+ // Always clear previous wpep-build so we deploy fresh output
105
+ if (fs.existsSync(targetDir)) {
106
+ fs.rmSync(targetDir, { recursive: true, force: true });
107
+ }
114
108
 
115
- if (foundDir) {
116
- console.log(chalk.gray(`Auto-detected build in "${foundDir}". Copying to "wpep-build"...`));
117
- fs.cpSync(path.join(process.cwd(), foundDir), targetDir, { recursive: true });
109
+ // Auto-detect build output and copy to wpep-build
110
+ const possibleDirs = ['out', 'dist', 'build'];
111
+ let foundDir = null;
112
+ for (const d of possibleDirs) {
113
+ if (fs.existsSync(path.join(process.cwd(), d))) {
114
+ foundDir = d;
115
+ break;
118
116
  }
119
117
  }
120
118
 
119
+ if (foundDir) {
120
+ console.log(chalk.gray(`Auto-detected build in "${foundDir}". Copying to "wpep-build"...`));
121
+ fs.cpSync(path.join(process.cwd(), foundDir), targetDir, { recursive: true });
122
+ }
123
+
121
124
  outputDir = 'wpep-build';
122
125
  const outDir = path.join(process.cwd(), outputDir);
123
126
 
@@ -138,6 +141,7 @@ export default async function deployCommand(options) {
138
141
  spinner.text = `Creating deployment session for ${allFiles.length} files...`;
139
142
 
140
143
  let deployId;
144
+ let remoteManifest = null;
141
145
  try {
142
146
  const createRes = await fetch(`${url}/wp-json/wpep/v1/deployments/create`, {
143
147
  method: 'POST',
@@ -154,6 +158,7 @@ export default async function deployCommand(options) {
154
158
  throw new Error(`Invalid response from server: ${JSON.stringify(createData)}`);
155
159
  }
156
160
  deployId = createData.deployment_id;
161
+ remoteManifest = createData.remote_manifest;
157
162
  } catch (e) {
158
163
  spinner.fail('Failed to create deployment session.');
159
164
  throw e;
@@ -161,9 +166,27 @@ export default async function deployCommand(options) {
161
166
 
162
167
  spinner.succeed(`Created deployment session: ${chalk.bold(deployId)}`);
163
168
 
164
- console.log(chalk.gray('\nUploading files...'));
169
+ // Build a set of unchanged files by comparing hashes with the remote manifest
170
+ const unchangedFiles = new Set();
171
+ if (remoteManifest && remoteManifest.files) {
172
+ for (const [filePath, localHash] of Object.entries(filesList)) {
173
+ const remoteMeta = remoteManifest.files[filePath];
174
+ if (remoteMeta && remoteMeta === localHash) {
175
+ unchangedFiles.add(filePath);
176
+ }
177
+ }
178
+ }
179
+
180
+ const changedFiles = allFiles.filter(f => !unchangedFiles.has(path.relative(outDir, f)));
181
+ const skippedCount = allFiles.length - changedFiles.length;
182
+
183
+ if (skippedCount > 0) {
184
+ console.log(chalk.green(`\n⚔ ${skippedCount} unchanged files will be copied server-side (not re-uploaded).`));
185
+ }
186
+ console.log(chalk.gray(`Uploading ${changedFiles.length} changed files...\n`));
187
+
165
188
  let uploaded = 0;
166
- for (const file of allFiles) {
189
+ for (const file of changedFiles) {
167
190
  const relativePath = path.relative(outDir, file);
168
191
  const data = fs.readFileSync(file);
169
192
  const base64Data = data.toString('base64');
@@ -203,5 +226,6 @@ export default async function deployCommand(options) {
203
226
  throw e;
204
227
  }
205
228
 
206
- console.log(chalk.green.bold(`\nšŸŽ‰ Success! Your site is live at ${url}\n`));
229
+ console.log(chalk.green.bold(`\nšŸŽ‰ Success! Your site is live at ${url}`));
230
+ console.log(chalk.gray(` ${uploaded} files uploaded, ${skippedCount} files copied from previous deployment.\n`));
207
231
  }
@@ -0,0 +1,68 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+
5
+ const WORKFLOW_TEMPLATE = `name: Deploy to WordPress (WPEP)
6
+
7
+ on:
8
+ push:
9
+ branches: [main]
10
+
11
+ jobs:
12
+ deploy:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version: '20'
20
+ cache: 'npm'
21
+
22
+ - name: Install dependencies
23
+ run: npm ci
24
+
25
+ - name: Deploy to WordPress
26
+ run: npx @bootmap/wpep-cli deploy --url \${{ secrets.WPEP_URL }} --key \${{ secrets.WPEP_API_KEY }}
27
+ `;
28
+
29
+ export default async function githubCommand() {
30
+ const cwd = process.cwd();
31
+ const workflowDir = path.join(cwd, '.github', 'workflows');
32
+ const workflowPath = path.join(workflowDir, 'wpep-deploy.yml');
33
+
34
+ // Create .github/workflows if it doesn't exist
35
+ if (!fs.existsSync(workflowDir)) {
36
+ fs.mkdirSync(workflowDir, { recursive: true });
37
+ }
38
+
39
+ // Write the workflow file
40
+ fs.writeFileSync(workflowPath, WORKFLOW_TEMPLATE, 'utf8');
41
+
42
+ // Read .wpeprc.json to show the user their URL
43
+ let siteUrl = 'https://your-site.com';
44
+ const rcPath = path.join(cwd, '.wpeprc.json');
45
+ if (fs.existsSync(rcPath)) {
46
+ try {
47
+ const config = JSON.parse(fs.readFileSync(rcPath, 'utf8'));
48
+ if (config.url) siteUrl = config.url;
49
+ } catch (e) { /* ignore */ }
50
+ }
51
+
52
+ console.log(chalk.green.bold('\nāœ… GitHub Actions workflow created!\n'));
53
+ console.log(chalk.gray(` File: .github/workflows/wpep-deploy.yml\n`));
54
+
55
+ console.log(chalk.yellow.bold('šŸ“‹ Next steps:\n'));
56
+ console.log(chalk.white('1. Go to your GitHub repo → Settings → Secrets and variables → Actions'));
57
+ console.log(chalk.white('2. Add these two repository secrets:\n'));
58
+
59
+ console.log(chalk.cyan(' WPEP_URL') + chalk.gray(` → ${siteUrl}`));
60
+ console.log(chalk.cyan(' WPEP_API_KEY') + chalk.gray(` → (copy from WordPress → WPEP → Settings)\n`));
61
+
62
+ console.log(chalk.white('3. Push to the ') + chalk.green('main') + chalk.white(' branch:\n'));
63
+ console.log(chalk.gray(' git add .'));
64
+ console.log(chalk.gray(' git commit -m "Add WPEP auto-deploy"'));
65
+ console.log(chalk.gray(' git push origin main\n'));
66
+
67
+ console.log(chalk.green.bold('šŸš€ After that, every push to main will auto-deploy to WordPress!\n'));
68
+ }