Facio is a project skeleton generation tool written in Python. It’s designed to make it easy for you to build a single project template, or as many templates as you like, with one command to bootstrap a new project.
facio my_new_project -t my_remote_template
Facio: /ˈfa.ki.oː/ - Latin, meaning to make, do, act, perform, cause, bring about.
Are you forever creating new projects? Re-creating the same standard cruft over and over? Then Facio may be for you. It allows you to create your standard project skeleton once, then easily create new projects from that standard skeleton as many times as you like.
It can be as simple or as advanced as you need it to be. You can write logic into your skeleton, store it in a git or mercurial repository, or even write hooks to be run before or after your skeleton has been built. You can also have as many templates as you like and quickly reference them by name or pick from a list.
Facio aims to solve your standard project skeleton woes.
facio my_new_project -t django_skeleton
See LICENSE file in the Git Repository.
See AUTHORS file in the Git Repository.
To the amazing Tech Team at Poke London. And thanks to Jack for helping me name it (and pointing out grammatical errors). <3.
Facio can be installed on system using the standard python package installers pip or easy_install.
Note
sudo is used in the following commands for system wide installation.
Facio is written in python, the only requirement you need is to have one of the following python versions installed.
cd /where/you/want/it/to/live
git clone git@github.com:krak3n/facio.git
cd facio
sudo python setup.py install
Once you have installed Facio using one of the above methods you can very the install by checking that the facio script was installed by running:
which facio
> /usr/local/bin/facio
If all went well facio is now available from your command line.
Facio is designed to simple and easy and also flexible. Here is how to use it:
Facio is a command line application, after you have installed Facio you should now have a facio command available on your command line. You can use it straight away without any configuration. It won’t give you anything useful but you can see the basics of how Facio works.
Lets create a new project called foo:
Note
$ denotes your shell prompt throughout this page
$ facio foo
The above command will bootstrap a very simple sample project which is bundled with Facio and just contains some HTML and CSS. It will be created in the directory in which the facio command was run and be called foo.
You should be able to open this in a web browser to see more information about Facio.
But this isn’t particularly useful for building your skeleton so let’s go on.
We will call project skeletons templates. These templates are designed for reuse and should be kept maintained and updated as you learn new and better ways of creating your projects.
Let’s keep it simple to start with by creating a simple HTML project template.
Create a new directory somewhere on your system and lets call it html_template. Inside it make 1 html file, you can call it whatever you like but for sanity we will refer to it as index.html. Now inside this file add the following:
<html>
<head>
<title>{{ PROJECT_NAME }}</title>
</head>
<body>
<h1>Welcome to {{ PROJECT_NAME|upper() }}</h1>
<p>My first Facio generated project template!</p>
</body>
</html>
Now let’s tell Facio to use this template: change to a new directory where you would like the project to be created and run:
$ facio bar -t /path/to/html_template
A new directory will be created called bar in your current working directory and inside you’ll find index.html with the following content:
<html>
<head>
<title>bar</title>
</head>
<body>
<h1>Welcome to BAR</h1>
<p>My first Facio generated project template!</p>
</body>
</html>
You’ll notice that because we used one of Jinja2’s builtin filters, upper in the h1 tags, the project name has been capitalised.
A full list of built in Jinja2 filters can be found here.
Facio can be configured using a file called ~/.facio.cfg and how Facio runs can also be defined by the command line interface, we will take a look at the command line first.
Facio is a command line application. Here is how you use it:
Note
Throughout this document $ represents a terminal prompt.
The first argument you pass to the facio command must always be a project name. For example:
$ facio hello_world
If no additional optional arguments are supplied then Facio will use its default template to create the project.
For Facio to use a different template than its default you must pass either of the following arguments:
The --template or its short hand equivalent -t takes a string which can be either of the following:
For example:
$ facio foo -t /my/local/template
$ facio bar -t git+git@github.com:krak3n/Facio-Django-Template.git
$ facio baz -t django
The --select or it’s -s short hand is used for selecting a template which is defined in the '.facio.cfg configuration file, see the Configuration File section for more information on how to define multiple templates.
For example:
$ facio foo --select
You will be given a prompt asking you to choose a template, once chosen facio will process the selected template.
You may also need to define more variables to be used when rendering your template. You can do this using the –vars argument.
You can add extra variables to the context using --vars optional argument. This argument takes a string which should contain a comma delimited list of key value pairs separated by an = operator.
For example:
$ facio foo -t bar --vars x=1,y=2,z=3
This above example would define 3 new context variables when rendering the template with the fllowing values:
And could be used in templates as follows:
<html>
</head>
<title>{{ PROJECT_NAME }}</title>
</head>
<body>
<h1>{{ PROJECT_NAME }}</h1>
<ul>
<li>X = {{ x ]}</li>
<li>Y = {{ y ]}</li>
<li>Z = {{ z ]}</li>
</ul>
</body>
</html>
You can also define a configuration file called .facio.cfg. This configuration file should live in your home directory with your other . (dot) files. This configuration file should be in an ini style format.
For example:
[section1]
option = value
[section2]
option = value
The [template] section allows you to define in the .facio.cfg file multiple templates you use on a regular basis so you can access them quickly from facio.
For example:
[template]
django = git+git@github.com:me/django-template.git
rails = git+git@github.com:me/rails-template.git
The [files] section allows you to specify files from your template to skip when copying or skip rendering by jinja2.
The files section takes 2 options:
For example:
[files]
copy_ignore = .env,*.pyc
render_ignore = .coverage,*.ico
In addition to the defaults facio would not copy over any file named .env or any file name ending in .pyc. It would also not render with the template engine, in addition to the defaults, any file named .coverage or any file name ending in .ico.
Templates are the bare bones of your project with key parts where you would put things like the project name replaced with Jinja2 template syntax.
These templates can live locally on your file system or they can live on a remote git repository. See Configuration & Command Line for more on this.
This is a basic HTML project template:
<html>
<head>
<title>{{ PROJECT_NAME }}</title>
</head>
<body>
<h1>Hello world, I am {{ PROJECT_NAME }}</h1>
</body>
</html>
In the above example {{ PROJECT_NAME }} will be replaced with whatever you set the project name to be on the command line, so for example: $ facio -n foo would result in {{ PROJECT_NAME }} being replaced by foo.
Your project can be made up of any file types, any directory structure, it will all be copied and processed.
Of course project name is not always enough so for these situations you can send extra variables to facio for use in the template processing. To do this run facio with the --vars flag passing a comma separated list, for example:
facio hello_world --vars foo=bar,something=else
Accessing these variables in templates is easy:
Hello World
foo={{ foo }}
something={{ something }}
As Jinja2 is used to render the templates, you can use conditions, and other Jinja2 functionality, for example:
{% if foo == 'bar' %}
Foo is bar
{% else %}
Foo is not bar
{% endif %}
See the Jinja2 Documentation.
You can rename a directory and/or file by using double curly braces around the variable name, for example:
Warning
Do not include spaces, use {{var_name}}.ext and not {{ var_name }}.ext
Below is a file structure of a raw template with 1 directory to be renamed and 1 file to be renamed to the content of foo.
- /path/to/template/
- {{foo}}/
- another.txt
- {{foo}}.txt
- some_file.txt
- some_other_file.txt
Below is the rendered content.
- /path/to/template/
- bar/
- another.txt
- bar.txt
- some_file.txt
- some_other_file.txt
Facio has the ability to run hooks. Hooks are pieces of code that are run either before or after the project template is rendered.
Hooks are defined on a per project basis and are set in a file which resides in the project template itself. This file is called .facio.hooks.yml. It is a YAML formated file consisting of a before and an after list of python dotted paths to code to run. For example:
before:
- path.to.foo
after:
- path.to.bar
Facio also has some bundled hooks you can use out of the box.
These will continue to grow and improve as Facio matures further. Currently there are only python related hooks.
For Django projects a secret key is need to protect your project. Django generates this for you when you run django-admin.py startproject for example. Facio can also do this and add a new variable to the template context.
To use this create a .facio.hooks.yml file at the root of your project template and add the following:
before:
- facio.hooks.django.secret
And in your template you can use the {{ DJANGO_SECRET_KEY }} variable, for example the secret key would normally go in settings.py:
...
SECRET_KEY = '{{ DJANGO_SECRET_KEY }}'
..
Facio can automatically create a python virtual environment for your project. Add this hook in either the before or after list.
after:
- facio.hooks.python.virtualenv
This hook will ask for the following information with sensible defaults set, so you can just press enter to skip.
This hook allows you to install your project as a python module provided your project has a setup.py correctly configured in the templates root directory.
Note
Since your template is required to have been processed before this hook can be run you should only define this hook in the after list of ~/.facio.hooks.yml.
after:
- facio.hooks.python.setup
This hook will ask you for the following information with sensible defaults set so you can just press enter to skip.
You can write your own hook if you need to. Your hook will need to meet the following criteria:
Warning
How to add your custom hook onto the python path is beyond the scope of this documentation. If your hook cannot be imported it will not work. See http://www.scotttorborg.com/python-packaging/ for a very helpful guide on python packaging.
Let’s make a simple hook that prints “hello world”. Create a file in your home directory a new directory called my_hooks and inside create 2 files:
And add the following content into hello.py.
def run():
print 'hello world'
This has created a new python module called my_hooks and inside we have a hello.py python file that can be imported containing our run function.
That’s it, now all we need to do is get it on the python path. How to add your custom hook onto the python path is beyond the scope of this document, see http://www.scotttorborg.com/python-packaging/ for a very helpful guide on python packaging.
Facio has a state module where the current state of Facio is stored. Simply import it:
from facio.state import state
You can also add extra context variables in hooks for use in your template.
# my_hooks.foo
from facio.state import state
def run():
state.update_context_variables({'FOO': 'bar'})
The hook above adds a new FOO context variable with the value of bar so you can use {{ FOO }} in your templates.
You can also access returned values from other hooks that have run. This can be useful to provide sensible defaults in prompts or even to not run the currently executing hook unless another in the chain has run.
# my_hooks.bar
from facio.state import state
def run():
call = state.get_hook_call('my_hooks.foo')
if call:
print 'my_hooks.foo has run'
As described above hook data can be stored for use by other hooks later in the chain. To save data all you need to do is have your run function return something.
# my_hooks.baz
def run():
return "foobar"
When baz is called the value foobar will be stored so later hooks can use it:
# my_hooks.faff
def run():
print state.get_hook_call('my_hooks.baz')
This will print "foobar".
If you want your hook to prompt a user for information or print out helpful colored messages you can extend from the FacioBase class. You can find more information about this in the API documentation.
# my_hooks.flop
from facio.base import FacioBase
class Flop(FacioBase):
def __init__(self):
val = self.gather('Enter a number: ')
self.out('You entered: {0}'.format(val))
def run():
flop = Flop()
return flop
As mentioned Facio has several bundled hooks, you can use these as templates to writting your own.
Here you can find documentation for Facio’s API.
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Facio
Facio is a project scaffolding tool originally developed for Django and expanded to be framework agnostic. You can use Facio to bootstrap any sort of project.
-h –help Show this help text. –version Show version. -t –template <path> Template path, can be repository link
(git+ / hg+) or a template name defined in ~/.facio.cfg.
--vars <variables> | |
Comma separated key=value pairs of values to be used in processing templates. |
Load the ~/.facio.cfg ini style configuration file, providing an easily queryable dict representation of the config attributes.
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Parse the config file using ConfigParser module.
Parameters: | name (str) – The file name to read in the users home dir – optional |
---|---|
Returns: | ConfirgParser or bool |
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Returns list of of file copy ignore globs from configuration file.
Returns: | list |
---|
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Obtain the template with from the command line interface or from prompting the user to choose a template from the config file.
Returns: | str or bool |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Returns list of of file render ignore globs from configuration file.
Returns: | list |
---|
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Return a specific context variable value.
Parameters: | name (str) – Context variable name |
---|---|
Returns: | str or None – None if name not found in var list |
Returns the current context variables at time of call.
Retutns: | dict |
---|
Returns a hook call result, else returns false if the module path is not in the hook call list.
Parameters: | module_path (str) – The python dotted path to the module |
---|---|
Returns: | Call result |
Return the project root, which is the current working directory plus the project name.
Returns: | str |
---|
Use the sh library to return the current working directory using the unix command pwd.
Returns: | str |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Saves a hook call to state
Parameters: |
|
---|---|
Returns: | list – The call list or tuples |
Set the project name to the state.
Parameters: | name (str) – The project name from facio.config.CommandLineInterface |
---|
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Update the context varaibles dict with new values.
** Usage: **
from facio.state import state
dictionary = {
'bar': 'baz',
'fib': 'fab',
}
state.update_context_variables(dictionary)
Parameters: | dictionary (dict) – Dictionary of new key values |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Copy template from origin path to state.get_project_root().
Parameters: | callback (function – default None) – A callback function to be called after copy is complete |
---|---|
Returns: | bool |
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Returns a list of files to ignore for rendering based on get_render_ignore_globs patterns.
Parameters: | files (list) – List of files to check against |
---|---|
Returns: | list – list of filenames |
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Renames directories that are named after context variables, for example: {{PROJECT_NAME}}.
Returns: | generator |
---|
Rename files that are named after context variables, for example: {{PROJECT_NAME}}.py
Returns: | generator |
---|
Reads the template and uses Jinja 2 to replace context variables with their real values.
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Update the ignore glob patterns to include the list provided.
** Usage: **
from facio.template import Template
t = Template('foo', '/path/to/foo')
globs = [
'*.png',
'*.gif',
]
t.update_copy_ignore_globs(globs)
Parameters: | globs (list) – A list of globs |
---|
Update the render ignore glob patterns to include the list provided.
** Usage: **
from facio.template import Template
t = Template('foo', '/path/to/foo')
globs = [
'*.png',
'*.gif',
]
t.update_render_ignore_globs(globs)
Parameters: | globs (list) – A list of globs |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Base Version Control System Class all VCS related classes should extend from, provides common API.
This class should be overridden in VCS subclass, if not a FacioException will be raised.
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Create a temporary directory to clone the template to.
Returns: | str – Temp directory path |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Template.copy callback function to remove created temp directory.
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Git Version Control System for cloning git repositories.
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Create a temporary directory to clone the template to.
Returns: | str – Temp directory path |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Template.copy callback function to remove created temp directory.
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Mercurial Version Control System for cloning hg repositories.
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Create a temporary directory to clone the template to.
Returns: | str – Temp directory path |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Template.copy callback function to remove created temp directory.
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Below is documentatin for the facio.hooks.Hook class.
Print error that does not result in an exit (Red)
Parameters: | message (str) – Message to print to user |
---|
Common message prompting for gathering input form the end user.
Parameters: | message (str) – Message to print to user |
---|
Has a hooks module run.
Parameters: | path (str) – The hooks python module path |
---|---|
Returns: | False if not run else the modules returned data |
Import module to run in before or post hooks.
Parameters: | path (str) – The python path to the module |
---|
Print message information to user (Blue)
Parameters: | message (str) – Message to print to user |
---|
** Optional Key Word Arguments **
Parameters: | color – Clint color function to use |
---|---|
Type: | function – default blue |
Print a success message (Green)
Parameters: | message (str) – Message to print to user |
---|
Print a warning message (Yellow)
Parameters: | message (str) – Message to print to user |
---|
Returns the default path to python, if virtualenv hooks has been called use that path, else use the current executing python, this should be the systems python in most cases.
Returns: | str – path to python executable |
---|
Gets the install args from the user, for example setup.py install or develop.
Returns: | str – The install type |
---|
Gets the path to python to run setup.py against. Detect if the virtualenv hooks has run, if so the default path to python should come from the path to this virtual environment, else it should be the system default python path.
Returns: | str – The path to python |
---|
Fancy helping out? Fork, commit and issue a pull request :)
I can’t guarantee I will accept your pull request, but here some things which will help:
I use Git Flow to develop this project, as such the branch structure is as follows:
So please create new features from the develop branch. Pull requests onto master directly will not be accepted unless it is a hotfix.
Note
This section assumes familiarity with python virtual environments and virtualenvwrapper.
First create a fork of http://github.com/krak3n/facio so it’s in your own github account, then clone:
$ git clone git@github.com:you/facio.git
Once cloned switch to the develop branch:
$ git fetch --all
$ git checkout develop
Create a python virtual environment:
$ virtualenv facio --no-site-packages
$ workon facio
Now you can install the code as a development egg with the development dependencies, this includes everything you need to run tests and debug code.
$ make develop
Facio and it’s dependencies will now be installed into your virtual environment.
I use Vagrant for development so I have bundled the facio repository with a Vagrantfile.
There are the following dependencies:
Once you have all the dependencies installed it should be a simple case of running vagrant up at the root of the repository. Once it’s finished you should have a development environment with all of the facio dependencies installed into a python virtual environment. All you have to do is run:
$ make develop
On the vagrant box.