Setup your personal environment with a single command. Useful when a shell-script becomes too complicated or not enough for this task.
- Configuration file has well-defined structure and simple syntax.
- Allows setup process to be granural, selective and adjustible.
- Has no external dependencies, so can be used right away in a completely fresh environment.
- Written with Bash 3.2, so it works even on macOS with its stock Bash installation.
curl -fsSL PUT-URL-TO-YOUR-TWIGS-FILE-HERE.txt | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/shrpnsld/nest-in/master/nest-in)" -- --
wget -qO- PUT-URL-TO-YOUR-TWIGS-FILE-HERE.txt | /bin/bash -c "$(wget -qO- https://raw.githubusercontent.com/shrpnsld/nest-in/master/nest-in)" -- --
$ nest-in [-sdnh] [-- [<twigs.txt>]]
<target>...
– specify targets to nest.-d
– show all target dependencies.-n
– do not run, only show assembled script.--system-info
– print detected system information.-- <twigs.txt>
– specify file with nesting configuration (should be the last argument).--
– read nesting configuration from stdin (should be the last argument).-h
– show help message.
If no --
was passed, then input file is searched under ~/.config/nest-in/twigs.txt
.
The entire nesting process is divided into steps. Each nesting step is defined as a target with zero or more requirements, dependencies, and artifacts, with or without a script. Requirements are preconditions for the environment that must be met for the target to be nested. Dependencies are other targets that are nested before the target itself. Artifacts are files, directories, or programs produced by the target. If these artifacts already exist, the target is considered to be already nested. All shell scripts for the chosen targets will be combined into a single work script, sharing the same scope.
Target declaration should start at the beginning of the line and should not be preceeded by any whitespace. Each script line should be preceeded by any whitespace. Target names can include letters, numbers, dashes, and underscore characters, but they must always start with a letter.
cmake
brew install cmake
Dependencies should be listed after a /
.
nvim / dotfiles stow
brew install neovim
cd ~/.dotfiles
stow nvim
Artifacts are also listed after the same /
as dependencies. Each artifact should be enclosed in a pair of single quotes and can specify either a path to a file/directory or a program name:
dotfiles / git '~/.dotfiles/'
git clone https://github.com/me/dotfiles ~/.dotfiles
Requirements are listed after the same /
as dependencies and artifacts. Each requirement should be enclosed in square brackets. The same target can have multiple variants with different sets of requirements, each variant can have its own dependencies, artifacts, and script. The example below declares first variant to be chosen if the operating system is macOS, and the second variant will be chosen if the environment has the available program apt-get
.
installer / [macos]
INSTALL='brew install'
installer / [avail:apt-get]
INSTALL='sudo apt install'
If multiple target variants have their requirements met, the first one declared will be chosen. So, with the example below, if the environment has both curl
and wget
installed, the first variant will be chosen; if only wget
is present, then the second one will be selected.
downloader / [avail:curl]
DOWNLOAD='curl -fsSL'
downloader / [avail:wget]
DOWNLOAD='wget -qO-'
Targets might not always have a variant with met requirements for every environment. In such a case, if nothing depends on this target, it will be skipped without an error. In the example below, when nesting in Ubuntu, brew
doesn't have any variant for Ubuntu, but since pkgmgr / [ubuntu]
does not depend on it, there is no error.
brew / [macos]
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/.../install.sh)"
pkgmgr / [macos] brew
INSTALL='brew install'
pkgmgr / [ubuntu]
INSTALL='sudo apt install'
By default, all targets will be chosen for nesting. But some targets may not have been designed to be included in the default nesting. In the example below, the cleanup
target removes configurations and uninstalls programs that were installed previously. To exclude it from the default nesting, it can be marked with !
after its name, and it will be nested only if specified through a command line or if it is a dependency for another target.
cleanup!
cd ~/.dotfiles
stow --delete tmux ranger nvim git
rm -rf ~/.dotfiles
brew uninstall tmux ranger nvim git stow
brew autoremove
When some work needs to be done before or after nesting, .first
and .last
targets can be used. These targets can have requirements but cannot have dependencies or artifacts. In the example below, .first
defines a global variable with the dotfiles path.
.first
DOTFILES_PATH="~/.dotfiles"
When predefined requirements are not enough, .reqs
target can be used to define Bash functions as custom requirements. This target can't have requirements, dependencies or artifacts.
spyware / [name:jasonbourne]
sudo apt install spyware
.reqs
name() { [[ $USER == $1 ]]; }
Requirements always constist of a name and may have a set of specifiers, where each specifier is preceeded by a :
. Requirement names can have letters, numbers, dash and underscore characters, but always should start with a letter. For all predefined requirements, any number of last specifiers or version components can be dropped, to make requirement broader.
[avail:<command>]
– specified command is available.
[os:<name>:<version>]
– OS name or Linux distribution matches specified name and version.
[macos:<version>]
– OS is macOS and matches specified version.
[cygwin]
– OS is cygwin
[linux:<family>:<dist>:<version>]
– OS is Linux and matches specified family, distribution and version.
[arch|debian|redhat|suse:<distribution>:<version>]
– predefined shortcuts of [linux:::]
requirement for some Linux families.
[manjaro|ubuntu|elementary|kali|tails|fedora|opensuse:<version>]
– predefined shortcuts of [linux:::]
requirement for some Linux distributions.
Artifact types are determined by the following examples:
~/artifact/is/a/file
– path with no trailing/
~/artifact/is/a/direcotry/
– path with trailing/
program
– no path, just a name
Comments are not supported and any syntax-check pass for a shell-style comment is just a happy accident :)
# "nvim" depends on "pkgmgr" and "dotfiles" being nested first
nvim / pkgmgr dotfiles
$INSTALL neovim
cd "$DOTFILES_DIR"
stow nvim
# "dotfiles" needs no dependencies but it produces two artifacts: directory "~/.dotfiles/"
# and program "stow". if both already exist, then target is considered nested.
dotfiles / '~/.dotfiles/' 'stow'
$INSTALL stow
git clone https://github.com/me/dotfiles "$DOTFILES_DIR"
# this variant of "pkgmgr" will be used only on macOS. its script uses Homebrew,
# so target declaration lists dependency on "brew" target.
pkgmgr / [macos] brew
$INSTALL='brew install'
$UNINSTALL='brew uninstall'
# this variant of "pkgmgr" will be used only if "apt-get" is available.
pkgmgr / [avail:apt-get]
$INSTALL='sudo apt-get -y install'
$UNINSTALL='sudo apt-get -y uninstall'
# target for installing Homebrew on macOS. it lists "brew" as an artifact,
# so if Homebrew is already installed, there's no need for installation
brew / [macos] 'brew'
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# dedicated target for cleanup, intended to be used only explicitly, like so:
# $ nest-in cleanup
clenaup!
cd "$DOTFILES_DIR"
stow --delete nvim
rm -rf "$DOTFILES_DIR"
$UNINSTALL nvim stow
# "setup" target that defines a variable that would be used throughout the nesting process
.first
DOTFILES_DIR="$HOME/.dotfiles"
# "home" and "work" targets specify what should be nested through their dependencies.
# they are made dedicated, because there's no sense for both of them to be included
# in the default nesting at the same time.
home! / git-personal pet-projects
work! / git-work caffeinate games
# your personal ".gitconfig" for your pet projects
git-personal! / stow
cd "$DOTFILES_DIR"
stow git-personal
# setting up ".gitconfig" at your work
git-work!
read -p 'git name: ' name
read -p 'git email: ' email
git config --global user.name="$name"
git config --global user.email="$email"
# will keep your status "online", while you're avoiding your responsibilities
caffeinate
$INSTALL caffeinate
# keeps your sanity and helps to get through your work day
games
$INSTALL nethack myman ninvaders
pet-projects / git-personal
mkdir "$PROJECTS_DIR"
cd "$PROJECTS_DIR"
git clone https://github.com/me/pet
git clone https://github.com/me/another-pet
git clone https://github.com/me/and-another-one
# in case you're fired, hide the evidence
cleanup!
$UNINSTALL games caffeinate
# and you've seen all this before
pkgmgr / [macos] brew
$INSTALL='brew install'
$UNINSTALL='brew uninstall'
pkgmgr / [avail:apt-get]
$INSTALL='sudo apt-get -y install'
$UNINSTALL='sudo apt-get -y uninstall'
brew / [macos] 'brew'
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
.first
DOTFILES_DIR="$HOME/.dotfiles"
PROJECTS_DIR="$HOME/Projects"
https://gist.github.com/shrpnsld/933a367bcba6a3b42b82fd901b7736eb