7.0 KiB
bootstrap
CLI utility for defining custom project initialization scripts.
Overview
bootstrap
is a tool for quickly defining your own init-like scripts. If you
are familiar with tools like
npm init
nix flake init
django-admin startproject
mix phx.new
- etc.
this project will feel at home. Ultimately the goal is to create (optionally) interactive scripts like those mentioned in the above list to quickly scaffold your new projects in a consistent way.
We start with an example. Consider the following spec, which we'll name
touch
(this example exists as a pre-packaged spec):
{
"filename": {
"type": "line",
"prompt": "What file should I create for you? "
}
}
and its associated runner:
#!/usr/bin/env bash
echo "Creating $FILENAME"
touch "$OUT/$FILENAME"
Running bootstrap
with these two files configured will invoke the following
interactive script:
$> bootstrap touch
What file should I create for you? hello-world.txt
Creating hello-world.txt
$> ls
... hello-world.txt ...
You should now see a new hello-world.txt
file in your current working
directory.
Installation
Nix
It is recommended you use Nix to install bootstrap
. If using a flake, specify
bootstrap
as an inputs
attribute the normal way. Otherwise, if you have a
new enough version of nix, import the executable like so:
(builtins.getFlake "github:jrpotter/bootstrap/${version}").packages.${system}.default;
If flakes is not enabled or your nix version does not support
builtins.getFlake
, you can instead use:
(import (pkgs.fetchFromGitHub {
owner = "jrpotter";
repo = "bootstrap";
rev = "${version}";
sha256 = "${sha256}";
})).packages.${system}.default;
Source
If you do not have Nix or prefer building from source, clone this repository and run
$> make BUILD=release
The bootstrap
binary will be made available in dist/release
by default.
Usage
Runners
A spec refers to any directory containing a file named runner
. The only
requirement enforced by bootstrap
is for this file to be an executable (e.g.
chmod +x
), but typically the runner
is a shell script:
#!/usr/bin/env bash
...
The runner
is invoked with its current working directory set to that of the
directory containing it. For instance, if we have a runner
script living in
directory ~/Documents/specs/example
with contents:
#!/usr/bin/env bash
echo "$PWD"
the output of bootstrap example
will always be e.g.
> bootstrap example
/home/jrpotter/Documents/specs/example
regardless of where we call the bootstrap
command.
Exit Code
bootstrap
always invokes the runner
using the system sh
command:
> /bin/sh sh -c ./runner
The exit code emitted by bootstrap
will mirror that returned by the runner
executable.
Specs
If interested in making the runner
more flexible, you can provide different
environment variables in the form of a spec.json
file. This file must live
in the same spec as the runner
. When invoking bootstrap
, the file is used to
determine what prompts should be displayed to the user before executing the
runner
file. The user's responses are then included as environment variables
to the runner
process.
The file contents should consist of a top-level JSON object and any number of
child objects called fields. A typical spec.json
file looks like:
{
"fieldname": {
"type": "line",
"prompt": "Prompt for field> "
},
...
}
In this example, the first field is called "fieldname"
. bootstrap
sees this
field and writes the prompt "Prompt for field> "
to stdout
. Since
"fieldname"
has type "line"
, bootstrap
will wait for the user to input
a string (submitted with a newline).
If the user were to enter fieldvalue
in response to the prompt, the runner
script would then have access to an environment variable FIELDNAME
set to
fieldvalue
. Field names should respect the POSIX standard
on environment variable naming. In particular, all field names consist solely
of alphanumeric characters or underscores and cannot start with a digit.
Types
The value of type
determines how a field is prompted for. Note the value of
type
is case insenstive. The currently supported list of types are:
line
- The simplest prompt type. Takes in a free-form response submitted after a
newline (
\n
) is encountered.
- The simplest prompt type. Takes in a free-form response submitted after a
newline (
Root Directory
All specs should exist in the same root directory. As an example of what this
directory might look like, refer to specs
at the top-level of this project.
When invoking bootstrap <name>
, <name>
is expected to correspond to some
spec found within the root directory.
To tell bootstrap
where your specs are located, you can provide the path to
the root directory using the -d
option like so:
> bootstrap -d ~/Documents/specs example
...
If no option is set, bootstrap
will fallback to using the value of the
BOOTSTRAP_ROOT_DIR
environment variable. If this also isn't set, bootstrap
will abort with an appropriate error message.
Other Environment Variables
By default, the runner
command will have the following environment variables
defined. Defining these fields in a spec.json
file will override the default
values:
OUT
- The directory
bootstrap
was invoked from. Named since this is usually where you want to initialize new files of your project in.
- The directory
Supplied Specs
A number of specs are provided out of the box. If you installed bootstrap
using nix
, the BOOTSTRAP_ROOT_DIR
will automatically be set to the location
of these specs. Keep in mind this list is very opinionated - they
reflect my personal needs for projects. Feel free to specify a different root
directory if these do not fit your needs.
As a suggestion, use nix
from within your runner
scripts for maximum
reproducibility. Refer to the provided specs for inspiration on how you can do
this.
Development
This tool was originally written for personal usage and, as such, any functionality (or lack thereof) reflects my own needs as I have come across them. If interested in adding more capabilities, please send a PR or just fork the project for your own purposes.
Testing
We use Sput for unit tests. To run tests, type:
$> make test
Tests are located in the test
directory. test/suites.c
serves as the
entrypoint for the test runner.
Documentation
We use doxygen for documentation generation. Run either of the following two commands to generate documentation locally:
$> make docs
$> doxygen
Formatting
We use clang-format
to ensure consistent formatting. A pre-commit
file is
included in .githooks
to enforce usage. Run the following to configure git
to use it:
git config --local core.hooksPath .githooks/
If running direnv, this is done automatically upon entering the project directory.