A project scaffolding tool inspired by cookiecutter.
We needed a tool to create new source code projects from templates. In addition, we needed the tool to be a library written in Go. Scafall takes project templates, asks the end-user some questions and produces an output folder.
Scafall differs from some other Go scaffolding/templating tools as it passes through unknown template subsitutions. For example, if your input application source or documentation contains a {{.Foo}}
template and no argument is provided (either programmatically or by the end-user) then the output file will contain the string {{.Foo}}
. This allows the generation of projects where the generated source contains templates.
As a Go developer you can install scafall
into your GOBIN
directory.
$ go install github.com/buildpacks-community/scafall@latest
The scafall
CLI should now be available for use
$ scafall http://github.com/AidanDelaney/scafall-python-eg.git
✔ Please input a project name: pyexample
? Which Python version to use: [Use arrows to move, type to filter]
▸ python3.10
python3.9
python3.8
How many digits of Pi to render: 3
$ cd pyexample
$ ./print_pi.py
The programmatic API is documented on pkg.go.dev
, which contains more examples. A basic example will prompt the end-user for any values the project scaffolding requires:
package main
import (
"fmt"
scafall "github.com/buildpacks-community/scafall/pkg"
)
func main() {
s := scafall.NewScafall(scafall.WithOutputfolder("python-pi"))
err := s.Scaffold("http://github.com/AidanDelaney/scafall-python-eg.git")
if err != nil {
fmt.Printf("scaffolding failed: %s", err)
}
}
When using scafall
programmatically you may want to provide values for template variables. In scafall
these are termed arguments. An argument may define map[string]string{"PI": "3.14"}
any prompting for an alternative value to PI
is skipped and the 3.14
values is used in templates. This is particularly useful where the calling code calculates a value, such as a username, and does not want the end-user to be prompted to chage this value.
Project templates are normal source code projects with the addition of a prompts.toml
file. The prompts.toml
file defines questions to ask of the end-user. The answers to the questions are available as template variables. For example, suppose we have a project template to create a new Python project, we only need to ask the end-user which python interpreter to use and how many python digits to generate:
$ scafall http://github.com/AidanDelaney/scafall-python-eg.git
? Which Python version to use: [Use arrows to move, type to filter]
▸ python3.10
python3.9
python3.8
✔ How many digits of Pi to render: 3
2022/04/06 20:28:41 create /print_pi.py
The values for the python interpreter and number of digits to render are available as {{.PythonVersion}}
and {{.NumDigits}}
respectively. Thus the input template
#!env -- {{.PythonVersion}}
from math import pi
print("%.{{.NumDigits}}f" % pi)
is generated as
#!env -- python3.10
from math import pi
print("%.3f" % pi)
A project template containing a prompts.toml
file will produce a generated project that omits the prompts.toml
file. In addition, any root-level README.md
file in the project template is not propagated to the generated project. This allows the project template to contain a README.md
to explain usage of the project template.
The prompts.toml
file is a sequence of [[prompt]]
which must each deine a name
and prompt
. A minimal example is
[[prompt]]
name = "NumDigits"
prompt = "How many digits of Pi to render"
An example with two prompts is
[[prompt]]
name = "PythonVersion"
prompt = "Which Python version to use"
required = true
choices = ["python3.10", "python3.9", "python3.8"]
[[prompt]]
name = "NumDigits"
prompt = "How many digits of Pi to render"
default = "3"
The choices
and default
fields are mutually exclusive. In the case that both choices
and default
are used, the default
is silently ignored and the first of choices
becomes the default.