BSoft Laravel Sync
Deploy Laravel and PHP projects directly from VS Code or Cursor using Git-aware file detection, SSH and rsync.
The extension is designed for developers deploying Laravel or Pure PHP applications to Linux servers. It uses your existing OpenSSH configuration and never stores SSH passwords, private keys, .env files, or application secrets.
Features
- Laravel, Pure PHP, and generic project detection
- Multiple deployment targets in one workspace (QuickPick before deploy)
- Optional
localPath so a Flutter (or other) workspace can deploy a nested PHP API
- Git-aware changed file detection
- Preview deployment
- Deploy changed files
- Deploy last commit
- Full deployment
- SSH connection testing
- rsync file transfer
- project-specific excludes
- protected sensitive files
- optional composer install
- optional frontend asset build
- Laravel cache clearing
- optional queue restart
- remote deletion confirmation
- macOS support
- Linux support
- Windows support through WSL
Requirements
macOS
macOS normally includes OpenSSH and rsync. Confirm they are available:
git --version
ssh -V
rsync --version
Linux
Example install (Debian/Ubuntu):
sudo apt update
sudo apt install -y git openssh-client rsync
Windows 11
Requirements:
- Git
- Windows OpenSSH Client
- WSL
- Ubuntu or another compatible WSL distro
- rsync installed inside WSL
Example:
wsl --install -d Ubuntu
Then inside WSL:
sudo apt update
sudo apt install -y rsync openssh-client
Test:
wsl rsync --version
BSoft Laravel Sync automatically uses WSL rsync on Windows. SSH authentication still uses the Windows OpenSSH client and your Windows SSH keys / ssh-agent. You do not need to copy private keys into WSL.
SSH Setup
Create a host alias in ~/.ssh/config. The extension runs ssh <alias> and never reads this file itself. Prefer SSH keys instead of passwords. For passphrase-protected keys, unlock them with ssh-agent / ssh-add before deploying.
macOS / Linux example
Host example-vps
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
Confirm outside the editor:
ssh example-vps
Windows example
Host example-vps
HostName 203.0.113.10
User deploy
IdentityFile C:\Users\YourUser\.ssh\id_ed25519
IdentitiesOnly yes
Confirm:
ssh example-vps
Project Configuration
You do not need to create .bsoft-laravel-sync.json by hand.
- Open your project folder in VS Code or Cursor. For a Flutter app with a nested PHP API, open the Flutter workspace root.
- Press
Cmd+Shift+P / Ctrl+Shift+P.
- Run BSoft Laravel Sync: Configure Project.
The wizard asks for deployment name, environment, SSH Host, Remote Path, automation toggles, and optional Excluded Files. Nothing is written until you choose Save Configuration.
That creates or updates .bsoft-laravel-sync.json in the workspace root. This file is local configuration only and is never uploaded to the Remote Server. Editors offer autocomplete for this file via the bundled JSON schema.
For workspaces with a targets array, edit .bsoft-laravel-sync.json directly (Configure Project opens the file instead of flattening it back to a single target).
Example: Laravel app (workspace is the Laravel project)
{
"name": "My Laravel Production",
"environment": "production",
"sshHost": "example-vps",
"remotePath": "/home/example/app",
"autoClearCache": true,
"autoComposerInstall": true,
"autoBuildAssets": true,
"restartQueueAfterDeploy": false,
"confirmDeletes": true,
"exclude": ["mobile_app/**"]
}
Existing Laravel configs keep working without projectType, localPath, or targets. Detection uses the workspace root and finds artisan.
Example: MyCareer (multiple folders in one workspace)
Workspace root contains more than one deployable project. List them under targets. Preview, Deploy, Full Deploy, and Test Connection ask which target to use when more than one is defined.
{
"name": "MyCareer Production",
"environment": "production",
"sshHost": "mycareer-vps",
"confirmDeletes": true,
"targets": [
{
"name": "MyCareer API",
"localPath": "mycareer_api",
"remotePath": "/home/mycareer/domains/api.mycareer.mk/public_html",
"projectType": "php",
"autoClearCache": false,
"autoComposerInstall": false,
"autoBuildAssets": false,
"restartQueueAfterDeploy": false
},
{
"name": "MyCareer Website",
"localPath": "mycareer_web",
"remotePath": "/home/mycareer/public_html",
"projectType": "php",
"autoClearCache": false,
"autoComposerInstall": false,
"autoBuildAssets": false,
"restartQueueAfterDeploy": false
}
]
}
Each target is resolved relative to the currently opened workspace root. Deploying MyCareer API uploads, compares, and deletes only files under mycareer_api. It never touches mycareer_web.
A single-target targets array deploys that folder without a picker. Configs that still use top-level localPath / remotePath (no targets key) keep working unchanged.
Example: nested PHP API with top-level localPath (legacy)
Workspace root is the Flutter project. Deploy only mycareer_api:
{
"name": "MyCareer API",
"environment": "production",
"sshHost": "mycareer-vps",
"localPath": "mycareer_api",
"remotePath": "/home/mycareer/public_html/mycareer_api",
"autoComposerInstall": true,
"autoBuildAssets": false,
"confirmDeletes": true
}
projectType can be omitted (auto). The extension detects Pure PHP because mycareer_api has PHP files and no artisan file. Preview, Test Connection, and Deploy use mycareer_api as the source. Artisan commands are not run.
Configuration properties
| Property |
Description |
name |
Friendly deployment label shown in Preview and history |
environment |
Informal label such as production, staging, or development |
sshHost |
SSH Host alias from OpenSSH config |
remotePath |
Absolute Remote Path to the project on the server (single-target / legacy files) |
localPath |
Optional workspace-relative source folder (single-target / legacy files) |
targets |
Optional list of deployable folders. When more than one target exists, commands ask which one to deploy |
targets[].name |
Label shown in the target picker |
targets[].localPath |
Workspace-relative folder for that target |
targets[].remotePath |
Absolute Remote Path for that target |
targets[].projectType |
Optional per-target auto, laravel, php, or generic |
targets[].exclude |
Extra exclude globs for that target (merged with top-level exclude) |
targets[].autoClearCache |
Per-target Laravel cache clearing |
targets[].autoComposerInstall |
Per-target Composer install |
targets[].autoBuildAssets |
Per-target frontend build |
targets[].restartQueueAfterDeploy |
Per-target queue restart |
projectType |
auto (default), laravel, php, or generic. auto detects Laravel from an artisan file, Pure PHP from .php files, otherwise Generic |
autoClearCache |
Run php artisan optimize:clear after relevant Deploys (Laravel only) |
autoComposerInstall |
Run production composer install when Composer files change |
autoBuildAssets |
Run frontend build when frontend/Vite files change |
restartQueueAfterDeploy |
Run php artisan queue:restart after PHP/config Deploys (default off) |
confirmDeletes |
Require confirmation before remote deletions |
exclude |
Additional Excluded Files globs (cannot override Protected Files) |
Project-file values override matching VS Code settings. Forbidden keys (password, APP_KEY, private key fields, and similar) are ignored.
Do not put secrets in this file. Production .env stays on the Remote Server.
Commands
| Command |
What it does |
| BSoft Laravel Sync: Configure Project |
Guided wizard to create/update .bsoft-laravel-sync.json |
| BSoft Laravel Sync: Test Connection |
SSH handshake plus remote path and PHP checks (Artisan is required only for Laravel) |
| BSoft Laravel Sync: Preview Changes |
Shows deployable Git changes, Protected/Excluded Files, and post-deploy actions |
| BSoft Laravel Sync: Deploy Changed Files |
Uploads working-tree Changed Files |
| BSoft Laravel Sync: Deploy Last Commit |
Uploads the files from HEAD |
| BSoft Laravel Sync: Full Deploy |
Synchronizes project source; preserves remote-only production data |
| BSoft Laravel Sync: Show Deployment History |
Shows recent deployment history |
| BSoft Laravel Sync: Show Actions |
Compact action picker (also opened from the status bar) |
| BSoft Laravel Sync: Open Output |
Opens the BSoft Laravel Sync output channel |
| BSoft Laravel Sync: Open Settings |
Opens extension settings |
Deployment Flow
- Resolve deployment targets (
targets or legacy top-level localPath / remotePath)
- If more than one target exists, ask which one to deploy
- Resolve that target’s source folder relative to the workspace root
- Detect project type (Laravel, Pure PHP, or Generic)
- Detect Git changes in the selected source folder only
- Apply Protected Files and Excluded Files rules (including per-target
exclude)
- Preview
- Verify SSH
- Transfer with rsync to the selected
remotePath
- Run configured post-deploy actions for that target (Artisan only for Laravel)
Safety
.env is protected and never uploaded
- private keys and certificate material (
*.pem, *.key, *.p12, *.pfx) are protected
.bsoft-laravel-sync.json is local only and never uploaded
.git/, .github/, node_modules/, and vendor/ are protected
- remote deletions require confirmation when configured
- production migration commands never run silently
- Full Deploy never uses rsync
--delete
- Output Channel logs redact values that look like secrets
Windows / WSL Notes
Windows path conversion is handled internally by the extension.
C:\Projects\my-laravel-app
becomes:
/mnt/c/Projects/my-laravel-app
SSH continues to use Windows OpenSSH and your Windows ssh-agent. WSL is used only for rsync.
Troubleshooting
SSH connection fails
Confirm ssh example-vps works in a terminal without a password prompt. Unlock passphrase-protected keys with ssh-add. Check HostName, User, Port, and host key trust.
rsync not found
- macOS / Linux: Install rsync and ensure it is on PATH.
- Windows: Install rsync inside WSL (
sudo apt install rsync) and verify with wsl rsync --version.
WSL missing
Install WSL (wsl --install -d Ubuntu), reboot if prompted, then install rsync inside the distro.
remote path missing
The configured Remote Path does not exist or is not accessible for the SSH user. Create the directory or fix permissions / path spelling.
permission denied
SSH authentication failed in batch mode. Unlock your key with the system agent, or run ssh <host> once in a terminal.
artisan not found
The Remote Path may not be a Laravel application root, or artisan is missing there.
composer failure
Composer may be missing on the Remote Server, or composer.json / lockfile may be inconsistent. Check the Output Channel for details.
npm build failure
Node/npm may be missing on the Remote Server, or the frontend build script failed. Check the Output Channel for details.
License
MIT
Author
BSoft
Website: https://www.bsoft.mk