Integrate Git and Puppet-Lint into your Puppet workflow

As a Systems Administrator the majority of my work entails designing, building, deploying, and supporting systems and I never thought I’d have a reason to add a tool like Git to my toolchain. However, my discovery of Puppet and embracing of the DevOps philosophy has fundamentally altered the way I handle systems administration tasks. Alone, Puppet is an incredibly powerful tool that delivers a consistent and scaleable administration experience, but integrating it with other tools with give your Puppet Master super powers (well, not really, but it will extend its functionality significantly). In this post, we’re going to discuss how to integrate Puppet-Lint and Git into the traditional Puppet workflow to proactively block the deployment of Puppet manifests that contain syntax errors.

Installing Puppet-Lint

The fist step in integrating Puppet-Lint and Git into your workflow is to install the Puppet-Lint Ruby Gem. At the time of this writing the Ruby Gem program doesn’t seem to be available in either the base or EPEL repositories so we’ll have to download it from another source and manually install it. First, though, we need to install some dependencies:

[bash]yum install ruby-devel ruby-docs ruby-ri ruby-irb ruby-rdoc[/bash]

Once the dependencies have been installed we’ll download and extract the latest version of Ruby Gems:



[bash]tar zxf rubygems-1.8.25.tgz[/bash]

Next, we’ll install Ruby Gems:

[bash]cd rubygems-1.8.25[/bash]


[bash]ruby ./setup.rb[/bash]

With all the hard work out of the way we can now install the Puppet-Lint gem:

[bash]gem install puppet-lint[/bash]

Now that Puppet-Lint is installed we can start doing syntax analysis on our modules with the following command:

[bash]puppet-lint –with-filename [/bash]

For example, if we had a module called apache that we wanted to perform a syntax check on we would issue the following command:

[bash]puppet-lint –with-filename /etc/puppet/modules/apache[/bash]

Installing Git

Installing Git on RHEL/CentOS systems is trivial and can be done with a single command:

[bash]yum install git[/bash]

Note that the above command installs the minimal set of components needed for a working Git implementation  If you’d like Git and all of its plug-ins you can install the git-all package instead.

Initializing the Puppet module Git repository

One of the benefits of Git is that it can initialize a repository anywhere on disk and we’re going to do just that with our Puppet module directory:

[bash]git init[/bash]


[bash]git add *[/bash]


[bash]git commit -a -m ‘I’m version controlling my Puppet modules now!'[/bash]

The above commands will create the an empty Git repository, add all of our Puppet modules, and commit the files for the first time.

Creating the Git pre-commit hook

Git can use pre-commit hooks to run custom checks against code and block the commit if one or more of the pre-commit hooks fail. With that knowledge, it would seem logical to create a pre-commit hook to lint each of our edited Puppet manifests prior to committing the changes. In our case the pre-commit hook will be a simple bash script as shown below:


declare -a files

files=$(git diff –cached –name-only)

for file in ${files[@]}
puppet-lint –with-filename "$file"
if [ $RC -ne 0 ]
exit $RC

exit 0

The above code should be saved into the pre-commit file in the .git/hooks directory in the repository. Additionally, the pre-commit hook should should be made executable with:

[bash]chmod +x .git/hooks/pre-commit[/bash]

Bringing it all together

We now have Git and Puppet-Lint in place and each is working independently  Let’s look at a potential real world example to see how all the pieces fit together. First, we’ll examine a maifest that breaks some of the Puppet Style Guide rules and see what Puppet-Lint outputs:


class test {

file {"/tmp/foor_bar.txt" :

ensure => present
mode => 644,




In the above class we’re breaking some pretty important rules of the Puppet Style Guide as shown in the below output:


[user1@puppet-box modules]# git commit test/manifests/init.pp -m ‘Added my first broken module to source control’
test/manifests/init.pp – ERROR: trailing whitespace found on line 5
test/manifests/init.pp – ERROR: two-space soft tabs not used on line 5
test/manifests/init.pp – ERROR: two-space soft tabs not used on line 7
test/manifests/init.pp – WARNING: class not documented on line 1
test/manifests/init.pp – WARNING: indentation of => is not properly aligned on line 7
test/manifests/init.pp – WARNING: double quoted string containing no variables on line 3
test/manifests/init.pp – WARNING: unquoted file mode on line 7
test/manifests/init.pp – WARNING: mode should be represented as a 4 digit octal value or symbolic mode on line 7


Now we’ll tidy the manifest up a bit:

class test {

file {‘/tmp/foor_bar.txt’ :

ensure => present,
mode => ‘0644’,



And try the commit again:


[user1@puppet-box]# git commit test/manifests/init.pp -m ‘Added my first working module to source control’
test/manifests/init.pp – WARNING: class not documented on line 1
[master 4f37997] Added my first working module to source control
1 files changed, 10 insertions(+), 0 deletions(-)

create mode 100644 test/manifests/init.pp


Success! You’ll notice that Puppet-Lint still outputs a warning about the class not being documented, but that’s not severe enough to create problems with catalog compilation so the commit is still successful. This is a good time to note that commits will only be blocked when error conditions are encountered. Warning states will be reported, but not block the commit.

While integrating Git and Puppet-Lint into your workflow solves some pretty big problems (accountability, roll-back, etc) the solution outlined in this post is only the beginning. In a future post we’ll look at leveraging Puppet environments and Git branches to stage and test our Puppet manifests in style.

Leave a Reply

Your email address will not be published. Required fields are marked *