Package: booty-cli
Booty is a language and command line utility for bootstrapping the setup of personal OS installs. Its goal is to execute all of the things
that you do after your first boot, like install packages through your package manager, configure your shell, setup your terminal tools/IDE,
and install SDKs for various programming languages, without you having to download everything manually and run various scripts in the right
order.You model your setup process as Targets and declare which ones depend on which ones, then executebootyto set everything up, if
everything isn't already setup. Your install.booty file will specify varioussetupandis_setupmethods thatbootyuses to set your
system up, and to test if everything was actually setup.InstallInstalling from pippipinstallbooty-cliOr download the appropriate binary from the latest release. This script just downloads it for you and marks it as executable, but you should
move it somewhere on your path. The script will drop it in yourpwd.Linuxcurlhttps://raw.githubusercontent.com/naddeoa/booty/master/scripts/booty-download-linux.sh|bashMac x86curlhttps://raw.githubusercontent.com/naddeoa/booty/master/scripts/booty-download-mac-x86.sh|bashMac Armcurlhttps://raw.githubusercontent.com/naddeoa/booty/master/scripts/booty-download-mac-universal.sh|bashUsageYou can runbooty --helpto see all of the options. You'll generally cd to a directory with aninstall.bootyfile and runbooty.
You'll see a table that shows the status of all of your targets and you'll be prompted to install all of the missing ones, which will
display in real-time as a second table with a row for each target getting setup.Usage: booty [OPTIONS]

Options:
  -c, --config TEXT   Path to the booty file. Defaults to ./install.booty
  -s, --status        Check the status of all known targets
  -i, --install       Install all uninstalled targets
  -d, --debug         See the AST of the config file
  -l, --log-dir TEXT  Where to store logs. Defaults to ./logs
  --no-sudo           Don't allow booty to prompt with sudo -v. Instead, you
                      can manually run sudo -v before using booty to cache
                      credentials for any targets that use sudo. By default,
                      booty runs sudo -v upfront if you use sudo in any
                      targets.
  -y, --yes           Don't prompt for confirmation
  --help              Show this message and exit.Testing/Dry RunsSetting your system up is inherently side effect prone. Every command fromapttopipis probably doing something to some part of your
system that you didn't realize. If you want to execute yourinstall.bootyfile without worrying about what might go wrong before you use
it for real then the best way to do that is via Docker (which is how this repo does "integration" testing).There is apublic docker image (naddeoa/booty:ubuntu22.04)that you can
base your own Dockerfile on that mimics what a fresh install looks like for Ubuntu 22.04 users. You can create an image that does roughly
what you would do to your system to get thebootycommand working, and then run it and make sure everything works as expected. The image's
user is namedmyuserand its password ispassword. You can see how its created inDockerfile.base.This Dockerfile is what I use to test my own installs.fromnaddeoa/booty:ubuntu22.04RUNsudoapt-getinstall-ycurl#JustlikeIRL,installcurlfirstRUNcurlhttps://raw.githubusercontent.com/naddeoa/booty/master/scripts/booty-download-linux.sh|bash#MANUALinstallbootywithoutpip# Copy my ssh keys in so I can clone my private reposCOPY./ssh./.sshRUNsudochown-Rmyuser:myuser./.sshRUNchmod600.ssh/id_rsaCOPY./examples/install.booty./# This would be on my path IRLENVPATH="/home/myuser/.local/bin:${PATH}"CMD["bash"]Then you build and run the image, and execute booty.# Build the imagedockerbuild.-tmy-booty-test# Run itdockerrun--rm-it--entrypointbashmy-booty-test## Inside the image now./booty_linux_x86_64-yYou can't test everything here but you can get pretty far. One thing I can't test here, for example, ischshbecause that requires logging
out, but I gets me confident enough to use it on a fresh install and know I won't mess things up. In practice, you do this sort of a test
infrequently, when you initially create yourinstall.bootyfile or when you make big changes to it.Syntax SupportSeenaddeoa/vim-bootyfor the syntax plugin.Booty LanguageFor full examples, check out theexamplesfolder in github.The Booty language is a small language inspired primarily by make, with some additions to better address the problem space. There are a few
different types of entities.RecipesRecipes are reusable pieces of code that Targets and other Recipes can invoke. You can think of these as classes that implement aRecipeby defining thesetupandis_setupmethods. Recipes are invoked with parameters being separated by commas, likerecipe_name(foo, bar).
Parameters can optionally container whitespace as well, meaningrecipe_name(a b c)is a single parametera b c. The body of the recipe
methods consists of one or more executable statements which can either be a line to invoke in shell or another recipe invocation.This is a recipe namedaptwho'ssetupexecutes the shell commandsudo apt-get install -y $((packages)), where$((packages))will be
substituted by the value of the paraemterpackageswhen it's run. Itsis_setupexecutes a multiline shell statement. The shell is
invoked viabash -c ....recipe apt(packages):setup:sudoapt-getinstall-y$((packages))is_setup:forpkgin$(echo$((packages))|tr" ""\n");doif ! dpkg -l "$pkg" &> /dev/null; thenecho"$pkgis not installed."exit1fidoneTargetsTargets are the main piece of a booty file. They invoke a recipe to accomplish their goal. This is a target namedessentialsthat invokes
theaptrecipe, passing it the parameterwget git vim autokey-gtk silversearcher-ag gawk xclip.essentials:apt(wgetgitvimautokey-gtksilversearcher-aggawkxclip)Custom TargetsCustom Targets are similar to recipes, but they're defined inline. You would use this for something that didn't require any code shared
between other recipes. The body of a Custom Target follows the same rules as a Recipe.pyenv:setup:curlhttps://pyenv.run|bashis_setup:test-e~/.pyenv/bin/pyenv# Can also be multiline and invoke different commands/recipes.pyenv:setup:apt(python3)curlhttps://pyenv.run|bashis_setup:test-e~/.pyenv/bin/pyenvDependenciesDependencies determine the execution order at setup time. The way that you declare this is flexible. You can use either thedepends onsyntax (->) or thedepended uponsyntax (<-). This is allowed because it's sometimes easier to manage a bunch of dependencies in a
single line when they're logically related. Its personal preference for how you maintain your install.booty file.#thetarget`essentials`isdependeduponbyfooandbar.essentials<-foobar#thetarget`baz`dependsonbar.baz->barStdlibCertain recipes are included in booty by default. These include the following.recipe apt(packages):setup:sudoapt-getinstall-y$((packages))is_setup:forpkgin$(echo$((packages))|tr" ""\n");doif ! dpkg -l "$pkg" &> /dev/null; thenecho"$pkgis not installed."exit1fidonerecipe ppa(name):setup:sudoadd-apt-repository$((name))sudoaptupdateis_setup:grep$((name))/etc/apt/sources.listrecipe pipx(packages):setup:pipxinstall$((packages))is_setup:pipxlist|grep$((packages))recipe git(repo dist):setup:gitclone$((repo))$((dist))is_setup:test-d$((dist))recipe git_shallow(repo dist):setup:gitclone--depth1$((repo))$((dist))is_setup:test-d$((dist))recipe ln(src dst):setup:mkdir-p$(dirname$((dst)))ln-fs$((src))$((dst))is_setup:test-L$((dst))&&test-e$((dst))recipe cp(src dst):setup:mkdir-p$(dirname$((dst)))cp-r$((src))$((dst))is_setup:test-e$((dst))Any of these can be referenced from any booty.install file, and you can redifine them locally if you want different recipe logic that uses
the same name.FAQWhy wasn't make good enough?You can check theexperimentsfolder to see what it looks like
to implement theexample bootyfile in make. It's a fair bit longer
and clunkier for a few reasons:Every target in make is designed to be an actual file. This great for building things but it means that you end up marking a lot of
targets as.PHONYif they don't result in differences on the file system.Implementing the--statusflag from booty is very, very ugly. You'll just manually define two modes of each target and hard code a phony
target that runs them all, with no particular attention paid to the output.Same goes for running the setup code -- manually enumerating all targets as a phony target's dependency gets you the--installflag in
booty.Output in general isn't very digestible.Each line of a target is executed in a new shell. Good for sandboxing commands but it gets very ugly when you want to do something like anifstatement.There are a lot of good things about make though. My favorite relevant parts are:Easy, independent dependency specificationPlain old shell for each target definition.