Skip to content

feat(repos): Replace the repo list page with the new repo tree-view#110290

Open
ryan953 wants to merge 3 commits intomasterfrom
ryan953/repos-list-as-tree
Open

feat(repos): Replace the repo list page with the new repo tree-view#110290
ryan953 wants to merge 3 commits intomasterfrom
ryan953/repos-list-as-tree

Conversation

@ryan953
Copy link
Member

@ryan953 ryan953 commented Mar 10, 2026

TL/DR: Replaced the list view at /settings/repos/ with this tree view, the same one from /settings/seer/scm/

/settings/repos/ /settings/seer/scm/
SCR-20260309-rjas SCR-20260309-rpew

Repositories Settings Page: Tree-Based SCM Integration View

2026-03-10T03:12:51Z by Showboat 0.6.1

What This PR Does

This PR replaces the flat repository list in Settings > Repositories with the tree-based SCM integration view that already existed in Seer Automation settings. It also promotes the component from the getsentry-only gsApp package into app (OSS core) so both pages can share it.

Before: A flat panel listing connected repos, an AlertLink prompting users to visit the Integrations page, and pagination. No filtering or search.

After: Repos organized as a tree (Provider → Integration Account → Repository), a filter dropdown (All / Connected / Disconnected), a URL-synced search input, and inline connect/disconnect controls. The page header now explains what connecting repos unlocks: Suspect Commits, Suggested Assignees, Resolved via Commit/PR, and Seer.

Component Relocation

Six files moved from static/gsApp/views/seerAutomation/components/ to static/app/components/repositories/:

git diff master...HEAD --name-status | grep '^R' | awk '{print $3}'
static/app/components/repositories/repoProviderIcon.tsx
static/app/components/repositories/scmIntegrationTree/scmIntegrationTree.tsx
static/app/components/repositories/scmIntegrationTree/scmIntegrationTreeNodes.ts
static/app/components/repositories/scmIntegrationTree/scmIntegrationTreeRow.tsx
static/app/components/repositories/scmIntegrationTree/types.ts
static/app/components/repositories/scmIntegrationTree/useScmIntegrationTreeData.ts

Two Consumers, One Component

Both pages import from the shared location but pass different providerFilter props — Repositories settings uses "all" to show every SCM provider, while the Seer Automation page uses "seer-supported" to restrict to Seer-compatible providers. The Seer page previously let users toggle this via a dropdown; that was removed since the default is always the right value there.

grep -rn 'providerFilter' static/app/views/settings/organizationRepositories/index.tsx static/gsApp/views/seerAutomation/scm.tsx
static/app/views/settings/organizationRepositories/index.tsx:53:          providerFilter="all"
static/gsApp/views/seerAutomation/scm.tsx:44:            providerFilter="seer-supported"

Tests

The old organizationRepositories.spec.tsx and index.spec.tsx were deleted along with the flat-list component they covered. ScmIntegrationTree carries its own test coverage from its previous home in the seerAutomation directory. No new tests were added for the Repositories settings page.

@ryan953 ryan953 requested a review from a team as a code owner March 10, 2026 02:56
@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Mar 10, 2026
Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicated filter constants across two pages
    • Extracted REPO_FILTER_OPTIONS and repoParser to a new shared constants file in scmIntegrationTree/constants.ts, eliminating the duplication across both pages.

Create PR

Or push these changes by commenting:

@cursor push e6498d87d7
Preview (e6498d87d7)
diff --git a/static/app/components/repositories/scmIntegrationTree/constants.ts b/static/app/components/repositories/scmIntegrationTree/constants.ts
new file mode 100644
--- /dev/null
+++ b/static/app/components/repositories/scmIntegrationTree/constants.ts
@@ -1,0 +1,17 @@
+import {parseAsStringLiteral} from 'nuqs';
+
+import type {SelectOption} from '@sentry/scraps/compactSelect';
+
+import {t} from 'sentry/locale';
+
+import type {RepoFilter} from './types';
+
+export const REPO_FILTER_OPTIONS: Array<SelectOption<RepoFilter>> = [
+  {value: 'all' as const, label: t('All repos')},
+  {value: 'connected' as const, label: t('Connected Repos')},
+  {value: 'not-connected' as const, label: t('Disconnected Repos')},
+];
+
+export const repoParser = parseAsStringLiteral(
+  REPO_FILTER_OPTIONS.map(option => option.value)
+).withDefault('all');

diff --git a/static/app/views/settings/organizationRepositories/index.tsx b/static/app/views/settings/organizationRepositories/index.tsx
--- a/static/app/views/settings/organizationRepositories/index.tsx
+++ b/static/app/views/settings/organizationRepositories/index.tsx
@@ -1,4 +1,4 @@
-import {parseAsString, parseAsStringLiteral, useQueryState} from 'nuqs';
+import {parseAsString, useQueryState} from 'nuqs';
 
 import {CompactSelect, type SelectOption} from '@sentry/scraps/compactSelect';
 import {Input} from '@sentry/scraps/input';
@@ -6,6 +6,10 @@
 import {ExternalLink} from '@sentry/scraps/link';
 
 import AnalyticsArea from 'sentry/components/analyticsArea';
+import {
+  REPO_FILTER_OPTIONS,
+  repoParser,
+} from 'sentry/components/repositories/scmIntegrationTree/constants';
 import {ScmIntegrationTree} from 'sentry/components/repositories/scmIntegrationTree/scmIntegrationTree';
 import type {RepoFilter} from 'sentry/components/repositories/scmIntegrationTree/types';
 import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
@@ -13,16 +17,6 @@
 import useOrganization from 'sentry/utils/useOrganization';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
 
-const REPO_FILTER_OPTIONS: Array<SelectOption<RepoFilter>> = [
-  {value: 'all' as const, label: t('All repos')},
-  {value: 'connected' as const, label: t('Connected Repos')},
-  {value: 'not-connected' as const, label: t('Disconnected Repos')},
-];
-
-const repoParser = parseAsStringLiteral(
-  REPO_FILTER_OPTIONS.map(option => option.value)
-).withDefault('all');
-
 export default function OrganizationRepositories() {
   const organization = useOrganization();
 

diff --git a/static/gsApp/views/seerAutomation/scm.tsx b/static/gsApp/views/seerAutomation/scm.tsx
--- a/static/gsApp/views/seerAutomation/scm.tsx
+++ b/static/gsApp/views/seerAutomation/scm.tsx
@@ -1,4 +1,4 @@
-import {parseAsString, parseAsStringLiteral, useQueryState} from 'nuqs';
+import {parseAsString, useQueryState} from 'nuqs';
 
 import {CompactSelect, type SelectOption} from '@sentry/scraps/compactSelect';
 import {Input} from '@sentry/scraps/input';
@@ -6,6 +6,10 @@
 import {ExternalLink} from '@sentry/scraps/link';
 
 import AnalyticsArea from 'sentry/components/analyticsArea';
+import {
+  REPO_FILTER_OPTIONS,
+  repoParser,
+} from 'sentry/components/repositories/scmIntegrationTree/constants';
 import {ScmIntegrationTree} from 'sentry/components/repositories/scmIntegrationTree/scmIntegrationTree';
 import type {RepoFilter} from 'sentry/components/repositories/scmIntegrationTree/types';
 import {t, tct} from 'sentry/locale';
@@ -14,16 +18,6 @@
 import SeerSettingsPageContent from 'getsentry/views/seerAutomation/components/seerSettingsPageContent';
 import SeerSettingsPageWrapper from 'getsentry/views/seerAutomation/components/seerSettingsPageWrapper';
 
-const REPO_FILTER_OPTIONS: Array<SelectOption<RepoFilter>> = [
-  {value: 'all' as const, label: t('All repos')},
-  {value: 'connected' as const, label: t('Connected Repos')},
-  {value: 'not-connected' as const, label: t('Disconnected Repos')},
-];
-
-const repoParser = parseAsStringLiteral(
-  REPO_FILTER_OPTIONS.map(option => option.value)
-).withDefault('all');
-
 export default function SeerAutomationSCM() {
   const [searchTerm, setSearchTerm] = useQueryState(
     'search',
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file was just re-written to show the tree-view

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced by the tree-view.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant