Today, I had a small issue while using Puppet. To make it quick: I got a circular dependency in my Puppet recipes and Puppet failed with a verbose but not so helpful message:

err: Could not apply complete catalog: Found dependency cycles in the following relationships:
User[root] => File[/usr/share/locale/locale.alias], Package[python-setuptools] ...
# INSERT TONS of other dependencies here
... try using the '--graph' option and open the '.dot' files in OmniGraffle or GraphViz

Of course, I tried the ‘–graph’ option but, due to it’s size, the generated diagram was anything but readable.

I decided to script my way out of this tangle and, with help from this forum entry, I was able to quickly piece together a Ruby script that detects cycles in a graph:

require 'rubygems'
require 'rgl/connected_components'
require 'rgl/adjacency'

graph = RGL::DirectedAdjacencyGraph[
  *File.open("data.txt").read.split(/,[ \n]+/).each.map do |line|
    line.strip.chomp(",").split(' => ')
  end.flatten]

inv_comp_map = {}
graph.strongly_connected_components.comp_map.each do |v, n|
  (inv_comp_map[n] ||= []) << v
end
puts inv_comp_map.values.delete_if { |scc| scc.size == 1 }.inspect

The script reads a {{data.txt}} file containing lines of the form:

User[dom] => File[foo],
User[bar] => User[dom],
File[foo] => User[bar],
User[foobar] => File[foo]

and outputs :

[["User[dom]", "File[foo]", "User[bar]"]]

To use the script:

gem install rgl
cat > detect_cycle.rb # copy and paste the script
cat > data.txt        # copy and paste Puppet's dependency information
ruby detect_cycle.rb

Rejoice ! ;-)