Skip to content
Browse files

Build a dependency graph within to_file so that groups are defined be…

…fore they are used
  • Loading branch information...
1 parent 0ac72ef commit 2c882bdeb9056ac01e1ef715ca596af3ecf12f48 @wingrunr21 committed Apr 29, 2012
Showing with 99 additions and 2 deletions.
  1. +1 −0 gitolite.gemspec
  2. +1 −0 lib/gitolite.rb
  3. +33 −2 lib/gitolite/config.rb
  4. +64 −0 spec/config_spec.rb
View
1 gitolite.gemspec
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
s.add_development_dependency "simplecov", "~> 0.6.2"
s.add_dependency "grit", "~> 2.5.0"
s.add_dependency "hashery", "~> 1.5.0"
+ s.add_dependency "plexus", "~> 0.5.10"
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
View
1 lib/gitolite.rb
@@ -1,6 +1,7 @@
module Gitolite
require 'grit'
require 'hashery'
+ require 'plexus'
require 'gitolite/ssh_key'
require 'gitolite/config'
require 'gitolite/gitolite_admin'
View
35 lib/gitolite/config.rb
@@ -68,7 +68,8 @@ def to_file(path=".", filename=@filename)
new_conf = File.join(path, filename)
File.open(new_conf, "w") do |f|
#Output groups
- @groups.each_value {|group| f.write group.to_s }
+ dep_order = build_groups_depgraph
+ dep_order.each {|group| f.write group.to_s }
gitweb_descs = []
@repos.each do |k, v|
@@ -106,7 +107,7 @@ def process_config(config)
line = cleanup_config_line(l)
next if line.empty? #lines are empty if we killed a comment
- case line.strip
+ case line
#found a repo definition
when /^repo (.*)/
#Empty our current context
@@ -199,8 +200,38 @@ def method_missing(meth, *args, &block)
end
end
+ # Builds a dependency tree from the groups in order to ensure all groups
+ # are defined before they are used
+ def build_groups_depgraph
+ dp = ::Plexus::Digraph.new
+
+ # Add each group to the graph
+ @groups.each_value do |group|
+ # Select group names from the users
+ subgroups = group.users.select {|u| u =~ /^#{Group::PREPEND_CHAR}.*$/}
+ .map{|g| get_group g.gsub(Group::PREPEND_CHAR, '') }
+
+ subgroups.each do |subgroup|
+ dp.add_edge! subgroup, group
+ end
+ end
+
+ # Figure out if we have a good depedency graph
+ dep_order = dp.topsort
+
+ if dep_order.empty?
+ raise GroupDependencyError unless @groups.empty?
+ end
+
+ dep_order
+ end
+
#Raised when something in a config fails to parse properly
class ParseError < RuntimeError
end
+
+ # Raised when group dependencies cannot be suitably resolved for output
+ class GroupDependencyError < RuntimeError
+ end
end
end
View
64 spec/config_spec.rb
@@ -1,3 +1,4 @@
+require 'plexus'
require 'gitolite/config'
require 'spec_helper'
@@ -317,6 +318,69 @@
c = Gitolite::Config.init
lambda{ c.to_file('/home/test.rb') }.should raise_error(ArgumentError)
end
+
+ it 'should resolve group dependencies such that all groups are defined before they are used' do
+ c = Gitolite::Config.init
+ c.filename = "test_deptree.conf"
+
+ # Build some groups out of order
+ g = Gitolite::Config::Group.new "groupa"
+ g.add_users "bob", "@groupb"
+ c.add_group(g)
+
+ g = Gitolite::Config::Group.new "groupb"
+ g.add_users "joe", "sam", "susan", "andrew"
+ c.add_group(g)
+
+ g = Gitolite::Config::Group.new "groupc"
+ g.add_users "jane", "@groupb", "brandon"
+ c.add_group(g)
+
+ g = Gitolite::Config::Group.new "groupd"
+ g.add_users "larry", "@groupc"
+ c.add_group(g)
+
+ # Write the config to a file
+ file = c.to_file('/tmp')
+
+ # Read the conf and make sure our order is correct
+ f = File.read(file)
+ lines = f.lines.map {|l| l.strip}
+
+ # Compare the file lines. Spacing is important here since we are doing a direct comparision
+ lines[0].should == "@groupb = andrew joe sam susan"
+ lines[1].should == "@groupc = @groupb brandon jane"
+ lines[2].should == "@groupd = @groupc larry"
+ lines[3].should == "@groupa = @groupb bob"
+
+ # Cleanup
+ File.unlink(file)
+ end
+
+ it 'should raise a GroupDependencyError if there is a cyclic dependency' do
+ c = Gitolite::Config.init
+ c.filename = "test_deptree.conf"
+
+ # Build some groups out of order
+ g = Gitolite::Config::Group.new "groupa"
+ g.add_users "bob", "@groupb"
+ c.add_group(g)
+
+ g = Gitolite::Config::Group.new "groupb"
+ g.add_users "joe", "sam", "susan", "@groupc"
+ c.add_group(g)
+
+ g = Gitolite::Config::Group.new "groupc"
+ g.add_users "jane", "@groupa", "brandon"
+ c.add_group(g)
+
+ g = Gitolite::Config::Group.new "groupd"
+ g.add_users "larry", "@groupc"
+ c.add_group(g)
+
+ # Attempt to write the config file
+ lambda{ c.to_file('/tmp')}.should raise_error(Gitolite::Config::GroupDependencyError)
+ end
end
describe "#cleanup_config_line" do

0 comments on commit 2c882bd

Please sign in to comment.
Something went wrong with that request. Please try again.