Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Made the default home page configurable with `DEFAULT_HOME_VIEW_PAGE`, defaulting to Code Search and supporting Ask. [#1677](https://github.com/sourcebot-dev/sourcebot/pull/1677)
- Require authentication for the streaming and blocking Ask APIs in Public SaaS deployments. [#1679](https://github.com/sourcebot-dev/sourcebot/pull/1679)
- Fixed local repositories whose path contains spaces or non-ASCII characters resolving to a percent-encoded path that could not be indexed. [#1690](https://github.com/sourcebot-dev/sourcebot/pull/1690)

## [5.1.14] - 2026-09-17

Expand Down
32 changes: 31 additions & 1 deletion packages/shared/src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { readFile } from 'fs/promises';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { DEFAULT_CONFIG_SETTINGS } from './constants.js';
import { getConfigSettings, resolveConfigSettings } from './utils.js';
import type { Repo } from '@sourcebot/db';
import { getConfigSettings, getRepoPath, resolveConfigSettings } from './utils.js';

// Mock fs/promises so loadConfig doesn't hit the filesystem.
// The config schema has no required fields, so '{}' is valid.
Expand Down Expand Up @@ -110,3 +111,32 @@ describe('resolveConfigSettings', () => {
);
});
});

describe('getRepoPath', () => {
const localRepo = (cloneUrl: string) => ({
id: 1,
external_codeHostType: 'genericGitHost',
cloneUrl,
}) as unknown as Repo;

test('returns the on-disk path of a local repository', () => {
expect(getRepoPath(localRepo('file:///repos/project'))).toEqual({
path: '/repos/project',
isReadOnly: true,
});
});

test('returns the on-disk path of a local repository whose path contains spaces', () => {
expect(getRepoPath(localRepo('file:///Users/me/Code Projects/my repo'))).toEqual({
path: '/Users/me/Code Projects/my repo',
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
isReadOnly: true,
});
});

test('returns the on-disk path of a local repository whose path contains percent-encodable characters', () => {
expect(getRepoPath(localRepo('file:///repos/caf\u00e9/[legacy]'))).toEqual({
path: '/repos/caf\u00e9/[legacy]',
isReadOnly: true,
});
});
});
5 changes: 4 additions & 1 deletion packages/shared/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ConfigSettings } from "./types.js";
import { Org, Repo } from "@sourcebot/db";
import type { SourcebotConfig } from "@sourcebot/schemas/v3/index.type";
import path from "path";
import { fileURLToPath } from "url";
import { env, isRemotePath, loadConfig } from "./env.server.js";

// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem
Expand Down Expand Up @@ -109,7 +110,9 @@ export const getRepoPath = (repo: Repo): { path: string, isReadOnly: boolean } =
const cloneUrl = new URL(repo.cloneUrl);
if (repo.external_codeHostType === 'genericGitHost' && cloneUrl.protocol === 'file:') {
return {
path: cloneUrl.pathname,
// @note: URL.pathname is percent-encoded (e.g., spaces become %20),
// so decode it back into the on-disk path.
path: fileURLToPath(cloneUrl),
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
isReadOnly: true,
}
}
Expand Down