Skip to content

Commit bcb516f

Browse files
committed
Use async filesystem operations
1 parent 7088500 commit bcb516f

3 files changed

Lines changed: 45 additions & 60 deletions

File tree

src/ConfigOfExecGroup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ export class ConfigOfExecGroup implements vscode.Disposable {
289289
relativeToWsPosix: string;
290290
}> {
291291
path = await this._resolveVariables(path, false, moreVarsToResolve);
292-
path = c2fs.resolveSymlinksInPattern(path);
292+
path = await c2fs.resolveSymlinksInPattern(path, this._shared.log);
293293

294294
const normPattern = path.replace(/\\/g, '/');
295295
const isAbsolute = pathlib.posix.isAbsolute(normPattern) || pathlib.win32.isAbsolute(normPattern);

src/util/FSWrapper.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as fs from 'fs';
22
import * as path from 'path';
33
import { promisify } from 'util';
44
import * as cp from 'child_process';
5+
import { Logger } from '../Logger';
56

67
///
78

@@ -80,14 +81,15 @@ export function getLastModiTime(filePath: string): Promise<number> {
8081
* in file patterns without losing the globbing functionality.
8182
*
8283
* @param pattern - A file path or glob pattern that may contain symlinks
84+
* @param logger - Logger to log errors during symlink resolution
8385
* @returns The pattern with symlinks resolved
8486
*
8587
* @example
8688
* // If 'build-out' is a symlink to '/tmp/build-cache'
87-
* resolveSymlinksInPattern('/src/build-out/**\/*test*')
89+
* await resolveSymlinksInPattern('/src/build-out/**\/*test*', logger)
8890
* // Returns: '/tmp/build-cache/**\/*test*'
8991
*/
90-
export function resolveSymlinksInPattern(pattern: string): string {
92+
export async function resolveSymlinksInPattern(pattern: string, logger: Logger): Promise<string> {
9193
if (!pattern) return pattern;
9294

9395
const isAbsolute = path.isAbsolute(pattern);
@@ -104,30 +106,26 @@ export function resolveSymlinksInPattern(pattern: string): string {
104106

105107
const hasGlob = /[*?[\]{}!]/.test(segment);
106108
if (hasGlob) {
107-
// Stop resolution at first glob
108109
resolvedSegments.push(...segments.slice(i));
109110
break;
110111
}
111112

112113
currentPath = path.join(currentPath, segment);
113114
try {
114-
const stats = fs.lstatSync(currentPath);
115+
const stats = await promisify(fs.lstat)(currentPath);
115116
if (stats.isSymbolicLink()) {
116-
// Resolve the symlink
117-
const realPath = fs.realpathSync(currentPath);
117+
const realPath = await promisify(fs.realpath)(currentPath);
118118
currentPath = realPath;
119119

120120
// Update resolved segments with the real path components
121121
const realSegments = realPath.split(path.sep).filter(s => s.length > 0);
122122
resolvedSegments.length = 0; // Clear previous segments
123123
resolvedSegments.push(...realSegments);
124124
} else {
125-
// Not a symlink, just add the segment
126125
resolvedSegments.push(segment);
127126
}
128-
} catch (_err) {
129-
// Path doesn't exist yet or we can't access it
130-
// We can't resolve non-existing paths, so return as-is
127+
} catch (err) {
128+
logger.error('Error resolving symlink:', currentPath, err);
131129
resolvedSegments.push(segment);
132130
}
133131
}

test/FSWrapper.symlinks.test.ts

Lines changed: 36 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import * as fse from 'fs-extra';
44

55
import { resolveSymlinksInPattern } from '../src/util/FSWrapper';
66
import { settings, isWin } from './Common';
7+
import { Logger } from '../src/Logger';
78

89
describe(path.basename(__filename), function () {
910
let tempDir: string;
11+
let logger: Logger;
1012

1113
beforeEach(async function () {
1214
// Create temporary directory for symlink tests
1315
tempDir = path.join(settings.workspaceFolderUri.fsPath, '.test-symlinks');
1416
await fse.ensureDir(tempDir);
17+
logger = new Logger();
1518
});
1619

1720
afterEach(async function () {
@@ -33,10 +36,9 @@ describe(path.basename(__filename), function () {
3336
await fse.symlink(realDir, symlinkDir);
3437

3538
const pattern = path.join(symlinkDir, '**', '*test*');
36-
const resolved = resolveSymlinksInPattern(pattern);
39+
const result = await resolveSymlinksInPattern(pattern, logger);
3740

38-
assert.strictEqual(resolved, path.join(realDir, '**', '*test*'));
39-
});
41+
assert.strictEqual(result, path.join(realDir, '**', '*test*')); });
4042

4143
it('should resolve nested symlinks', async function () {
4244
if (isWin) {
@@ -52,11 +54,10 @@ describe(path.basename(__filename), function () {
5254
await fse.symlink(intermediateSymlink, finalSymlink);
5355

5456
const pattern = path.join(finalSymlink, '**', '*test*');
55-
const resolved = resolveSymlinksInPattern(pattern);
57+
const result = await resolveSymlinksInPattern(pattern, logger);
5658

5759
// Should resolve to the ultimate real path
58-
assert.strictEqual(resolved, path.join(realDir, '**', '*test*'));
59-
});
60+
assert.strictEqual(result, path.join(realDir, '**', '*test*')); });
6061

6162
it('should resolve symlink at the root of pattern', async function () {
6263
if (isWin) {
@@ -70,10 +71,9 @@ describe(path.basename(__filename), function () {
7071
await fse.symlink(realDir, symlinkDir);
7172

7273
const pattern = path.join(symlinkDir, '**', '*test*');
73-
const resolved = resolveSymlinksInPattern(pattern);
74+
const result = await resolveSymlinksInPattern(pattern, logger);
7475

75-
assert.strictEqual(resolved, path.join(realDir, '**', '*test*'));
76-
});
76+
assert.strictEqual(result, path.join(realDir, '**', '*test*')); });
7777

7878
it('should resolve symlink in the middle of path', async function () {
7979
if (isWin) {
@@ -88,10 +88,9 @@ describe(path.basename(__filename), function () {
8888
await fse.symlink(realDir, symlinkDir);
8989

9090
const pattern = path.join(subDir, '**', '*test*');
91-
const resolved = resolveSymlinksInPattern(pattern);
91+
const result = await resolveSymlinksInPattern(pattern, logger);
9292

93-
assert.strictEqual(resolved, path.join(realDir, 'subdirectory', '**', '*test*'));
94-
});
93+
assert.strictEqual(result, path.join(realDir, 'subdirectory', '**', '*test*')); });
9594
});
9695

9796
describe('glob pattern preservation', function () {
@@ -107,10 +106,9 @@ describe(path.basename(__filename), function () {
107106
await fse.symlink(realDir, symlink);
108107

109108
const pattern = path.join(symlink, '**', '*');
110-
const resolved = resolveSymlinksInPattern(pattern);
109+
const result = await resolveSymlinksInPattern(pattern, logger);
111110

112-
assert.strictEqual(resolved, path.join(realDir, '**', '*'));
113-
});
111+
assert.strictEqual(result, path.join(realDir, '**', '*')); });
114112

115113
it('should preserve * wildcards', async function () {
116114
if (isWin) {
@@ -124,10 +122,9 @@ describe(path.basename(__filename), function () {
124122
await fse.symlink(realDir, symlink);
125123

126124
const pattern = path.join(symlink, '*test*');
127-
const resolved = resolveSymlinksInPattern(pattern);
125+
const result = await resolveSymlinksInPattern(pattern, logger);
128126

129-
assert.strictEqual(resolved, path.join(realDir, '*test*'));
130-
});
127+
assert.strictEqual(result, path.join(realDir, '*test*')); });
131128

132129
it('should preserve ? wildcards', async function () {
133130
if (isWin) {
@@ -141,10 +138,9 @@ describe(path.basename(__filename), function () {
141138
await fse.symlink(realDir, symlink);
142139

143140
const pattern = path.join(symlink, 'test??.exe');
144-
const resolved = resolveSymlinksInPattern(pattern);
141+
const result = await resolveSymlinksInPattern(pattern, logger);
145142

146-
assert.strictEqual(resolved, path.join(realDir, 'test??.exe'));
147-
});
143+
assert.strictEqual(result, path.join(realDir, 'test??.exe')); });
148144

149145
it('should preserve [] character ranges', async function () {
150146
if (isWin) {
@@ -158,10 +154,9 @@ describe(path.basename(__filename), function () {
158154
await fse.symlink(realDir, symlink);
159155

160156
const pattern = path.join(symlink, 'test[0-9].exe');
161-
const resolved = resolveSymlinksInPattern(pattern);
157+
const result = await resolveSymlinksInPattern(pattern, logger);
162158

163-
assert.strictEqual(resolved, path.join(realDir, 'test[0-9].exe'));
164-
});
159+
assert.strictEqual(result, path.join(realDir, 'test[0-9].exe')); });
165160

166161
it('should preserve {} brace expansions', async function () {
167162
if (isWin) {
@@ -175,10 +170,9 @@ describe(path.basename(__filename), function () {
175170
await fse.symlink(realDir, symlink);
176171

177172
const pattern = path.join(symlink, '{test,Test,TEST}*');
178-
const resolved = resolveSymlinksInPattern(pattern);
173+
const result = await resolveSymlinksInPattern(pattern, logger);
179174

180-
assert.strictEqual(resolved, path.join(realDir, '{test,Test,TEST}*'));
181-
});
175+
assert.strictEqual(result, path.join(realDir, '{test,Test,TEST}*')); });
182176

183177
it('should preserve complex glob patterns', async function () {
184178
if (isWin) {
@@ -192,38 +186,33 @@ describe(path.basename(__filename), function () {
192186
await fse.symlink(realDir, symlink);
193187

194188
const pattern = path.join(symlink, '{build,Build,BUILD}', '**', '*{test,Test,TEST}*');
195-
const resolved = resolveSymlinksInPattern(pattern);
189+
const result = await resolveSymlinksInPattern(pattern, logger);
196190

197-
assert.strictEqual(resolved, path.join(realDir, '{build,Build,BUILD}', '**', '*{test,Test,TEST}*'));
198-
});
191+
assert.strictEqual(result, path.join(realDir, '{build,Build,BUILD}', '**', '*{test,Test,TEST}*')); });
199192

200193
it('should return pattern unchanged when no symlinks exist', async function () {
201194
const regularDir = path.join(tempDir, 'regular-dir');
202195
await fse.ensureDir(regularDir);
203196

204197
const pattern = path.join(regularDir, '**', '*test*');
205-
const resolved = resolveSymlinksInPattern(pattern);
198+
const result = await resolveSymlinksInPattern(pattern, logger);
206199

207-
assert.strictEqual(resolved, pattern);
208-
});
200+
assert.strictEqual(result, pattern); });
209201

210202
it('should handle non-existent paths gracefully', async function () {
211203
const nonExistentPath = path.join(tempDir, 'does-not-exist', '**', '*test*');
212-
const resolved = resolveSymlinksInPattern(nonExistentPath);
204+
const result = await resolveSymlinksInPattern(nonExistentPath, logger);
213205

214-
assert.strictEqual(resolved, nonExistentPath);
215-
});
206+
assert.strictEqual(result, nonExistentPath); });
216207

217208
it('should handle empty pattern', async function () {
218-
const resolved = resolveSymlinksInPattern('');
219-
assert.strictEqual(resolved, '');
220-
});
209+
const result = await resolveSymlinksInPattern('', logger);
210+
assert.strictEqual(result, ''); });
221211

222212
it('should handle pattern with only glob characters', async function () {
223213
const pattern = '**/*test*';
224-
const resolved = resolveSymlinksInPattern(pattern);
225-
assert.strictEqual(resolved, pattern);
226-
});
214+
const result = await resolveSymlinksInPattern(pattern, logger);
215+
assert.strictEqual(result, pattern); });
227216

228217
it('should return absolute paths', async function () {
229218
if (isWin) {
@@ -237,11 +226,10 @@ describe(path.basename(__filename), function () {
237226
await fse.symlink(realDir, symlinkDir);
238227

239228
const pattern = path.join(symlinkDir, '**', '*');
240-
const resolved = resolveSymlinksInPattern(pattern);
229+
const result = await resolveSymlinksInPattern(pattern, logger);
241230

242-
assert.ok(path.isAbsolute(resolved), 'Resolved path should be absolute');
243-
assert.strictEqual(resolved, path.join(realDir, '**', '*'));
244-
});
231+
assert.ok(path.isAbsolute(result), 'Resolved path should be absolute');
232+
assert.strictEqual(result, path.join(realDir, '**', '*')); });
245233

246234
it('should stop resolving at first glob pattern', async function () {
247235
if (isWin) {
@@ -256,10 +244,9 @@ describe(path.basename(__filename), function () {
256244

257245
// Pattern has glob early, then more path components
258246
const pattern = path.join(symlink, '*', 'subdir', 'file.txt');
259-
const resolved = resolveSymlinksInPattern(pattern);
247+
const result = await resolveSymlinksInPattern(pattern, logger);
260248

261249
// Should resolve up to the symlink, then keep the rest as-is
262-
assert.strictEqual(resolved, path.join(realDir, '*', 'subdir', 'file.txt'));
263-
});
250+
assert.strictEqual(result, path.join(realDir, '*', 'subdir', 'file.txt')); });
264251
});
265252
});

0 commit comments

Comments
 (0)