From 51abc6882af792b1192702513fae32580d22a257 Mon Sep 17 00:00:00 2001 From: peternhale Date: Mon, 17 Aug 2026 13:38:49 -0600 Subject: [PATCH 1/3] fix: pass manual publish checks as JSON --- .github/workflows/manual-publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index c61cd170..3e20bfaf 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -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' From 044aab4fb98fbc466b6d267cdda2613cd938c44b Mon Sep 17 00:00:00 2001 From: peternhale Date: Mon, 17 Aug 2026 13:44:46 -0600 Subject: [PATCH 2/3] fix: validate manual publish CI checks --- scripts/verify-web-distribution.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/verify-web-distribution.mjs b/scripts/verify-web-distribution.mjs index 1e87f28a..4fab2509 100644 --- a/scripts/verify-web-distribution.mjs +++ b/scripts/verify-web-distribution.mjs @@ -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', ]) { From 36b696e21aedeb4183bf6487cbde58a3c448070b Mon Sep 17 00:00:00 2001 From: peternhale Date: Mon, 17 Aug 2026 14:49:08 -0600 Subject: [PATCH 3/3] fix: deliver retrieved logs to webview --- lana/src/commands/RetrieveLogFile.ts | 23 ++++++++------- .../__tests__/RetrieveLogFile.test.ts | 29 ++++++++++--------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/lana/src/commands/RetrieveLogFile.ts b/lana/src/commands/RetrieveLogFile.ts index b604bfa8..3757edba 100644 --- a/lana/src/commands/RetrieveLogFile.ts +++ b/lana/src/commands/RetrieveLogFile.ts @@ -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, @@ -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(); @@ -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 { - const logExists = await fileOrFolderExists(logUri); - if (!logExists) { - const logBody = await getLogBody(logId); - await writeFile(logUri, logBody); - } - } } diff --git a/lana/src/commands/__tests__/RetrieveLogFile.test.ts b/lana/src/commands/__tests__/RetrieveLogFile.test.ts index eebeae4b..77a4bed0 100644 --- a/lana/src/commands/__tests__/RetrieveLogFile.test.ts +++ b/lana/src/commands/__tests__/RetrieveLogFile.test.ts @@ -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([ { @@ -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(); @@ -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(); }); @@ -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'); }); @@ -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, @@ -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 () => { @@ -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', @@ -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(); @@ -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'); }); }); });