Skip to content

Commit 67f02d8

Browse files
committed
feat(amazonq): skip registering run command log file
1 parent e01574f commit 67f02d8

File tree

4 files changed

+163
-3
lines changed

4 files changed

+163
-3
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "The logs emitted by the Agent during user command execution will be accepted and written to .amazonq/dev/run_command.log file in the user's local repository."
4+
}

packages/core/src/amazonq/session/sessionState.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
*/
55

66
import * as vscode from 'vscode'
7+
import * as path from 'path'
78
import { ToolkitError } from '../../shared/errors'
89
import globals from '../../shared/extensionGlobals'
910
import { getLogger } from '../../shared/logger/logger'
1011
import { AmazonqCreateUpload, Span, telemetry } from '../../shared/telemetry/telemetry'
11-
import { VirtualFileSystem } from '../../shared/virtualFilesystem'
12+
import { FileProvider, VirtualFileSystem } from '../../shared/virtualFilesystem'
1213
import { CodeReference, UploadHistory } from '../webview/ui/connector'
1314
import { AuthUtil } from '../../codewhisperer/util/authUtil'
1415
import { randomUUID } from '../../shared/crypto'
@@ -27,8 +28,10 @@ import {
2728
} from '../commons/types'
2829
import { prepareRepoData, getDeletedFileInfos, registerNewFiles, PrepareRepoDataOptions } from '../util/files'
2930
import { uploadCode } from '../util/upload'
31+
import { VirtualMemoryFile } from '../../shared/virtualMemoryFile'
3032

3133
export const EmptyCodeGenID = 'EMPTY_CURRENT_CODE_GENERATION_ID'
34+
const RunCommandLogFileName = '.amazonq/dev/run_command.log'
3235

3336
export interface BaseMessenger {
3437
sendAnswer(params: any): void
@@ -103,6 +106,44 @@ export abstract class CodeGenBase {
103106
case CodeGenerationStatus.COMPLETE: {
104107
const { newFileContents, deletedFiles, references } =
105108
await this.config.proxyClient.exportResultArchive(this.conversationId)
109+
110+
const logFileInfo = newFileContents.find(
111+
(file: { zipFilePath: string; fileContent: string }) =>
112+
file.zipFilePath === RunCommandLogFileName
113+
)
114+
getLogger().info(`Log file info: %O`, logFileInfo)
115+
if (logFileInfo) {
116+
const filePath = path.join(this.config.workspaceRoots[0], RunCommandLogFileName)
117+
// const fileUri = vscode.Uri.file(filePath)
118+
getLogger().info(`Writing log file to ${filePath}`)
119+
120+
try {
121+
// ensure the directory exists before writing the log file
122+
// const logDir = path.dirname(fileUri.fsPath)
123+
// Optionally check if the directory exists or create it recursively
124+
const encoder = new TextEncoder()
125+
const contents = encoder.encode(logFileInfo.fileContent)
126+
const provider: FileProvider = new VirtualMemoryFile(contents)
127+
const options = {
128+
create: true,
129+
overwrite: true,
130+
}
131+
await provider.write(logFileInfo.fileContent, options)
132+
// await fs.writeFile(fileUri, logFileInfo.fileContent, {
133+
// create: true,
134+
// overwrite: true,
135+
// })
136+
} catch (e) {
137+
const errorDetails =
138+
e instanceof Error ? `${e.name}: ${e.message}\nStack Trace: ${e.stack}` : String(e)
139+
getLogger().error(`Error writing log file. Details: ${errorDetails}`)
140+
}
141+
142+
// getLogger().info(`Log file written to ${filePath}`)
143+
newFileContents.splice(newFileContents.indexOf(logFileInfo), 1)
144+
getLogger().info(`Log file removed from new files %O`, newFileContents)
145+
}
146+
106147
const newFileInfo = registerNewFiles(
107148
fs,
108149
newFileContents,
@@ -111,6 +152,7 @@ export abstract class CodeGenBase {
111152
this.conversationId,
112153
this.getScheme()
113154
)
155+
getLogger().info(`New files Info registered: %O`, newFileInfo)
114156
telemetry.setNumberOfFilesGenerated(newFileInfo.length)
115157

116158
this.handleGenerationComplete(messenger, newFileInfo, action)
@@ -361,6 +403,7 @@ export abstract class BaseCodeGenState extends CodeGenBase implements SessionSta
361403
workspaceFolders: this.config.workspaceFolders,
362404
action,
363405
})
406+
getLogger().debug(`Code generation result: %O`, codeGeneration)
364407

365408
if (codeGeneration && !action.tokenSource?.token.isCancellationRequested) {
366409
this.config.currentCodeGenerationId = codeGenerationId

packages/core/src/shared/virtualFilesystem.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55

66
import * as vscode from 'vscode'
7+
import { getLogger } from './logger/logger'
8+
import { get } from 'lodash'
79

810
type StatNoType = Omit<vscode.FileStat, 'type'>
911

@@ -46,7 +48,7 @@ export class VirtualFileSystem implements vscode.FileSystemProvider {
4648
if (this.fileProviders[key] !== undefined) {
4749
throw new Error('Cannot re-register a provider for the same URI')
4850
}
49-
51+
getLogger().info(`in registerProvider: %s, %O`, key, provider)
5052
this.fileProviders[key] = provider
5153
const onDidChange = provider.onDidChange(() => {
5254
this._onDidChangeFile.fire([{ uri, type: vscode.FileChangeType.Changed }])
@@ -56,8 +58,11 @@ export class VirtualFileSystem implements vscode.FileSystemProvider {
5658
}
5759

5860
private getProvider(uri: vscode.Uri): FileProvider {
61+
getLogger().info(`in getProvider: ${this.uriToKey(uri)}`)
5962
const provider = this.fileProviders[this.uriToKey(uri)]
63+
getLogger().info(`provider is: ${provider}`)
6064
if (!provider) {
65+
getLogger().info(`error in getProvider: ${this.errorMessage}`)
6166
throw new Error(this.errorMessage)
6267
}
6368
return provider

packages/core/src/test/amazonqDoc/session/sessionState.test.ts

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import assert from 'assert'
88
import sinon from 'sinon'
99
import { DocPrepareCodeGenState } from '../../../amazonqDoc'
1010
import { createMockSessionStateAction } from '../../amazonq/utils'
11-
1211
import { createTestContext, setupTestHooks } from '../../amazonq/session/testSetup'
12+
import * as filesModule from '../../../amazonq/util/files'
13+
import { CodeGenBase as CodeGenBaseClass } from '../../../amazonq/session/sessionState'
1314

1415
describe('sessionStateDoc', () => {
1516
const context = createTestContext()
@@ -26,4 +27,111 @@ describe('sessionStateDoc', () => {
2627
})
2728
})
2829
})
30+
31+
describe('CodeGenBase generateCode log file handling', () => {
32+
const RunCommandLogFileName = '.amazonq/dev/run_command.log'
33+
34+
class TestCodeGen extends CodeGenBaseClass {
35+
public generatedFiles: any[] = []
36+
constructor(config: any, tabID: string) {
37+
super(config, tabID)
38+
}
39+
protected handleProgress(_messenger: any): void {}
40+
protected getScheme(): string {
41+
return 'file'
42+
}
43+
protected getTimeoutErrorCode(): string {
44+
return 'test_timeout'
45+
}
46+
protected handleGenerationComplete(_messenger: any, newFileInfo: any[]): void {
47+
this.generatedFiles = newFileInfo
48+
}
49+
protected handleError(_messenger: any, _codegenResult: any): Error {
50+
throw new Error('handleError called')
51+
}
52+
}
53+
54+
let testConfig: any, fakeProxyClient: any, fsMock: any, messengerMock: any, testAction: any, telemetryMock: any
55+
56+
beforeEach(() => {
57+
fakeProxyClient = {
58+
getCodeGeneration: sinon.stub().resolves({
59+
codeGenerationStatus: { status: 'Complete' },
60+
codeGenerationRemainingIterationCount: 0,
61+
codeGenerationTotalIterationCount: 1,
62+
}),
63+
exportResultArchive: sinon.stub(),
64+
}
65+
66+
testConfig = {
67+
conversationId: 'conv1',
68+
uploadId: 'upload1',
69+
workspaceRoots: ['/workspace'],
70+
proxyClient: fakeProxyClient,
71+
}
72+
73+
fsMock = {
74+
writeFile: sinon.stub().resolves(),
75+
}
76+
77+
messengerMock = { sendAnswer: sinon.spy() }
78+
79+
telemetryMock = {
80+
setCodeGenerationResult: sinon.spy(),
81+
setNumberOfFilesGenerated: sinon.spy(),
82+
setAmazonqNumberOfReferences: sinon.spy(),
83+
setGenerateCodeIteration: sinon.spy(),
84+
setGenerateCodeLastInvocationTime: sinon.spy(),
85+
recordUserCodeGenerationTelemetry: sinon.spy(),
86+
}
87+
88+
testAction = {
89+
fs: fsMock,
90+
messenger: messengerMock,
91+
tokenSource: { token: { isCancellationRequested: false, onCancellationRequested: () => {} } },
92+
}
93+
})
94+
95+
afterEach(() => {
96+
sinon.restore()
97+
})
98+
99+
it('writes to the log file, present or not', async () => {
100+
const logFileInfo = {
101+
zipFilePath: RunCommandLogFileName,
102+
fileContent: 'newLog',
103+
}
104+
const otherFile = { zipFilePath: 'other.ts', fileContent: 'other' }
105+
106+
fakeProxyClient.exportResultArchive.resolves({
107+
newFileContents: [logFileInfo, otherFile],
108+
deletedFiles: [],
109+
references: [],
110+
})
111+
112+
// Minimal stubs to simulate file processing behavior.
113+
sinon.stub(filesModule, 'registerNewFiles').callsFake((_, newFileContents: any[]) => newFileContents)
114+
sinon.stub(filesModule, 'getDeletedFileInfos').callsFake(() => [])
115+
116+
const testCodeGen = new TestCodeGen(testConfig, 'tab1')
117+
118+
await testCodeGen.generateCode({
119+
messenger: messengerMock,
120+
fs: fsMock,
121+
codeGenerationId: 'codegen2',
122+
telemetry: telemetryMock,
123+
workspaceFolders: {} as any,
124+
action: testAction,
125+
})
126+
127+
const expectedFilePath = `${testConfig.workspaceRoots[0]}/${RunCommandLogFileName}`
128+
const fileUri = vscode.Uri.file(expectedFilePath)
129+
sinon.assert.calledWith(fsMock.writeFile, fileUri, new TextEncoder().encode('newLog'), {
130+
create: true,
131+
overwrite: true,
132+
})
133+
134+
assert.deepStrictEqual(testCodeGen.generatedFiles, [otherFile])
135+
})
136+
})
29137
})

0 commit comments

Comments
 (0)