How Santa's Elves Keep their Workshop Tidy
Code::TidyAll bills itself as
Your all-in-one code tidier and validator. Many people do not know this, but
tidyall has become an indispensable part of the toolkit in Santa's workshop. It makes it trivial for the elves to keep their code formatting consistent and clean.
Many of us are familiar with Perl::Tidy (the Perl module for reformatting your source code in a consistent manner according to a set of rules,) so we'll start with it as an example. Since the elves work as a team, it's easiest for them to add their common
.perltidyrc to their Git repository. Next they create an rc file for the
tidyall command line utility. They add this to the top level of their repository and call it
Each section of a
.tidyallrc file begins by specifying the tidier/formatter which is being configured. In this case it's the Code::TidyAll::Plugin::PerlTidy plugin which plugs Perl::Tidy into
select args accept File::Zglob patterns (i.e. shell glob pattern). This allows the elves to configure which files the plugin should be applied to. Similarly, they can also add
ignore patterns to exclude arbitrary files and patterns.
argv param lets the elves specify a set of arguments to pass to Perl::Tidy. In this case the elves use the
profile arg, which tells
perltidy where to find a valid
$ROOT is a special variable provided by
tidyall which means the top level of the repository it has been added to.
Now, they're all set.
tidyall -a will tidy everything which matches the
select statements in the configuration.
tidyall -g is much like
tidyall -a but it is restricted to all files which have been changed but not yet committed to the git repository they're currently working in.
Let's have a look at an example. This is the repository the elves are working on:
$ tree . |-- Changes |-- MANIFEST.SKIP |-- dist.ini |-- lib | `-- Acme | `-- Claus | | `-- Roles | | | `-- JellyBelly.pm | | `-- Sleigh.pm | `-- Claus.pm `-- santas-workshop.psgi
tidyall to check everything, using the
$ tidyall -a [tidied] lib/Acme/Claus.pm [checked] lib/Acme/Claus/Sleigh.pm [checked] santas-workshop.psgi
You can see from the above that tidyall only checked the files that we configured it to look at. Now, what if we only want to check the files which have uncommitted changes in Git.
$ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: lib/Acme/Claus.pm
We have one modified file,
lib/Acme/Claus.pm. So, let's constrain this further and use the
-g flag. This should mean that only one file gets checked and possibly tidied.
tidyall -g [tidied] lib/Acme/Claus.pm
It worked! At this point the elves essentially have a wrapper around
perltidy, which lets them restrict which files the transformations are applied to. Helpful, right? Let's take it a step further. Two of the elves on the gifting geolocation team, Holly and Max, are pretty good about tidying their files before they commit them to the workshop's main repo. However, the other half of the team, Buddy and Peppermint aren't quite so disciplined. How can Holly and Max ensure that Buddy and Peppermint work together with the rest of the team? Well, since they're using Git, there are a few things they can do. (This would be a good time to note that
tidyall has Subversion support too.)
No Untidy Code Makes it Past this Hook
The first thing Holly and Max can try is using a pre-commit hook. Setting it up is easy.
mkdir -p git/hooks
Now create git/hooks/pre-commit with the following content:
Then create git/setup.sh
Now, all Holly and Max need to do is tell Buddy and Peppermint to check out the latest commits from master and run the following command:
This will set up a hook which runs before any git commit in the local repo is finalized. Note that the hook will not tidy your files. It will merely warn you about untidy code and prevent the commit. (You can get the same behaviour at the command line by supplying the
--check-only arg). At this point you can check what the problems are and then run
tidyall -g as appropriate. Then be sure to perform your tidying before you commit. If you don't,
tidyall will be fooled into thinking that your commits are clean, even if you haven't staged the tidied bits.
For example, what happens if someone tries to commit untidy code?
$ git commit [checked] lib/Acme/Claus.pm *** needs tidying 1 file did not pass tidyall check
In a traditional git setup it's also possible to install similar pre-receive hooks that run on the main repository whenever someone pushes code to it. However, since Santa's workshop runs Github Enterprise - where pre-receive hooks aren't possible without some wrangling - pre-commit hooks and a strong lecture on always installing them clientside will have to do.
Testing to Keep Buddy and Peppermint in Line
Now, it's entirely possible that someone will forget to enable the hook or even intentionally bypass it. (You can do this with
git commit --no-verify). Let's put another safeguard in place to catch the naughty elves.
Let's create a file called t/tidyall.t and add the following lines:
Now we'll have a failing test whenever something untidy makes it into the master branch. Holly and Max are now safe in the knowledge that whenever untidy code is tested via their CS (continuous santagration) that the test suite will curse loudly and the perpetrator(s) will be exposed. In fact, it should look a little bit like this:
$ prove t/tidyall.t t/tidyall.t .. 1..4 [checked] lib/Acme/Claus.pm *** needs tidying # *** needs tidying not ok 1 - lib/Acme/Claus.pm # Failed test 'lib/Acme/Claus.pm' # at t/tidyall.t line 3. ok 2 - lib/Acme/Claus/Sleigh.pm ok 3 - santas-workshop.psgi [checked] t/tidyall.t ok 4 - t/tidyall.t # Looks like you failed 1 test of 4. Dubious, test returned 1 (wstat 256, 0x100) Failed 1/4 subtests Test Summary Report ------------------- t/tidyall.t (Wstat: 256 Tests: 4 Failed: 1) Failed test: 1 Non-zero exit status: 1 Files=1, Tests=4, 1 wallclock secs ( 0.03 usr 0.01 sys + 0.24 cusr 0.02 csys = 0.30 CPU) Result: FAIL
Can the Elves add a vim Key Binding?
Of course. They can add the following to their .vimrc file:
" Run tidyall on the current buffer. If an error occurs, show it and leave it " in tidyall.ERR, and undo any changes. command! TidyAll :call TidyAll() function! TidyAll() let cur_pos = getpos( '.' ) let cmdline = ':1,$!tidyall --mode editor --pipe %:p 2> tidyall.ERR' execute( cmdline ) if v:shell_error echo "\nContents of tidyall.ERR:\n\n" . system( 'cat tidyall.ERR' ) silent undo else call system( 'rm tidyall.ERR' ) endif call setpos( '.', cur_pos ) endfunction " Uncomment to set leader to , " let mapleader = ',' " Bind to ,t (or leader+t) map <leader>t :TidyAll<cr>
There may also be the odd elf who swears by emacs. The emacs code can be found in the repository https://github.com/autarch-code/perl-code-tidyall/blob/master/etc/editors/tidyall.el
So far we have perltidy set up, we have a Git hook to enforce it and a test to make sure the hook is being enforced. What's next?
The great thing about this module is that it has many plugins, so it's not just about tidying Perl code. You can any of the following plugins to your projects:
With this much plugin support
tidyall can be used for front end as well as back end development. As mentioned above, it works with Subversion as well as Git. It helps Santa's elves keep their workshop clean and tidy. Maybe it can help your workshop as well.