NOTE: Strap is currently being ported from a previous version. It currently works, but it doesn't install very much. Yet.
strap
is a dotfiles framework that is designed to do one thing extremely well:
Strap will take your machine from a zero state (e.g. right after you buy it or receive it from your employer) and install and configure everything you want on your machine in a single command, in one shot. Run strap and you can rest assured that your machine will have your favorite command line tools, system defaults, gui applications, and development tools installed and ready to go.
Strap was initially created to help newly-hired software engineers get up and running and productive in minutes after they receive their machine instead of the hours or days it usually takes. But Strap isn't limited to software engineering needs - anyone can use Strap to install what they want - graphics software, video editing, web browsers, whatever.
Here's a little strap run
recording to whet your appetite for the things Strap can do for you:
If you want, you can run it immediately:
curl -fsSL https://raw.githubusercontent.com/ultimatedotfiles/strap/master/run | bash
Or if you have custom hooks to run as well:
curl -fsSL https://raw.githubusercontent.com/ultimatedotfiles/strap/master/run | bash -s -- --with-hook-package='com.github.myorg:myrepo:gitref'
Or you can install it manually.
curl -fsSL https://raw.githubusercontent.com/ultimatedotfiles/strap/master/install | bash
Then set the following in ~/.bash_profile, ~/.bashrc or ~/.zshrc (or whatever you prefer):
export PATH="$HOME/.strap/releases/current/bin:$PATH"
How does strap work?
Strap is a command line tool with a set of sub-commands and packages written in 100% Bash scripts to ensure it can run out of the box on pretty much any Mac or Linux machine created in the last few years. It doesn't require Ruby, Python, Java or any other tool to be installed on your box first in order to run (but you can install those with Strap as you see fit). Strap should work on any machine with Bash 3.2.57 or later.
That said, don't worry if you prefer zsh or any other shell. Bash is just used to write and run strap - you can install and use zsh or any other shell you prefer.
Don't worry, we're not re-inventing the wheel.
Strap is mostly a set of really convenient wrapper functions and commands built on top of homebrew. Strap fills a really important gap: while homebrew is primarily concerned with managing packages on your machine, Strap leverages that and also enables user and machine-specific configuration for those packages.
For example, you might have installed a homebrew package and then in its output, that package says something like:
After this is installed, modify your ~/.bash_profile or ~/.zshrc to include the following init line...
Well, that's nice, but shouldn't that part be automated too? Also what about operating system default configuration? That's not a software package, it's configuration, and homebrew mostly doesn't address those concerns. But Strap does.
Strap is homebrew + automating 'the last mile' to get you to an ideally-configured machine without having to work. It is basically: "I want to use one command and that's it. If you need something from me, prompt me, but don't make me do any work at all beyond that." Now that's what we're talking about!
Even though Strap is built with bash, you don't have to know bash at all to use it. Once installed, Strap looks
at an easy-to-understand strap.yml
configuration file that lists every thing you want installed or modified
on your system.
It reads this list and converges your machine to have the same state represented in the strap.yml
file: If
something in the strap.yml file already exists on your machine, strap just skips it. If something in the file doesn't
exist yet, strap will install it for you. It can automatically upgrade things for you too.
This means Strap is idempotent - you can run it over and over again, and your machine will be in your desired state every time you run it. Nice!
Based on this, you can (and should) run strap regularly to ensure your system is configured how you want it and your software is up-to-date at all times.
Strap was purposefully designed to have a very lean core and delegate most functionality to packages. Why is this important?
Because you can extend Strap for your needs.
That's right - if Strap doesn't have something you need, you can write a simple bash package to provide it. Strap gives you the ability to package up your bash functions, version them, and supply them to other Strap packages. This means Strap supports import-like behavior: a package can depend on other packages and import those packages' library functions and variables.
Additionally, a Strap package can contain scripts that hook in to Strap's run lifecycle, kind of like a 'plugin'. This allows you to extend and enhance functionality that may not be in Strap by default.
And this helps prevent lock-in. It allows you to get what you need on your timeline without waiting on anyone. And it allows for a community to grow and provide open-source packages we can all benefit from. And packages are just folders, so they can be created as simply as creating a new git repository. And because GitHub is pretty much the de-facto git origin for open-source software these days, Strap has native git and GitHub integration too.
Because Strap is pluggable, you can 'point' it to any strap-compatible git repository. This means that you can have your own git repo that represents the customizations you care about. And you can share this with the world so others can have awesome machine setups just like you.
But sometimes you (or companies) don't want the world to see what is installed on a machine for privacy or security reasons. If this is a concern for you, you can host your Strap configuration and packages in a private git repository. Because of Strap's native GitHub integration, it can securely authenticate with GitHub and utilize your private git repositories (assuming your GitHub user account has access to said repositories).
You can even mix and match repositories: use Strap's defaults, then add in your company's private repository, then finally add in your personal repository to fill in any remaining missing gaps. Any number of sources can be used during a strap run for machine convergence.
Strap is a command line tool. Assuming the strap installation's bin
directory is in your $PATH
as shown in the
installation instructions above, you can just type strap
and see what happens:
me@myhost:~$ strap
strap version 0.0.1
usage: strap [options...] <command> [command_options...]
commands:
help Display help for a command
run Runs strap to ensure your machine is fully configured
version Display the version of strap
See `strap help <command>' for information on a specific command.
For full documentation, see: https://github.com/ultimatedotfiles/strap
The strap
command itself is quite simple - it basically loads some common environment settings and that's pretty
much it. From there, it delegates most functionality to sub-commands, very similar to how the git
command-line tool
works. The sub-commands available to you are the combined set of Strap's built-in sub-commands and any sub-commands
made available by any Strap packages you reference.
Strap is designed to have a lean core with most functionality coming from packages. This section explains what packages are, how to use them, and how to write your own package(s) if you want to add or extend Strap functionality.
A Strap package is just a folder with bash scripts described by a package.yml
file.
This means Strap can access functionality from anywhere it can access a folder. And because git repositories are
folders, Strap can pull in functionality from anywhere it can access a git repository via a simple git clone
command
based on the package's unique identifier.
A Strap Package Identifier is a string that uniquely identifies a Strap package.
The package identifier string format MUST adhere to the following definition:
strap-package-id = group-id ":" package-name [":" package-version]
group-id = "com.github." github-account-name
where
github-account-name
equals a valid github username or organization name, for examplejsmith
orultimatedotfiles
package-name
equals a git repository name within the specified github account, for examplecool-package
package-version
, if present, equals a git refname that MUST be a tag, branch or commit sha that can be provided as an argument togit checkout
.
A package release SHOULD always have a package-version
string that conforms to the semantic version name scheme
defined in the Semantic Versioning 2.0.0 specification.
Some examples:
com.github.acme:hello:0.2.1
com.github.ultimatedotfiles:cool-package:1.0.3
NOTE: we realize it is a rather constrictive requirement to have all packages hosted on github and conform to the specified location and naming scheme. These restrictions will be relaxed when Strap's functionality is enhanced to support arbitrary repository locations (e.g. bitbucket, gitlab, Artifactory, etc).
How does Strap download a package based on the package identifier?
Consider the following Strap Package Identifier example:
com.github.acme:hello:1.0.2
This tells strap to download the package source code obtained by (effectively) running:
git clone https://github.com/acme/hello
cd hello
git checkout tags/1.0.2
If there is not a :package-version
suffix in a strap-package-id
, a :package-version
value of :HEAD
will be
assumed and the git repository's origin/HEAD
will be used as the package source.
For example, consider the following Strap package id:
com.github.acme:hello
This indicates the package source code will be obtained by (effectively) running:
git clone https://github.com/acme/hello
and no specific branch will be checked out (implying the default branch will be used, which is master
in most cases).
WARNING:
It is strongly recommended to always specify a
:package-version
suffix in every strap package idenfier to ensure deterministic (repeatable) behavior. Omitting:package-version
suffixes - and relying on the:HEAD
default - can cause errors or problems during astrap
run. Omission can be useful while developing a package, but it is recommended to provide a:package-version
suffix at all other times.
Any package referenced by you (or by other packages) that are not included in the Strap installation
are automatically downloaded and stored in your $HOME/.strap/packages
directory.
This directory is organized according to the following rules based on the Strap Package ID. An example Strap
Package ID of com.github.acme:hello:1.0.2
will be used for illustration purposes.
-
The strap package id's
group-id
component is parsed, and period characters (.
) are replaced with forward-slash characters (/
). For example, thegroup-id
ofcom.github.acme
becomescom/github/acme
-
The resulting string is appended with a forward-slash (
/
). For example,com/github/acme
becomescom/github/acme/
-
The resulting string is appended with the package id's
package-name
component. For example,com/github/acme/
becomescom/github/acme/hello
-
The resulting string is appended with a forward-slash (
/
). For example,com/github/acme/hello
becomescom/github/acme/hello/
-
The resulting string is appended with the
strap-package-id
'spackage-version
component if one exists, orHEAD
if one doesn't exist. For example:- A strap package id of
com.github.acme:hello:1.0.2
becomescom/github/acme/hello/1.0.2
and - A strap package id of
com.github.acme:hello
becomescom/github/acme/hello/HEAD
- A strap package id of
-
The resulting string is appended to the string
$HOME/.strap/packages/
. For example,com/github/acme/hello/1.0.2
becomes$HOME/.strap/packages/com/github/acme/hello/1.0.2
-
The resulting string is used as the argument to the
mkdir -p
command, which is used to create the directory where that package's code will be downloaded, for example:mkdir -p "$HOME/.strap/packages/com/github/acme/hello/1.0.2"
A strap package is a folder containing:
- A
META/package.yml
file - Any number of bash scripts
Assuming https://github.com/acme/hello
was a strap package repository, here is an example of what its directory
structure might look like:
cmd/
hello
hooks/
run
lib/
hello.sh
META/
package.yml
The above tree shows the following:
-
META/package.yml
is a Strap package yaml file. This file contains metadata about your package that Strap uses to ensure your package can be referenced by other packages, as well as enable any Strap sub-commands your package might provide, and more. -
cmd/hello
is an executable script that can be executed as a strap sub-command. That is, a strap user could typestrap hello
and strap would delegate execution to yourcmd/hello
script. When committing this file to source control, ensure that the file's executable flag is set, for examplechmod u+x cmd/hello
. -
hooks/run
is an executable script that will execute whenstrap run
is called. For example, if a strap user typesstrap run
to kick off a run, strap will in turn invokehooks/run
as part of that execution phase. Scripts in thehooks
directory must match exactly the name of the strap command being run. Additionally, when committing this file to source control, also ensure that the file's executable flag is set, for examplechmod u+x hooks/run
. -
lib/hello.sh
is a bash script that may export shell variables and functions that can be sourced (used) by other packagesFor example, if
lib/hello.sh
had a function definition like this:com::github::acme::hello() { echo "hello" }
other packages could import
hello.sh
and then they would be able to invokecom::github::acme::hello
when they wanted.We will cover how to import package library scripts soon.