summaryrefslogtreecommitdiff
path: root/packages/cli/src/utils/handleAutoUpdate.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/utils/handleAutoUpdate.ts')
-rw-r--r--packages/cli/src/utils/handleAutoUpdate.ts139
1 files changed, 139 insertions, 0 deletions
diff --git a/packages/cli/src/utils/handleAutoUpdate.ts b/packages/cli/src/utils/handleAutoUpdate.ts
new file mode 100644
index 00000000..1ef2d475
--- /dev/null
+++ b/packages/cli/src/utils/handleAutoUpdate.ts
@@ -0,0 +1,139 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { spawn } from 'node:child_process';
+import { UpdateObject } from '../ui/utils/updateCheck.js';
+import { LoadedSettings } from '../config/settings.js';
+import { getInstallationInfo } from './installationInfo.js';
+import { updateEventEmitter } from './updateEventEmitter.js';
+import { HistoryItem, MessageType } from '../ui/types.js';
+
+export function handleAutoUpdate(
+ info: UpdateObject | null,
+ settings: LoadedSettings,
+ projectRoot: string,
+) {
+ if (!info) {
+ return;
+ }
+
+ const installationInfo = getInstallationInfo(
+ projectRoot,
+ settings.merged.disableAutoUpdate ?? false,
+ );
+
+ let combinedMessage = info.message;
+ if (installationInfo.updateMessage) {
+ combinedMessage += `\n${installationInfo.updateMessage}`;
+ }
+
+ updateEventEmitter.emit('update-received', {
+ message: combinedMessage,
+ });
+
+ if (!installationInfo.updateCommand || settings.merged.disableAutoUpdate) {
+ return;
+ }
+
+ const updateCommand = installationInfo.updateCommand.replace(
+ '@latest',
+ `@${info.update.latest}`,
+ );
+
+ const updateProcess = spawn(updateCommand, { stdio: 'pipe', shell: true });
+ let errorOutput = '';
+ updateProcess.stderr.on('data', (data) => {
+ errorOutput += data.toString();
+ });
+
+ updateProcess.on('close', (code) => {
+ if (code === 0) {
+ updateEventEmitter.emit('update-success', {
+ message:
+ 'Update successful! The new version will be used on your next run.',
+ });
+ } else {
+ updateEventEmitter.emit('update-failed', {
+ message: `Automatic update failed. Please try updating manually. (command: ${updateCommand}, stderr: ${errorOutput.trim()})`,
+ });
+ }
+ });
+
+ updateProcess.on('error', (err) => {
+ updateEventEmitter.emit('update-failed', {
+ message: `Automatic update failed. Please try updating manually. (error: ${err.message})`,
+ });
+ });
+ return updateProcess;
+}
+
+export function setUpdateHandler(
+ addItem: (item: Omit<HistoryItem, 'id'>, timestamp: number) => void,
+ setUpdateInfo: (info: UpdateObject | null) => void,
+) {
+ let successfullyInstalled = false;
+ const handleUpdateRecieved = (info: UpdateObject) => {
+ setUpdateInfo(info);
+ const savedMessage = info.message;
+ setTimeout(() => {
+ if (!successfullyInstalled) {
+ addItem(
+ {
+ type: MessageType.INFO,
+ text: savedMessage,
+ },
+ Date.now(),
+ );
+ }
+ setUpdateInfo(null);
+ }, 60000);
+ };
+
+ const handleUpdateFailed = () => {
+ setUpdateInfo(null);
+ addItem(
+ {
+ type: MessageType.ERROR,
+ text: `Automatic update failed. Please try updating manually`,
+ },
+ Date.now(),
+ );
+ };
+
+ const handleUpdateSuccess = () => {
+ successfullyInstalled = true;
+ setUpdateInfo(null);
+ addItem(
+ {
+ type: MessageType.INFO,
+ text: `Update successful! The new version will be used on your next run.`,
+ },
+ Date.now(),
+ );
+ };
+
+ const handleUpdateInfo = (data: { message: string }) => {
+ addItem(
+ {
+ type: MessageType.INFO,
+ text: data.message,
+ },
+ Date.now(),
+ );
+ };
+
+ updateEventEmitter.on('update-received', handleUpdateRecieved);
+ updateEventEmitter.on('update-failed', handleUpdateFailed);
+ updateEventEmitter.on('update-success', handleUpdateSuccess);
+ updateEventEmitter.on('update-info', handleUpdateInfo);
+
+ return () => {
+ updateEventEmitter.off('update-received', handleUpdateRecieved);
+ updateEventEmitter.off('update-failed', handleUpdateFailed);
+ updateEventEmitter.off('update-success', handleUpdateSuccess);
+ updateEventEmitter.off('update-info', handleUpdateInfo);
+ };
+}