Sensible is a modern, declarative task runner for developers. It helps automate your project workflows with a clean and readable config format, without the overhead of traditional tools like Make, Ansible, or Bash scripts.
- 🔧 Declarative configuration using HCL
- 🖥️ Run tasks locally or remotely via SSH
- 🌍 Multi-environment support (base, staging, production, etc.)
- 🔐 Built-in encrypted secrets management
- 📊 System facts (CPU, memory, disk, OS info) available in actions
- 📦 Built-in components:
shell,installer,cron - 🔑 SSH authentication via password or private key
curl -sSL https://raw.githubusercontent.com/laughing-nerd/sensible/main/install.sh | bashYou can also specify a version:
VERSION=v1.0.0 curl -sSL https://raw.githubusercontent.com/laughing-nerd/sensible/main/install.sh | bashsensible initThis creates the following structure:
.sensible/
└── base/
├── actions/
│ └── sample-action.hcl
├── resources/
│ ├── hosts.hcl
│ └── values.hcl
└── secrets/
└── .secrets.enc
sensible run -f sample-actionInitialize sensible in your project directory.
sensible init # Creates .sensible/base/ structure
sensible init -e staging # Creates .sensible/staging/ structureRun an action file.
sensible run -f <action-file> # Run action from base environment
sensible run -f <action-file> -e staging # Run action from staging environmentManage encrypted secrets.
# Set a secret
sensible secret set -k API_KEY -v "my-secret-value"
sensible secret set -k DB_PASSWORD -v "password123" -e staging
# Get a secret
sensible secret get -k API_KEY
sensible secret get -a # Get all secrets
# Remove a secret
sensible secret remove -k API_KEYDefine reusable variables for your actions.
values {
server_address = "192.168.1.100:22"
app_name = "my-app"
deploy_path = "/var/www/my-app"
}Use in actions with ${values.variable_name}:
shell "deploy" {
command = "rsync -avz ./dist/ ${values.deploy_path}"
}Define remote hosts organized into groups for SSH execution.
group "Web Servers" {
host "web1" {
address = "192.168.1.10:22"
username = "deploy"
password = "secret" # or use private_key
timeout = 30 # optional, in seconds
}
host "web2" {
address = "${values.server_address}"
username = "deploy"
private_key = "/path/to/key" # alternative to password
}
}
group "DB Servers" {
host "db1" {
address = "192.168.1.20:22"
username = "admin"
password = "${secrets.db_password}"
}
}Secrets are stored encrypted in .secrets.enc. Manage them with the CLI:
sensible secret set -k DB_PASSWORD -v "supersecret"Use in actions with ${secrets.secret_name}:
shell "connect-db" {
command = "mysql -u admin -p${secrets.db_password} mydb"
}Actions are defined in .hcl files inside the actions/ directory. Each action can contain multiple components that execute sequentially.
action "My Action" {
# Optional: specify host groups for remote execution
# Omit this line to run locally
groups = ["Web Servers", "DB Servers"]
shell "step 1" {
command = "echo 'Hello World'"
}
installer "step 2" {
packages = ["git", "curl"]
}
}Execute shell commands locally or remotely.
shell "build app" {
command = "npm run build"
}
shell "deploy" {
command = <<-EOT
cd /var/www/app
git pull origin main
npm install
pm2 restart all
EOT
}Install packages using the system's package manager. Automatically detects the available package manager (apt, dnf, pacman, brew, etc.).
installer "install dependencies" {
packages = ["nginx", "git", "curl"]
preferred = "apt" # optional: prefer a specific package manager
}Supported package managers: apt, apt-get, dnf, pacman, yum, apk, brew, port, nix-env, pkg, zypper
Add or remove cron jobs.
# Add a cron job
cron "backup job" {
type = "add"
expression = "0 2 * * *" # Every day at 2 AM
job = "/scripts/backup.sh"
user = "root" # optional
}
# Remove a cron job
cron "remove old job" {
type = "remove"
job = "/scripts/old-backup.sh"
}Sensible supports three types of variables that can be used in your actions:
User-defined variables from values.hcl:
shell "example" {
command = "echo ${values.app_name}"
}Encrypted secrets managed via CLI:
shell "example" {
command = "echo ${secrets.api_key}"
}System information automatically gathered at runtime:
| Fact | Description |
|---|---|
facts.os |
Operating system (linux, darwin, etc.) |
facts.os_version |
OS version |
facts.kernel_version |
Kernel version |
facts.architecture |
System architecture |
facts.hostname |
Machine hostname |
facts.uptime |
System uptime in seconds |
facts.boot_time |
Boot timestamp |
facts.cpu_model |
CPU model name |
facts.cpu_cores |
Number of CPU cores |
facts.cpu_logical_cores |
Number of logical CPU cores |
facts.memory_total_mb |
Total memory in MB |
facts.memory_available_mb |
Available memory in MB |
facts.disk_total |
Total disk space in MB |
facts.disk_used |
Used disk space in MB |
facts.disk_free |
Free disk space in MB |
Example:
shell "system info" {
command = "echo 'Running on ${facts.os} with ${facts.memory_total_mb}MB RAM'"
}Sensible supports multiple environments. Each environment has its own configuration:
# Initialize different environments
sensible init -e base # Default
sensible init -e staging
sensible init -e productionThis creates:
.sensible/
├── base/
│ ├── actions/
│ ├── resources/
│ └── secrets/
├── staging/
│ ├── actions/
│ ├── resources/
│ └── secrets/
└── production/
├── actions/
├── resources/
└── secrets/
Run actions in specific environments:
sensible run -f deploy -e staging
sensible run -f deploy -e productionNote: Values from base environment are loaded first, then overridden by environment-specific values.
action "Deploy Application" {
groups = ["Web Servers"]
shell "pull latest code" {
command = "cd ${values.deploy_path} && git pull origin main"
}
shell "install dependencies" {
command = "cd ${values.deploy_path} && npm ci --production"
}
shell "run migrations" {
command = "cd ${values.deploy_path} && npm run migrate"
}
shell "restart services" {
command = "sudo systemctl restart ${values.app_name}"
}
shell "health check" {
command = "curl -f http://localhost:${values.app_port}/health || exit 1"
}
}