gitolite-dtg 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +22 -0
- data/README.markdown +58 -0
- data/Rakefile +34 -0
- data/gitolite-dtg.gemspec +29 -0
- data/lib/gitolite-dtg/config/group.rb +53 -0
- data/lib/gitolite-dtg/config/repo.rb +83 -0
- data/lib/gitolite-dtg/config.rb +267 -0
- data/lib/gitolite-dtg/gitolite_admin.rb +154 -0
- data/lib/gitolite-dtg/version.rb +5 -0
- data/lib/gitolite-dtg.rb +9 -0
- data/spec/concurrency_spec.rb +2 -0
- data/spec/config_spec.rb +459 -0
- data/spec/configs/complicated.conf +302 -0
- data/spec/configs/simple.conf +5 -0
- data/spec/gitolite_admin_spec.rb +4 -0
- data/spec/group_spec.rb +126 -0
- data/spec/repo_spec.rb +184 -0
- data/spec/spec_helper.rb +2 -0
- metadata +194 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'abbrev'
|
2
|
+
|
3
|
+
module Gitolite
|
4
|
+
module Dtg
|
5
|
+
class GitoliteAdmin
|
6
|
+
attr_accessor :gl_admin, :config
|
7
|
+
|
8
|
+
CONF = "gitolite.conf"
|
9
|
+
CONFDIR = "conf"
|
10
|
+
BRANCH = "master"
|
11
|
+
|
12
|
+
# Intialize with the path to
|
13
|
+
# the gitolite-admin repository
|
14
|
+
def initialize(path, options = {})
|
15
|
+
@path = path
|
16
|
+
@gl_admin = Grit::Repo.new(path)
|
17
|
+
|
18
|
+
@conf = options[:conf] || CONF
|
19
|
+
@confdir = options[:confdir] || CONFDIR
|
20
|
+
@branch = options[:branch] || BRANCH
|
21
|
+
|
22
|
+
# Load the configuration
|
23
|
+
load_data
|
24
|
+
end
|
25
|
+
|
26
|
+
# This method will destroy the in-memory data structures and reload everything
|
27
|
+
# from the file system
|
28
|
+
def reload!
|
29
|
+
load_data
|
30
|
+
end
|
31
|
+
|
32
|
+
#Checks to see if the given path is a gitolite-admin repository
|
33
|
+
#A valid repository contains a conf folder, keydir folder,
|
34
|
+
#and a configuration file within the conf folder
|
35
|
+
def self.is_gitolite_admin_repo?(dir)
|
36
|
+
# First check if it is a git repository
|
37
|
+
begin
|
38
|
+
repo = Grit::Repo.new(dir)
|
39
|
+
rescue Grit::InvalidGitRepositoryError
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
# If we got here it is a valid git repo,
|
44
|
+
# now check directory structure
|
45
|
+
cbl = repo.tree / 'conf/gitolite.conf'
|
46
|
+
if cbl != nil
|
47
|
+
return true
|
48
|
+
else
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# repo_name - the repository name
|
54
|
+
# username - the authenticated user name
|
55
|
+
# resource_path - the path relative to the repository root that
|
56
|
+
# the user is requesting access to
|
57
|
+
# wanted_access - the type of access the user is requesting. can
|
58
|
+
# be 'R' or 'W'
|
59
|
+
def authorize(repo_name, username, resource_path, wanted_access)
|
60
|
+
if @config == nil
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
repo = @config.repos[repo_name]
|
64
|
+
if repo != nil
|
65
|
+
repo.permissions.each do |perm_hash|
|
66
|
+
perm_hash.each do |perm, list|
|
67
|
+
#process a permission line
|
68
|
+
list.each do |refex, users|
|
69
|
+
|
70
|
+
ul = []
|
71
|
+
users.each do |user|
|
72
|
+
if user[0,1]=='@'
|
73
|
+
gname = user.gsub('@', '')
|
74
|
+
if ((@config.special_groups.include? gname) == false)
|
75
|
+
grp = @config.flat_groups[gname]
|
76
|
+
ul.concat(grp)
|
77
|
+
else
|
78
|
+
ul.push(user)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
ul.push(user)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
ul.uniq!
|
85
|
+
|
86
|
+
user_matches = false
|
87
|
+
if ((ul.include? "@all") || ((ul.include? "@raven") && (username != nil)) || (ul.include? username))
|
88
|
+
user_matches = true
|
89
|
+
end
|
90
|
+
|
91
|
+
if user_matches == false
|
92
|
+
next
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
refex_applies = false;
|
97
|
+
if refex == ''
|
98
|
+
refex_applies = true;
|
99
|
+
else
|
100
|
+
dirs = []
|
101
|
+
dirs.push(refex)
|
102
|
+
dirs.push(resource_path)
|
103
|
+
|
104
|
+
common_prefix = dirs.abbrev.keys.min_by {|key| key.length}.chop
|
105
|
+
common_directory = common_prefix.sub(%r{/[^/]*$}, '')
|
106
|
+
|
107
|
+
if common_directory != ''
|
108
|
+
refex_applies = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
if !refex_applies
|
112
|
+
next # if rule refex does not refer to the resource the user requested, go to the next rule
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
access_matches = false
|
117
|
+
if (perm.include? wanted_access)
|
118
|
+
access_matches = true
|
119
|
+
end
|
120
|
+
|
121
|
+
# authorization cases. at this point, user_matches==true and refex_applies==true:
|
122
|
+
if (perm == "-")
|
123
|
+
return false
|
124
|
+
elsif (user_matches && access_matches)
|
125
|
+
#print "Access allowed by matching rule: " + perm + " "
|
126
|
+
#print list
|
127
|
+
#print "\n"
|
128
|
+
return true
|
129
|
+
else
|
130
|
+
next
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
return false
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
def load_data
|
142
|
+
head = @gl_admin.commits(@branch).first
|
143
|
+
config_blob = head.tree / File.join(@confdir, @conf)
|
144
|
+
if config_blob != nil
|
145
|
+
@config = Config.new(config_blob)
|
146
|
+
else
|
147
|
+
@config = nil
|
148
|
+
print 'gitolite configuration could not be found in repository at ' + File.join(@path,@confdir,@conf)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/gitolite-dtg.rb
ADDED
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,459 @@
|
|
1
|
+
require 'gratr'
|
2
|
+
require 'gitolite/config'
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Gitolite::Config do
|
6
|
+
conf_dir = File.join(File.dirname(__FILE__),'configs')
|
7
|
+
|
8
|
+
describe "#new" do
|
9
|
+
it 'should read a simple configuration' do
|
10
|
+
c = Gitolite::Config.new(File.join(conf_dir, 'simple.conf'))
|
11
|
+
c.repos.length.should == 2
|
12
|
+
c.groups.length.should == 0
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should read a complex configuration' do
|
16
|
+
c = Gitolite::Config.new(File.join(conf_dir, 'complicated.conf'))
|
17
|
+
c.groups.length.should == 5
|
18
|
+
c.repos.length.should == 12
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'gitweb operations' do
|
22
|
+
before :all do
|
23
|
+
@config = Gitolite::Config.new(File.join(conf_dir, 'complicated.conf'))
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should correctly read gitweb options for an existing repo' do
|
27
|
+
r = @config.get_repo('gitolite')
|
28
|
+
r.owner.should == "Sitaram Chamarty"
|
29
|
+
r.description.should == "fast, secure, access control for git in a corporate environment"
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should correctly read a gitweb option with no owner for an existing repo' do
|
33
|
+
r = @config.get_repo('foo')
|
34
|
+
r.owner.should be nil
|
35
|
+
r.description.should == "Foo is a nice test repo"
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should correctly read gitweb options for a new repo' do
|
39
|
+
r = @config.get_repo('foobar')
|
40
|
+
r.owner.should == "Bob Zilla"
|
41
|
+
r.description.should == "Foobar is top secret"
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should correctly read gitweb options with no owner for a new repo' do
|
45
|
+
r = @config.get_repo('bar')
|
46
|
+
r.owner.should be nil
|
47
|
+
r.description.should == "A nice place to get drinks"
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should raise a ParseError when a description is not specified' do
|
51
|
+
t = Tempfile.new('bad_conf.conf')
|
52
|
+
t.write('gitolite "Bob Zilla"')
|
53
|
+
t.close
|
54
|
+
|
55
|
+
lambda { Gitolite::Config.new(t.path) }.should raise_error(Gitolite::Config::ParseError)
|
56
|
+
|
57
|
+
t.unlink
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should raise a ParseError when a Gitweb description is specified for a group' do
|
61
|
+
t = Tempfile.new('bad_conf.conf')
|
62
|
+
t.write('@gitolite "Bob Zilla" = "Test description"')
|
63
|
+
t.close
|
64
|
+
|
65
|
+
lambda { Gitolite::Config.new(t.path) }.should raise_error(Gitolite::Config::ParseError)
|
66
|
+
|
67
|
+
t.unlink
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#init" do
|
73
|
+
it 'should create a valid, blank Gitolite::Config' do
|
74
|
+
c = Gitolite::Config.init
|
75
|
+
|
76
|
+
c.should be_an_instance_of Gitolite::Config
|
77
|
+
c.repos.should_not be nil
|
78
|
+
c.repos.length.should be 0
|
79
|
+
c.groups.should_not be nil
|
80
|
+
c.groups.length.should be 0
|
81
|
+
c.filename.should == "gitolite.conf"
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should create a valid, blank Gitolite::Config with the given filename' do
|
85
|
+
filename = "test.conf"
|
86
|
+
c = Gitolite::Config.init(filename)
|
87
|
+
|
88
|
+
c.should be_an_instance_of Gitolite::Config
|
89
|
+
c.repos.should_not be nil
|
90
|
+
c.repos.length.should be 0
|
91
|
+
c.groups.should_not be nil
|
92
|
+
c.groups.length.should be 0
|
93
|
+
c.filename.should == filename
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "repo management" do
|
98
|
+
before :each do
|
99
|
+
@config = Gitolite::Config.new(File.join(conf_dir, 'complicated.conf'))
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#get_repo" do
|
103
|
+
it 'should fetch a repo by a string containing the name' do
|
104
|
+
@config.get_repo('gitolite').should be_an_instance_of Gitolite::Config::Repo
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should fetch a repo via a symbol representing the name' do
|
108
|
+
@config.get_repo(:gitolite).should be_an_instance_of Gitolite::Config::Repo
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should return nil for a repo that does not exist' do
|
112
|
+
@config.get_repo(:glite).should be nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#has_repo?" do
|
117
|
+
it 'should return false for a repo that does not exist' do
|
118
|
+
@config.has_repo?(:glite).should be false
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should check for the existance of a repo given a repo object' do
|
122
|
+
r = @config.repos["gitolite"]
|
123
|
+
@config.has_repo?(r).should be true
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should check for the existance of a repo given a string containing the name' do
|
127
|
+
@config.has_repo?('gitolite').should be true
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should check for the existance of a repo given a symbol representing the name' do
|
131
|
+
@config.has_repo?(:gitolite).should be true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "#add_repo" do
|
136
|
+
it 'should throw an ArgumentError for non-Gitolite::Config::Repo objects passed in' do
|
137
|
+
lambda{ @config.add_repo("not-a-repo") }.should raise_error(ArgumentError)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should add a given repo to the list of repos' do
|
141
|
+
r = Gitolite::Config::Repo.new('cool_repo')
|
142
|
+
nrepos = @config.repos.size
|
143
|
+
@config.add_repo(r)
|
144
|
+
|
145
|
+
@config.repos.size.should == nrepos + 1
|
146
|
+
@config.has_repo?(:cool_repo).should be true
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should merge a given repo with an existing repo' do
|
150
|
+
#Make two new repos
|
151
|
+
repo1 = Gitolite::Config::Repo.new('cool_repo')
|
152
|
+
repo2 = Gitolite::Config::Repo.new('cool_repo')
|
153
|
+
|
154
|
+
#Add some perms to those repos
|
155
|
+
repo1.add_permission("RW+", "", "bob", "joe", "sam")
|
156
|
+
repo1.add_permission("R", "", "sue", "jen", "greg")
|
157
|
+
repo1.add_permission("-", "refs/tags/test[0-9]", "@students", "jessica")
|
158
|
+
repo1.add_permission("RW", "refs/tags/test[0-9]", "@teachers", "bill", "todd")
|
159
|
+
repo1.add_permission("R", "refs/tags/test[0-9]", "@profs")
|
160
|
+
|
161
|
+
repo2.add_permission("RW+", "", "jim", "cynthia", "arnold")
|
162
|
+
repo2.add_permission("R", "", "daniel", "mary", "ben")
|
163
|
+
repo2.add_permission("-", "refs/tags/test[0-9]", "@more_students", "stephanie")
|
164
|
+
repo2.add_permission("RW", "refs/tags/test[0-9]", "@student_teachers", "mike", "judy")
|
165
|
+
repo2.add_permission("R", "refs/tags/test[0-9]", "@leaders")
|
166
|
+
|
167
|
+
#Add the repos
|
168
|
+
@config.add_repo(repo1)
|
169
|
+
@config.add_repo(repo2)
|
170
|
+
|
171
|
+
#Make sure perms were properly merged
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should overwrite an existing repo when overwrite = true' do
|
175
|
+
#Make two new repos
|
176
|
+
repo1 = Gitolite::Config::Repo.new('cool_repo')
|
177
|
+
repo2 = Gitolite::Config::Repo.new('cool_repo')
|
178
|
+
|
179
|
+
#Add some perms to those repos
|
180
|
+
repo1.add_permission("RW+", "", "bob", "joe", "sam")
|
181
|
+
repo1.add_permission("R", "", "sue", "jen", "greg")
|
182
|
+
repo2.add_permission("RW+", "", "jim", "cynthia", "arnold")
|
183
|
+
repo2.add_permission("R", "", "daniel", "mary", "ben")
|
184
|
+
|
185
|
+
#Add the repos
|
186
|
+
@config.add_repo(repo1)
|
187
|
+
@config.add_repo(repo2, true)
|
188
|
+
|
189
|
+
#Make sure repo2 overwrote repo1
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "#rm_repo" do
|
194
|
+
it 'should remove a repo for the Gitolite::Config::Repo object given' do
|
195
|
+
r = @config.get_repo(:gitolite)
|
196
|
+
r2 = @config.rm_repo(r)
|
197
|
+
r2.name.should == r.name
|
198
|
+
r2.permissions.length.should == r.permissions.length
|
199
|
+
r2.owner.should == r.owner
|
200
|
+
r2.description.should == r.description
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'should remove a repo given a string containing the name' do
|
204
|
+
r = @config.get_repo(:gitolite)
|
205
|
+
r2 = @config.rm_repo('gitolite')
|
206
|
+
r2.name.should == r.name
|
207
|
+
r2.permissions.length.should == r.permissions.length
|
208
|
+
r2.owner.should == r.owner
|
209
|
+
r2.description.should == r.description
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should remove a repo given a symbol representing the name' do
|
213
|
+
r = @config.get_repo(:gitolite)
|
214
|
+
r2 = @config.rm_repo(:gitolite)
|
215
|
+
r2.name.should == r.name
|
216
|
+
r2.permissions.length.should == r.permissions.length
|
217
|
+
r2.owner.should == r.owner
|
218
|
+
r2.description.should == r.description
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "group management" do
|
224
|
+
before :each do
|
225
|
+
@config = Gitolite::Config.new(File.join(conf_dir, 'complicated.conf'))
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "#has_group?" do
|
229
|
+
it 'should find the staff group using a symbol' do
|
230
|
+
@config.has_group?(:staff).should be true
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should find the staff group using a string' do
|
234
|
+
@config.has_group?('staff').should be true
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should find the staff group using a Gitolite::Config::Group object' do
|
238
|
+
g = Gitolite::Config::Group.new("staff")
|
239
|
+
@config.has_group?(g).should be true
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "#get_group" do
|
244
|
+
it 'should return the Gitolite::Config::Group object for the group name String' do
|
245
|
+
g = @config.get_group("staff")
|
246
|
+
g.is_a?(Gitolite::Config::Group).should be true
|
247
|
+
g.size.should == 6
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should return the Gitolite::Config::Group object for the group name Symbol' do
|
251
|
+
g = @config.get_group(:staff)
|
252
|
+
g.is_a?(Gitolite::Config::Group).should be true
|
253
|
+
g.size.should == 6
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "#add_group" do
|
258
|
+
it 'should throw an ArgumentError for non-Gitolite::Config::Group objects passed in' do
|
259
|
+
lambda{ @config.add_group("not-a-group") }.should raise_error(ArgumentError)
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should add a given group to the groups list' do
|
263
|
+
g = Gitolite::Config::Group.new('cool_group')
|
264
|
+
ngroups = @config.groups.size
|
265
|
+
@config.add_group(g)
|
266
|
+
@config.groups.size.should be ngroups + 1
|
267
|
+
@config.has_group?(:cool_group).should be true
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
describe "#rm_group" do
|
273
|
+
it 'should remove a group for the Gitolite::Config::Group object given' do
|
274
|
+
g = @config.get_group(:oss_repos)
|
275
|
+
g2 = @config.rm_group(g)
|
276
|
+
g.should_not be nil
|
277
|
+
g2.name.should == g.name
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'should remove a group given a string containing the name' do
|
281
|
+
g = @config.get_group(:oss_repos)
|
282
|
+
g2 = @config.rm_group('oss_repos')
|
283
|
+
g2.name.should == g.name
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'should remove a group given a symbol representing the name' do
|
287
|
+
g = @config.get_group(:oss_repos)
|
288
|
+
g2 = @config.rm_group(:oss_repos)
|
289
|
+
g2.name.should == g.name
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "#to_file" do
|
296
|
+
it 'should create a file at the given path with the config\'s file name' do
|
297
|
+
c = Gitolite::Config.init
|
298
|
+
file = c.to_file('/tmp')
|
299
|
+
File.file?(File.join('/tmp', c.filename)).should be true
|
300
|
+
File.unlink(file)
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should create a file at the given path when a different filename is specified' do
|
304
|
+
filename = "test.conf"
|
305
|
+
c = Gitolite::Config.init
|
306
|
+
c.filename = filename
|
307
|
+
file = c.to_file('/tmp')
|
308
|
+
File.file?(File.join('/tmp', filename)).should be true
|
309
|
+
File.unlink(file)
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'should raise an ArgumentError when an invalid path is specified' do
|
313
|
+
c = Gitolite::Config.init
|
314
|
+
lambda { c.to_file('/does/not/exist') }.should raise_error(ArgumentError)
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'should raise an ArgumentError when a filename is specified in the path' do
|
318
|
+
c = Gitolite::Config.init
|
319
|
+
lambda{ c.to_file('/home/test.rb') }.should raise_error(ArgumentError)
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'should resolve group dependencies such that all groups are defined before they are used' do
|
323
|
+
c = Gitolite::Config.init
|
324
|
+
c.filename = "test_deptree.conf"
|
325
|
+
|
326
|
+
# Build some groups out of order
|
327
|
+
g = Gitolite::Config::Group.new "groupa"
|
328
|
+
g.add_users "bob", "@groupb"
|
329
|
+
c.add_group(g)
|
330
|
+
|
331
|
+
g = Gitolite::Config::Group.new "groupb"
|
332
|
+
g.add_users "joe", "sam", "susan", "andrew"
|
333
|
+
c.add_group(g)
|
334
|
+
|
335
|
+
g = Gitolite::Config::Group.new "groupc"
|
336
|
+
g.add_users "jane", "@groupb", "brandon"
|
337
|
+
c.add_group(g)
|
338
|
+
|
339
|
+
g = Gitolite::Config::Group.new "groupd"
|
340
|
+
g.add_users "larry", "@groupc"
|
341
|
+
c.add_group(g)
|
342
|
+
|
343
|
+
# Write the config to a file
|
344
|
+
file = c.to_file('/tmp')
|
345
|
+
|
346
|
+
# Read the conf and make sure our order is correct
|
347
|
+
f = File.read(file)
|
348
|
+
lines = f.lines.map {|l| l.strip}
|
349
|
+
|
350
|
+
# Compare the file lines. Spacing is important here since we are doing a direct comparision
|
351
|
+
lines[0].should == "@groupb = andrew joe sam susan"
|
352
|
+
lines[1].should == "@groupc = @groupb brandon jane"
|
353
|
+
lines[2].should == "@groupd = @groupc larry"
|
354
|
+
lines[3].should == "@groupa = @groupb bob"
|
355
|
+
|
356
|
+
# Cleanup
|
357
|
+
File.unlink(file)
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'should raise a GroupDependencyError if there is a cyclic dependency' do
|
361
|
+
c = Gitolite::Config.init
|
362
|
+
c.filename = "test_deptree.conf"
|
363
|
+
|
364
|
+
# Build some groups out of order
|
365
|
+
g = Gitolite::Config::Group.new "groupa"
|
366
|
+
g.add_users "bob", "@groupb"
|
367
|
+
c.add_group(g)
|
368
|
+
|
369
|
+
g = Gitolite::Config::Group.new "groupb"
|
370
|
+
g.add_users "joe", "sam", "susan", "@groupc"
|
371
|
+
c.add_group(g)
|
372
|
+
|
373
|
+
g = Gitolite::Config::Group.new "groupc"
|
374
|
+
g.add_users "jane", "@groupa", "brandon"
|
375
|
+
c.add_group(g)
|
376
|
+
|
377
|
+
g = Gitolite::Config::Group.new "groupd"
|
378
|
+
g.add_users "larry", "@groupc"
|
379
|
+
c.add_group(g)
|
380
|
+
|
381
|
+
# Attempt to write the config file
|
382
|
+
lambda{ c.to_file('/tmp')}.should raise_error(Gitolite::Config::GroupDependencyError)
|
383
|
+
end
|
384
|
+
|
385
|
+
it 'should resolve group dependencies even when there are disconnected portions of the graph' do
|
386
|
+
c = Gitolite::Config.init
|
387
|
+
c.filename = "test_deptree.conf"
|
388
|
+
|
389
|
+
# Build some groups out of order
|
390
|
+
g = Gitolite::Config::Group.new "groupa"
|
391
|
+
g.add_users "bob", "timmy", "stephanie"
|
392
|
+
c.add_group(g)
|
393
|
+
|
394
|
+
g = Gitolite::Config::Group.new "groupb"
|
395
|
+
g.add_users "joe", "sam", "susan", "andrew"
|
396
|
+
c.add_group(g)
|
397
|
+
|
398
|
+
g = Gitolite::Config::Group.new "groupc"
|
399
|
+
g.add_users "jane", "earl", "brandon", "@groupa"
|
400
|
+
c.add_group(g)
|
401
|
+
|
402
|
+
g = Gitolite::Config::Group.new "groupd"
|
403
|
+
g.add_users "larry", "chris", "emily"
|
404
|
+
c.add_group(g)
|
405
|
+
|
406
|
+
# Write the config to a file
|
407
|
+
file = c.to_file('/tmp')
|
408
|
+
|
409
|
+
# Read the conf and make sure our order is correct
|
410
|
+
f = File.read(file)
|
411
|
+
lines = f.lines.map {|l| l.strip}
|
412
|
+
|
413
|
+
# Compare the file lines. Spacing is important here since we are doing a direct comparision
|
414
|
+
lines[0].should == "@groupd = chris emily larry"
|
415
|
+
lines[1].should == "@groupb = andrew joe sam susan"
|
416
|
+
lines[2].should == "@groupa = bob stephanie timmy"
|
417
|
+
lines[3].should == "@groupc = @groupa brandon earl jane"
|
418
|
+
|
419
|
+
# Cleanup
|
420
|
+
File.unlink(file)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
describe "#cleanup_config_line" do
|
425
|
+
before(:each) do
|
426
|
+
@config = Gitolite::Config.init
|
427
|
+
end
|
428
|
+
|
429
|
+
it 'should remove comments' do
|
430
|
+
s = "#comment"
|
431
|
+
@config.instance_eval { cleanup_config_line(s) }.empty?.should == true
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'should remove inline comments, keeping content before the comment' do
|
435
|
+
s = "blablabla #comment"
|
436
|
+
@config.instance_eval { cleanup_config_line(s) }.should == "blablabla"
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'should pad = with spaces on each side' do
|
440
|
+
s = "bob=joe"
|
441
|
+
@config.instance_eval { cleanup_config_line(s) }.should == "bob = joe"
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'should replace multiple space characters with a single space' do
|
445
|
+
s = "bob = joe"
|
446
|
+
@config.instance_eval { cleanup_config_line(s) }.should == "bob = joe"
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'should cleanup whitespace at the beginning and end of lines' do
|
450
|
+
s = " bob = joe "
|
451
|
+
@config.instance_eval { cleanup_config_line(s) }.should == "bob = joe"
|
452
|
+
end
|
453
|
+
|
454
|
+
it 'should cleanup whitespace and comments effectively' do
|
455
|
+
s = " bob = joe #comment"
|
456
|
+
@config.instance_eval { cleanup_config_line(s) }.should == "bob = joe"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|