README
¶
Mooncake 
Space fighters provisioning tool, Chookity!
Installation
go install github.com/alehatsman/mooncake@latest
Usage
Mooncake features an animated TUI (Text User Interface) by default that shows real-time progress with an animated character. Use --raw to disable the animation.
# Run configuration
mooncake run --config config.yml
# With variables file
mooncake run --config config.yml --vars vars.yml
# With sudo password
mooncake run --config config.yml --sudo-pass <password>
# Filter by tags
mooncake run --config config.yml --tags dev
mooncake run --config config.yml --tags dev,prod,test
# Preview what would be executed (dry-run)
mooncake run --config config.yml --dry-run
# Disable animated UI (use raw console output)
mooncake run --config config.yml --raw
# With debug logging
mooncake run --config config.yml --log-level debug
CLI Flags
--config, -c: Path to configuration file (required)--vars, -v: Path to variables file--log-level, -l: Log level - debug, info, or error (default: info)--sudo-pass, -s: Sudo password for steps withbecome: true--tags, -t: Filter steps by tags (comma-separated)--dry-run: Preview what would be executed without making any changes (validates and shows preview)--raw, -r: Disable animated TUI and use raw console output
Features
- Execute shell commands with templating
- Create files and directories with configurable permissions
- Render templates using pongo2
- Conditional execution with expressions
- Include other configuration files
- Load variables from external files
- File tree iteration with with_filetree
- List iteration with with_items
- Relative path resolution
- Global system facts (os, arch)
- Animated TUI with live progress tracking
- Dry-run mode for safe preview and validation
Dry-Run Mode
Preview what would be executed and validate your configuration without making any changes to your system:
mooncake run --config config.yml --dry-run
What it does:
- Validates YAML syntax and step structure
- Checks that required files exist (template sources, included configs, variable files)
- Verifies paths can be expanded and variables resolved
- Shows commands that would be executed
- Shows files and directories that would be created
- Shows templates that would be rendered
- Shows variables that would be set
- Processes includes recursively to show all steps
Example output:
[1/3] Create directory
[DRY-RUN] Would create directory: /home/user/.config (mode: 0755)
[2/3] Render config
[DRY-RUN] Would template: ./template.j2 -> /home/user/.config/app.conf (mode: 0644)
[3/3] Run setup
[DRY-RUN] Would execute: apt install neovim
[DRY-RUN] With sudo privileges
Use cases:
- Test and validate configurations before applying them
- Preview changes in production environments
- Debug complex configurations with conditionals and includes
- Verify variable substitution and template rendering
- Check that all required files exist
File Structure
YAML Format
Configuration files are YAML arrays of steps. Each step must have exactly one action:
- name: First step
shell: echo "hello"
- name: Second step
file:
path: /tmp/test
state: directory
Path Resolution
Mooncake uses Node.js-like relative path resolution. All relative paths are resolved relative to the current configuration file, not the working directory.
project/
├── main.yml
├── configs/
│ ├── neovim.yml
│ └── templates/
│ └── init.lua.j2
└── dotfiles/
└── .bashrc
main.yml:
- name: Include neovim config
include: ./configs/neovim.yml # Relative to main.yml
configs/neovim.yml:
- name: Render init.lua
template:
src: ./templates/init.lua.j2 # Relative to neovim.yml
dest: ~/.config/nvim/init.lua
Organizing Configurations
You can organize your configuration in multiple ways:
Flat structure:
provisioning/
├── main.yml
├── linux.yml
├── macos.yml
└── common.yml
Nested structure:
provisioning/
├── main.yml
├── os/
│ ├── linux.yml
│ └── macos.yml
├── apps/
│ ├── neovim.yml
│ ├── tmux.yml
│ └── zsh.yml
└── templates/
├── init.lua.j2
├── tmux.conf.j2
└── zshrc.j2
main.yml:
- vars:
config_dir: ~/.config
- name: Load OS-specific configuration
include: ./os/linux.yml
when: os == "linux"
- name: Load OS-specific configuration
include: ./os/macos.yml
when: os == "darwin"
- name: Setup neovim
include: ./apps/neovim.yml
- name: Setup tmux
include: ./apps/tmux.yml
Path Types
Relative paths (start with ./ or ../):
include: ./configs/app.yml
src: ../templates/config.j2
Absolute paths:
dest: /etc/nginx/nginx.conf
path: /var/log/app
Home directory paths (use ~ or template filter):
dest: ~/.config/nvim/init.lua
dest: "{{ '~/.config' | expanduser }}/nvim/init.lua"
Configuration
Variables
Define and use variables throughout your configuration:
- vars:
config_dir: ~/.config
nvim_dir: "{{config_dir}}/nvim"
- name: Create nvim directory
file:
path: "{{nvim_dir}}"
state: directory
Include Variables
Load variables from external YAML files:
- name: Load environment variables
include_vars: ./env.yml
Global Variables
Available in all steps:
os: linux | darwin | windowsarch: amd64 | arm64 | etc
File
Create files or directories with optional permissions:
- name: Create directory
file:
path: ~/.config/nvim
state: directory
mode: "0755"
- name: Create empty file
file:
path: ~/.config/nvim/init.lua
state: file
mode: "0644"
- name: Create file with content
file:
path: /tmp/test.txt
state: file
content: "Hello World"
Template
Render templates using pongo2 syntax:
- name: Render config file
template:
src: ./init.lua.j2
dest: ~/.config/nvim/init.lua
mode: "0644"
vars:
port: 8080
debug: true
Template supports pongo2 features:
# Variables
{{ variable_name }}
# Conditionals
{% if debug %}
debug_mode = true
{% endif %}
# Loops
{% for item in items %}
- {{ item }}
{% endfor %}
# Filters
{{ path|expanduser }} # Expands ~ to home directory
{{ text|upper }}
Shell
Execute shell commands:
- name: Install packages
shell: brew install neovim ripgrep
- name: Run multiple commands
shell: |
echo "Starting setup"
mkdir -p ~/.local/bin
echo "Setup complete"
Include
Include other configuration files:
- name: Include Linux configuration
include: ./linux.yml
- name: Include with relative path
include: ./configs/neovim.yml
Conditional Execution
Use when to conditionally execute steps:
- name: Install Linux packages
shell: apt install neovim
when: os == "linux"
- name: Install macOS packages
shell: brew install neovim
when: os == "darwin"
- name: Complex condition
shell: echo "ARM Mac"
when: os == "darwin" && arch == "arm64"
Supported operators:
- Comparison:
==,!=,>,<,>=,<= - Logical:
&&,||,! - Arithmetic:
+,-,*,/,%
File Tree Iteration
Iterate over files in a directory:
- name: Copy dotfiles
template:
src: "{{ item.src }}"
dest: "~/.config/{{ item.name }}"
with_filetree: ./dotfiles
Each iteration provides:
item.src: Source file pathitem.name: File nameitem.is_dir: Boolean indicating if item is directory
List Iteration
Iterate over a list of items using with_items:
- vars:
packages:
- neovim
- ripgrep
- tmux
- fzf
- name: Install packages
shell: brew install {{ item }}
with_items: "{{ packages }}"
You can also iterate over inline lists:
- vars:
users:
- alice
- bob
- charlie
- name: Create user directories
file:
path: "/home/{{ item }}"
state: directory
mode: "0755"
with_items: "{{ users }}"
Each iteration provides the current item in the item variable
Tags
Filter execution by tags. When tags filter is specified, only steps with matching tags are executed:
- name: Step without tags
shell: echo "Always runs when no filter specified"
- name: Install development tools
shell: brew install neovim ripgrep
tags:
- dev
- tools
- name: Setup production
shell: setup-production.sh
tags:
- prod
- name: Deploy to staging
shell: deploy-staging.sh
tags:
- deploy
- staging
Behavior:
- No tags filter: All steps execute (including untagged steps)
- With tags filter: Only steps with matching tags execute; untagged steps are skipped
- Multiple tags: Step executes if it has ANY of the specified tags
Run with tags:
# Run only dev-tagged steps
mooncake run --config config.yml --tags dev
# Run dev OR prod tagged steps
mooncake run --config config.yml --tags dev,prod
# Run steps tagged with deploy OR staging
mooncake run --config config.yml --tags deploy,staging
Sudo/Become
Execute commands with sudo:
- name: Install system package
shell: apt install neovim
become: true
Provide sudo password:
mooncake run --config config.yml --sudo-pass <password>
Note: Only steps with become: true will use sudo.
File Permissions
Specify file permissions in octal format:
- name: Create executable script
file:
path: ~/.local/bin/script.sh
state: file
content: "#!/bin/bash\necho hello"
mode: "0755"
- name: Create private config
template:
src: ./secret.yml.j2
dest: ~/.config/secret.yml
mode: "0600"
Examples
See the examples/ directory for complete working examples:
- Basic examples: Hello world, files, conditionals, tags
- Advanced examples: Multi-file configurations, includes, variables
Example Configuration
- vars:
config_dir: ~/.config
nvim_dir: "{{config_dir}}/nvim"
nvim_config: "{{nvim_dir}}/init.lua"
- name: Ensure neovim config directory exists
file:
path: "{{nvim_dir}}"
state: directory
mode: "0755"
- name: Render neovim configuration
template:
src: ./init.lua.j2
dest: "{{nvim_config}}"
mode: "0644"
- name: Install neovim on macOS
shell: brew install neovim
when: os == "darwin"
- name: Install neovim on Linux
shell: apt install neovim
become: true
when: os == "linux"
- name: Copy plugin configs
template:
src: "{{item.src}}"
dest: "{{nvim_dir}}/lua/{{item.name}}"
with_filetree: ./nvim/lua
when: item.is_dir == false