Skip to content

feat: display project path in sidebar and allow same remote path with different hosts#1690

Open
yuzhichang wants to merge 3 commits intogeneralaction:mainfrom
yuzhichang:feat/remote-project-path-display
Open

feat: display project path in sidebar and allow same remote path with different hosts#1690
yuzhichang wants to merge 3 commits intogeneralaction:mainfrom
yuzhichang:feat/remote-project-path-display

Conversation

@yuzhichang
Copy link
Copy Markdown

@yuzhichang yuzhichang commented Apr 8, 2026

Summary

  • Sidebar path display: Add path info after project name in sidebar

    • Local projects show absolute path (e.g., /home/user/project)
    • Remote projects show hostname:path (e.g., tower01:/home/user/repo)
    • When remote.host is unavailable, falls back to displaying connectionId
  • Fix ProjectConflictError: Allow remote projects with the same path but different SSH hosts

    • Drop unique index on projects.path (with migration 0013_remove_projects_path_unique.sql)
    • Conflict detection now checks sshConnectionId - two remote projects with the same path but different sshConnectionId are allowed
    • Fix SSH connection error: ssh-config:alias format is now resolved via SSH config parser

Test plan

  • Create two remote projects with same path on different hosts — both should be saved without conflict
  • Verify sidebar shows correct format for local and remote projects
  • Unit tests added for remote-vs-remote project path collisions

Summary by CodeRabbit

  • New Features

    • Sidebar project entries now show host and path for remote projects for clearer identification.
  • Bug Fixes

    • Refined conflict detection when saving projects with SSH connections to avoid false conflicts for the same remote connection and correctly treat distinct remote connections.
    • Fixed SSH config alias resolution for connections that were never saved to the database.
    • Fixed connection error display issue where remote projects showed "Connection error".
  • Tests

    • Added tests for remote-save conflict and non-conflict scenarios to prevent regressions.
  • Database

    • Removed unique constraint on projects.path to allow remote projects with the same path but different SSH connection IDs.

Summary by CodeRabbit

  • New Features

    • Sidebar now shows host:path for remote projects.
    • Connect via ssh-config aliases (direct SSH-config alias connections).
  • Bug Fixes

    • Improved project-save conflict logic to allow remote projects with the same path when they use different SSH connection IDs; prevents duplicate remote entries for identical connection+path.
  • Database

    • Removed DB-level unique constraint on project path to support distinct remote connections sharing a path.
  • Tests

    • Added tests for remote-save conflict and non-conflict scenarios.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

@yuzhichang is attempting to deploy a commit to the General Action Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refines project save conflict logic to allow same-path remote projects when sshConnectionId matches; drops DB uniqueness on projects.path; updates sidebar to display a memoized remote-aware name; adds tests and an SSH IPC handler for ssh-config: aliases.

Changes

Cohort / File(s) Summary
Database conflict & schema
src/main/services/DatabaseService.ts, src/main/db/schema.ts, drizzle/0013_remove_projects_path_unique.sql, drizzle/meta/0013_snapshot.json, drizzle/meta/_journal.json
Removed DB-level unique index on projects.path; saveProject now SELECTs sshConnectionId and isRemote and only throws ProjectConflictError for incompatible cases, permitting same-path remote projects with matching sshConnectionId. Added migration and drizzle metadata.
UI: sidebar display
src/renderer/components/sidebar/LeftSidebar.tsx
ProjectItem computes isRemote and a memoized displayName; UI renders displayName (remote: name (host:path) with host fallback, non-remote: name (path)).
IPC: SSH alias connect
src/main/ipc/sshIpc.ts
Adds handling for SSH_IPC_CHANNELS.CONNECT when arg starts with ssh-config:: resolves alias (percent-decode), builds an in-memory SshConfig, calls sshService.connect, starts monitoring, emits ssh_connect_success telemetry, and returns success/failure without DB persistence.
Tests
src/test/main/DatabaseService.saveProject.test.ts
New tests for remote project conflict/allow-listing: asserts ProjectConflictError and allows insert when appropriate; verifies insert/update call behavior.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer
    participant DatabaseService
    participant DB
    Renderer->>DatabaseService: saveProject(project)
    DatabaseService->>DB: SELECT id, sshConnectionId, isRemote WHERE path = project.path
    alt no existing row
        DB-->>DatabaseService: no rows
        DatabaseService->>DB: INSERT project
        DB-->>DatabaseService: insert result
        DatabaseService-->>Renderer: resolve(insert result)
    else existing row found
        DB-->>DatabaseService: row {id, sshConnectionId, isRemote}
        alt existing.id == project.id
            DatabaseService->>DB: UPDATE project
            DB-->>DatabaseService: update result
            DatabaseService-->>Renderer: resolve(update result)
        else different id
            alt both remote and sshConnectionId equal
                DatabaseService->>DB: INSERT project (allowed)
                DB-->>DatabaseService: insert result
                DatabaseService-->>Renderer: resolve(insert result)
            else incompatible (non-remote mismatch or sshConnectionId differs)
                DatabaseService-->>Renderer: reject(ProjectConflictError)
            end
        end
    end
Loading
sequenceDiagram
    participant Renderer
    participant MainIPC
    participant SSHService
    participant Monitor
    participant Telemetry
    Renderer->>MainIPC: CONNECT("ssh-config:alias%20enc")
    MainIPC->>MainIPC: decode alias -> resolveSshConfigHost(alias)
    alt alias resolved
        MainIPC->>SSHService: connect(in-memory SshConfig)
        SSHService-->>MainIPC: connectionId
        MainIPC->>Monitor: startMonitoring(connectionId)
        MainIPC->>Telemetry: emit("ssh_connect_success", {authType})
        MainIPC-->>Renderer: { success: true, connectionId }
    else alias not found
        MainIPC-->>Renderer: { success: false, error }
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through rows and trimmed the vine,

Paths now share if keys align,
I nibbled tests and tuned the key,
Sent aliases sailing, safe and free,
I bounce—good code, carrot time! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title directly and clearly summarizes the two main changes: sidebar path display and allowing remote projects with the same path but different hosts.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/services/DatabaseService.ts`:
- Around line 220-232: Add unit tests covering remote-vs-remote project path
collisions for DatabaseService.validate or the method that contains
isNewProjectRemote/isExistingProjectRemote logic: create two test cases—(1) both
projects marked isRemote with identical sshConnectionId values and the same path
and assert that ProjectConflictError is thrown with existingByPath.id/name/path
included, and (2) both projects marked isRemote with different sshConnectionId
values and the same path and assert that no error is thrown (operation
succeeds). Use the same setup pattern as the existing test (populate
existingByPath and project objects) and target the branch conditions involving
isNewProjectRemote, isExistingProjectRemote, and sameSshConnection to ensure the
new remote-specific behavior is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe104978-5710-4f3d-adf7-2669e338fce3

📥 Commits

Reviewing files that changed from the base of the PR and between c797ed5 and 0493a9e.

📒 Files selected for processing (2)
  • src/main/services/DatabaseService.ts
  • src/renderer/components/sidebar/LeftSidebar.tsx

@yuzhichang yuzhichang force-pushed the feat/remote-project-path-display branch from 0493a9e to 1971bf5 Compare April 8, 2026 03:35
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/components/sidebar/LeftSidebar.tsx`:
- Around line 89-94: The remote display label currently uses project.path inside
the useMemo for displayName, which can show the wrong path when a remote project
provides remotePath; update the useMemo (displayName) to use remotePath when
isRemote (fall back to project.path) and keep the host from remote.host;
specifically change the branch that builds `${project.name} (${remote.host ??
'...'}:${project.path})` to use `${remote.host ?? '...'}:${remote.remotePath ??
project.path}` (reference: useMemo -> displayName, project, isRemote,
remote.host, remote.remotePath).

In `@src/test/main/DatabaseService.saveProject.test.ts`:
- Around line 172-193: The test "throws ProjectConflictError when both remote
projects share the same sshConnectionId and path" is using mismatched paths so
it doesn't truly validate the same-path conflict; update the queued mock row
pushed into selectResults to use the same path as baseProject (i.e., change the
existing project's path from '/srv/project-one' to baseProject.path or
'/tmp/project-one') so the call to service.saveProject(baseProject) exercises
the intended same-path conflict branch referenced by selectResults and the
expected ProjectConflictError assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b86dde0-174e-43ab-a90d-ffc4c3247ee3

📥 Commits

Reviewing files that changed from the base of the PR and between 0493a9e and 1971bf5.

📒 Files selected for processing (3)
  • src/main/services/DatabaseService.ts
  • src/renderer/components/sidebar/LeftSidebar.tsx
  • src/test/main/DatabaseService.saveProject.test.ts
✅ Files skipped from review due to trivial changes (1)
  • src/main/services/DatabaseService.ts

…same path but different hosts

- Display project path in sidebar after project name:
  - Local projects show absolute path
  - Remote projects show hostname:path (e.g., tower01:/home/user/repo)
- Fix ProjectConflictError for remote projects by allowing same path
  with different sshConnectionId (different SSH hosts)
- Remove unique index on projects.path (with migration) since remote
  projects can share paths when connected via different SSH hosts
- Add SSH config alias support: when connectionId starts with
  "ssh-config:", resolve via SSH config parser instead of DB lookup
- Fix connection error display: show connectionId as fallback when
  remote.host is unavailable
- Add unit tests for remote-vs-remote project path collisions

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@yuzhichang yuzhichang force-pushed the feat/remote-project-path-display branch from bd49319 to 953c63e Compare April 8, 2026 05:25
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/ipc/sshIpc.ts`:
- Around line 465-474: The SshConfig creation sets username to an empty string
when sshConfigHost.user is undefined, which can cause SSH auth failures; update
the username assignment in the SshConfig object (constructed in sshIpc.ts) to
validate or default to the current OS user by using os.userInfo().username when
sshConfigHost.user is falsy, or alternatively throw/return an error if no
username can be determined—ensure the change touches the SshConfig construction
(username field) and any calling code that expects a non-empty username.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 137fb801-803e-4586-b248-8b20b66bc862

📥 Commits

Reviewing files that changed from the base of the PR and between bd49319 and 953c63e.

📒 Files selected for processing (8)
  • drizzle/0013_remove_projects_path_unique.sql
  • drizzle/meta/0013_snapshot.json
  • drizzle/meta/_journal.json
  • src/main/db/schema.ts
  • src/main/ipc/sshIpc.ts
  • src/main/services/DatabaseService.ts
  • src/renderer/components/sidebar/LeftSidebar.tsx
  • src/test/main/DatabaseService.saveProject.test.ts
✅ Files skipped from review due to trivial changes (3)
  • drizzle/meta/_journal.json
  • drizzle/meta/0013_snapshot.json
  • src/main/services/DatabaseService.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/main/DatabaseService.saveProject.test.ts

…ig lacks user

When connecting via SSH config alias (e.g., "ssh-config:tower02"), the
username from sshConfigHost.user may be undefined. Previously defaulted to
empty string which causes SSH auth failures. Now falls back to the current
OS username via os.userInfo().username.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/ipc/sshIpc.ts`:
- Around line 466-475: The code constructs an SshConfig and currently forces
authType = sshConfigHost.identityFile ? 'key' : 'agent', which overrides the
resolved host intent; instead preserve the resolved auth choice from the host
object (e.g. use sshConfigHost.authType if present) and only fallback to a
sensible default when absent. Update the SshConfig creation (variable config) to
set authType = sshConfigHost.authType ?? (sshConfigHost.identityFile ? 'key' :
'agent') and set useAgent = sshConfigHost.useAgent ?? (authType === 'agent'), or
equivalent logic that prefers sshConfigHost.authType/identityAgent flags so you
don't collapse passphrase-protected-key or explicit agent intents.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1caf8af4-0dbb-4dc1-870c-4c13323880bb

📥 Commits

Reviewing files that changed from the base of the PR and between 953c63e and f6dd06f.

📒 Files selected for processing (1)
  • src/main/ipc/sshIpc.ts

…SH config alias

When connecting via SSH config alias, identityAgent (e.g. SSH_AUTH_SOCK)
should take priority over identityFile for determining auth type and useAgent.
Previously useAgent was always false when identityFile was set, ignoring
identityAgent which represents explicit intent to use SSH agent forwarding.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant