@rudderhq/cli 0.3.6-canary.5 → 0.3.6-canary.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/dist/index.js CHANGED
@@ -13788,6 +13788,7 @@ function createByteProgress(label, options = {}) {
13788
13788
  init_version();
13789
13789
  var DEFAULT_DESKTOP_RELEASE_REPO = "Undertone0809/rudder";
13790
13790
  var DESKTOP_UPDATE_QUIT_ARG = "--rudder-update-quit";
13791
+ var DESKTOP_UPDATE_FORCE_ARG = "--rudder-update-force";
13791
13792
  var STABLE_SEMVER_RE = /^[0-9]+\.[0-9]+\.[0-9]+$/;
13792
13793
  var CANARY_SEMVER_RE = /^[0-9]+\.[0-9]+\.[0-9]+-canary\.[0-9]+$/;
13793
13794
  var CLI_REGISTRY_LATEST_URL = "https://registry.npmjs.org/@rudderhq%2fcli/latest";
@@ -13871,7 +13872,7 @@ function createDesktopProgressFactory() {
13871
13872
  async function waitForDesktopApplySignal() {
13872
13873
  process.stdin.setEncoding("utf8");
13873
13874
  process.stdin.resume();
13874
- await new Promise((resolve, reject) => {
13875
+ return await new Promise((resolve, reject) => {
13875
13876
  let buffer = "";
13876
13877
  const cleanup = () => {
13877
13878
  process.stdin.off("data", onData);
@@ -13882,9 +13883,15 @@ async function waitForDesktopApplySignal() {
13882
13883
  buffer += chunk;
13883
13884
  const lines = buffer.split(/\r?\n/);
13884
13885
  buffer = lines.pop() ?? "";
13885
- if (lines.some((line) => line.trim() === "apply")) {
13886
+ const commands = lines.map((line) => line.trim());
13887
+ if (commands.includes("force-apply")) {
13886
13888
  cleanup();
13887
- resolve();
13889
+ resolve({ force: true });
13890
+ return;
13891
+ }
13892
+ if (commands.includes("apply")) {
13893
+ cleanup();
13894
+ resolve({ force: false });
13888
13895
  }
13889
13896
  };
13890
13897
  const onEnd = () => {
@@ -14597,14 +14604,6 @@ function isInstalledDesktopCurrent(metadata, releaseTag, assetName, assetChecksu
14597
14604
  metadata && metadata.releaseTag === releaseTag && metadata.assetName === assetName && metadata.assetChecksum === assetChecksum
14598
14605
  );
14599
14606
  }
14600
- function buildForceQuitCommand(target) {
14601
- if (target.platform === "windows") return { command: "taskkill.exe", args: ["/IM", `${DESKTOP_APP_NAME}.exe`, "/T", "/F"] };
14602
- return { command: "pkill", args: ["-x", DESKTOP_APP_NAME] };
14603
- }
14604
- function forceQuitDesktopProcesses(target) {
14605
- const command = buildForceQuitCommand(target);
14606
- spawnSync3(command.command, command.args, { stdio: "ignore" });
14607
- }
14608
14607
  function forceQuitDesktopProcess(pid, target) {
14609
14608
  if (target.platform === "windows") {
14610
14609
  spawnSync3("taskkill.exe", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
@@ -14615,6 +14614,36 @@ function forceQuitDesktopProcess(pid, target) {
14615
14614
  } catch {
14616
14615
  }
14617
14616
  }
14617
+ function quotePowerShellString(value) {
14618
+ return `'${value.replaceAll("'", "''")}'`;
14619
+ }
14620
+ function findDesktopExecutablePids(executablePath, target) {
14621
+ if (target.platform === "windows") {
14622
+ const result2 = spawnSync3("powershell.exe", [
14623
+ "-NoProfile",
14624
+ "-Command",
14625
+ `Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -eq ${quotePowerShellString(executablePath)} } | Select-Object -ExpandProperty ProcessId`
14626
+ ], {
14627
+ encoding: "utf8",
14628
+ stdio: ["ignore", "pipe", "ignore"]
14629
+ });
14630
+ if (result2.status !== 0) return [];
14631
+ return result2.stdout.split(/\r?\n/).map((line) => Number.parseInt(line.trim(), 10)).filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid);
14632
+ }
14633
+ const result = spawnSync3("ps", ["-eo", "pid=,args="], {
14634
+ encoding: "utf8",
14635
+ stdio: ["ignore", "pipe", "ignore"]
14636
+ });
14637
+ if (result.status !== 0) return [];
14638
+ return result.stdout.split(/\r?\n/).flatMap((line) => {
14639
+ const match = line.match(/^\s*(\d+)\s+(.+)$/);
14640
+ if (!match) return [];
14641
+ const pid = Number.parseInt(match[1], 10);
14642
+ const commandLine = match[2];
14643
+ const matchesExecutable = commandLine === executablePath || commandLine.startsWith(`${executablePath} `);
14644
+ return matchesExecutable && pid !== process.pid ? [pid] : [];
14645
+ });
14646
+ }
14618
14647
  function isRunningInsideDesktopExecutable() {
14619
14648
  return path21.basename(process.execPath).toLowerCase().startsWith(DESKTOP_APP_NAME.toLowerCase());
14620
14649
  }
@@ -14628,10 +14657,13 @@ async function waitForUpdateQuitResponse(responsePath, timeoutMs = 8e3) {
14628
14657
  }
14629
14658
  return null;
14630
14659
  }
14631
- async function requestDesktopQuit(executablePath, target) {
14660
+ async function requestDesktopQuit(executablePath, target, options = {}) {
14632
14661
  if (!await pathExists2(executablePath)) return { ok: true, status: "not_running" };
14633
14662
  const responsePath = path21.join(tmpdir(), `rudder-update-quit-${process.pid}-${Date.now()}.json`);
14634
- const result = spawnSync3(executablePath, [`${DESKTOP_UPDATE_QUIT_ARG}=${responsePath}`], {
14663
+ const result = spawnSync3(executablePath, [
14664
+ `${DESKTOP_UPDATE_QUIT_ARG}=${responsePath}`,
14665
+ ...options.forceUpdate ? [DESKTOP_UPDATE_FORCE_ARG] : []
14666
+ ], {
14635
14667
  stdio: "ignore",
14636
14668
  timeout: 5e3
14637
14669
  });
@@ -14639,7 +14671,7 @@ async function requestDesktopQuit(executablePath, target) {
14639
14671
  return null;
14640
14672
  }
14641
14673
  try {
14642
- return await waitForUpdateQuitResponse(responsePath);
14674
+ return await waitForUpdateQuitResponse(responsePath, options.responseTimeoutMs);
14643
14675
  } finally {
14644
14676
  await rm2(responsePath, { force: true });
14645
14677
  }
@@ -14668,6 +14700,12 @@ async function waitForProcessExit(pid, timeoutMs = 2e4, intervalMs = 250) {
14668
14700
  }
14669
14701
  return !processExists(pid);
14670
14702
  }
14703
+ async function waitForProcessesExit(pids, waitForExit) {
14704
+ const uniquePids = [...new Set(pids)];
14705
+ if (uniquePids.length === 0) return true;
14706
+ const results = await Promise.all(uniquePids.map((pid) => waitForExit(pid)));
14707
+ return results.every(Boolean);
14708
+ }
14671
14709
  async function removePathWithRetry(targetPath, attempts = 5) {
14672
14710
  for (let attempt = 0; attempt < attempts; attempt += 1) {
14673
14711
  try {
@@ -14680,50 +14718,100 @@ async function removePathWithRetry(targetPath, attempts = 5) {
14680
14718
  return false;
14681
14719
  }
14682
14720
  async function prepareForDesktopReplace(paths, target, options = {}) {
14683
- const forceQuit = options.forceQuitDesktopProcesses ?? forceQuitDesktopProcesses;
14684
14721
  const forceQuitPid = options.forceQuitDesktopProcess ?? forceQuitDesktopProcess;
14685
14722
  const waitForExit = options.waitForDesktopProcessExit ?? waitForProcessExit;
14723
+ const findPids = options.findDesktopExecutablePids ?? findDesktopExecutablePids;
14724
+ async function forceQuitPidAndConfirm(pid) {
14725
+ forceQuitPid(pid, target);
14726
+ await delay2(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
14727
+ if (!await waitForExit(pid)) {
14728
+ throw new Error(`Rudder Desktop process ${pid} did not exit after force-quit fallback. Close Rudder and rerun start.`);
14729
+ }
14730
+ }
14731
+ async function forceQuitPidsAndConfirm(pids) {
14732
+ const uniquePids = [...new Set(pids)];
14733
+ for (const pid of uniquePids) {
14734
+ forceQuitPid(pid, target);
14735
+ }
14736
+ await delay2(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
14737
+ if (!await waitForProcessesExit(uniquePids, waitForExit)) {
14738
+ throw new Error(`Rudder Desktop process${uniquePids.length === 1 ? "" : "es"} ${uniquePids.join(", ")} did not exit after force-quit fallback. Close Rudder and rerun start.`);
14739
+ }
14740
+ }
14741
+ let quitPid = null;
14742
+ let managedExecutablePids = [];
14686
14743
  const hasManagedExecutable = await pathExists2(paths.executablePath);
14687
14744
  if (hasManagedExecutable) {
14688
- let quitResponse = await requestDesktopQuit(paths.executablePath, target);
14689
- while (quitResponse && !quitResponse.ok && quitResponse.status === "active_runs" && options.waitForActiveRuns) {
14745
+ managedExecutablePids = findPids(paths.executablePath, target);
14746
+ const requestQuit = () => requestDesktopQuit(paths.executablePath, target, {
14747
+ forceUpdate: options.forceUpdate,
14748
+ responseTimeoutMs: options.updateQuitResponseTimeoutMs
14749
+ });
14750
+ let quitResponse = await requestQuit();
14751
+ while (quitResponse && !quitResponse.ok && quitResponse.status === "active_runs" && options.waitForActiveRuns && !options.forceUpdate) {
14690
14752
  p15.log.warn(
14691
14753
  `Rudder Desktop has ${quitResponse.totalRuns} active run${quitResponse.totalRuns === 1 ? "" : "s"}; waiting before replacing Desktop.`
14692
14754
  );
14693
14755
  await delay2(options.activeRunPollIntervalMs ?? 15e3);
14694
- quitResponse = await requestDesktopQuit(paths.executablePath, target);
14756
+ quitResponse = await requestQuit();
14695
14757
  }
14696
14758
  if (quitResponse && !quitResponse.ok && quitResponse.status === "active_runs") {
14759
+ if (!options.forceUpdate) {
14760
+ throw new Error(
14761
+ `Rudder Desktop has ${quitResponse.totalRuns} active run${quitResponse.totalRuns === 1 ? "" : "s"}. Stop active work, then rerun start.`
14762
+ );
14763
+ }
14697
14764
  throw new Error(
14698
- `Rudder Desktop has ${quitResponse.totalRuns} active run${quitResponse.totalRuns === 1 ? "" : "s"}. Stop active work, then rerun start.`
14765
+ `Rudder Desktop still has ${quitResponse.totalRuns} active run${quitResponse.totalRuns === 1 ? "" : "s"} after the force-update request. Stop active work, then rerun start.`
14699
14766
  );
14700
14767
  }
14701
- const quitPid = readUpdateQuitPid(quitResponse);
14768
+ if (quitResponse && !quitResponse.ok && quitResponse.status === "failed") {
14769
+ throw new Error(quitResponse.message);
14770
+ }
14771
+ quitPid = readUpdateQuitPid(quitResponse);
14702
14772
  if (quitPid) {
14703
14773
  p15.log.info(`Waiting for existing Rudder Desktop process ${quitPid} to exit before replacing it.`);
14704
14774
  if (!await waitForExit(quitPid)) {
14705
14775
  p15.log.warn(`Rudder Desktop process ${quitPid} did not exit in time; attempting force-quit fallback.`);
14706
- forceQuitPid(quitPid, target);
14707
- await delay2(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
14776
+ await forceQuitPidAndConfirm(quitPid);
14708
14777
  }
14709
14778
  } else if (isLegacyUnconfirmedUpdateQuit(quitResponse)) {
14710
14779
  const graceMs = options.legacyUpdateQuitGraceMs ?? LEGACY_UPDATE_QUIT_GRACE_MS;
14711
14780
  p15.log.warn(
14712
- `Existing Rudder Desktop acknowledged update quit without a process id; waiting ${Math.ceil(graceMs / 1e3)}s before force-quit fallback.`
14781
+ `Existing Rudder Desktop acknowledged update quit without a process id; waiting ${Math.ceil(graceMs / 1e3)}s before replacement.`
14713
14782
  );
14714
14783
  await delay2(graceMs);
14715
- forceQuit(target);
14716
- await delay2(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
14784
+ if (managedExecutablePids.length > 0 && !await waitForProcessesExit(managedExecutablePids, waitForExit)) {
14785
+ p15.log.warn(
14786
+ `Existing Rudder Desktop did not exit after acknowledging update quit; attempting path-scoped force-quit for process${managedExecutablePids.length === 1 ? "" : "es"} ${managedExecutablePids.join(", ")}.`
14787
+ );
14788
+ await forceQuitPidsAndConfirm(managedExecutablePids);
14789
+ }
14790
+ } else if (!quitResponse) {
14791
+ if (options.forceUpdate && managedExecutablePids.length > 0) {
14792
+ p15.log.warn(
14793
+ `Existing Rudder Desktop did not respond to the update quit request; attempting path-scoped force-quit for process${managedExecutablePids.length === 1 ? "" : "es"} ${managedExecutablePids.join(", ")}.`
14794
+ );
14795
+ await forceQuitPidsAndConfirm(managedExecutablePids);
14796
+ } else {
14797
+ throw new Error("Existing Rudder Desktop did not respond to the update quit request. Close Rudder and rerun start.");
14798
+ }
14717
14799
  } else {
14718
14800
  await delay2(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
14719
14801
  }
14720
14802
  } else if (!isRunningInsideDesktopExecutable()) {
14721
- forceQuit(target);
14803
+ throw new Error("Cannot find the managed Rudder Desktop executable to request a safe update quit. Close Rudder and rerun start.");
14722
14804
  }
14723
14805
  const replacePath = target.platform === "windows" ? paths.installRoot : paths.appPath;
14724
14806
  if (await removePathWithRetry(replacePath)) return;
14725
- forceQuit(target);
14726
- await delay2(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
14807
+ if (!quitPid) {
14808
+ if (managedExecutablePids.length > 0) {
14809
+ await forceQuitPidsAndConfirm(managedExecutablePids);
14810
+ if (await removePathWithRetry(replacePath, 6)) return;
14811
+ }
14812
+ throw new Error(`Failed to replace existing Rudder Desktop at ${replacePath}. Close Rudder and rerun start.`);
14813
+ }
14814
+ await forceQuitPidAndConfirm(quitPid);
14727
14815
  if (await removePathWithRetry(replacePath, 6)) return;
14728
14816
  throw new Error(`Failed to replace existing Rudder Desktop at ${replacePath}. Close Rudder and rerun start.`);
14729
14817
  }
@@ -15056,22 +15144,26 @@ async function startCommand(opts) {
15056
15144
  () => assertChecksumMatch(cachedAsset.path, expectedChecksum),
15057
15145
  desktopProgressJson ? "verifying_checksum" : null
15058
15146
  );
15147
+ let applySignal = null;
15059
15148
  if (desktopProgressJson && opts.desktopWaitForApply === true) {
15060
15149
  writeDesktopProgress({
15061
15150
  phase: "ready_to_install",
15062
15151
  message: "Desktop update is downloaded and verified.",
15063
15152
  percent: 100
15064
15153
  });
15065
- await waitForDesktopApplySignal();
15154
+ applySignal = await waitForDesktopApplySignal();
15066
15155
  writeDesktopProgress({
15067
15156
  phase: "preparing_restart",
15068
- message: "Applying Desktop update..."
15157
+ message: applySignal.force ? "Applying Desktop update and quitting active runs..." : "Applying Desktop update..."
15069
15158
  });
15070
15159
  }
15071
15160
  await runStartPhase(
15072
15161
  "Replacing existing Rudder Desktop if needed...",
15073
15162
  "Existing Desktop install is ready for replacement.",
15074
- () => prepareForDesktopReplace(installPaths, target, { waitForActiveRuns: opts.waitForActiveRuns === true }),
15163
+ () => prepareForDesktopReplace(installPaths, target, {
15164
+ waitForActiveRuns: opts.waitForActiveRuns === true,
15165
+ forceUpdate: applySignal?.force === true
15166
+ }),
15075
15167
  desktopProgressJson ? opts.waitForActiveRuns === true ? "waiting_for_active_runs" : "preparing_restart" : null
15076
15168
  );
15077
15169
  await runStartPhase(