JSDaffodil is a lightweight, declarative deployment automation framework for Node.js that simplifies remote server deployments through SSH. Inspired by pydaffodil, it provides a clean, step-by-step API for executing deployment tasks on remote servers.
- π Archive-Based File Transfer - Efficient tar.gz compression for fast bulk file transfers
- π Cross-Platform Support - Works seamlessly on Windows, Linux, and macOS
- π Multi-Key SSH Authentication - Automatic fallback across multiple SSH key types
- π¦ Dual Module Support - Full support for both CommonJS and ESM
- π― Step-by-Step Execution - Declarative task execution with clear progress tracking
- π Ignore Pattern Support -
.scpignorefile for excluding files from transfers - π¨ Beautiful CLI Output - Styled terminal output with progress bars and spinners
- β‘ Zero External Dependencies - Pure Node.js implementation for archive creation
This project includes comprehensive documentation:
- GUIDELINES.md - Complete usage guide with examples, best practices, troubleshooting, and real-world scenarios. Includes sample code from the
samples/directory. - DOCUMENTATION.md - Developer documentation covering architecture, code organization, testing, and extension points for contributors.
- CONTRIBUTING.md - Contribution guidelines, code review process, and collaboration best practices.
For quick examples, check the samples/ directory:
samples/sample.mjs- ESM module examplesamples/sample.cjs- CommonJS module example
npm install @marcuwynu23/jsdaffodil// test.mjs or test.js (with "type": "module" in package.json)
import { Daffodil } from "@marcuwynu23/jsdaffodil";
const deployer = new Daffodil({
remoteUser: "deployer",
remoteHost: "231.142.34.222",
remotePath: "/var/www/myapp",
port: 22, // Optional, defaults to 22
});
const steps = [
{
step: "Transfer application files",
command: async () => {
await deployer.transferFiles("./dist", "/var/www/myapp");
},
},
{
step: "Install dependencies",
command: () => deployer.sshCommand("cd /var/www/myapp && npm install"),
},
{
step: "Restart application",
command: () => deployer.sshCommand("pm2 restart myapp"),
},
];
await deployer.deploy(steps);// test.cjs or test.js (without "type": "module")
const { Daffodil } = require("@marcuwynu23/jsdaffodil");
const deployer = new Daffodil({
remoteUser: "deployer",
remoteHost: "231.142.34.222",
remotePath: "/var/www/myapp",
});
const steps = [
{
step: "Transfer files",
command: async () => {
await deployer.transferFiles("./dist", "/var/www/myapp");
},
},
{
step: "Run remote command",
command: () => deployer.sshCommand("ls -la /var/www/myapp"),
},
];
deployer.deploy(steps).catch(console.error);new Daffodil({
remoteUser: string, // SSH username
remoteHost: string, // Server hostname or IP
remotePath?: string, // Default remote path (default: ".")
port?: number, // SSH port (default: 22)
ignoreFile?: string, // Ignore file path (default: ".scpignore")
})Establishes SSH connection using available SSH keys. Automatically tries multiple key types (id_rsa, id_ed25519, id_ecdsa, id_dsa) in order.
Transfers files from local directory to remote server using archive-based compression.
localPath(string): Local directory path to transferdestinationPath(string, optional): Remote destination path (defaults toremotePath)
Features:
- Creates tar.gz archive locally (cross-platform)
- Transfers single archive file for efficiency
- Automatically extracts on remote server
- Respects
.scpignorepatterns - Cleans up archives after successful transfer
Executes a command locally and returns the output.
cmd(string): Command to execute
Executes a command on the remote server via SSH.
cmd(string): Command to execute remotely
Creates a directory on the remote server.
dirName(string): Directory name to create
Executes a series of deployment steps sequentially.
steps(Array): Array of step objects withstep(description) andcommand(async function)
JSDaffodil uses an efficient archive-based transfer method:
- Local Archive Creation - Files are compressed into a tar.gz archive using cross-platform Node.js libraries
- Single File Transfer - Only one archive file is transferred, significantly faster than individual file transfers
- Remote Extraction - Archive is automatically extracted on the remote server
- Automatic Cleanup - Both local and remote archives are cleaned up after successful transfer
This approach is especially beneficial for:
- Large projects with many files
- Slow network connections
- Reducing SSH connection overhead
JSDaffodil works seamlessly across all major operating systems:
- β Windows - Uses Node.js tar library (no external dependencies)
- β Linux - Native support
- β macOS - Native support
The framework automatically attempts to connect using multiple SSH key types in order:
id_rsaid_ed25519id_ecdsaid_dsa
This ensures compatibility with various SSH key configurations.
Create a .scpignore file in your project root to exclude files from transfers:
# Dependencies
node_modules
vendor
# Environment files
.env
.env.local
.env.production
# Logs
*.log
logs/
# Build artifacts
dist/
build/
*.map
# IDE
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
Ensure your SSH key is properly configured:
# Generate SSH key if needed
ssh-keygen -t ed25519 -C "[email protected]"
# Copy public key to server
ssh-copy-id deployer@your-server-ip
# Test connection
ssh deployer@your-server-ipAlways handle errors in your deployment scripts:
try {
await deployer.deploy(steps);
} catch (error) {
console.error("Deployment failed:", error.message);
process.exit(1);
}Use environment variables for sensitive information:
const deployer = new Daffodil({
remoteUser: process.env.DEPLOY_USER,
remoteHost: process.env.DEPLOY_HOST,
remotePath: process.env.DEPLOY_PATH,
});Add conditional logic to your deployment steps:
const steps = [
{
step: "Transfer files",
command: async () => {
await deployer.transferFiles("./dist", "/var/www/myapp");
},
},
{
step: "Run migrations",
command: () => deployer.sshCommand("cd /var/www/myapp && npm run migrate"),
},
...(process.env.NODE_ENV === "production"
? [
{
step: "Restart production server",
command: () => deployer.sshCommand("pm2 restart myapp"),
},
]
: []),
];| Option | Type | Default | Description |
|---|---|---|---|
remoteUser |
string |
Required | SSH username for remote server |
remoteHost |
string |
Required | Remote server hostname or IP address |
remotePath |
string |
"." |
Default remote directory path |
port |
number |
22 |
SSH port number |
ignoreFile |
string |
".scpignore" |
Path to ignore patterns file |
- Node.js >= 14.0.0
- SSH access to remote server
- SSH key configured in
~/.ssh/(or%USERPROFILE%\.ssh\on Windows)
Contributions are welcome! Please read our CONTRIBUTING.md guide for contribution guidelines, code review process, and best practices.
For developers, see DOCUMENTATION.md for architecture details and development setup.
MIT License - feel free to use this project for any purpose.
Inspired by pydaffodil - a Python deployment automation framework.
Made with β€οΈ by Mark Wayne B. Menorca