cramae

cramae

A configuration management tool inspired by mitamae, rewritten in Crystal. Fast, single-binary provisioning with a mitamae-compatible DSL.

Crystal License CI

Why

mitamae uses mruby for a Chef-like DSL — fast and single-binary. cramae brings the same experience to Crystal:

  • Crystal-native DSL — mitamae-like syntax using Crystal blocks and with receiver
  • Single binary — compile and deploy, no runtime dependencies
  • Type-safe — Crystal's compiler catches errors before execution
  • Dual format — Crystal DSL or YAML recipes

Quick Start

Install

# Build from source (requires Crystal >= 1.10)
git clone https://github.com/example/cramae.git
cd cramae
shards install
crystal build src/cli_entry.cr -o /usr/local/bin/cramae

Your first recipe (Crystal DSL)

# recipe.cr
require "cramae"

Cramae.recipe do
  package "nginx" do
    action :install
  end

  service "nginx" do
    action [:enable, :start]
  end

  file "/etc/nginx/nginx.conf" do
    content <<-CONF
      worker_processes 1;
      events { worker_connections 1024; }
      http {
        server {
          listen 80;
          location / { root /var/www/html; }
        }
      }
    CONF
    mode "644"
    owner "root"
    group "root"
    notifies :reload, "service[nginx]"
  end

  directory "/var/www/html" do
    mode "755"
    owner "www-data"
    group "www-data"
  end

  file "/var/www/html/index.html" do
    content "<h1>Hello from Cramae!</h1>\n"
    mode "644"
  end
end
cramae run recipe.cr          # apply
cramae run --dry-run recipe.cr  # preview

Or use YAML recipes

# recipe.yml
resources:
  - type: package
    name: nginx
    action: install

  - type: service
    name: nginx
    action: enable,start

  - type: file
    path: /etc/nginx/nginx.conf
    content: |
      server { listen 80; }
    mode: "644"
    owner: root
    group: root
cramae local recipe.yml
cramae local --dry-run recipe.yml  # preview

Commands

Command Description
cramae local RECIPE... Run YAML recipes
cramae run RECIPE... Run Crystal DSL recipes
cramae version Print version
cramae help [COMMAND] Show help

Options

Flag Description
-n, --dry-run Preview changes without applying
-l, --log-level LEVEL Set log level (debug, info, warn, error)
-j, --node-json FILE Load node attributes from JSON (local only)
-y, --node-yaml FILE Load node attributes from YAML (local only)
--color, --no-color Enable/disable colored output

Resource Reference

file

Manage file content, permissions, and ownership.

file "/etc/hosts" do
  action :create       # create, delete, edit
  content "127.0.0.1 localhost\n"
  mode "644"
  owner "root"
  group "root"
  sensitive false      # hide content in diffs
  atomic_update false  # atomic via tempfile + rename
  not_if "test -f /etc/hosts"
end

directory

directory "/var/www" do
  action :create       # create, delete
  mode "755"
  owner "www-data"
  group "www-data"
end

execute

execute "systemctl daemon-reload" do
  action :run
  cwd "/tmp"
  only_if "test -f /etc/systemd/system/nginx.service"
end

package

package "nginx" do
  action :install      # install, remove
  version "1.24.0"
end

service

service "nginx" do
  action [:enable, :start]  # start, stop, restart, reload, enable, disable
end

template

Supports {{ variable }} substitution.

template "/etc/nginx/sites-enabled/default" do
  source "default.erb"      # auto-looks in templates/
  mode "644"
  variables({"port" => "80", "server_name" => "example.com"})
end

git

git "/opt/myapp" do
  repository "https://github.com/example/myapp.git"
  revision "main"
  recursive true
  depth 1
end

link

link "/usr/local/bin/myapp" do
  to "/opt/myapp/bin/myapp"
  force true
end

user / group

user "myapp" do
  uid 1001
  gid "myapp"
  shell "/bin/bash"
  home "/home/myapp"
  create_home true
end

group "myapp" do
  gid 1001
end

gem_package

gem_package "bundler" do
  action :install      # install, uninstall, upgrade
  version "2.4.0"
end

http_request

http_request "/tmp/data.json" do
  action :get           # get, post, put, delete
  url "https://api.example.com/data"
  headers({"Authorization" => "Bearer token"})
end

remote_file / remote_directory

remote_file "/etc/config.yml" do
  source "files/config.yml"    # auto-looks in files/
  mode "600"
end

remote_directory "/opt/static" do
  source "files/static/"
  mode "755"
end

Notifications & Subscriptions

Trigger actions on other resources when a resource changes:

file "/etc/nginx/nginx.conf" do
  content "..."
  notifies :reload, "service[nginx]"        # delayed by default
  notifies :restart, "service[nginx]", :immediately
end

service "nginx" do
  action :nothing                            # only act when notified
  subscribes :reload, "file[/etc/nginx/nginx.conf]"
end

Conditional Execution

file "/tmp/cache" do
  content "data"
  only_if "test -f /etc/app.conf"   # run only if command succeeds
  not_if "test -f /tmp/cache"       # skip if command succeeds
end

Node Attributes

cramae local --node-json nodes/production.json recipe.yml
{"env": "production", "hostname": "web01"}

Project Structure

src/
├── cli_entry.cr          # Binary entry point
├── cramae.cr             # Library entry, global Log
└── cramae/
    ├── backend.cr        # Shell command execution
    ├── cli.cr            # CLI parsing
    ├── command_result.cr # Command execution result
    ├── dsl.cr            # Crystal-native DSL
    ├── logger.cr         # Colored, indented logging
    ├── node.cr           # Node attributes
    ├── recipe.cr         # Recipe, RecipeLoader
    ├── recipe_executor.cr# Recipe execution engine
    ├── resource.cr       # 15 resource types
    ├── shellwords.cr     # Shell escaping
    ├── specinfra.cr      # Native system operations
    └── executors/        # One executor per resource type

Development

shards install
crystal build src/cli_entry.cr -o bin/cramae
crystal spec                   # 46 tests
crystal spec spec/cramae/dsl_spec.cr  # specific test file

See CONTRIBUTING.md for setup, code style, and how to add a new resource type, and CHANGELOG.md for release history. AI coding agents should read AGENTS.md before making changes.

License

MIT

Repository

cramae

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 1
  • about 4 hours ago
  • June 24, 2026
License

MIT License

Links
Synced at

Wed, 24 Jun 2026 09:41:47 GMT

Languages