From b5f1c10d2131467c3f014be7de2cccd9ee0d9ea6 Mon Sep 17 00:00:00 2001 From: andrew-eldridge Date: Wed, 12 Aug 2026 11:19:48 -0400 Subject: [PATCH 1/2] Revert "fix(vscode): Fix invalid workflow-designtime artifacts path with symlink (#9501)" This reverts commit 6c2be795c91fd905eeeb1e6c22910f2a4343d986. --- .../__test__/projectFilesConsistency.test.ts | 117 ------------------ .../projectFilesConsistency.ts | 26 +--- 2 files changed, 1 insertion(+), 142 deletions(-) diff --git a/apps/vs-code-designer/src/app/projectConsistency/__test__/projectFilesConsistency.test.ts b/apps/vs-code-designer/src/app/projectConsistency/__test__/projectFilesConsistency.test.ts index 2078492a4f0..db92c03a5ed 100644 --- a/apps/vs-code-designer/src/app/projectConsistency/__test__/projectFilesConsistency.test.ts +++ b/apps/vs-code-designer/src/app/projectConsistency/__test__/projectFilesConsistency.test.ts @@ -46,10 +46,6 @@ vi.mock('fs-extra', () => ({ pathExists: vi.fn(), readFile: vi.fn(), readdir: vi.fn(), - lstat: vi.fn(), - readlink: vi.fn(), - symlink: vi.fn(), - remove: vi.fn(), })); vi.mock('../../utils/appSettings/localSettings', async (importActual) => { @@ -93,10 +89,6 @@ const mockedFse = fse as unknown as { pathExists: ReturnType; readFile: ReturnType; readdir: ReturnType; - lstat: ReturnType; - readlink: ReturnType; - symlink: ReturnType; - remove: ReturnType; }; const mockedAddOrUpdate = localSettings.addOrUpdateLocalAppSettings as unknown as ReturnType; const mockedGetLocalSettingsJson = localSettings.getLocalSettingsJson as unknown as ReturnType; @@ -898,115 +890,6 @@ describe('projectFilesConsistency', () => { }); }); }); - - describe('artifacts junction', () => { - const designTimeDir = `${projectPath}/workflow-designtime`; - const artifactsTarget = `${projectPath}/Artifacts`; - const artifactsLink = `${designTimeDir}/Artifacts`; - - beforeEach(() => { - // Default: valid design-time directory so host/settings are not rewritten - const hostPath = `${designTimeDir}/host.json`; - const settingsPath = `${designTimeDir}/local.settings.json`; - const validHost = JSON.stringify({ - version: '2.0', - extensionBundle: { id: 'Microsoft.Azure.Functions.ExtensionBundle.Workflows', version: '[1.*, 2.0.0)' }, - extensions: { workflow: { settings: { [workflowOperationDiscoveryHostModeKey]: 'true' } } }, - }); - const validSettings = JSON.stringify({ - Values: { - APP_KIND: 'workflowapp', - FUNCTIONS_WORKER_RUNTIME: 'dotnet', - FUNCTIONS_INPROC_NET8_ENABLED: '1', - ProjectDirectoryPath: projectPath, - }, - }); - mockFiles({ [designTimeDir]: '', [hostPath]: validHost, [settingsPath]: validSettings, [artifactsTarget]: '' }); - mockedFse.lstat.mockRejectedValue(new Error('ENOENT')); - mockedFse.symlink.mockResolvedValue(undefined); - mockedFse.readlink.mockResolvedValue(artifactsTarget); - mockedFse.remove.mockResolvedValue(undefined); - }); - - it('creates a junction when Artifacts exists and no link is present', async () => { - mockedFse.lstat.mockRejectedValue(new Error('ENOENT')); - - await ensureDesignTimeFiles(context, projectPath); - - expect(mockedFse.symlink).toHaveBeenCalledWith( - expect.stringContaining('Artifacts'), - expect.stringContaining('workflow-designtime'), - 'junction' - ); - }); - - it('does not create a junction when Artifacts folder does not exist', async () => { - // Remove Artifacts from the mock file system - mockFiles({ - [designTimeDir]: '', - [`${designTimeDir}/host.json`]: JSON.stringify({ - version: '2.0', - extensionBundle: { id: 'Microsoft.Azure.Functions.ExtensionBundle.Workflows', version: '[1.*, 2.0.0)' }, - extensions: { workflow: { settings: { [workflowOperationDiscoveryHostModeKey]: 'true' } } }, - }), - [`${designTimeDir}/local.settings.json`]: JSON.stringify({ - Values: { - APP_KIND: 'workflowapp', - FUNCTIONS_WORKER_RUNTIME: 'dotnet', - FUNCTIONS_INPROC_NET8_ENABLED: '1', - ProjectDirectoryPath: projectPath, - }, - }), - }); - - await ensureDesignTimeFiles(context, projectPath); - - expect(mockedFse.symlink).not.toHaveBeenCalled(); - }); - - it('does not recreate junction when it already points to correct target', async () => { - mockedFse.lstat.mockResolvedValue({ isSymbolicLink: () => true }); - mockedFse.readlink.mockResolvedValue(artifactsTarget); - - await ensureDesignTimeFiles(context, projectPath); - - expect(mockedFse.symlink).not.toHaveBeenCalled(); - expect(mockedFse.remove).not.toHaveBeenCalled(); - }); - - it('recreates junction when it points to wrong target', async () => { - mockedFse.lstat.mockResolvedValue({ isSymbolicLink: () => true }); - mockedFse.readlink.mockResolvedValue('/some/other/path'); - - await ensureDesignTimeFiles(context, projectPath); - - expect(mockedFse.remove).toHaveBeenCalled(); - expect(mockedFse.symlink).toHaveBeenCalledWith( - expect.stringContaining('Artifacts'), - expect.stringContaining('workflow-designtime'), - 'junction' - ); - }); - - it('does not overwrite a real directory at the link path', async () => { - mockedFse.lstat.mockResolvedValue({ isSymbolicLink: () => false }); - - await ensureDesignTimeFiles(context, projectPath); - - expect(mockedFse.symlink).not.toHaveBeenCalled(); - expect(mockedFse.remove).not.toHaveBeenCalled(); - }); - - it('logs a warning when symlink creation fails', async () => { - mockedFse.lstat.mockRejectedValue(new Error('ENOENT')); - mockedFse.symlink.mockRejectedValue(new Error('EPERM')); - - await ensureDesignTimeFiles(context, projectPath); - - const lines = loggedLines(); - expect(lines.some((l) => l.includes('junction'))).toBe(true); - }); - }); }); describe('ensureHostFile', () => { diff --git a/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts b/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts index fc7c2cd7a08..8766e1df95d 100644 --- a/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts +++ b/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts @@ -5,7 +5,6 @@ import { ProjectDirectoryPathKey, appKindSetting, - artifactsDirectory, connectionsFileName, designTimeDirectoryName, extensionBundleId, @@ -270,10 +269,6 @@ export async function ensureDesignTimeFiles( changedArtifacts.push(`${designTimeArtifactPrefix}${localSettingsFileName}`); } - const artifactsTarget = path.join(projectPath, artifactsDirectory); - const artifactsLink = path.join(designTimeDirectory.fsPath, artifactsDirectory); - await ensureArtifactsSymlink(artifactsLink, artifactsTarget); - return { changedArtifacts }; } @@ -427,26 +422,7 @@ async function readFileTextSafe(filePath: string): Promise { return ''; } -async function ensureArtifactsSymlink(artifactsLink: string, artifactsTarget: string): Promise { - if (await fse.pathExists(artifactsTarget)) { - try { - const linkStat = await fse.lstat(artifactsLink).catch(() => null); - if (linkStat && linkStat.isSymbolicLink()) { - const currentTarget = await fse.readlink(artifactsLink); - if (!arePathsEqual(currentTarget, artifactsTarget)) { - await fse.remove(artifactsLink); - await fse.symlink(artifactsTarget, artifactsLink, 'junction'); - } - } else if (!linkStat) { - await fse.symlink(artifactsTarget, artifactsLink, 'junction'); - } - } catch { - ext.outputChannel.appendLog(localize('artifactsJunctionFailed', 'Failed to create Artifacts junction in design-time directory.')); - } - } -} - -function arePathsEqual(path1?: string, path2?: string): boolean { +function arePathsEqual(path1: unknown, path2: unknown): boolean { if (typeof path1 !== 'string' || typeof path2 !== 'string' || !path1 || !path2) { return false; } From f69a0b171416e39b70f9187590d4887063bcef44 Mon Sep 17 00:00:00 2001 From: andrew-eldridge Date: Wed, 12 Aug 2026 11:21:03 -0400 Subject: [PATCH 2/2] fix function signature --- .../src/app/projectConsistency/projectFilesConsistency.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts b/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts index 8766e1df95d..2f8e857aeb0 100644 --- a/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts +++ b/apps/vs-code-designer/src/app/projectConsistency/projectFilesConsistency.ts @@ -422,7 +422,7 @@ async function readFileTextSafe(filePath: string): Promise { return ''; } -function arePathsEqual(path1: unknown, path2: unknown): boolean { +function arePathsEqual(path1?: string, path2?: string): boolean { if (typeof path1 !== 'string' || typeof path2 !== 'string' || !path1 || !path2) { return false; }