@magentrix-corp/magentrix-cli 1.3.5 → 1.3.7

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 CHANGED
@@ -505,7 +505,9 @@ magentrix vue-build-stage
505
505
  1. Select from linked projects or enter path manually
506
506
  2. Run `npm run build` in the Vue project
507
507
  3. Validate the build output
508
- 4. Copy to workspace `src/iris-apps/<app-slug>/`
508
+ 4. Copy to workspace `src/iris-apps/<app-slug>/` (excludes index.html, favicon.ico)
509
+ 5. Check sync status and prompt to pull if needed (required before publishing)
510
+ 6. Prompt to publish to Magentrix
509
511
 
510
512
  #### Vue Development Server
511
513
  ```bash
@@ -621,7 +623,7 @@ magentrix run-dev # Start dev server with platform assets
621
623
  # Deployment - Option A (from Vue project folder)
622
624
  cd ~/my-vue-app # Work from your Vue project
623
625
  magentrix vue-build-stage # Build and select workspace to stage into
624
- # Follow prompts, then navigate to workspace to publish
626
+ # Prompted: "Do you want to publish to Magentrix now?" → Yes/No
625
627
 
626
628
  # Deployment - Option B (from Magentrix workspace)
627
629
  cd ~/magentrix-workspace # Navigate to Magentrix workspace
@@ -678,6 +680,19 @@ When running from a Vue project, the command looks for registered workspaces in
678
680
 
679
681
  **Note**: Existing workspaces are auto-registered when you run any command from them.
680
682
 
683
+ #### "Publishing Iris apps from an out-of-sync workspace is not allowed"
684
+ When running `vue-build-stage` from a Vue project, the CLI checks if the target workspace is in sync with the server. If there are pending remote changes, you must pull first.
685
+
686
+ **Why this is required:**
687
+ - Prevents deployment conflicts
688
+ - Ensures your app is deployed alongside the latest server state
689
+ - Avoids overwriting changes made by other team members
690
+
691
+ **To resolve:**
692
+ 1. Navigate to the workspace: `cd /path/to/workspace`
693
+ 2. Pull latest changes: `magentrix pull`
694
+ 3. Re-run `vue-build-stage` from your Vue project
695
+
681
696
  #### "Missing required field in config.ts: slug (appPath)"
682
697
  Your Vue project's `config.ts` is missing the app identifier. Add an `appPath` or `slug` field.
683
698
 
@@ -785,7 +800,7 @@ magentrix status # Verify everything is in sync
785
800
  cd ~/my-vue-app # Navigate to your Vue project
786
801
  magentrix iris-link # Link project (first time only)
787
802
  magentrix vue-build-stage # Build and select workspace to stage into
788
- # Navigate to workspace and run: magentrix publish
803
+ # Prompted: "Do you want to publish to Magentrix now?" → Yes/No
789
804
  ```
790
805
 
791
806
  **Option B: From Magentrix workspace**
@@ -419,6 +419,61 @@ async function buildFromVueProject(options) {
419
419
  console.log(chalk.gray(`Staged to: ${stageResult.stagedPath}`));
420
420
  console.log();
421
421
 
422
+ // Check if workspace might be out of sync and offer to pull first
423
+ console.log(chalk.gray('Checking workspace sync status...'));
424
+ const syncStatus = await checkWorkspaceSyncStatus(workspacePath);
425
+
426
+ if (syncStatus.needsPull) {
427
+ console.log();
428
+ console.log(chalk.yellow('⚠ Your workspace may be out of sync with the server.'));
429
+
430
+ const shouldPull = await confirm({
431
+ message: 'Would you like to pull latest changes first?',
432
+ default: true
433
+ });
434
+
435
+ if (shouldPull) {
436
+ console.log();
437
+ console.log(chalk.blue('Running pull from workspace...'));
438
+ console.log();
439
+
440
+ const pullSuccess = await runCommandFromWorkspace(workspacePath, 'pull');
441
+
442
+ if (!pullSuccess) {
443
+ console.log();
444
+ console.log(chalk.yellow('Pull encountered issues. You may want to resolve them manually.'));
445
+
446
+ const continueAnyway = await confirm({
447
+ message: 'Do you still want to continue with publishing?',
448
+ default: false
449
+ });
450
+
451
+ if (!continueAnyway) {
452
+ console.log();
453
+ console.log(chalk.cyan('To continue manually:'));
454
+ console.log(chalk.white(` 1. Navigate to workspace: ${chalk.yellow(`cd "${workspacePath}"`)}`));
455
+ console.log(chalk.white(` 2. Run ${chalk.yellow('magentrix pull')} to resolve conflicts`));
456
+ console.log(chalk.white(` 3. Run ${chalk.yellow('magentrix publish')} to deploy`));
457
+ return;
458
+ }
459
+ }
460
+ console.log();
461
+ } else {
462
+ // User declined to pull - block publishing from out-of-sync workspace
463
+ console.log();
464
+ console.log(chalk.red('Publishing Iris apps from an out-of-sync workspace is not allowed.'));
465
+ console.log(chalk.gray('This prevents conflicts and ensures your deployment is based on the latest server state.'));
466
+ console.log();
467
+ console.log(chalk.cyan('To continue:'));
468
+ console.log(chalk.white(` 1. Navigate to workspace: ${chalk.yellow(`cd "${workspacePath}"`)}`));
469
+ console.log(chalk.white(` 2. Run ${chalk.yellow('magentrix pull')} to sync with server`));
470
+ console.log(chalk.white(` 3. Run ${chalk.yellow('magentrix publish')} to deploy`));
471
+ return;
472
+ }
473
+ } else if (syncStatus.checked) {
474
+ console.log(chalk.green('✓ Workspace is in sync'));
475
+ }
476
+
422
477
  // Ask if they want to publish now
423
478
  const shouldPublish = await confirm({
424
479
  message: 'Do you want to publish to Magentrix now?',
@@ -430,7 +485,7 @@ async function buildFromVueProject(options) {
430
485
  console.log(chalk.blue('Running publish from workspace...'));
431
486
  console.log();
432
487
 
433
- const publishSuccess = await runPublishFromWorkspace(workspacePath);
488
+ const publishSuccess = await runCommandFromWorkspace(workspacePath, 'publish');
434
489
 
435
490
  if (!publishSuccess) {
436
491
  console.log();
@@ -448,17 +503,64 @@ async function buildFromVueProject(options) {
448
503
  }
449
504
 
450
505
  /**
451
- * Run magentrix publish from a specific workspace directory.
506
+ * Check if a workspace needs to pull (has remote changes or conflicts).
507
+ *
508
+ * @param {string} workspacePath - Path to the Magentrix workspace
509
+ * @returns {Promise<{checked: boolean, needsPull: boolean}>}
510
+ */
511
+ async function checkWorkspaceSyncStatus(workspacePath) {
512
+ return new Promise((resolvePromise) => {
513
+ const isWindows = process.platform === 'win32';
514
+ const npmCmd = isWindows ? 'npx.cmd' : 'npx';
515
+
516
+ let output = '';
517
+
518
+ const child = spawn(npmCmd, ['magentrix', 'status'], {
519
+ cwd: workspacePath,
520
+ stdio: ['inherit', 'pipe', 'pipe'],
521
+ shell: isWindows
522
+ });
523
+
524
+ child.stdout.on('data', (data) => {
525
+ output += data.toString();
526
+ });
527
+
528
+ child.stderr.on('data', (data) => {
529
+ output += data.toString();
530
+ });
531
+
532
+ child.on('close', (code) => {
533
+ // Check output for signs of remote changes or conflicts
534
+ const lowerOutput = output.toLowerCase();
535
+ const needsPull = lowerOutput.includes('conflict') ||
536
+ lowerOutput.includes('remote') ||
537
+ lowerOutput.includes('server has changes') ||
538
+ lowerOutput.includes('out of sync') ||
539
+ lowerOutput.includes('modified on server');
540
+
541
+ resolvePromise({ checked: code === 0, needsPull });
542
+ });
543
+
544
+ child.on('error', () => {
545
+ // If we can't check, assume it's fine and let them proceed
546
+ resolvePromise({ checked: false, needsPull: false });
547
+ });
548
+ });
549
+ }
550
+
551
+ /**
552
+ * Run a magentrix command from a specific workspace directory.
452
553
  *
453
554
  * @param {string} workspacePath - Path to the Magentrix workspace
454
- * @returns {Promise<boolean>} - True if publish succeeded
555
+ * @param {string} command - The magentrix command to run (e.g., 'pull', 'publish')
556
+ * @returns {Promise<boolean>} - True if command succeeded
455
557
  */
456
- async function runPublishFromWorkspace(workspacePath) {
558
+ async function runCommandFromWorkspace(workspacePath, command) {
457
559
  return new Promise((resolvePromise) => {
458
560
  const isWindows = process.platform === 'win32';
459
561
  const npmCmd = isWindows ? 'npx.cmd' : 'npx';
460
562
 
461
- const child = spawn(npmCmd, ['magentrix', 'publish'], {
563
+ const child = spawn(npmCmd, ['magentrix', command], {
462
564
  cwd: workspacePath,
463
565
  stdio: 'inherit',
464
566
  shell: isWindows
@@ -469,7 +571,7 @@ async function runPublishFromWorkspace(workspacePath) {
469
571
  });
470
572
 
471
573
  child.on('error', (err) => {
472
- console.log(chalk.yellow(`Warning: Could not run publish: ${err.message}`));
574
+ console.log(chalk.yellow(`Warning: Could not run ${command}: ${err.message}`));
473
575
  resolvePromise(false);
474
576
  });
475
577
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magentrix-corp/magentrix-cli",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "CLI tool for synchronizing local files with Magentrix cloud platform",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,10 +1,13 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { existsSync, mkdirSync, cpSync, rmSync, readdirSync } from 'node:fs';
3
- import { join, resolve } from 'node:path';
3
+ import { join, resolve, basename } from 'node:path';
4
4
  import { validateIrisBuild } from './validator.js';
5
5
  import { updateLastBuild } from './linker.js';
6
6
  import { EXPORT_ROOT, IRIS_APPS_DIR } from '../../vars/global.js';
7
7
 
8
+ // Files to exclude when staging Iris apps (not needed on server)
9
+ const EXCLUDED_FILES = new Set(['index.html', 'favicon.ico']);
10
+
8
11
  /**
9
12
  * Build a Vue project using npm run build.
10
13
  *
@@ -176,9 +179,20 @@ export function stageToWorkspace(distPath, slug, workspacePath = process.cwd())
176
179
  rmSync(targetDir, { recursive: true, force: true });
177
180
  }
178
181
 
179
- // Copy dist contents to target
182
+ // Copy dist contents to target, excluding unnecessary files
180
183
  try {
181
- cpSync(resolvedDistPath, targetDir, { recursive: true });
184
+ cpSync(resolvedDistPath, targetDir, {
185
+ recursive: true,
186
+ filter: (src) => {
187
+ // Use basename for reliable cross-platform filename extraction
188
+ const filename = basename(src);
189
+ // Only exclude specific files, not directories
190
+ if (EXCLUDED_FILES.has(filename)) {
191
+ return false;
192
+ }
193
+ return true;
194
+ }
195
+ });
182
196
  } catch (err) {
183
197
  return {
184
198
  success: false,