From 290e28ee75db7278db01c8bbc3177f02619c5e6c Mon Sep 17 00:00:00 2001
From: Andrew Berezovskyi
Date: Sun, 18 May 2025 19:40:00 +0200
Subject: [PATCH 001/103] Update MCP readme for VSCode (#309)
The env values are nested incorrectly.
---
docs/docs/more/mcp-server.mdx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/docs/more/mcp-server.mdx b/docs/docs/more/mcp-server.mdx
index d992f7de..8521e06d 100644
--- a/docs/docs/more/mcp-server.mdx
+++ b/docs/docs/more/mcp-server.mdx
@@ -84,10 +84,10 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)
"sourcebot": {
"type": "stdio",
"command": "npx",
- "args": ["-y", "@sourcebot/mcp@latest"]
- },
- "env": {
- "SOURCEBOT_HOST": "http://localhost:3000"
+ "args": ["-y", "@sourcebot/mcp@latest"],
+ "env": {
+ "SOURCEBOT_HOST": "http://localhost:3000"
+ }
}
}
}
From b71a28b39207ebe46f8bef7f85673746f8f575a6 Mon Sep 17 00:00:00 2001
From: msukkari
Date: Thu, 22 May 2025 13:36:20 -0700
Subject: [PATCH 002/103] fix repo failure UI extending past bounds
---
.../[domain]/components/errorNavIndicator.tsx | 74 +++++++++++--------
.../components/warningNavIndicator.tsx | 30 +++++---
2 files changed, 65 insertions(+), 39 deletions(-)
diff --git a/packages/web/src/app/[domain]/components/errorNavIndicator.tsx b/packages/web/src/app/[domain]/components/errorNavIndicator.tsx
index a63a469d..2f024a61 100644
--- a/packages/web/src/app/[domain]/components/errorNavIndicator.tsx
+++ b/packages/web/src/app/[domain]/components/errorNavIndicator.tsx
@@ -11,6 +11,7 @@ import { useQuery } from "@tanstack/react-query";
import { ConnectionSyncStatus, RepoIndexingStatus } from "@sourcebot/db";
import { getConnections } from "@/actions";
import { getRepos } from "@/actions";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
export const ErrorNavIndicator = () => {
const domain = useDomain();
@@ -62,18 +63,27 @@ export const ErrorNavIndicator = () => {
The following connections have failed to sync:
- {connections
- .slice(0, 10)
- .map(connection => (
-
captureEvent('wa_error_nav_job_pressed', {})}>
-
- {connection.name}
-
-
- ))}
+
+ {connections
+ .slice(0, 10)
+ .map(connection => (
+ captureEvent('wa_error_nav_job_pressed', {})}>
+
+
+
+ {connection.name}
+
+
+ {connection.name}
+
+
+
+
+ ))}
+
{connections.length > 10 && (
And {connections.length - 10} more...
@@ -93,23 +103,29 @@ export const ErrorNavIndicator = () => {
The following repositories failed to index:
- {repos
- .slice(0, 10)
- .filter(item => item.linkedConnections.length > 0) // edge case: don't show repos that are orphaned and awaiting gc.
- .map(repo => (
- // Link to the first connection for the repo
-
captureEvent('wa_error_nav_job_pressed', {})}>
-
-
- {repo.repoName}
-
-
-
- ))}
+
+ {repos
+ .slice(0, 10)
+ .filter(item => item.linkedConnections.length > 0) // edge case: don't show repos that are orphaned and awaiting gc.
+ .map(repo => (
+ // Link to the first connection for the repo
+ captureEvent('wa_error_nav_job_pressed', {})}>
+
+
+
+ {repo.repoName}
+
+
+ {repo.repoName}
+
+
+
+
+ ))}
+
{repos.length > 10 && (
And {repos.length - 10} more...
diff --git a/packages/web/src/app/[domain]/components/warningNavIndicator.tsx b/packages/web/src/app/[domain]/components/warningNavIndicator.tsx
index 496c8f0a..dc176b1d 100644
--- a/packages/web/src/app/[domain]/components/warningNavIndicator.tsx
+++ b/packages/web/src/app/[domain]/components/warningNavIndicator.tsx
@@ -10,6 +10,7 @@ import useCaptureEvent from "@/hooks/useCaptureEvent";
import { env } from "@/env.mjs";
import { useQuery } from "@tanstack/react-query";
import { ConnectionSyncStatus } from "@prisma/client";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
export const WarningNavIndicator = () => {
const domain = useDomain();
@@ -45,16 +46,25 @@ export const WarningNavIndicator = () => {
The following connections have references that could not be found:
- {connections.slice(0, 10).map(connection => (
-
captureEvent('wa_warning_nav_connection_pressed', {})}>
-
- {connection.name}
-
-
- ))}
+
+ {connections.slice(0, 10).map(connection => (
+ captureEvent('wa_warning_nav_connection_pressed', {})}>
+
+
+
+ {connection.name}
+
+
+ {connection.name}
+
+
+
+
+ ))}
+
{connections.length > 10 && (
And {connections.length - 10} more...
From 536bf8aa7962484ff1776aacb678f18275647d76 Mon Sep 17 00:00:00 2001
From: msukkari
Date: Sat, 24 May 2025 11:54:51 -0700
Subject: [PATCH 003/103] improve platform support ui in docs
---
docs/docs/connections/overview.mdx | 13 +++----------
docs/self-hosting/overview.mdx | 13 +++----------
docs/snippets/platform-support.mdx | 10 ++++++++++
3 files changed, 16 insertions(+), 20 deletions(-)
create mode 100644 docs/snippets/platform-support.mdx
diff --git a/docs/docs/connections/overview.mdx b/docs/docs/connections/overview.mdx
index b259da28..a237aa67 100644
--- a/docs/docs/connections/overview.mdx
+++ b/docs/docs/connections/overview.mdx
@@ -3,6 +3,8 @@ title: Overview
sidebarTitle: Overview
---
+import SupportedPlatforms from '/snippets/platform-support.mdx'
+
To connect your code to Sourcebot you create **connections**. A **connection** is a configuration object that describes how Sourcebot should fetch information from a supported code host.
There are two ways to define connections:
@@ -23,15 +25,6 @@ There are two ways to define connections:
### Supported code hosts
-
-
-
-
-
-
-
-
-
-
+
Missing your code host? [Submit a feature request on GitHub](https://github.com/sourcebot-dev/sourcebot/discussions/categories/ideas).
\ No newline at end of file
diff --git a/docs/self-hosting/overview.mdx b/docs/self-hosting/overview.mdx
index e6939789..28189b9f 100644
--- a/docs/self-hosting/overview.mdx
+++ b/docs/self-hosting/overview.mdx
@@ -3,6 +3,8 @@ title: Self-host Sourcebot
sidebarTitle: Overview
---
+import SupportedPlatforms from '/snippets/platform-support.mdx'
+
Want a managed solution? Checkout [Sourcebot Cloud](/docs/getting-started).
Sourcebot is open source and can be self-hosted using our official [Docker image](https://github.com/sourcebot-dev/sourcebot/pkgs/container/sourcebot).
@@ -75,16 +77,7 @@ Sourcebot is open source and can be self-hosted using our official [Docker image
Sourcebot supports indexing public & private code on the following code hosts:
-
-
-
-
-
-
-
-
-
-
+
Missing your code host? [Submit a feature request on GitHub](https://github.com/sourcebot-dev/sourcebot/discussions/categories/ideas).
diff --git a/docs/snippets/platform-support.mdx b/docs/snippets/platform-support.mdx
new file mode 100644
index 00000000..38266b2d
--- /dev/null
+++ b/docs/snippets/platform-support.mdx
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 60a3528394aa02e5a3fbb4764bb828c7cfd01394 Mon Sep 17 00:00:00 2001
From: Michael Sukkarieh
Date: Wed, 28 May 2025 16:08:42 -0700
Subject: [PATCH 004/103] V4 (#311)
Sourcebot V4 introduces authentication, performance improvements and code navigation. Checkout the [migration guide](https://docs.sourcebot.dev/self-hosting/upgrade/v3-to-v4-guide) for information on upgrading your instance to v4.
### Changed
- [**Breaking Change**] Authentication is now required by default. Notes:
- When setting up your instance, email / password login will be the default authentication provider.
- The first user that logs into the instance is given the `owner` role. ([docs](https://docs.sourcebot.dev/docs/more/roles-and-permissions)).
- Subsequent users can request to join the instance. The `owner` can approve / deny requests to join the instance via `Settings` > `Members` > `Pending Requests`.
- If a user is approved to join the instance, they are given the `member` role.
- Additional login providers, including email links and SSO, can be configured with additional environment variables. ([docs](https://docs.sourcebot.dev/self-hosting/configuration/authentication)).
- Clicking on a search result now takes you to the `/browse` view. Files can still be previewed by clicking the "Preview" button or holding `Cmd` / `Ctrl` when clicking on a search result. [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
### Added
- [Sourcebot EE] Added search-based code navigation, allowing you to jump between symbol definition and references when viewing source files. [Read the documentation](https://docs.sourcebot.dev/docs/search/code-navigation). [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
- Added collapsible filter panel. [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
### Fixed
- Improved scroll performance for large numbers of search results. [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
---
.cursor/rules/style.mdc | 7 +
.env.development | 8 +-
CHANGELOG.md | 23 +-
docs/docs.json | 14 +-
docs/docs/agents/review-agent.mdx | 2 +
docs/docs/connections/gitea.mdx | 4 +-
docs/docs/connections/github.mdx | 4 +-
docs/docs/connections/gitlab.mdx | 4 +-
docs/docs/connections/overview.mdx | 4 +-
docs/docs/more/api-keys.mdx | 8 +
docs/docs/more/mcp-server.mdx | 3 +-
docs/docs/more/roles-and-permissions.mdx | 3 +-
docs/docs/search/code-navigation.mdx | 44 +
docs/docs/search/search-contexts.mdx | 6 +-
docs/images/api_key.png | Bin 0 -> 143280 bytes
docs/images/demo.mp4 | Bin 10251261 -> 0 bytes
docs/images/join_request_email.png | Bin 0 -> 152715 bytes
docs/images/login.png | Bin 139844 -> 120957 bytes
docs/images/login_basic.png | Bin 0 -> 92533 bytes
docs/images/pending_approval.png | Bin 0 -> 79432 bytes
docs/self-hosting/configuration.mdx | 59 --
.../configuration/authentication.mdx | 118 +++
.../declarative-config.mdx | 4 -
.../configuration/environment-variables.mdx | 64 ++
.../{more => configuration}/tenancy.mdx | 2 +-
.../transactional-emails.mdx | 3 +-
docs/self-hosting/license-key.mdx | 2 +-
docs/self-hosting/more/authentication.mdx | 63 --
docs/self-hosting/overview.mdx | 12 +-
docs/self-hosting/upgrade/v3-to-v4-guide.mdx | 61 ++
docs/snippets/bitbucket-app-password.mdx | 4 +-
docs/snippets/bitbucket-token.mdx | 4 +-
docs/snippets/schemas/v3/index.schema.mdx | 10 +
packages/backend/src/constants.ts | 1 +
packages/crypto/src/index.ts | 22 +
.../migration.sql | 2 +
.../migration.sql | 2 +
.../migration.sql | 21 +
.../migration.sql | 2 +
.../20250523175553_add_api_key/migration.sql | 20 +
packages/db/prisma/schema.prisma | 54 +-
packages/mcp/CHANGELOG.md | 3 +
packages/mcp/src/client.ts | 9 +-
packages/mcp/src/env.ts | 2 +
packages/mcp/src/index.ts | 5 +-
packages/schemas/src/v3/index.schema.ts | 10 +
packages/schemas/src/v3/index.type.ts | 4 +
packages/web/.eslintrc.json | 3 +-
packages/web/package.json | 5 +
packages/web/src/actions.ts | 853 ++++++++++++++----
.../web/src/app/[domain]/browse/README.md | 12 +
.../[domain]/browse/[...path]/codePreview.tsx | 150 ---
.../[...path]/components/codePreviewPanel.tsx | 226 +++++
.../components/rangeHighlightingExtension.ts | 39 +
.../app/[domain]/browse/[...path]/page.tsx | 142 ++-
.../[domain]/browse/browseStateProvider.tsx | 73 ++
.../browse/components/bottomPanel.tsx | 136 +++
.../browse/hooks/useBrowseNavigation.ts | 59 ++
.../[domain]/browse/hooks/useBrowseState.ts | 12 +
.../web/src/app/[domain]/browse/layout.tsx | 26 +
.../secretCombobox.tsx | 2 +-
.../[domain]/components/editorContextMenu.tsx | 3 +-
.../app/[domain]/components/fileHeader.tsx | 30 +-
.../components/keyboardShortcutHint.tsx | 16 -
.../components/lightweightCodeHighlighter.tsx | 276 ++++++
.../[domain]/components/navigationMenu.tsx | 59 +-
.../[domain]/components/pendingApproval.tsx | 60 ++
.../components/repositorySnapshot.tsx | 34 +-
.../resubmitAccountRequestButton.tsx | 64 ++
.../components/searchBar/searchBar.tsx | 20 +-
.../searchBar/searchSuggestionsBox.tsx | 2 +-
.../searchBar/useSuggestionsData.ts | 8 +-
.../[domain]/components/settingsDropdown.tsx | 18 +-
.../src/app/[domain]/components/topBar.tsx | 1 -
.../[domain]/components/whatsNewIndicator.tsx | 179 ++++
.../connections/[id]/components/repoList.tsx | 2 +-
.../app/[domain]/connections/[id]/page.tsx | 8 +-
.../components/newConnectionCard.tsx | 30 +-
.../web/src/app/[domain]/connections/page.tsx | 2 +
packages/web/src/app/[domain]/layout.tsx | 24 +-
packages/web/src/app/[domain]/page.tsx | 2 -
.../src/app/[domain]/repos/addRepoButton.tsx | 33 +-
.../web/src/app/[domain]/repos/columns.tsx | 8 +-
packages/web/src/app/[domain]/repos/page.tsx | 7 +-
.../app/[domain]/repos/repositoryTable.tsx | 12 +-
.../codePreviewPanel/codePreview.tsx | 132 ++-
.../components/codePreviewPanel/index.tsx | 79 +-
.../search/components/filterPanel/entry.tsx | 4 +
.../search/components/filterPanel/index.tsx | 110 ++-
.../filterPanel/useFilterMatches.ts | 36 +
.../filterPanel/useGetSelectedFromQuery.ts | 17 +
.../searchResultsPanel/codePreview.tsx | 90 --
.../searchResultsPanel/fileMatch.tsx | 39 +-
.../searchResultsPanel/fileMatchContainer.tsx | 63 +-
.../components/searchResultsPanel/index.tsx | 121 +--
.../lightweightCodeMirror.tsx | 81 --
packages/web/src/app/[domain]/search/page.tsx | 157 ++--
.../app/[domain]/settings/(general)/page.tsx | 1 +
.../app/[domain]/settings/apiKeys/columns.tsx | 160 ++++
.../app/[domain]/settings/apiKeys/page.tsx | 269 ++++++
.../settings/components/sidebar-nav.tsx | 3 +-
.../web/src/app/[domain]/settings/layout.tsx | 49 +-
.../app/[domain]/settings/license/page.tsx | 121 +++
.../members/components/inviteMemberCard.tsx | 28 +-
.../members/components/requestsList.tsx | 236 +++++
.../app/[domain]/settings/members/page.tsx | 96 +-
.../web/src/app/api/(server)/repos/route.ts | 3 +-
.../web/src/app/api/(server)/search/route.ts | 3 +-
.../web/src/app/api/(server)/source/route.ts | 5 +-
.../app/components/keyboardShortcutHint.tsx | 8 +-
packages/web/src/app/globals.css | 265 ++++--
packages/web/src/app/layout.tsx | 4 +-
.../src/app/login/components/loginForm.tsx | 8 +-
packages/web/src/app/login/page.tsx | 2 +
packages/web/src/auth.ts | 102 ++-
.../components/ui/animatedResizableHandle.tsx | 11 +
packages/web/src/components/ui/checkbox.tsx | 30 +
packages/web/src/components/ui/data-table.tsx | 1 +
.../web/src/components/ui/loading-button.tsx | 30 +
packages/web/src/components/ui/switch.tsx | 29 +
.../web/src/components/ui/tab-switcher.tsx | 3 +-
.../web/src/ee/features/billing/actions.ts | 72 +-
.../codeNav/components/exploreMenu/index.tsx | 204 +++++
.../components/exploreMenu/referenceList.tsx | 116 +++
.../components/symbolHoverPopup/index.tsx | 138 +++
.../symbolDefinitionPreview.tsx | 62 ++
.../symbolHoverTargetsExtension.ts | 78 ++
.../useHoveredOverSymbolInfo.ts | 139 +++
.../ee/features/publicAccess/publicAccess.tsx | 139 +++
packages/web/src/ee/sso/sso.tsx | 168 ++++
packages/web/src/emails/constants.ts | 3 +
packages/web/src/emails/inviteUserEmail.tsx | 17 +-
.../src/emails/joinRequestApprovedEmail.tsx | 96 ++
.../src/emails/joinRequestSubmittedEmail.tsx | 141 +++
packages/web/src/emails/magicLinkEmail.tsx | 6 +-
packages/web/src/env.mjs | 27 +-
.../review-agent/nodes/fetchFileContent.ts | 4 +-
packages/web/src/features/codeNav/actions.ts | 119 +++
packages/web/src/features/codeNav/schemas.ts | 20 +
packages/web/src/features/codeNav/types.ts | 4 +
.../src/features/entitlements/constants.ts | 18 +-
.../features/entitlements/planProvider.tsx | 10 +-
.../web/src/features/entitlements/server.ts | 101 ++-
.../entitlements/useHasEntitlement.ts | 8 +-
.../web/src/features/entitlements/usePlan.ts | 7 -
.../web/src/features/search/fileSourceApi.ts | 15 +-
.../web/src/features/search/listReposApi.ts | 11 +-
packages/web/src/features/search/schemas.ts | 4 +
packages/web/src/features/search/searchApi.ts | 202 +++--
packages/web/src/features/search/types.ts | 4 +-
.../web/src/hooks/useCodeMirrorHighlighter.ts | 86 ++
...n.ts => useCodeMirrorLanguageExtension.ts} | 2 +-
packages/web/src/hooks/useCodeMirrorTheme.ts | 204 +++--
packages/web/src/hooks/useTailwind.ts | 13 -
packages/web/src/initialize.ts | 129 ++-
packages/web/src/lib/constants.ts | 6 +-
packages/web/src/lib/errorCodes.ts | 6 +
.../searchResultHighlightExtension.ts | 12 +-
packages/web/src/lib/newsData.ts | 22 +
packages/web/src/lib/posthogEvents.ts | 22 +-
packages/web/src/lib/serviceError.ts | 4 +-
packages/web/src/lib/types.ts | 13 +
packages/web/src/lib/utils.ts | 29 +-
packages/web/src/middleware.ts | 8 +-
packages/web/src/tailwind.ts | 5 +
packages/web/src/types.ts | 7 +
packages/web/tailwind.config.ts | 242 +++--
schemas/v3/index.json | 5 +
yarn.lock | 441 ++++++++-
169 files changed, 7135 insertions(+), 1783 deletions(-)
create mode 100644 .cursor/rules/style.mdc
create mode 100644 docs/docs/more/api-keys.mdx
create mode 100644 docs/docs/search/code-navigation.mdx
create mode 100644 docs/images/api_key.png
delete mode 100644 docs/images/demo.mp4
create mode 100644 docs/images/join_request_email.png
create mode 100644 docs/images/login_basic.png
create mode 100644 docs/images/pending_approval.png
delete mode 100644 docs/self-hosting/configuration.mdx
create mode 100644 docs/self-hosting/configuration/authentication.mdx
rename docs/self-hosting/{more => configuration}/declarative-config.mdx (88%)
create mode 100644 docs/self-hosting/configuration/environment-variables.mdx
rename docs/self-hosting/{more => configuration}/tenancy.mdx (90%)
rename docs/self-hosting/{more => configuration}/transactional-emails.mdx (76%)
delete mode 100644 docs/self-hosting/more/authentication.mdx
create mode 100644 docs/self-hosting/upgrade/v3-to-v4-guide.mdx
create mode 100644 packages/db/prisma/migrations/20250519235121_add_org_metadata_field/migration.sql
create mode 100644 packages/db/prisma/migrations/20250520021309_add_pending_approval_user/migration.sql
create mode 100644 packages/db/prisma/migrations/20250520022249_add_account_request/migration.sql
create mode 100644 packages/db/prisma/migrations/20250520182630_add_guest_role/migration.sql
create mode 100644 packages/db/prisma/migrations/20250523175553_add_api_key/migration.sql
create mode 100644 packages/web/src/app/[domain]/browse/README.md
delete mode 100644 packages/web/src/app/[domain]/browse/[...path]/codePreview.tsx
create mode 100644 packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx
create mode 100644 packages/web/src/app/[domain]/browse/[...path]/components/rangeHighlightingExtension.ts
create mode 100644 packages/web/src/app/[domain]/browse/browseStateProvider.tsx
create mode 100644 packages/web/src/app/[domain]/browse/components/bottomPanel.tsx
create mode 100644 packages/web/src/app/[domain]/browse/hooks/useBrowseNavigation.ts
create mode 100644 packages/web/src/app/[domain]/browse/hooks/useBrowseState.ts
create mode 100644 packages/web/src/app/[domain]/browse/layout.tsx
delete mode 100644 packages/web/src/app/[domain]/components/keyboardShortcutHint.tsx
create mode 100644 packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx
create mode 100644 packages/web/src/app/[domain]/components/pendingApproval.tsx
create mode 100644 packages/web/src/app/[domain]/components/resubmitAccountRequestButton.tsx
create mode 100644 packages/web/src/app/[domain]/components/whatsNewIndicator.tsx
create mode 100644 packages/web/src/app/[domain]/search/components/filterPanel/useFilterMatches.ts
create mode 100644 packages/web/src/app/[domain]/search/components/filterPanel/useGetSelectedFromQuery.ts
delete mode 100644 packages/web/src/app/[domain]/search/components/searchResultsPanel/codePreview.tsx
delete mode 100644 packages/web/src/app/[domain]/search/components/searchResultsPanel/lightweightCodeMirror.tsx
create mode 100644 packages/web/src/app/[domain]/settings/apiKeys/columns.tsx
create mode 100644 packages/web/src/app/[domain]/settings/apiKeys/page.tsx
create mode 100644 packages/web/src/app/[domain]/settings/license/page.tsx
create mode 100644 packages/web/src/app/[domain]/settings/members/components/requestsList.tsx
create mode 100644 packages/web/src/components/ui/animatedResizableHandle.tsx
create mode 100644 packages/web/src/components/ui/checkbox.tsx
create mode 100644 packages/web/src/components/ui/loading-button.tsx
create mode 100644 packages/web/src/components/ui/switch.tsx
create mode 100644 packages/web/src/ee/features/codeNav/components/exploreMenu/index.tsx
create mode 100644 packages/web/src/ee/features/codeNav/components/exploreMenu/referenceList.tsx
create mode 100644 packages/web/src/ee/features/codeNav/components/symbolHoverPopup/index.tsx
create mode 100644 packages/web/src/ee/features/codeNav/components/symbolHoverPopup/symbolDefinitionPreview.tsx
create mode 100644 packages/web/src/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension.ts
create mode 100644 packages/web/src/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo.ts
create mode 100644 packages/web/src/ee/features/publicAccess/publicAccess.tsx
create mode 100644 packages/web/src/ee/sso/sso.tsx
create mode 100644 packages/web/src/emails/constants.ts
create mode 100644 packages/web/src/emails/joinRequestApprovedEmail.tsx
create mode 100644 packages/web/src/emails/joinRequestSubmittedEmail.tsx
create mode 100644 packages/web/src/features/codeNav/actions.ts
create mode 100644 packages/web/src/features/codeNav/schemas.ts
create mode 100644 packages/web/src/features/codeNav/types.ts
delete mode 100644 packages/web/src/features/entitlements/usePlan.ts
create mode 100644 packages/web/src/hooks/useCodeMirrorHighlighter.ts
rename packages/web/src/hooks/{useSyntaxHighlightingExtension.ts => useCodeMirrorLanguageExtension.ts} (89%)
delete mode 100644 packages/web/src/hooks/useTailwind.ts
create mode 100644 packages/web/src/lib/newsData.ts
create mode 100644 packages/web/src/tailwind.ts
create mode 100644 packages/web/src/types.ts
diff --git a/.cursor/rules/style.mdc b/.cursor/rules/style.mdc
new file mode 100644
index 00000000..6d3e8046
--- /dev/null
+++ b/.cursor/rules/style.mdc
@@ -0,0 +1,7 @@
+---
+description:
+globs:
+alwaysApply: true
+---
+- Always use 4 spaces for indentation
+- Filenames should always be camelCase. Exception: if there are filenames in the same directory with a format other than camelCase, use that format to keep things consistent.
\ No newline at end of file
diff --git a/.env.development b/.env.development
index f4ca1600..1c90cd01 100644
--- a/.env.development
+++ b/.env.development
@@ -18,10 +18,10 @@ SRC_TENANT_ENFORCEMENT_MODE=strict
AUTH_SECRET="00000000000000000000000000000000000000000000"
AUTH_URL="http://localhost:3000"
# AUTH_CREDENTIALS_LOGIN_ENABLED=true
-# AUTH_GITHUB_CLIENT_ID=""
-# AUTH_GITHUB_CLIENT_SECRET=""
-# AUTH_GOOGLE_CLIENT_ID=""
-# AUTH_GOOGLE_CLIENT_SECRET=""
+# AUTH_EE_GITHUB_CLIENT_ID=""
+# AUTH_EE_GITHUB_CLIENT_SECRET=""
+# AUTH_EE_GOOGLE_CLIENT_ID=""
+# AUTH_EE_GOOGLE_CLIENT_SECRET=""
DATA_CACHE_DIR=${PWD}/.sourcebot # Path to the sourcebot cache dir (ex. ~/sourcebot/.sourcebot)
# CONFIG_PATH=${PWD}/config.json # Path to the sourcebot config file (if one exists)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b8b551b..2dbea0cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+Sourcebot V4 introduces authentication, performance improvements and code navigation. Checkout the [migration guide](https://docs.sourcebot.dev/self-hosting/upgrade/v3-to-v4-guide) for information on upgrading your instance to v4.
+
+### Changed
+- [**Breaking Change**] Authentication is now required by default. Notes:
+ - When setting up your instance, email / password login will be the default authentication provider.
+ - The first user that logs into the instance is given the `owner` role. ([docs](https://docs.sourcebot.dev/docs/more/roles-and-permissions)).
+ - Subsequent users can request to join the instance. The `owner` can approve / deny requests to join the instance via `Settings` > `Members` > `Pending Requests`.
+ - If a user is approved to join the instance, they are given the `member` role.
+ - Additional login providers, including email links and SSO, can be configured with additional environment variables. ([docs](https://docs.sourcebot.dev/self-hosting/configuration/authentication)).
+- Clicking on a search result now takes you to the `/browse` view. Files can still be previewed by clicking the "Preview" button or holding `Cmd` / `Ctrl` when clicking on a search result. [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
+
+### Added
+- [Sourcebot EE] Added search-based code navigation, allowing you to jump between symbol definition and references when viewing source files. [Read the documentation](https://docs.sourcebot.dev/docs/search/code-navigation). [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
+- Added collapsible filter panel. [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
+- Added Sourcebot API key management for external clients. [#311](https://github.com/sourcebot-dev/sourcebot/pull/311)
+
+### Fixed
+- Improved scroll performance for large numbers of search results. [#315](https://github.com/sourcebot-dev/sourcebot/pull/315)
+
## [3.2.1] - 2025-05-15
### Added
@@ -93,8 +112,8 @@ Sourcebot v3 is here and brings a number of structural changes to the tool's fou
### Added
- Added parallelized repo indexing and connection syncing via Redis & BullMQ. See the [architecture overview](https://docs.sourcebot.dev/self-hosting/overview#architecture).
- Added repo indexing progress indicators in the navbar.
-- Added authentication support via OAuth or email/password. For instructions on enabling, see [this doc](https://docs.sourcebot.dev/self-hosting/more/authentication).
-- Added the following UI for managing your deployment when **[auth is enabled](https://docs.sourcebot.dev/self-hosting/more/authentication)**:
+- Added authentication support via OAuth or email/password. For instructions on enabling, see [this doc](https://docs.sourcebot.dev/self-hosting/configuration/authentication).
+- Added the following UI for managing your deployment when **[auth is enabled](https://docs.sourcebot.dev/self-hosting/configuration/authentication)**:
- connection management: create and manage your JSON configs via a integrated web-editor.
- secrets: import personal access tokens (PAT) into Sourcebot (AES-256 encrypted). Reference secrets in your connection config by name.
- team & invite management: invite users to your instance to give them access. Configure team [roles & permissions](https://docs.sourcebot.dev/docs/more/roles-and-permissions).
diff --git a/docs/docs.json b/docs/docs.json
index a3bac5cc..607deb9d 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -50,6 +50,7 @@
"pages": [
"docs/search/syntax-reference",
"docs/search/multi-branch-indexing",
+ "docs/search/code-navigation",
"docs/search/search-contexts"
]
},
@@ -63,6 +64,7 @@
{
"group": "More",
"pages": [
+ "docs/more/api-keys",
"docs/more/roles-and-permissions",
"docs/more/mcp-server"
]
@@ -77,17 +79,16 @@
"group": "Getting Started",
"pages": [
"self-hosting/overview",
- "self-hosting/configuration",
"self-hosting/license-key"
]
},
{
- "group": "More",
+ "group": "Configuration",
"pages": [
- "self-hosting/more/authentication",
- "self-hosting/more/tenancy",
- "self-hosting/more/transactional-emails",
- "self-hosting/more/declarative-config"
+ "self-hosting/configuration/environment-variables",
+ "self-hosting/configuration/authentication",
+ "self-hosting/configuration/transactional-emails",
+ "self-hosting/configuration/declarative-config"
]
},
{
@@ -98,6 +99,7 @@
{
"group": "Upgrade",
"pages": [
+ "self-hosting/upgrade/v3-to-v4-guide",
"self-hosting/upgrade/v2-to-v3-guide"
]
}
diff --git a/docs/docs/agents/review-agent.mdx b/docs/docs/agents/review-agent.mdx
index 27d9932d..1d81d58f 100644
--- a/docs/docs/agents/review-agent.mdx
+++ b/docs/docs/agents/review-agent.mdx
@@ -53,6 +53,7 @@ Before you get started, make sure you have an OpenAPI account that you can creat
directory that you mount to Sourcebot

- `OPENAI_API_KEY`: Your OpenAI API key
+ - `REVIEW_AGENT_API_KEY`: The Sourcebot API key that the review agent uses to hit the Sourcebot API to fetch code context
- `REVIEW_AGENT_AUTO_REVIEW_ENABLED` (default: `false`): If enabled, the review agent will automatically review any new or updated PR. If disabled, you must invoke it using the command defined by `REVIEW_AGENT_REVIEW_COMMAND`
- `REVIEW_AGENT_REVIEW_COMMAND` (default: `review`): The command that invokes the review agent (ex. `/review`) when a user comments on the PR. Don't include the slash character in this value.
@@ -76,6 +77,7 @@ Before you get started, make sure you have an OpenAPI account that you can creat
GITHUB_APP_ID: "my-github-app-id"
GITHUB_APP_WEBHOOK_SECRET: "my-github-app-webhook-secret"
GITHUB_APP_PRIVATE_KEY_PATH: "/data/review-agent-key.pem"
+ REVIEW_AGENT_API_KEY: "sourcebot-my-key"
OPENAI_API_KEY: "sk-proj-my-open-api-key"
```
diff --git a/docs/docs/connections/gitea.mdx b/docs/docs/connections/gitea.mdx
index 7e670d2d..810085a2 100644
--- a/docs/docs/connections/gitea.mdx
+++ b/docs/docs/connections/gitea.mdx
@@ -82,7 +82,7 @@ Next, provide the access token via the `token` property, either as an environmen
- Environment variables are only supported in a [declarative config](/self-hosting/more/declarative-config) and cannot be used in the web UI.
+ Environment variables are only supported in a [declarative config](/self-hosting/configuration/declarative-config) and cannot be used in the web UI.
1. Add the `token` property to your connection config:
```json
@@ -107,7 +107,7 @@ Next, provide the access token via the `token` property, either as an environmen
- Secrets are only supported when [authentication](/self-hosting/more/authentication) is enabled.
+ Secrets are only supported when [authentication](/self-hosting/configuration/authentication) is enabled.
1. Navigate to **Secrets** in settings and create a new secret with your PAT:
diff --git a/docs/docs/connections/github.mdx b/docs/docs/connections/github.mdx
index 58a1d59f..52bb2f3e 100644
--- a/docs/docs/connections/github.mdx
+++ b/docs/docs/connections/github.mdx
@@ -111,7 +111,7 @@ Next, provide the PAT via the `token` property, either as an environment variabl
- Environment variables are only supported in a [declarative config](/self-hosting/more/declarative-config) and cannot be used in the web UI.
+ Environment variables are only supported in a [declarative config](/self-hosting/configuration/declarative-config) and cannot be used in the web UI.
1. Add the `token` property to your connection config:
```json
@@ -136,7 +136,7 @@ Next, provide the PAT via the `token` property, either as an environment variabl
- Secrets are only supported when [authentication](/self-hosting/more/authentication) is enabled.
+ Secrets are only supported when [authentication](/self-hosting/configuration/authentication) is enabled.
1. Navigate to **Secrets** in settings and create a new secret with your PAT:
diff --git a/docs/docs/connections/gitlab.mdx b/docs/docs/connections/gitlab.mdx
index 87f01661..4740bcc0 100644
--- a/docs/docs/connections/gitlab.mdx
+++ b/docs/docs/connections/gitlab.mdx
@@ -116,7 +116,7 @@ Next, provide the PAT via the `token` property, either as an environment variabl
- Environment variables are only supported in a [declarative config](/self-hosting/more/declarative-config) and cannot be used in the web UI.
+ Environment variables are only supported in a [declarative config](/self-hosting/configuration/declarative-config) and cannot be used in the web UI.
1. Add the `token` property to your connection config:
```json
@@ -141,7 +141,7 @@ Next, provide the PAT via the `token` property, either as an environment variabl
- Secrets are only supported when [authentication](/self-hosting/more/authentication) is enabled.
+ Secrets are only supported when [authentication](/self-hosting/configuration/authentication) is enabled.
1. Navigate to **Secrets** in settings and create a new secret with your PAT:
diff --git a/docs/docs/connections/overview.mdx b/docs/docs/connections/overview.mdx
index a237aa67..65bac506 100644
--- a/docs/docs/connections/overview.mdx
+++ b/docs/docs/connections/overview.mdx
@@ -11,11 +11,11 @@ There are two ways to define connections:
- This is only supported when self-hosting, and is the default mechanism to define connections. Connections are defined in a [JSON file](/self-hosting/more/declarative-config)
+ This is only supported when self-hosting, and is the default mechanism to define connections. Connections are defined in a [JSON file](/self-hosting/configuration/declarative-config)
and the path to the file is provided through the `CONFIG_PATH` environment variable
- This is the only way to define connections when using Sourcebot Cloud, and can be configured when self-hosting by enabling [authentication](/self-hosting/more/authentications).
+ This is the only way to define connections when using Sourcebot Cloud, and can be configured when self-hosting by enabling [authentication](/self-hosting/configuration/authentications).
In this method, connections are defined and managed within the webapp:
diff --git a/docs/docs/more/api-keys.mdx b/docs/docs/more/api-keys.mdx
new file mode 100644
index 00000000..4aa31a69
--- /dev/null
+++ b/docs/docs/more/api-keys.mdx
@@ -0,0 +1,8 @@
+---
+title: API Keys
+---
+
+An API Key is required when querying Sourcebot outside the context of the web app client (ex. MCP server, review agent). To create an API key, login to your Sourcebot instance and navigate to
+**Settings -> API Keys**:
+
+
\ No newline at end of file
diff --git a/docs/docs/more/mcp-server.mdx b/docs/docs/more/mcp-server.mdx
index 8521e06d..378e8280 100644
--- a/docs/docs/more/mcp-server.mdx
+++ b/docs/docs/more/mcp-server.mdx
@@ -4,7 +4,7 @@ sidebarTitle: Sourcebot MCP server
---
-This feature is only available when [self-hosting](/self-hosting) with [authentication](/self-hosting/more/authentication) disabled.
+This feature is only available when [self-hosting](/self-hosting)
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is an open standard for providing context to LLMs. The [@sourcebot/mcp](https://www.npmjs.com/package/@sourcebot/mcp) package is a MCP server that enables LLMs to interface with your Sourcebot instance, enabling MCP clients like Cursor, Vscode, and others to have context over your entire codebase.
@@ -176,6 +176,7 @@ Parameters:
| Name | Default | Description |
|:-------------------------|:-----------------------|:--------------------------------------------------|
| `SOURCEBOT_HOST` | http://localhost:3000 | URL of your Sourcebot instance. |
+| `SOURCEBOT_API_KEY` | - | Sourcebot API key. |
| `DEFAULT_MINIMUM_TOKENS` | 10000 | Minimum number of tokens to return in responses. |
| `DEFAULT_MATCHES` | 10000 | Number of code matches to fetch per search. |
| `DEFAULT_CONTEXT_LINES` | 5 | Lines of context to include above/below matches. |
diff --git a/docs/docs/more/roles-and-permissions.mdx b/docs/docs/more/roles-and-permissions.mdx
index 92ff91a7..43be9319 100644
--- a/docs/docs/more/roles-and-permissions.mdx
+++ b/docs/docs/more/roles-and-permissions.mdx
@@ -4,8 +4,7 @@ title: Roles and Permissions
Looking to sync permissions with your identify provider? We're working on it - [reach out](https://www.sourcebot.dev/contact) to us to learn more
-If you're using Sourcebot Cloud, or are self-hosting with [authentication](/self-hosting/more/authentication) enabled, you may have multiple members in your organization. Each
-member has a role which defines their permissions:
+Each member has a role which defines their permissions within an organization:
| Role | Permission |
| :--- | :--------- |
diff --git a/docs/docs/search/code-navigation.mdx b/docs/docs/search/code-navigation.mdx
new file mode 100644
index 00000000..daced8ba
--- /dev/null
+++ b/docs/docs/search/code-navigation.mdx
@@ -0,0 +1,44 @@
+---
+title: Code navigation
+sidebarTitle: Code navigation
+---
+
+import SearchContextSchema from '/snippets/schemas/v3/searchContext.schema.mdx'
+
+
+This feature is only available in [Sourcebot cloud](app.sourcebot.dev) or with an active Enterprise license when [self-hosting](/self-hosting). Please add your [license key](/self-hosting/license-key) to activate it.
+
+
+**Code navigation** allows you to jump between symbol definition and references when viewing source files in Sourcebot. This feature is enabled **automatically** when a valid license key is present and works with all popular programming languages.
+
+
+
+
+## Features
+
+| Feature | Description |
+|:--------|:------------|
+| **Hover popover** | Hovering over a symbol reveals the symbol's definition signature as a inline preview. |
+| **Go to definition** | Clicking the "go to definition" button in the popover or clicking the symbol name navigates to the symbol's definition. |
+| **Find references** | Clicking the "find all references" button in the popover lists all references in the explore panel. |
+| **Explore panel** | Lists all references and definitions for the symbol selected in the popover. |
+
+## How does it work?
+
+Code navigation is **search-based**, meaning it uses the same code search engine and [query language](/docs/search/syntax-reference) to estimate a symbol's references and definitions. We refer to these estimations as "search heuristics". We have two search heuristics to enable the following operations:
+
+### Find references
+Given a `symbolName`, along with information about the file the symbol is contained within (`git_revision`, and `language`), runs the following search:
+
+```bash
+\\b{symbolName}\\b rev:{git_revision} lang:{language} case:yes
+```
+
+### Find definitions
+Given a `symbolName`, along with information about the file the symbol is contained within (`git_revision`, and `language`), runs the following search:
+
+```bash
+sym:\\b{symbolName}\\b rev:{git_revision} lang:{language}
+```
+
+Note that the `sym:` prefix is used to filter the search by symbol definitions. These are created at index time by [universal ctags](https://ctags.io/).
diff --git a/docs/docs/search/search-contexts.mdx b/docs/docs/search/search-contexts.mdx
index d701cc60..e6afca59 100644
--- a/docs/docs/search/search-contexts.mdx
+++ b/docs/docs/search/search-contexts.mdx
@@ -1,6 +1,6 @@
---
title: Search contexts
-sidebarTitle: Search contexts (EE)
+sidebarTitle: Search contexts
---
import SearchContextSchema from '/snippets/schemas/v3/searchContext.schema.mdx'
@@ -16,7 +16,7 @@ A **search context** is a user-defined grouping of repositories that helps focus
- `( context:project1 or context:project2 ) logger\.debug` - search for debug log calls in project1 and project2
-Search contexts are defined in the `context` object inside of a [declarative config](/self-hosting/more/declarative-config). Repositories can be included / excluded from a search context by specifying the repo's URL in either the `include` array or `exclude` array. Glob patterns are supported.
+Search contexts are defined in the `context` object inside of a [declarative config](/self-hosting/configuration/declarative-config). Repositories can be included / excluded from a search context by specifying the repo's URL in either the `include` array or `exclude` array. Glob patterns are supported.
## Example
@@ -41,7 +41,7 @@ shared/
├─ ...
```
-To make searching easier, we can create three search contexts in our [config.json](/self-hosting/more/declarative-config):
+To make searching easier, we can create three search contexts in our [config.json](/self-hosting/configuration/declarative-config):
- `web`: For all frontend-related code
- `backend`: For backend services and shared APIs
- `pipelines`: For all CI/CD configurations
diff --git a/docs/images/api_key.png b/docs/images/api_key.png
new file mode 100644
index 0000000000000000000000000000000000000000..64e94e6e59cf1acdc7e73a577665eda0a40763f2
GIT binary patch
literal 143280
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