Skip to content

Commit 5b707ce

Browse files
committed
feat(amazonq): skip registering run command log file
1 parent 4459171 commit 5b707ce

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { prepareRepoData, getDeletedFileInfos, registerNewFiles, PrepareRepoData
2929
import { uploadCode } from '../util/upload'
3030

3131
export const EmptyCodeGenID = 'EMPTY_CURRENT_CODE_GENERATION_ID'
32+
const RunCommandLogFileName = '.amazonq/dev/run_command.log'
3233

3334
export interface BaseMessenger {
3435
sendAnswer(params: any): void
@@ -103,6 +104,30 @@ export abstract class CodeGenBase {
103104
case CodeGenerationStatus.COMPLETE: {
104105
const { newFileContents, deletedFiles, references } =
105106
await this.config.proxyClient.exportResultArchive(this.conversationId)
107+
108+
const logFileInfo = newFileContents.find((file: any) => file.name === RunCommandLogFileName)
109+
if (logFileInfo) {
110+
const filePath = `${this.config.workspaceRoots[0]}/${RunCommandLogFileName}`
111+
const fileUri = vscode.Uri.file(filePath)
112+
113+
const fileExists = await fs
114+
.stat(fileUri)
115+
.then(() => true)
116+
.catch(() => false)
117+
118+
if (fileExists) {
119+
const fileContent = await fs.readFile(fileUri)
120+
const decodedContent = new TextDecoder().decode(fileContent)
121+
logFileInfo.content = decodedContent + logFileInfo.content
122+
} else {
123+
await fs.writeFile(fileUri, new TextEncoder().encode(logFileInfo.content), {
124+
create: true,
125+
overwrite: true,
126+
})
127+
}
128+
newFileContents.splice(newFileContents.indexOf(logFileInfo), 1)
129+
}
130+
106131
const newFileInfo = registerNewFiles(
107132
fs,
108133
newFileContents,

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

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { DocPrepareCodeGenState } from '../../../amazonqDoc'
1010
import { createMockSessionStateAction } from '../../amazonq/utils'
1111

1212
import { createTestContext, setupTestHooks } from '../../amazonq/session/testSetup'
13+
const filesModule = require('../../../amazonq/util/files')
1314

1415
describe('sessionStateDoc', () => {
1516
const context = createTestContext()
@@ -27,3 +28,179 @@ describe('sessionStateDoc', () => {
2728
})
2829
})
2930
})
31+
32+
describe('CodeGenBase generateCode log file handling', () => {
33+
const RunCommandLogFileName = '.amazonq/dev/run_command.log'
34+
35+
const registerNewFilesStub = sinon.stub()
36+
registerNewFilesStub.callsFake((fs: any, newFileContents: any[]) => {
37+
return newFileContents
38+
})
39+
const getDeletedFileInfosStub = sinon.stub()
40+
getDeletedFileInfosStub.callsFake((fs: any, deletedFiles: any[]) => {
41+
return []
42+
})
43+
44+
class TestCodeGen extends (require('../../../amazonq/session/sessionState') as any).CodeGenBase {
45+
public generatedFiles: any[] = []
46+
constructor(config: any, tabID: string) {
47+
super(config, tabID)
48+
}
49+
protected handleProgress(messenger: any, action: any, detail?: string): void {
50+
// no-op
51+
}
52+
protected getScheme(): string {
53+
return 'file'
54+
}
55+
protected getTimeoutErrorCode(): string {
56+
return 'test_timeout'
57+
}
58+
protected handleGenerationComplete(messenger: any, newFileInfo: any[], action: any): void {
59+
this.generatedFiles = newFileInfo
60+
}
61+
protected handleError(messenger: any, codegenResult: any): Error {
62+
throw new Error('handleError called')
63+
}
64+
}
65+
66+
let testConfig: any
67+
let fakeProxyClient: any
68+
let fsMock: any
69+
let telemetryMock: any
70+
let messengerMock: any
71+
let testAction: any
72+
73+
beforeEach(() => {
74+
fakeProxyClient = {
75+
getCodeGeneration: sinon.stub().resolves({
76+
codeGenerationStatus: { status: 'Complete' },
77+
codeGenerationRemainingIterationCount: 0,
78+
codeGenerationTotalIterationCount: 1,
79+
}),
80+
exportResultArchive: sinon.stub(),
81+
}
82+
83+
testConfig = {
84+
conversationId: 'conv1',
85+
uploadId: 'upload1',
86+
workspaceRoots: ['/workspace'],
87+
proxyClient: fakeProxyClient,
88+
}
89+
90+
fsMock = {
91+
stat: sinon.stub(),
92+
readFile: sinon.stub(),
93+
writeFile: sinon.stub(),
94+
}
95+
96+
telemetryMock = {
97+
setCodeGenerationResult: sinon.spy(),
98+
setNumberOfFilesGenerated: sinon.spy(),
99+
setAmazonqNumberOfReferences: sinon.spy(),
100+
setGenerateCodeIteration: sinon.spy(),
101+
setGenerateCodeLastInvocationTime: sinon.spy(),
102+
recordUserCodeGenerationTelemetry: sinon.spy(),
103+
}
104+
105+
messengerMock = {
106+
sendAnswer: sinon.spy(),
107+
}
108+
109+
testAction = {
110+
telemetry: telemetryMock,
111+
fs: fsMock,
112+
messenger: messengerMock,
113+
uploadHistory: {},
114+
tokenSource: { token: { isCancellationRequested: false, onCancellationRequested: () => {} } },
115+
}
116+
})
117+
118+
afterEach(() => {
119+
sinon.restore()
120+
})
121+
122+
it('should append existing log file content to new log content and remove the log file entry when file exists', async () => {
123+
const logFileInfo = {
124+
name: RunCommandLogFileName,
125+
content: 'newLog',
126+
}
127+
const otherFile = { name: 'other.ts', content: 'other' }
128+
129+
fakeProxyClient.exportResultArchive.resolves({
130+
newFileContents: [logFileInfo, otherFile],
131+
deletedFiles: [],
132+
references: [],
133+
})
134+
135+
fsMock.stat.resolves({})
136+
fsMock.readFile.resolves(Buffer.from('existing'))
137+
138+
sinon.stub(filesModule, 'registerNewFiles').callsFake(registerNewFilesStub)
139+
sinon.stub(filesModule, 'getDeletedFileInfos').callsFake(getDeletedFileInfosStub)
140+
141+
const testCodeGen = new TestCodeGen(testConfig, 'tab1')
142+
143+
await testCodeGen.generateCode({
144+
messenger: messengerMock,
145+
fs: fsMock,
146+
codeGenerationId: 'codegen1',
147+
telemetry: telemetryMock,
148+
workspaceFolders: {},
149+
action: testAction,
150+
})
151+
152+
const expectedFilePath = `${testConfig.workspaceRoots[0]}/${RunCommandLogFileName}`
153+
const fileUri = vscode.Uri.file(expectedFilePath)
154+
sinon.assert.calledWith(fsMock.stat, fileUri)
155+
sinon.assert.calledWith(fsMock.readFile, fileUri)
156+
157+
assert.strictEqual(logFileInfo.content, 'existingnewLog')
158+
assert.deepStrictEqual(testCodeGen.generatedFiles, [otherFile])
159+
160+
sinon.restore()
161+
})
162+
163+
it('should create a new log file when log file does not exist', async () => {
164+
const logFileInfo = {
165+
name: RunCommandLogFileName,
166+
content: 'newLog',
167+
}
168+
const otherFile = { name: 'other.ts', content: 'other' }
169+
170+
fakeProxyClient.exportResultArchive.resolves({
171+
newFileContents: [logFileInfo, otherFile],
172+
deletedFiles: [],
173+
references: [],
174+
})
175+
176+
fsMock.stat.rejects(new Error('Not found'))
177+
fsMock.writeFile.resolves()
178+
179+
sinon.stub(filesModule, 'registerNewFiles').callsFake(registerNewFilesStub)
180+
sinon.stub(filesModule, 'getDeletedFileInfos').callsFake(getDeletedFileInfosStub)
181+
182+
const testCodeGen = new TestCodeGen(testConfig, 'tab1')
183+
184+
await testCodeGen.generateCode({
185+
messenger: messengerMock,
186+
fs: fsMock,
187+
codeGenerationId: 'codegen2',
188+
telemetry: telemetryMock,
189+
workspaceFolders: {},
190+
action: testAction,
191+
})
192+
193+
const expectedFilePath = `${testConfig.workspaceRoots[0]}/${RunCommandLogFileName}`
194+
const fileUri = vscode.Uri.file(expectedFilePath)
195+
sinon.assert.calledWith(fsMock.stat, fileUri)
196+
sinon.assert.calledWith(fsMock.writeFile, fileUri, new TextEncoder().encode('newLog'), {
197+
create: true,
198+
overwrite: true,
199+
})
200+
sinon.assert.notCalled(fsMock.readFile)
201+
202+
assert.deepStrictEqual(testCodeGen.generatedFiles, [otherFile])
203+
204+
sinon.restore()
205+
})
206+
})

0 commit comments

Comments
 (0)