mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Adding functionality to check for Roo-Cline updates
This commit is contained in:
parent
86ce5236bc
commit
648f05c1d0
5 changed files with 297 additions and 4 deletions
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "roo-cline",
|
||||
"version": "2.0.3",
|
||||
"version": "2.1.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "roo-cline",
|
||||
"version": "2.0.3",
|
||||
"version": "2.1.2",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/bedrock-sdk": "^0.10.2",
|
||||
"@anthropic-ai/sdk": "^0.26.0",
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
"name": "roo-cline",
|
||||
"displayName": "Roo Cline",
|
||||
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
|
||||
"version": "2.1.1",
|
||||
"version": "2.1.2",
|
||||
"files": [
|
||||
"bin/roo-cline-2.1.1.vsix",
|
||||
"bin/roo-cline-2.1.2.vsix",
|
||||
"assets/icons/icon_Roo.png"
|
||||
],
|
||||
"icon": "assets/icons/icon_Roo.png",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { ClineProvider } from "./core/webview/ClineProvider"
|
|||
import { createClineAPI } from "./exports"
|
||||
import "./utils/path" // necessary to have access to String.prototype.toPosix
|
||||
import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
|
||||
import { scheduleUpdateChecks, checkForUpdates } from "./utils/version-check"
|
||||
|
||||
/*
|
||||
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
|
||||
|
|
@ -26,6 +27,11 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
|
||||
outputChannel.appendLine("Cline extension activated")
|
||||
|
||||
// Schedule periodic update checks
|
||||
scheduleUpdateChecks(context).catch(error => {
|
||||
outputChannel.appendLine(`Failed to initialize update checks: ${error}`)
|
||||
})
|
||||
|
||||
const sidebarProvider = new ClineProvider(context, outputChannel)
|
||||
|
||||
context.subscriptions.push(
|
||||
|
|
@ -37,6 +43,26 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand("cline.plusButtonClicked", async () => {
|
||||
outputChannel.appendLine("Plus button Clicked")
|
||||
|
||||
// Check for updates before starting new task
|
||||
const updateCheck = await checkForUpdates(context);
|
||||
if (updateCheck?.updateAvailable) {
|
||||
const choice = await vscode.window.showInformationMessage(
|
||||
`A new version of Roo Cline (${updateCheck.latest}) is available. It's recommended to update before starting a new task.`,
|
||||
'Update Now',
|
||||
'Continue Anyway',
|
||||
'Cancel'
|
||||
);
|
||||
|
||||
if (choice === 'Cancel') {
|
||||
return;
|
||||
} else if (choice === 'Update Now') {
|
||||
// User chose to update, don't start new task
|
||||
return;
|
||||
}
|
||||
// If 'Continue Anyway', proceed with new task
|
||||
}
|
||||
|
||||
await sidebarProvider.clearTask()
|
||||
await sidebarProvider.postStateToWebview()
|
||||
await sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
|
||||
|
|
|
|||
151
src/utils/__tests__/version-check.test.ts
Normal file
151
src/utils/__tests__/version-check.test.ts
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { exec } from 'child_process';
|
||||
import { checkForUpdates, VersionInfo } from '../version-check';
|
||||
import * as fs from 'fs/promises';
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock('child_process');
|
||||
jest.mock('fs/promises');
|
||||
jest.mock('vscode', () => ({
|
||||
window: {
|
||||
showInformationMessage: jest.fn(),
|
||||
showErrorMessage: jest.fn(),
|
||||
withProgress: jest.fn(async (options, task) => task()),
|
||||
},
|
||||
commands: {
|
||||
executeCommand: jest.fn(),
|
||||
},
|
||||
extensions: {
|
||||
getExtension: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('version-check', () => {
|
||||
let mockContext: vscode.ExtensionContext;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockContext = {} as vscode.ExtensionContext;
|
||||
|
||||
// Mock extension version
|
||||
(vscode.extensions.getExtension as jest.Mock).mockReturnValue({
|
||||
packageJSON: { version: '1.0.0' }
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkForUpdates', () => {
|
||||
it('should detect when update is available', async () => {
|
||||
// Mock CodeArtifact login script exists
|
||||
(fs.access as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
// Mock successful command executions
|
||||
(exec as unknown as jest.Mock).mockImplementation((cmd, callback) => {
|
||||
if (callback) {
|
||||
if (cmd.includes('codeartifact-login.sh')) {
|
||||
callback(null, { stdout: 'Login successful' }, '');
|
||||
} else if (cmd.includes('npm view')) {
|
||||
callback(null, { stdout: '1.1.0\n' }, '');
|
||||
}
|
||||
}
|
||||
return {
|
||||
stdout: cmd.includes('npm view') ? '1.1.0\n' : 'Login successful',
|
||||
stderr: '',
|
||||
};
|
||||
});
|
||||
|
||||
const result = await checkForUpdates(mockContext);
|
||||
|
||||
expect(result).toEqual({
|
||||
current: '1.0.0',
|
||||
latest: '1.1.0',
|
||||
updateAvailable: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle when no update is available', async () => {
|
||||
// Mock CodeArtifact login script exists
|
||||
(fs.access as jest.Mock).mockResolvedValue(undefined);
|
||||
|
||||
// Mock successful command executions with same version
|
||||
(exec as unknown as jest.Mock).mockImplementation((cmd, callback) => {
|
||||
if (callback) {
|
||||
if (cmd.includes('codeartifact-login.sh')) {
|
||||
callback(null, { stdout: 'Login successful' }, '');
|
||||
} else if (cmd.includes('npm view')) {
|
||||
callback(null, { stdout: '1.0.0\n' }, '');
|
||||
}
|
||||
}
|
||||
return {
|
||||
stdout: cmd.includes('npm view') ? '1.0.0\n' : 'Login successful',
|
||||
stderr: '',
|
||||
};
|
||||
});
|
||||
|
||||
const result = await checkForUpdates(mockContext);
|
||||
|
||||
expect(result).toEqual({
|
||||
current: '1.0.0',
|
||||
latest: '1.0.0',
|
||||
updateAvailable: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should fallback to direct auth token when login script missing', async () => {
|
||||
// Mock CodeArtifact login script does not exist
|
||||
(fs.access as jest.Mock).mockRejectedValue(new Error('ENOENT'));
|
||||
|
||||
// Mock successful command executions
|
||||
(exec as unknown as jest.Mock).mockImplementation((cmd, callback) => {
|
||||
if (callback) {
|
||||
if (cmd.includes('get-authorization-token')) {
|
||||
callback(null, { stdout: 'token123' }, '');
|
||||
} else if (cmd.includes('npm view')) {
|
||||
callback(null, { stdout: '1.1.0\n' }, '');
|
||||
}
|
||||
}
|
||||
return {
|
||||
stdout: cmd.includes('npm view') ? '1.1.0\n' : 'token123',
|
||||
stderr: '',
|
||||
};
|
||||
});
|
||||
|
||||
const result = await checkForUpdates(mockContext);
|
||||
|
||||
expect(result).toEqual({
|
||||
current: '1.0.0',
|
||||
latest: '1.1.0',
|
||||
updateAvailable: true
|
||||
});
|
||||
|
||||
// Verify it tried to get auth token directly
|
||||
expect(exec).toHaveBeenCalledWith(
|
||||
expect.stringContaining('aws codeartifact get-authorization-token'),
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle errors and return null', async () => {
|
||||
// Mock CodeArtifact login script exists but fails
|
||||
(fs.access as jest.Mock).mockResolvedValue(undefined);
|
||||
(exec as unknown as jest.Mock).mockImplementation((cmd, callback) => {
|
||||
if (callback) {
|
||||
callback(new Error('Command failed'), '', 'Error executing command');
|
||||
}
|
||||
throw new Error('Command failed');
|
||||
});
|
||||
|
||||
const result = await checkForUpdates(mockContext);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle missing extension version', async () => {
|
||||
// Mock extension not found
|
||||
(vscode.extensions.getExtension as jest.Mock).mockReturnValue(undefined);
|
||||
|
||||
const result = await checkForUpdates(mockContext);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
116
src/utils/version-check.ts
Normal file
116
src/utils/version-check.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as os from 'os';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
export interface VersionInfo {
|
||||
current: string;
|
||||
latest: string;
|
||||
updateAvailable: boolean;
|
||||
}
|
||||
|
||||
export async function checkForUpdates(context: vscode.ExtensionContext): Promise<VersionInfo | null> {
|
||||
try {
|
||||
// Get current version from package.json
|
||||
const currentVersion = vscode.extensions.getExtension('roo-vet.roo-cline')?.packageJSON.version;
|
||||
|
||||
// Run CodeArtifact login to ensure we have valid credentials
|
||||
const loginScript = path.join(__dirname, '..', '..', 'scripts', 'codeartifact-login.sh');
|
||||
if (await fs.access(loginScript).then(() => true).catch(() => false)) {
|
||||
await execAsync(`bash "${loginScript}"`);
|
||||
} else {
|
||||
// If login script doesn't exist, try to get auth token directly
|
||||
await execAsync('aws codeartifact get-authorization-token --domain roo --query authorizationToken --output text');
|
||||
}
|
||||
|
||||
// Get latest version from CodeArtifact
|
||||
const { stdout } = await execAsync('npm view roo-cline version');
|
||||
const latestVersion = stdout.trim();
|
||||
|
||||
return {
|
||||
current: currentVersion,
|
||||
latest: latestVersion,
|
||||
updateAvailable: currentVersion !== latestVersion
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to check for updates:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function scheduleUpdateChecks(context: vscode.ExtensionContext) {
|
||||
// Check for updates on startup
|
||||
const initialCheck = await checkForUpdates(context);
|
||||
if (initialCheck?.updateAvailable) {
|
||||
showUpdateNotification(initialCheck.latest);
|
||||
}
|
||||
|
||||
// Check for updates every 24 hours
|
||||
setInterval(async () => {
|
||||
const check = await checkForUpdates(context);
|
||||
if (check?.updateAvailable) {
|
||||
showUpdateNotification(check.latest);
|
||||
}
|
||||
}, 24 * 60 * 60 * 1000); // 24 hours in milliseconds
|
||||
}
|
||||
|
||||
function showUpdateNotification(latestVersion: string) {
|
||||
vscode.window.showInformationMessage(
|
||||
`A new version of Roo Cline (${latestVersion}) is available!`,
|
||||
'Update Now',
|
||||
'Later'
|
||||
).then(selection => {
|
||||
if (selection === 'Update Now') {
|
||||
installUpdate(latestVersion);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function installUpdate(version: string) {
|
||||
await vscode.window.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: "Updating Roo Cline...",
|
||||
cancellable: false
|
||||
}, async (progress) => {
|
||||
try {
|
||||
// Create temp directory for installation
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'roo-cline-update-'));
|
||||
|
||||
progress.report({ message: 'Installing new version...' });
|
||||
|
||||
// Install roo-cline in temp directory
|
||||
await execAsync('npm init -y', { cwd: tempDir });
|
||||
await execAsync('npm install roo-cline', { cwd: tempDir });
|
||||
|
||||
// Get path to vsix file
|
||||
const vsixPath = path.join(tempDir, 'node_modules', 'roo-cline', 'bin', `roo-cline-${version}.vsix`);
|
||||
|
||||
progress.report({ message: 'Installing extension...' });
|
||||
|
||||
// Install the extension
|
||||
const editor = process.env.TERM_PROGRAM === 'vscode' ? 'code' : 'cursor';
|
||||
await execAsync(`${editor} --install-extension "${vsixPath}"`);
|
||||
|
||||
// Clean up temp directory
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
|
||||
// Show success message with reload button
|
||||
const action = await vscode.window.showInformationMessage(
|
||||
'Roo Cline has been updated successfully!',
|
||||
'Reload Now'
|
||||
);
|
||||
|
||||
if (action === 'Reload Now') {
|
||||
// Reload the window to activate the new version
|
||||
await vscode.commands.executeCommand('workbench.action.reloadWindow');
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage('Failed to update Roo Cline. Please try again later.');
|
||||
console.error('Update failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue