Skip to content

feat(ssh): download folders from remote explorer#7793

Merged
AmethystLiang merged 10 commits into
stablyai:mainfrom
gatsby74:feat/ssh-folder-download
Jul 19, 2026
Merged

feat(ssh): download folders from remote explorer#7793
AmethystLiang merged 10 commits into
stablyai:mainfrom
gatsby74:feat/ssh-folder-download

Conversation

@gatsby74

@gatsby74 gatsby74 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds SSH remote folder download support from the File Explorer context menu.

  • Adds Download Folder for SSH directory rows.
  • Preserves nested folder structure when copying to a local destination.
  • Opens the native destination picker before SSH validation so remote latency does not delay the picker.
  • Uses a temp sibling folder and promotes it only after transfer completes.
  • Refuses to overwrite an existing destination folder.

Fixes #7780.

Screenshots

Screen recording of downloading a folder over SSH:

Download.Folder.over.SSH.mp4

Testing

  • pnpm lint — fails on current base with unrelated switch-exhaustiveness-check in src/renderer/src/components/right-sidebar/source-control-manual-review-url.ts:164:11 (Cases not matched: null)
  • pnpm typecheck
  • pnpm test
  • pnpm build
  • Added or updated high-quality tests that would catch regressions, or explained why tests were not needed

Additional focused checks run:

  • pnpm vitest run --config config/vitest.config.ts src/main/ipc/filesystem.test.ts src/renderer/src/components/right-sidebar/FileExplorer.test.tsx src/renderer/src/web/web-preload-api.test.ts
  • pnpm exec oxlint src/main/ipc/filesystem.ts src/main/ipc/filesystem.test.ts src/preload/api-types.ts src/preload/index.ts src/renderer/src/web/web-preload-api.ts src/renderer/src/web/web-preload-api.test.ts src/renderer/src/components/right-sidebar/FileExplorerRow.tsx src/renderer/src/components/right-sidebar/FileExplorer.test.tsx
  • pnpm run verify:localization-catalog
  • pnpm run verify:localization-coverage
  • Manual SSH check: right-clicked a remote folder, selected Download Folder, chose a local destination, and verified the folder downloaded successfully.

AI Review Report

AI-assisted review checked:

  • Cross-platform compatibility: uses Electron native dialogs and Node path.join; no shortcut or shell behavior changes.
  • SSH/remote/local compatibility: the new action is exposed for SSH folders only; local folder copy behavior is unchanged; paired web clients return an explicit unsupported error.
  • Supported agent/integration compatibility: no agent, GitHub/GitLab, or review-provider behavior is changed.
  • Performance/UX: the destination picker now opens before SSH stat/transfer work, avoiding click-to-dialog latency on remote connections.
  • UI quality: follows the existing File Explorer context-menu pattern and localized copy.

The review flagged the original pre-dialog SSH stat latency; the implementation was updated so native folder selection happens first.

Security Audit

Reviewed IPC and path-handling risks:

  • Renderer only passes the selected SSH row path and connection id through the preload API.
  • Main process resolves the SSH provider server-side and validates the remote target is a directory before transfer.
  • Local destination writes go through a user-selected native folder picker.
  • Downloads write into a hidden temporary sibling folder first, then promote on success; failed transfers clean up temp content.
  • Existing destination folders are not overwritten.
  • Remote entry names are sanitized before creating local paths.
  • No new secrets, dependencies, shell execution, or direct command construction are introduced.

Notes

  • X handle: not provided yet.
  • Full commands were run with Node v24.18.0 via fnm, matching the repo engine. Previous Node 26 local test failures were environment-related and do not reproduce on Node 24.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds folder download support for remote SSH directories. The main process now handles fs:downloadFolder with recursive directory transfer, destination checks, temp-folder promotion, and cleanup on failure. Preload exposes the new API, the File Explorer can trigger folder downloads for directories, and the web preload adapter rejects the operation in paired web clients. Locale strings and tests were updated across main, renderer, and web layers.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements #7780 by adding SSH folder download support with recursive preserved structure and a new context-menu action.
Out of Scope Changes check ✅ Passed The changes stay within the folder-download feature and supporting tests, API, UI text, and web stub updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately summarizes the main change: adding SSH folder downloads from the remote explorer.
Description check ✅ Passed The description covers the required summary, screenshots, testing, AI review, security audit, and notes sections in sufficient detail.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/ipc/filesystem.test.ts (1)

724-858: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the overwrite-refusal and file-as-folder guards.

The PR emphasizes refusing to overwrite an existing destination and rejecting a file passed as a folder, but neither path is exercised. All four new cases feed statMock an ENOENT (destination absent) and a directory remote stat. Consider adding cases where local stat resolves (destination exists → expect throw, no downloadFile/rename) and where provider.stat returns a non-directory type (expect the "Cannot download a file as a folder" rejection).


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b690f2b9-885f-4b1a-806a-fd0ed5751db0

📥 Commits

Reviewing files that changed from the base of the PR and between 45931a7 and d9e0436.

📒 Files selected for processing (13)
  • src/main/ipc/filesystem.test.ts
  • src/main/ipc/filesystem.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/components/right-sidebar/FileExplorer.test.tsx
  • src/renderer/src/components/right-sidebar/FileExplorerRow.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/web/web-preload-api.test.ts
  • src/renderer/src/web/web-preload-api.ts

Comment thread src/main/ipc/filesystem.ts Outdated
@gatsby74
gatsby74 force-pushed the feat/ssh-folder-download branch from d9e0436 to c4a5234 Compare July 8, 2026 09:09
@gatsby74

gatsby74 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Addressed CodeRabbit's test coverage note by adding guard tests for existing local destination folders and remote file-as-folder rejection. Re-ran focused filesystem tests: pnpm vitest run --config config/vitest.config.ts src/main/ipc/filesystem.test.ts

@AmethystLiang
AmethystLiang requested review from AmethystLiang and Jinwoo-H and removed request for Jinwoo-H July 9, 2026 16:53

@AmethystLiang AmethystLiang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the contribution — reviewed and validated end to end.

  • Remote folders download recursively with structure and hashes preserved.
  • Hardened cancellation, SFTP lifecycle, safe no-clobber publication, filename/file-type handling, and paired-web/localization parity.
  • Verified against a real Linux SSH/SFTP target, including successful download and existing-destination rejection.

Approving.

Restoring headless mobile tabs looks up the repo for the active
worktree id; the mock lacked getRepo, so the test only passed
incidentally. Add it explicitly and return undefined since wt-1
is a worktree id, not a registered repo.
- Folder downloads require SFTP, unavailable on system SSH (which
  offers only raw file operations). Add supportsFolderDownload flag
  to gate the feature in the UI layer.
- Reject symlinks at directory-entry level, preventing tree escapes
  and eliminating unnecessary stat calls.
- Check abort signal before opening dialog for better responsiveness
  when renderer closes.
- Log cleanup errors without re-throwing to preserve underlying
  transfer failures.
Enforce fail-closed gating and add Windows path traversal validation to
ensure downloads are only available when explicitly supported and safe.
Enforce fail-closed gating and add Windows path traversal validation to
ensure downloads are only available when explicitly supported and safe.
Move FolderDownloadOptions next to the SFTP download implementation and
narrow IFilesystemProvider.downloadFolder options to AbortSignal only so
types.ts stays within the 300-line oxlint budget when merged with main.
@AmethystLiang
AmethystLiang merged commit 8ed8f0d into stablyai:main Jul 19, 2026
3 checks passed
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.

[Feature]: Download folders from SSH remote

3 participants