Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overview

Singularity Dotfiles Manager is a robust, extendable CLI tool designed to automate the installation and management of your system configuration and application ecosystem. Built with Rust, and powered by the Rhai scripting engine for ultimate flexibility.

Key Features

  • Release-Based Configuration: Deploy entire dotfile environments from a single JSON release config.
  • Programmable Hooks: Use the built-in Rhai Hook API to run logic at specific points during the installation process.
  • App Suites: Install from curated lists of applications tailored to your specific dotfiles.
  • Developer Friendly: Full API documentation.
  • Cross-Distribution Ready: Intelligent path resolution (including ~) and distribution detection.

Installation

The Singularity Dotfiles Manager is written in Rust. You will need the Rust toolchain installed to build the application from source. Use the following command to install the dotfiles manager directly from git:

cargo install --git https://github.com/JTreske/singularity-dotfiles-manager.git

Usage

Find help using the singularity-dotfiles-manager by running:

singularity-dotfiles-manager --help
.
├── 📂 apps/
│   ├── 📄 arch.json            # App suite for Arch and Arch-like distros
│   └── 📄 ...                  # App suites for other systems
├── 📂 dependencies/            # Package dependencies for the dotfiles repo separated by newline
│   ├── 📄 arch                 # Packages list for Arch and Arch-like distros
│   └── 📄 ...                  # Packages lists for other distros
├── 📂 dotfiles/                # The part of the repository that is symlinked to the home directory
│   ├── 📂 .config/
│   │   └── ...
│   └── ...
├── 📂 hooks/
│   ├── 📜 arch.rhai            # Hooks script for Arch and Arch-like distros
│   └── 📜 ...                  # Scripts for other distributions
├── 📄 dotfiles-rolling.json    # Release config for rolling release on main
├── 📄 dotfiles-stable.json     # Release config for stable release
└── 📄 install.sh               # Optional bootstrap script

Install Dotfiles

The core command of Singularity. It pulls a dotfiles repository based on a configuration file.

singularity-dotfiles-manager install https://example.com/release-config.json

The dotfiles release config is a detailed description of your dotfiles repository. Find the JSON schema here.

Extend the installation workflow with Rhai hook scripts as described in Hooks API.

App Suite

Let the user install from a suite of applications defined in your profile. The app suite is defined in JSON. Find the JSON schema here.

singularity-dotfiles-manager apps

Hooks API

The application supports custom scripting via Rhai, a lightweight embedded scripting language for Rust. Hooks allow you to extend the application’s behavior at specific points in the dotfiles installation workflow, using a simple, powerful syntax.

Requirements

The application looks for a script file, named <os-name>.rhai (e.g., arch.rhai), within the configured hooks directory.

Script Structure

Your script is processed in two stages:

  1. Initialization: The script is compiled and executed from top to bottom. This is where you should define global variables (stored within the Scope).
  2. Hook Execution: The application searches for specific functions within your script. These functions must take no parameters. If a function is defined, it is executed; if it is missing, the application simply continues.

Hook Functions

The following functions can be implemented in your script:

  • pre_backup() - Executed before the backup of the active profile is taken.
  • post_backup_pre_deps() - Executed after the backup is created and before the dependencies of the new dotfiles repository are installed.
  • post_deps_pre_install() - Executed after the dependencies are installed and before the new dotfiles are installed into a profile.
  • post_install_pre_restore() - Executed after the installation of the dotfiles into the profile and before the selected restore items are copied from the backup to the new active profile (only in case of an update).
  • post_restore_pre_link() - Executed after the restore is complete and before the files from defined dotfiles directory in the active profile are linked to the users home directory.
  • finalize() - Executed after the linking is complete.

What the Application Provides

When your script runs, the application injects several constants and modules into the Global Scope. These are available everywhere in your script.

Constants

NameTypeDescription
infoHookInformationContains context about the current run (paths, flags).
runnerRunnerA handle to the internal system runner for package management.

HookInformation

update: bool - whether this is an update or a new installation of the dotfiles
backup: bool - whether backup is performed or not (backups are always performed if profile exists)
install_dir: ImmutableString - the directory where the profile is located (defaults to ~/.mydotfiles)
profile_dir: ImmutableString - the profiles directory (<install_dir>/<dotfiles-id>)
dotfiles_dir: ImmutableString - the dotfiles directory defined within the profile_dir

The api Module

The core of your scripting power comes from the api module. It provides three main namespaces:

  • api::log: Functions for printing output to the terminal without disrupting the UI.
  • api::runner: High-level system operations like installing packages or cloning repositories.
  • api::utility: Helper functions for file I/O, JSON parsing, and user prompts.

Note

View the full API reference for a detailed list of every available function.

Tip

Find the definitions JSON file here.


Quickstart & Best Practices

Communicating Between Hooks

Because the script’s Scope is preserved for the entire lifetime of the application, you can use global variables to share state between different hook functions.

#![allow(unused)]
fn main() {
// This runs during initialization
let keyboard_layout = "";

fn pre_backup() {
  keyboard_layout = api::utility::select("Choose your keyboard layout", "de", [["us", "us", ""], ["de", "de", ""]]);
}

fn post_restore_pre_link() {
  api::log::info(`You chose keyboard layout: ${keyboard_layout}`);
}
}

Accessing Context

You can use the info object to make decisions based on the current environment:

#![allow(unused)]
fn main() {
fn post_deps_pre_install() {
  api::log::info(`Installing to: ${info.install_dir}`);

  if info.update {
    api::log::remark("Update flag detected, skipping backup...");
  } else if info.backup {
    api::log::step("Performing pre-install backup...");
  }
}
}

log

Namespace: global/api/log

fn debug

fn debug(msg: String)
Logs the given debug message using tracing. This message will not be shown to the user.

fn step

fn step(msg: String)
Logs the given step message using cliclack and tracing.

fn info

fn info(msg: String)
Logs the given info message using cliclack and tracing.

fn success

fn success(msg: String)
Logs the given success message using cliclack and tracing.

fn remark

fn remark(msg: String)
Logs the given remark message using cliclack and tracing.

fn warn

fn warn(msg: String)
Logs the given waring message using cliclack and tracing.

fn error

fn error(msg: String)
Logs the given error message using cliclack and tracing.

runner

Namespace: global/api/runner

fn install_package

fn install_package(runner: HookRunner, package: String)
Installs the provided package.

fn os_short_name

fn os_short_name(runner: HookRunner) -> String
Returns the short ID of the current operating system (e.g., "arch, "debian").

fn run_command

fn run_command(runner: HookRunner, program: String, args: Array, interactive: bool, request_password_injection: bool, env: Map)
fn run_command(runner: HookRunner, program: String, args: Array, interactive: bool, request_password_injection: bool, env: Map, cwd: String)
Runs any command with the stored permissions.

If root privileges are needed, set program to sudo or interactive to true.

Note: interactive mode disrupts the clean UI.

fn clone_git_repository

fn clone_git_repository(runner: HookRunner, url: String, target: String)
fn clone_git_repository(runner: HookRunner, url: String, target: String, tag: String)
Clones a git repository.

utility

Namespace: global/api/utility

fn note

fn note(prompt: String, msg: String)
Displays a note to the user.

fn confirm

fn confirm(msg: String, initial_value: bool) -> bool
Displays a confirmation prompt to the user.

fn select

fn select(msg: String, initial_value: String, items: Array) -> String
Displays a single-choice selection menu.

fn multi_select

fn multi_select(msg: String, initial_values: Array, items: Array) -> Array
Displays a multi-choice selection menu.

fn resolve_home

fn resolve_home(path: String) -> String
Resolves `~` in a path to the user's home directory.

fn read_file

fn read_file(path: String) -> String
Reads the entire content of a file into a string.

fn write_file

fn write_file(path: String, content: String, append: bool)
Writes a string to a file, optionally appending to existing content.

fn regex_replace

fn regex_replace(input: String, pattern: String, replace: String) -> String
Replaces occurrences in a string using a Regular Expression.

fn create_tmp_dir

fn create_tmp_dir() -> TmpDir
Creates a temporary directory that is deleted when the handle is dropped.

fn get_tmp_path

fn get_tmp_path(tmp_dir: TmpDir) -> String
Returns the absolute path of a temporary directory handle.

fn remove_file

fn remove_file(path: String)
Removes a file from the filesystem.

fn copy_recursive

fn copy_recursive(src: String, dst: String)
Recursively copies a directory or file to a destination.

fn parse_json

fn parse_json(json: String) -> ?
Parses a JSON string into a Rhai Map or Array.

fn path_exists

fn path_exists(path: String) -> bool
Checks if a path exists on the filesystem.

fn ensure_dir

fn ensure_dir(path: String)
Ensures a directory exists, creating it and any parents if necessary.