Scaff is a small, unopinionated software project scaffolding tool.
As such, it is suitable for any programming language, build toolchain, or framework, as long as a "project" consists of textual files in a directory tree.
Currently, only installing from source is supported.
- Cabal 2.0 or newer
- GHC 8.0 or newer
Both these tools should come with a reasonable GHC install, to be found from https://www.haskell.org/ghc.
- Check out
[email protected]:tdammers/scaff
cd scaff
cabal sandbox init
cabal install
- (optionally)
sudo install ./.cabal-sandbox/bin/scaff /usr/local/bin/scaff
- stack (https://haskellstack.org/)
- Check out
[email protected]:tdammers/scaff
cd scaff
stack setup
(this will also install GHC if needed)stack install
- (optionally) copy the
scaff
binary somewhere global like/usr/local/bin/
.
Before you can use scaff
, you need to give it a little bit of extra
information.
- Create a directory
~/.scaff/
- Copy the
config.example.yaml
file into~/.scaff/config.yaml
, and edit it to taste - Create a file
~/.scaff/repos.list
, containing the following line:
http https://raw.githubusercontent.com/tdammers/scaff/master/templates/
This defines a repository of project templates using the HTTP(S) protocol (i.e., scaff will pull in templates over HTTP or HTTPS), and any templates you specify will be looked for below the given URL.
If you prefer getting templates from a local directory, you can use the local
repository type:
local /home/yourname/src/scaff/templates/
(Assuming that you checked out the scaff source repo into ~/src/scaff
)
You can add as many repositories you like; scaff will try them in order, until it finds the desired template.
If you ever use more than one repository, then it is useful to name them. Names are arbitrary; they cannot contain spaces however, and they should be unique. Here's how that works:
tdammers http https://raw.githubusercontent.com/tdammers/scaff/master/templates/
my-templates local /home/yourname/src/scaff/templates/
To create a new project based on a scaff template:
scaff TEMPLATE PROJECT_NAME [OUTPUT_DIR]
Where:
TEMPLATE
is the project template you want to use; you can prepend a repository name like so:REPOSITORY:TEMPLATE
, to force getting the template from the specified repository rather than trying the entire list in order.PROJECT_NAME
is the name of your project.OUTPUT_DIR
is the root directory for your project; if not given, the project name is used, below the CWD.
Depending on the template being used, scaff
may now ask you a number of
questions, and then generate the project into OUTPUT_DIR
.
scaff list
This will spit out a list of template names with the repositories they come from.
Scaff templates use the Ginger template language.
The two most important files in a template are ./files
and ./questions
; any
other files are used based on what these two say.
./files
is a list of files to generate; each line may follow one of two
formats:
PATH
- take the file at./PATH
, run it as a Ginger template, and save it asOUTPUT_DIR/PATH
INPUT:OUTPUT
- take the file at./INPUT
, run it as a Ginger template, and save is asOUTPUT_DIR/OUTPUT
.
Now, the fun part is that ./files
itself is also a Ginger template, so it
gets run through Ginger before reading the individual lines. This means that
you can use Ginger loops to make it generate multiple files from the same
template, to generate files conditionally, or to dynamically determine the
names of output files.
./questions
is a list of question definitions that will be asked in the same
order they are given in this file. Each line defines one question, following
the format:
KEY:QUESTION_TEXT:DEFAULT:QUESTION_TYPE
These elements work as follows:
KEY
defines a variable name ("key") under which the answer will be available in template files.QUESTION_TEXT
is the text that will be presented to the user when asking the question.DEFAULT
is the default value to be used when the user answers with a blank line.QUESTION_TYPE
defined the type of question, and can be one of the following:.
, a single-entry question: the user can enter one free-form answer, which gets exposed as a string. This is the default.?
, a Yes/No question: the user can enter "y" or "n", the answer gets exposed as a boolean.*
, a multi-entry question: the user can enter as many entries as they like, until they enter a blank line; answers are exposed as an array.- A list of comma-separated labels: the user can pick one of the labels, which is exposed as a string.
When running a template file through ginger, the following variables will be available:
- All environment variables from the process environment that start with
SCAFF_
will be exposed under theenv
variable, converted to "kebab case". That is, if you provide an environment variableSCAFF_SOMETHING_INTERESTING=olives
, then the template construct{{ env['something-interesting'] }}
will renderolives
. - The
author
key from~/.scaff/config
will be available as{{ author }}
- The current project name will be available as
{{ project }}
- Any answer to questions from the
./questions
file will be exposed under their defined keys.
The easiest way to publish templates is to host them somewhere public, such
that each template foo/bar
is situated somewhere under your root URL, e.g.
https://scaff-templates.yourname.com/templates/
. Code hosting services like
github, gitlab, bitbucket, etc., will all offer a way to list and access raw
code files directly over HTTPS for public repos, so for example for github, you
can do something like this in your repos.list
:
http https://raw.githubusercontent.com/yourname/scaff-templates/master/templates/
You will also need to have a templates.list
file directly under the root URL,
which should list the available templates. E.g., if you have:
https://raw.githubusercontent.com/yourname/scaff-templates/foo/bar
...then you need to have a file under this URL:
https://raw.githubusercontent.com/yourname/scaff-templates/templates.list
...which contains the following line:
foo/bar
This is a bit unfortunate, because we would much rather find templates automatically, but HTTP doesn't support this, and while many hosting options do offer directory listings of some sort, they are not standardized in any way, so there isn't a nice machine-friendly way of listing templates in a repository.
Note, further, that while the list
command will only list templates
explicitly mentioned in templates.list
, any other template below the root URL
can still be accessed - templates.list
is only used for discovery, it is by
no means a security feature.