SFTP Deploy
A VS Code extension that deploys your workspace files to a remote server via SFTP/SSH, with custom path mappings, multi-script pre/post deploy hooks, multi-target deployments, and SCM-aware changed-file tracking.
Features
- SFTP Upload — upload individual files, entire folders, or the whole workspace to a remote server
- Upload on Save — optionally auto-upload files whenever they are saved
- Custom Path Mappings — override the remote target path for any local directory
- Pre / Post Deploy Scripts — define multiple scripts (local or remote) to run before and after each deployment; each script can be individually enabled/disabled and run independently from the Scripts sidebar
- Multi-Target Deploy — define up to 8 remote hosts to deploy to simultaneously; each target inherits root settings as defaults and can add its own additional scripts on top of the global ones
- Per-Target Quick Pick — right-click any file or use the Changed Files toolbar to pick "All Targets" or any single target for that deploy only
- Changed Files Panel — view files modified since the last SCM commit (Git or SVN) in a hierarchical or flat list, check/uncheck individual files, and deploy only selected ones
- Flat / Tree View Toggle — switch the Changed Files panel between a folder hierarchy and a flat file list in one click
- Diff View — click any changed file to open a side-by-side diff against the committed version
- Deploy Status Panel — persistent WebView log that accumulates history across multiple deploy runs; open it at any time, even mid-deploy, and see the full history
- Config Editor Panel — form-based UI for creating and editing
.vscode/deploy.json
- Sidebar — dedicated activity bar panel with Deployment Status, Scripts, and Changed Files views
- JSON Schema Validation —
.vscode/deploy.json is validated with IntelliSense auto-complete support
Getting Started
- Open the Command Palette (
Ctrl+Shift+P / ⌘+Shift+P)
- Run Deploy: Open / Create Configuration to open the Config Editor or generate a template
.vscode/deploy.json
- Fill in your server details, path mappings, and scripts
- Run Deploy: Deploy Workspace, or use the Deploy ▶ submenu from any file's right-click menu
Configuration
The extension reads .vscode/deploy.json in your workspace root. The path is configurable via vscode-deploy.configPath in VS Code settings.
Single-target example
{
"host": "192.168.1.100",
"port": 22,
"username": "deploy",
"privateKeyPath": "~/.ssh/id_rsa",
"remotePath": "/var/www/html",
"localPath": "${workspaceFolder}/dist",
"uploadOnSave": false,
"ignore": ["node_modules/**", ".git/**", "*.log"],
"mappings": [
{
"localPath": "src/assets",
"remotePath": "/var/www/static",
"description": "Static assets served from a different root"
}
],
"preDeployScripts": [
{
"name": "Build",
"script": "npm run build",
"enabled": true,
"runLocal": true
}
],
"postDeployScripts": [
{
"name": "Restart app",
"script": "pm2 restart app",
"enabled": true,
"runLocal": false
}
],
"runScriptsOnFileDeploy": true,
"scmProvider": "git",
"connectTimeout": 10000
}
Multi-target example
When targets are defined, the root-level connection fields act as defaults — each target overrides only what differs. Path mappings and global scripts are always inherited.
{
"host": "staging.example.com",
"port": 22,
"username": "deploy",
"privateKeyPath": "~/.ssh/id_rsa",
"remotePath": "/var/www/html",
"preDeployScripts": [
{ "name": "Build", "script": "npm run build", "enabled": true, "runLocal": true }
],
"postDeployScripts": [
{ "name": "Restart", "script": "pm2 restart app", "enabled": true, "runLocal": false }
],
"targets": [
{
"name": "Staging",
"enabled": true,
"host": "staging.example.com",
"username": "deploy",
"remotePath": "/var/www/html"
},
{
"name": "Production",
"enabled": true,
"host": "prod.example.com",
"username": "deploy",
"remotePath": "/var/www/html",
"preDeployScripts": [
{ "name": "Notify team", "script": "curl -s https://hooks.example.com/deploy-start", "enabled": true, "runLocal": true }
],
"postDeployScripts": [
{ "name": "Purge CDN", "script": "curl -s https://cdn.example.com/purge", "enabled": true, "runLocal": true }
]
}
],
"scmProvider": "git",
"connectTimeout": 10000
}
Note: When targets are defined, deploying runs against all enabled targets in sequence. Additional scripts defined on a target are appended after the global scripts — both always run.
Properties
Root / defaults
| Property |
Type |
Default |
Description |
host |
string |
required |
Server hostname or IP address |
port |
number |
22 |
SSH/SFTP port |
username |
string |
required |
SSH username |
password |
string |
— |
SSH password (omit when using privateKeyPath) |
privateKeyPath |
string |
— |
Absolute path to a private key file (~ supported) |
passphrase |
string |
— |
Passphrase for an encrypted private key |
remotePath |
string |
required |
Default remote base path for uploads |
localPath |
string |
workspace root |
Local base path to upload from; supports ${workspaceFolder} |
uploadOnSave |
boolean |
false |
Auto-upload a file whenever it is saved |
ignore |
string[] |
["node_modules/**", ".git/**"] |
Glob patterns to exclude from workspace uploads |
mappings |
array |
[] |
Custom local→remote path overrides (see below) |
preDeployScripts |
array |
[] |
Scripts to run before deploying (see Scripts) |
postDeployScripts |
array |
[] |
Scripts to run after deploying (see Scripts) |
runScriptsOnFileDeploy |
boolean |
true |
Whether pre/post scripts also run for single-file deploys and upload-on-save |
scmProvider |
"none" | "git" | "svn" |
"none" |
SCM provider for the Changed Files panel |
gitPath |
string |
— |
Path to the git executable; leave empty to use the system PATH |
svnPath |
string |
— |
Path to the svn executable; leave empty to use the system PATH |
connectTimeout |
number |
10000 |
Connection timeout in milliseconds |
targets |
array |
[] |
Optional list of up to 8 remote targets (see Multi-Target) |
Path Mappings
Each entry in mappings overrides the upload destination for a local directory. The most-specific (longest) matching localPath wins when multiple entries overlap.
"mappings": [
{
"localPath": "src/assets",
"remotePath": "/var/www/static",
"description": "CDN root"
}
]
Scripts
preDeployScripts and postDeployScripts each accept an array of script objects:
| Field |
Type |
Description |
name |
string |
Optional label shown in the Scripts sidebar |
script |
string |
Shell script content — may be multi-line |
enabled |
boolean |
Uncheck to skip this script without removing it |
runLocal |
boolean |
true = run on the local machine; false = run on the remote server via SSH |
Scripts execute in array order. If any enabled script exits with a non-zero code the deployment is aborted.
Multi-Target Deployments
Define up to 8 remote hosts in the targets array. When at least one target is enabled:
- Deploying (workspace, file, or changed files) runs against all enabled targets in sequence
- Failed targets are reported individually; other targets still complete
- The Deploy Status Panel separates each target's output with a header
Target properties
Each target inherits all root-level fields as defaults. Only specify what differs:
| Property |
Type |
Description |
name |
string |
Display name shown in the sidebar and status output |
enabled |
boolean |
Set to false to temporarily disable a target without removing it |
host |
string |
required — remote hostname or IP |
port |
number |
Overrides the root port |
username |
string |
required — SSH username |
password |
string |
Overrides root auth; omit to inherit |
privateKeyPath |
string |
Overrides root key path; omit to inherit |
passphrase |
string |
Overrides root passphrase; omit to inherit |
remotePath |
string |
required — remote base path for this target |
connectTimeout |
number |
Overrides root connectTimeout |
preDeployScripts |
array |
Additional scripts appended after the global pre-deploy scripts |
postDeployScripts |
array |
Additional scripts appended after the global post-deploy scripts |
Script inheritance
| Target configuration |
What runs |
No preDeployScripts / postDeployScripts |
Global scripts only |
preDeployScripts defined |
Global scripts first, then target's additional scripts |
Commands
| Command |
Description |
| Deploy: Deploy Workspace |
Upload all files (respecting ignore list and mappings) with pre/post scripts |
| Deploy: Deploy Current File |
Upload only the active file |
| Deploy: Deploy (Skip Scripts) |
Upload a file or folder without running any scripts |
| Deploy: Deploy to Target… |
Choose a specific target or "All Targets" via Quick Pick, then deploy the selected file/folder |
| Deploy: Deploy Checked Files |
Upload the files checked in the Changed Files panel to all targets |
| Deploy: Deploy Checked Files to Target… |
Choose a specific target or "All Targets" for the checked files deployment |
| Deploy: Open / Create Configuration |
Open the Config Editor WebView |
| Deploy: Show Output |
Show the Deploy output channel |
| Deploy: Show Status Panel |
Open the Deploy Status WebView (also accessible from the status bar) |
| Deploy: Refresh Status |
Refresh the Deployment Status sidebar tree |
| Deploy: Refresh Changed Files |
Re-query SCM for changed files |
| Deploy: Check All Changed Files |
Check all items in the Changed Files panel |
| Deploy: Uncheck All Changed Files |
Uncheck all items in the Changed Files panel |
Deploy to Target… and Deploy Checked Files to Target… only appear when multiple targets are configured.
Open the Deploy sidebar from the activity bar.
Deployment Status
Shows the current state of the last deploy operation (idle / running / success / failed) with transferred/skipped/failed file counts.
Scripts
Lists configured scripts with their run status. Each script has an inline ▶ button to run it independently without triggering a full deployment.
Single target (no targets defined): flat list of all global pre and post scripts.
Multiple targets: scripts are grouped under collapsible sections, one per target. Each section shows the effective script list for that target (global scripts + the target's additional scripts combined). Clicking ▶ on a script under a target section runs it against that target's server.
Status icons update in real time:
⏳ spinning — currently running
✔ green — last run succeeded
✗ red — last run failed
○ — idle / disabled
Changed Files
Queries Git or SVN for files modified since the last commit. Check or uncheck individual files, then click Deploy Checked Files in the toolbar to upload only the selected ones.
- Checkbox state is persisted across VS Code restarts
- Click any file to open a side-by-side diff against the committed (HEAD) version
- Tree / Flat toggle — switch between a folder hierarchy and a flat list; in flat mode each file shows its directory as a description alongside the filename
Toolbar buttons (left to right):
| Button |
Description |
$(cloud-upload) |
Deploy all checked files to all targets |
$(server) |
Deploy checked files to a specific target (only when multiple targets configured) |
$(refresh) |
Re-query SCM for changed files |
$(check-all) |
Check all files |
$(circle-slash) |
Uncheck all files |
$(list-flat) / $(list-tree) |
Toggle between flat list and folder tree |
A Deploy ▶ submenu appears in both the Explorer and the editor right-click context menus:
- Deploy: Deploy Current File — upload the selected file with pre/post scripts
- Deploy: Deploy (Skip Scripts) — upload the selected file or folder, skipping all scripts
- Deploy: Deploy to Target… — pick a specific target or "All Targets" via Quick Pick (only shown when multiple targets are configured)
Deploy Status Panel
The status panel is a WebView that shows a live log of each deployment run.
- Not opened automatically — open it by clicking the status bar item (
$(cloud-upload) Deploy) or running Deploy: Show Status Panel
- Persistent history — logs accumulate across multiple deploy runs, separated by horizontal rules, until you explicitly clear them
- Open at any time — opening the panel mid-deploy (or after) replays the full log immediately; no output is lost
- Clear Logs button — manually clears all history and resets the status badge to Idle
Packaging and Internal Distribution
To package the extension as a .vsix for internal use:
npm run compile
vsce package
Install on any machine:
code --install-extension vscode-deploy-0.2.0.vsix
Or via the Extensions sidebar: ⋯ → Install from VSIX…
To uninstall the old version first:
code --uninstall-extension ShileiMao.vscode-deploy
Development
npm install # install dependencies
npm run compile # compile TypeScript → out/
npm run watch # compile in watch mode
npm test # run unit tests
npm run lint # lint source files