spatie/scotty
Scotty is a beautiful SSH task runner. Define tasks in a Scotty.sh file (bash with annotations) and run them on remote servers over SSH with clear, readable output. Compatible with Laravel Envoy, so you can use it as a drop-in replacement.
Install Scotty (preferred as a PHAR):
curl -L https://github.com/spatie/scotty/releases/latest/download/scotty -o scotty
chmod +x scotty
Or globally:
composer global require spatie/scotty
Create a Scotty.sh file in your project root with a basic server definition:
#!/usr/bin/env scotty
# @servers remote=deployer@your-server.com
Test SSH connectivity:
./scotty doctor
Write your first task (e.g., pullCode):
# @task on:remote
pullCode() {
cd /var/www/my-app
git pull origin main
}
Run the task:
./scotty run pullCode
Replace manual SSH deployments with a scripted workflow. For example, automate a Laravel deploy:
# @macro deploy pullCode runMigrations restartWorkers
# @task on:remote
pullCode() { cd /var/www/my-app && git pull origin main; }
# @task on:remote
restartWorkers() { php artisan horizon:terminate; }
Run with:
./scotty run deploy
Macro-Based Deploys
deploy, rollback).# @macro deploy pullCode installDeps migrate restartServices
scotty run deploy
Environment-Specific Scripts
@option to handle environment variables dynamically:
# @option env=production
# @task on:remote
deploy() { php artisan deploy --env=$ENV; }
scotty run deploy --env=staging
Local/Remote Hybrid Tasks
@task on:local and @task on:remote:
# @task on:local
buildAssets() { npm run build; }
# @task on:remote
deployAssets() { rsync -avz ./public/ user@server:/var/www/my-app/; }
Conditional Execution
if statements in tasks to handle edge cases:
# @task on:remote
backupDatabase() {
if [ "$ENV" = "production" ]; then
mysqldump -u user -p$db_password db_name > backup.sql
fi
}
Laravel Artisan Integration
Embed Scotty tasks in Laravel’s artisan commands for seamless CLI workflows:
// app/Console/Commands/DeployCommand.php
public function handle() {
$this->call('scotty:run', ['macro' => 'deploy']);
}
CI/CD Pipelines Use Scotty in GitHub Actions/GitLab CI:
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: ./scotty run deploy --branch=${{ github.ref_name }}
Shared Scripts
Store Scotty.sh in your repo to ensure consistency across environments. Use .gitignore to exclude sensitive data (e.g., SSH keys).
SSH Config Reuse
Leverage ~/.ssh/config for complex SSH setups (e.g., jump hosts, proxies):
# @servers production=prod-server
# ~/.ssh/config:
Host prod-server
HostName example.com
User deployer
ProxyCommand ssh -W %h:%p jump-host
SSH Key Management
~/.ssh/id_rsa). Specify custom keys via ~/.ssh/config or pass them via CLI:
scotty run deploy --ssh-key=/path/to/key.pem
Task Dependencies
--continue to bypass this:
scotty run deploy --continue
Blade vs. Bash Format
Scotty.sh) is preferred for simplicity and better IDE support (syntax highlighting, autocompletion).@option support (CLI flags are passed as Blade variables).Variable Scope
APP_DIR) are local to the task unless exported:
# @task on:remote
pullCode() {
export APP_DIR="/var/www/my-app" # Explicitly export
cd $APP_DIR
git pull origin $BRANCH
}
Pretend Mode Quirks
--pretend shows SSH commands but does not validate syntax. Always test with real runs after major changes.Pause/Resume Conflicts
p pauses execution but does not stop background processes (e.g., git pull or composer install). Use Ctrl+C to force-stop.Verbose Output Enable debug mode for detailed logs:
scotty run deploy --verbose
Dry Run with --pretend
Simulate execution to catch errors:
scotty run deploy --pretend
SSH Debugging
Use GIT_SSH_COMMAND or SSH_DEBUG to debug SSH issues:
SSH_DEBUG=1 scotty run deploy
Task Isolation Test tasks individually to isolate failures:
scotty run pullCode
scotty run runMigrations
Use scotty tasks
List available tasks/macros to avoid memorizing them:
scotty tasks
Alias Scotty
Add an alias to your shell config (~/.bashrc or ~/.zshrc):
alias scotty='./scotty'
Template Files
Start with a template Scotty.sh and customize it per project:
scotty init --format=bash --server=deployer@server.com
Environment Variables
Pass sensitive data via environment variables (e.g., DB_PASSWORD):
export DB_PASSWORD="secret"
scotty run deploy
Parallel Tasks
Use GNU Parallel or & for parallel execution (not natively supported in Scotty):
# @task on:remote
backup() {
mysqldump db_name > backup.sql &
php artisan optimize &
wait
}
Error Handling Add checks in tasks to fail gracefully:
# @task on:remote
deploy() {
if ! git pull origin $BRANCH; then
echo "Git pull failed!" >&2
exit 1
fi
}
Logging Redirect output to a log file for auditing:
scotty run deploy --log-file=deploy.log
Multi-Server Workflows Run tasks on multiple servers in sequence:
# @servers web-1=deployer@1.1.1.1 web-2=deployer@2.2.2.2
# @macro deploy deployToWeb1 deployToWeb2
# @task on:web-1
deployToWeb1() { ... }
# @task on:web-2
deployToWeb2() { ... }
How can I help you explore Laravel packages today?