Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ jobs:
exclude-web-vsix: 'true'
extensions-root: .
vsix-artifact-name: vsix-packages
required-ci-checks: E2E / Web E2E, E2E / Desktop E2E (macos-latest), E2E / Desktop E2E (windows-latest)
required-ci-checks: >-
["E2E / Web E2E", "E2E / Desktop E2E (macos-latest)", "E2E / Desktop E2E (windows-latest)"]
node-version: '24'
package-manager: pnpm
package-manager-version: '10'
Expand Down
23 changes: 12 additions & 11 deletions lana/src/commands/RetrieveLogFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import type { Context } from '../Context.js';
import { Item, Options, QuickPick } from '../display/QuickPick.js';
import { QuickPickWorkspace } from '../display/QuickPickWorkspace.js';
import {
fileOrFolderExists,
getLogBody,
listLogs,
writeFile,
Expand Down Expand Up @@ -65,8 +64,18 @@ export class RetrieveLogFile {
const logFileId = await RetrieveLogFile.getLogFile(logFiles);
if (logFileId) {
const logUri = this.getLogFileUri(Uri.parse(wsFolder.uri), logFileId);
const writeLogFile = this.writeLogFile(logUri, logFileId);
return LogView.createView(context, writeLogFile, logUri);
const logBody = await getLogBody(logFileId);

try {
await writeFile(logUri, logBody);
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
context.display.output(`Unable to cache retrieved log: ${msg}`, true);
}

// Webviews cannot always fetch workspace URIs in vscode.dev. Supplying the
// retrieved content directly keeps the analysis independent of that boundary.
return LogView.createView(context, undefined, logUri, logBody);
}
} finally {
loadingPicker.dispose();
Expand Down Expand Up @@ -154,12 +163,4 @@ export class RetrieveLogFile {
// Utils.joinPath works on both desktop (file://) and web (vscode-vfs://, memfs://).
return Utils.joinPath(wsUri, '.sfdx', 'tools', 'debug', 'logs', `${fileId}.log`);
}

private static async writeLogFile(logUri: Uri, logId: string): Promise<void> {
const logExists = await fileOrFolderExists(logUri);
if (!logExists) {
const logBody = await getLogBody(logId);
await writeFile(logUri, logBody);
}
}
}
29 changes: 15 additions & 14 deletions lana/src/commands/__tests__/RetrieveLogFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ describe('RetrieveLogFile', () => {
expect(logUri.toString()).toContain('selected-log-id.log');
});

it('should skip download when log file already exists', async () => {
it('should retrieve the current log even when a cached file exists', async () => {
mockPickOrReturn.mockResolvedValue(mockWorkspace);
mockListLogs.mockResolvedValue([
{
Expand All @@ -268,8 +268,9 @@ describe('RetrieveLogFile', () => {
},
]);
mockQuickPickPick.mockResolvedValue([{ logId: 'existing-log' }]);
// File already exists
mockFileOrFolderExists.mockResolvedValue(true);
mockGetLogBody.mockResolvedValue('current log content');
mockWriteFile.mockResolvedValue(undefined);
mockCreateView.mockResolvedValue({ panel: 'mock' });

const mockContext = createMockContext();
Expand All @@ -278,9 +279,8 @@ describe('RetrieveLogFile', () => {
const commandCallback = getCommandCallback();
await commandCallback();

// getLogBody + writeFile should NOT be called since file exists
expect(mockGetLogBody).not.toHaveBeenCalled();
expect(mockWriteFile).not.toHaveBeenCalled();
expect(mockGetLogBody).toHaveBeenCalledWith('existing-log');
expect(mockWriteFile).toHaveBeenCalledWith(expect.anything(), 'current log content');
expect(mockCreateView).toHaveBeenCalled();
});

Expand Down Expand Up @@ -310,7 +310,6 @@ describe('RetrieveLogFile', () => {
const commandCallback = getCommandCallback();
await commandCallback();

// getLogBody + writeFile SHOULD be called since file doesn't exist
expect(mockGetLogBody).toHaveBeenCalledWith('new-log');
expect(mockWriteFile).toHaveBeenCalledWith(expect.anything(), 'fetched log content');
});
Expand Down Expand Up @@ -734,8 +733,8 @@ describe('RetrieveLogFile', () => {
});
});

describe('writeLogFile', () => {
it('preserves the memfs URI through the deferred log write', async () => {
describe('retrieved log delivery', () => {
it('preserves the memfs URI while providing retrieved content to the webview', async () => {
const workspaceUri = Uri.parse('memfs:/test/workspace');
const testWs = new VSWorkspace({
uri: workspaceUri,
Expand Down Expand Up @@ -766,14 +765,14 @@ describe('RetrieveLogFile', () => {
const lastCall = mockRegisterCommand.mock.calls[mockRegisterCommand.mock.calls.length - 1];
await lastCall[1]();

const [, writeLogFile, logUri] = mockCreateView.mock.calls[0];
await writeLogFile;
const [, beforeSendLog, logUri, logBody] = mockCreateView.mock.calls[0];

expect(logUri).toEqual(
Uri.parse('memfs:/test/workspace/.sfdx/tools/debug/logs/download-me.log'),
);
expect(mockFileOrFolderExists).toHaveBeenCalledWith(logUri);
expect(mockWriteFile).toHaveBeenCalledWith(logUri, 'fetched body');
expect(beforeSendLog).toBeUndefined();
expect(logBody).toBe('fetched body');
});

it('should call getLogBody + writeFile when file does not exist', async () => {
Expand Down Expand Up @@ -810,7 +809,7 @@ describe('RetrieveLogFile', () => {
expect(mockWriteFile).toHaveBeenCalledWith(expect.anything(), 'fetched body');
});

it('should NOT call getLogBody/writeFile when file exists', async () => {
it('retrieves and overwrites an existing cached log file', async () => {
const testWs = new VSWorkspace({
uri: Uri.parse('file:///test/ws'),
name: 'test-ws',
Expand All @@ -830,6 +829,8 @@ describe('RetrieveLogFile', () => {
]);
mockQuickPickPick.mockResolvedValue([{ logId: 'already-exists' }]);
mockFileOrFolderExists.mockResolvedValue(true);
mockGetLogBody.mockResolvedValue('fresh log body');
mockWriteFile.mockResolvedValue(undefined);
mockCreateView.mockResolvedValue({ panel: 'mock' });

const mockContext = createMockContext();
Expand All @@ -838,8 +839,8 @@ describe('RetrieveLogFile', () => {
const lastCall = mockRegisterCommand.mock.calls[mockRegisterCommand.mock.calls.length - 1];
await lastCall[1]();

expect(mockGetLogBody).not.toHaveBeenCalled();
expect(mockWriteFile).not.toHaveBeenCalled();
expect(mockGetLogBody).toHaveBeenCalledWith('already-exists');
expect(mockWriteFile).toHaveBeenCalledWith(expect.anything(), 'fresh log body');
});
});
});
3 changes: 2 additions & 1 deletion scripts/verify-web-distribution.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ for (const requiredText of [
'uses: salesforcecli/github-workflows/.github/workflows/vscode-manual-publish.yml@ph/W-23832274-pnpm-stable-promotion',
'extension-name: lana',
"vsix-name-pattern: 'lana-*.vsix'",
'required-ci-checks: E2E',
'required-ci-checks: >-',
'["E2E / Web E2E", "E2E / Desktop E2E (macos-latest)", "E2E / Desktop E2E (windows-latest)"]',
'package-manager: pnpm',
'lockfile-path: pnpm-lock.yaml',
]) {
Expand Down
Loading