authorgravatar for nmharmon8@gmail.comNathan Harmon <nmharmon8@gmail.com> 2026-04-16 22:19:14-06:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-04-16 21:19:14-07:00
loged6b7fbf27973c8a5180e7074ba448d912c3cce4
tree4f3d35b7ec6546b80f832a965fb87f33792033fb
parentb991b9b9e635a55facda987c74957fab932e842e
signature Signed by PGP key B5690EEEBB952194

fix(server): honor gitignored files in workspace search (#2078)

Co-authored-by: Julius Marminge <julius0216@outlook.com>

2 files changed, 88 insertions(+), 10 deletions(-)

apps/server/src/server.test.ts+76-5
......@@ -370,9 +370,32 @@ const buildAppUnderTest = (options?: {
370370 ...options?.config,
371371 };
372372 const layerConfig = Layer.succeed(ServerConfig, config);
373 const gitCoreLayer = Layer.mock(GitCore)({
374 isInsideWorkTree: () => Effect.succeed(false),
375 listWorkspaceFiles: () =>
376 Effect.succeed({
377 paths: [],
378 truncated: false,
379 }),
380 filterIgnoredPaths: (_cwd, relativePaths) => Effect.succeed(relativePaths),
381 ...options?.layers?.gitCore,
382 });
373383 const gitManagerLayer = Layer.mock(GitManager)({
374384 ...options?.layers?.gitManager,
375385 });
386 const workspaceEntriesLayer = WorkspaceEntriesLive.pipe(
387 Layer.provide(WorkspacePathsLive),
388 Layer.provideMerge(gitCoreLayer),
389 );
390 const workspaceAndProjectServicesLayer = Layer.mergeAll(
391 WorkspacePathsLive,
392 workspaceEntriesLayer,
393 WorkspaceFileSystemLive.pipe(
394 Layer.provide(WorkspacePathsLive),
395 Layer.provide(workspaceEntriesLayer),
396 ),
397 ProjectFaviconResolverLive,
398 );
376399 const gitStatusBroadcasterLayer = options?.layers?.gitStatusBroadcaster
377400 ? Layer.mock(GitStatusBroadcaster)({
378401 ...options.layers.gitStatusBroadcaster,
......@@ -416,11 +439,7 @@ const buildAppUnderTest = (options?: {
416439 ...options?.layers?.open,
417440 }),
418441 ),
419 Layer.provide(
420 Layer.mock(GitCore)({
421 ...options?.layers?.gitCore,
422 }),
423 ),
442 Layer.provide(gitCoreLayer),
424443 Layer.provide(gitManagerLayer),
425444 Layer.provideMerge(gitStatusBroadcasterLayer),
426445 Layer.provide(
......@@ -2017,6 +2036,58 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
20172036 }).pipe(Effect.provide(NodeHttpServer.layerTest)),
20182037 );
20192038
2039 it.effect("routes websocket rpc projects.searchEntries excludes gitignored files", () =>
2040 Effect.gen(function* () {
2041 const fs = yield* FileSystem.FileSystem;
2042 const path = yield* Path.Path;
2043 const workspaceDir = yield* fs.makeTempDirectoryScoped({
2044 prefix: "t3-ws-project-search-gitignored-",
2045 });
2046 yield* fs.writeFileString(path.join(workspaceDir, ".gitignore"), ".venv/\n");
2047 yield* fs.makeDirectory(path.join(workspaceDir, ".venv", "lib"), { recursive: true });
2048 yield* fs.writeFileString(
2049 path.join(workspaceDir, ".venv", "lib", "ignored-search-target.ts"),
2050 "export const ignored = true;",
2051 );
2052 yield* fs.makeDirectory(path.join(workspaceDir, "src"), { recursive: true });
2053 yield* fs.writeFileString(
2054 path.join(workspaceDir, "src", "tracked.ts"),
2055 "export const ok = 1;",
2056 );
2057
2058 yield* buildAppUnderTest({
2059 layers: {
2060 gitCore: {
2061 isInsideWorkTree: () => Effect.succeed(true),
2062 listWorkspaceFiles: () =>
2063 Effect.succeed({
2064 paths: ["src/tracked.ts"],
2065 truncated: false,
2066 }),
2067 filterIgnoredPaths: (_cwd, relativePaths) =>
2068 Effect.succeed(
2069 relativePaths.filter((relativePath) => !relativePath.startsWith(".venv/")),
2070 ),
2071 },
2072 },
2073 });
2074
2075 const wsUrl = yield* getWsServerUrl("/ws");
2076 const response = yield* Effect.scoped(
2077 withWsRpcClient(wsUrl, (client) =>
2078 client[WS_METHODS.projectsSearchEntries]({
2079 cwd: workspaceDir,
2080 query: "ignored-search-target",
2081 limit: 10,
2082 }),
2083 ),
2084 );
2085
2086 assert.equal(response.entries.length, 0);
2087 assert.equal(response.truncated, false);
2088 }).pipe(Effect.provide(NodeHttpServer.layerTest)),
2089 );
2090
20202091 it.effect("routes websocket rpc projects.searchEntries errors", () =>
20212092 Effect.gen(function* () {
20222093 yield* buildAppUnderTest();
apps/server/src/server.ts+12-5
......@@ -185,13 +185,20 @@ const GitLayerLive = Layer.empty.pipe(
185185
186186const TerminalLayerLive = TerminalManagerLive.pipe(Layer.provide(PtyAdapterLive));
187187
188const WorkspaceEntriesLayerLive = WorkspaceEntriesLive.pipe(
189 Layer.provide(WorkspacePathsLive),
190 Layer.provideMerge(GitCoreLive),
191);
192
193const WorkspaceFileSystemLayerLive = WorkspaceFileSystemLive.pipe(
194 Layer.provide(WorkspacePathsLive),
195 Layer.provide(WorkspaceEntriesLayerLive),
196);
197
188198const WorkspaceLayerLive = Layer.mergeAll(
189199 WorkspacePathsLive,
190 WorkspaceEntriesLive.pipe(Layer.provide(WorkspacePathsLive)),
191 WorkspaceFileSystemLive.pipe(
192 Layer.provide(WorkspacePathsLive),
193 Layer.provide(WorkspaceEntriesLive.pipe(Layer.provide(WorkspacePathsLive))),
194 ),
200 WorkspaceEntriesLayerLive,
201 WorkspaceFileSystemLayerLive,
195202);
196203
197204const AuthLayerLive = ServerAuthLive.pipe(