gitolite-dtg 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gitolite-dtg (0.0.1)
|
5
|
+
gratr19 (~> 0.4.4.1)
|
6
|
+
grit (~> 2.5.0)
|
7
|
+
hashery (~> 1.5.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
blankslate (3.1.2)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
forgery (0.5.0)
|
15
|
+
gratr19 (0.4.4.1)
|
16
|
+
grit (2.5.0)
|
17
|
+
diff-lcs (~> 1.1)
|
18
|
+
mime-types (~> 1.15)
|
19
|
+
posix-spawn (~> 0.3.6)
|
20
|
+
hashery (1.5.0)
|
21
|
+
blankslate
|
22
|
+
json (1.6.6)
|
23
|
+
mime-types (1.19)
|
24
|
+
multi_json (1.3.2)
|
25
|
+
posix-spawn (0.3.6)
|
26
|
+
rdoc (3.12)
|
27
|
+
json (~> 1.4)
|
28
|
+
rspec (2.9.0)
|
29
|
+
rspec-core (~> 2.9.0)
|
30
|
+
rspec-expectations (~> 2.9.0)
|
31
|
+
rspec-mocks (~> 2.9.0)
|
32
|
+
rspec-core (2.9.0)
|
33
|
+
rspec-expectations (2.9.1)
|
34
|
+
diff-lcs (~> 1.1.3)
|
35
|
+
rspec-mocks (2.9.0)
|
36
|
+
simplecov (0.6.2)
|
37
|
+
multi_json (~> 1.3)
|
38
|
+
simplecov-html (~> 0.5.3)
|
39
|
+
simplecov-html (0.5.3)
|
40
|
+
|
41
|
+
PLATFORMS
|
42
|
+
ruby
|
43
|
+
|
44
|
+
DEPENDENCIES
|
45
|
+
forgery (~> 0.5.0)
|
46
|
+
gitolite-dtg!
|
47
|
+
rdoc (~> 3.12)
|
48
|
+
rspec (~> 2.9.0)
|
49
|
+
simplecov (~> 0.6.2)
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2010-2011 Stafford Brunk
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# gitolite-dtg #
|
2
|
+
Digital Technology Group, University of Cambridge
|
3
|
+
|
4
|
+
Forked from [wingrunr21/gitolite](https://github.com/wingrunr21/gitolite)
|
5
|
+
|
6
|
+
This gem provides a Ruby read-only interface to the [gitolite](https://github.com/sitaramc/gitolite) git backend system
|
7
|
+
(by parsing the configuration file found in the bare gitolite-admin repository). It aims to enable an application to query
|
8
|
+
gitolite repository permissions based on data written in the gitolite-admin repository.
|
9
|
+
|
10
|
+
This fork is designed to work as part of a Ruby authorization mechanism to gitolite repositories (see the related
|
11
|
+
gollum-dtg project for an example of how we use it).
|
12
|
+
|
13
|
+
Please see the upstream project for a Ruby API aiming to provide all management functionality (read and write) that is
|
14
|
+
available via the gitolite-admin repository (like SSH keys, adding/removing repositories, etc).
|
15
|
+
|
16
|
+
|
17
|
+
## Requirements ##
|
18
|
+
* Ruby 1.8.x or 1.9.x
|
19
|
+
* a working [gitolite](https://github.com/sitaramc/gitolite) installation
|
20
|
+
* appropiate read permisions for the <tt>gitolite-admin</tt> bare repository
|
21
|
+
|
22
|
+
## Installation ##
|
23
|
+
|
24
|
+
gem install gitolite-dtg
|
25
|
+
|
26
|
+
## Usage ##
|
27
|
+
|
28
|
+
### Load a gitolite-admin repo ###
|
29
|
+
|
30
|
+
require 'gitolite-dtg'
|
31
|
+
ga_repo = Gitolite::Dtg::GitoliteAdmin.new("/path/to/gitolite/repos/gitolite-admin.git")
|
32
|
+
|
33
|
+
This method can only be called on an existing gitolite-admin repo.
|
34
|
+
|
35
|
+
## Caveats ##
|
36
|
+
### Windows compatibility ###
|
37
|
+
The grit gem (which is used for under-the-hood git operations) does not currently support Windows. Until it does, gitolite will be unable to support Windows.
|
38
|
+
|
39
|
+
### Group Ordering ###
|
40
|
+
When the gitolite backend parses the config file, it does so in one pass. Because of this, groups that are modified after being used do not see those changes reflected in previous uses.
|
41
|
+
|
42
|
+
For example:
|
43
|
+
|
44
|
+
@groupa = bob joe sue
|
45
|
+
@groupb = jim @groupa
|
46
|
+
@groupa = sam
|
47
|
+
|
48
|
+
Group b will contain the users <tt>jim, bob, joe, and sue</tt>
|
49
|
+
|
50
|
+
The gitolite gem, on the other hand, will <em>always</em> output groups so that all modifications are represented before it is ever used. For the above example, group b will be output with the following users: <tt>jim, bob, joe, sue, and sam</tt>. The groups in the config file will look like this:
|
51
|
+
|
52
|
+
@groupa = bob joe sue sam
|
53
|
+
@groupb = jim @groupa
|
54
|
+
|
55
|
+
|
56
|
+
### Contributors ###
|
57
|
+
* Stafford Brunk - [wingrunr21](https://github.com/wingrunr21) (original developer of the API)
|
58
|
+
* Alexander Simonov - [simonoff](https://github.com/simonoff)
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
# Rake tasks from https://github.com/mojombo/rakegem/blob/master/Rakefile
|
6
|
+
|
7
|
+
# Helper Functions
|
8
|
+
def name
|
9
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
10
|
+
end
|
11
|
+
|
12
|
+
def version
|
13
|
+
line = File.read("lib/#{name}/version.rb")[/^\s*VERSION\s*=\s*.*/]
|
14
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Standard tasks
|
18
|
+
require 'rcov'
|
19
|
+
RSpec::Core::RakeTask.new(:spec)
|
20
|
+
task :test => :spec
|
21
|
+
task :default => :spec
|
22
|
+
|
23
|
+
require 'rdoc/task'
|
24
|
+
Rake::RDocTask.new do |rdoc|
|
25
|
+
rdoc.rdoc_dir = 'rdoc'
|
26
|
+
rdoc.title = "#{name} #{version}"
|
27
|
+
rdoc.rdoc_files.include('README*')
|
28
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Open an irb session preloaded with this library"
|
32
|
+
task :console do
|
33
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "gitolite-dtg/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gitolite-dtg"
|
7
|
+
s.version = Gitolite::Dtg::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Stafford Brunk", "Lucian Carata"]
|
10
|
+
s.email = ["wingrunr21@gmail.com", "lc525@cam.ac.uk"]
|
11
|
+
s.homepage = "https://github.com/lc525/gitolite-dtg"
|
12
|
+
s.summary = %q{A Ruby gem based on wingrunr21/gitolite for querying the permisions of gitolite repositories.}
|
13
|
+
s.description = %q{This gem provides a Ruby read-only interface to the gitolite git backend system (by parsing the configuration file found in the bare gitolite-admin repository). It aims to enable permission queries based on data written in the gitolite-admin repository. This fork is designed to work as part of a Ruby authorization mechanism to gitolite repositories.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "gitolite-dtg"
|
16
|
+
|
17
|
+
s.add_development_dependency "rspec", "~> 2.9.0"
|
18
|
+
s.add_development_dependency "forgery", "~> 0.5.0"
|
19
|
+
s.add_development_dependency "rdoc", "~> 3.12"
|
20
|
+
s.add_development_dependency "simplecov", "~> 0.6.2"
|
21
|
+
s.add_dependency "grit", "~> 2.5.0"
|
22
|
+
s.add_dependency "hashery", "~> 1.5.0"
|
23
|
+
s.add_dependency "gratr19", "~> 0.4.4.1"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Gitolite
|
2
|
+
module Dtg
|
3
|
+
class Config
|
4
|
+
# Represents a group inside the gitolite configuration. The name and users
|
5
|
+
# options are all encapsulated in this class. All users are stored as
|
6
|
+
# Strings!
|
7
|
+
class Group
|
8
|
+
attr_accessor :name, :users
|
9
|
+
|
10
|
+
PREPEND_CHAR = '@'
|
11
|
+
|
12
|
+
def initialize(name)
|
13
|
+
# naively remove the prepend char
|
14
|
+
# I don't think you can have two of them in a group name
|
15
|
+
@name = name.gsub(PREPEND_CHAR, '')
|
16
|
+
@users = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty!
|
20
|
+
@users.clear
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_user(user)
|
24
|
+
return if has_user?(user)
|
25
|
+
@users.push(user.to_s).sort!
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_users(*users)
|
29
|
+
fixed_users = users.flatten.map{ |u| u.to_s }
|
30
|
+
@users.concat(fixed_users).sort!.uniq!
|
31
|
+
end
|
32
|
+
|
33
|
+
def rm_user(user)
|
34
|
+
@users.delete(user.to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_user?(user)
|
38
|
+
@users.include? user.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def size
|
42
|
+
@users.length
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
members = @users.join(' ')
|
47
|
+
name = "#{PREPEND_CHAR}#{@name}"
|
48
|
+
"#{name.ljust(20)}= #{members}\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Gitolite
|
2
|
+
module Dtg
|
3
|
+
class Config
|
4
|
+
#Represents a repo inside the gitolite configuration. The name, permissions, and git config
|
5
|
+
#options are all encapsulated in this class
|
6
|
+
class Repo
|
7
|
+
ALLOWED_PERMISSIONS = /-|C|R|RW\+?(?:C?D?|D?C?)M?/
|
8
|
+
|
9
|
+
attr_accessor :permissions, :name, :config, :owner, :description
|
10
|
+
|
11
|
+
def initialize(name)
|
12
|
+
#Store the perm hash in a lambda since we have to create a new one on every deny rule
|
13
|
+
#The perm hash is stored as a 2D hash, with individual permissions being the first
|
14
|
+
#degree and individual refexes being the second degree. Both Hashes must respect order
|
15
|
+
@perm_hash_lambda = lambda { OrderedHash.new {|k,v| k[v] = OrderedHash.new{|k2, v2| k2[v2] = [] }} }
|
16
|
+
@permissions = Array.new.push(@perm_hash_lambda.call)
|
17
|
+
|
18
|
+
@name = name
|
19
|
+
@config = {} #git config
|
20
|
+
end
|
21
|
+
|
22
|
+
def clean_permissions
|
23
|
+
@permissions = Array.new.push(@perm_hash_lambda.call)
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_permission(perm, refex = "", *users)
|
27
|
+
if perm =~ ALLOWED_PERMISSIONS
|
28
|
+
#Handle deny rules
|
29
|
+
if perm == '-'
|
30
|
+
@permissions.push(@perm_hash_lambda.call)
|
31
|
+
end
|
32
|
+
|
33
|
+
@permissions.last[perm][refex].concat users.flatten
|
34
|
+
@permissions.last[perm][refex].uniq!
|
35
|
+
else
|
36
|
+
raise InvalidPermissionError, "#{perm} is not in the allowed list of permissions!"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_git_config(key, value)
|
41
|
+
@config[key] = value
|
42
|
+
end
|
43
|
+
|
44
|
+
def unset_git_config(key)
|
45
|
+
@config.delete(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
repo = "repo #{@name}\n"
|
50
|
+
|
51
|
+
@permissions.each do |perm_hash|
|
52
|
+
perm_hash.each do |perm, list|
|
53
|
+
list.each do |refex, users|
|
54
|
+
repo += " " + perm.ljust(6) + refex.ljust(25) + "= " + users.join(' ') + "\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@config.each do |k, v|
|
60
|
+
repo += " config " + k + " = " + v + "\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
repo
|
64
|
+
end
|
65
|
+
|
66
|
+
def gitweb_description
|
67
|
+
if @description.nil?
|
68
|
+
nil
|
69
|
+
else
|
70
|
+
desc = "#{@name} "
|
71
|
+
desc += "\"#{@owner}\" " unless @owner.nil?
|
72
|
+
desc += "= \"#{@description}\""
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#Gets raised if a permission that isn't in the allowed
|
77
|
+
#list is passed in
|
78
|
+
class InvalidPermissionError < ArgumentError
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require File.join(File.dirname(__FILE__), 'config', 'repo')
|
3
|
+
require File.join(File.dirname(__FILE__), 'config', 'group')
|
4
|
+
|
5
|
+
module Gitolite
|
6
|
+
module Dtg
|
7
|
+
class Config
|
8
|
+
attr_accessor :repos, :groups, :config_blob, :flat_groups, :special_groups
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
@special_groups = ["all","raven"]
|
12
|
+
@repos = {}
|
13
|
+
@groups = {}
|
14
|
+
@flat_groups = {}
|
15
|
+
@config_blob = config
|
16
|
+
process_config(config)
|
17
|
+
flatten_groups
|
18
|
+
end
|
19
|
+
|
20
|
+
#TODO: merge repo unless overwrite = true
|
21
|
+
def add_repo(repo, overwrite = false)
|
22
|
+
raise ArgumentError, "Repo must be of type Gitolite::Config::Repo!" unless repo.instance_of? Gitolite::Config::Repo
|
23
|
+
@repos[repo.name] = repo
|
24
|
+
end
|
25
|
+
|
26
|
+
def rm_repo(repo)
|
27
|
+
name = normalize_repo_name(repo)
|
28
|
+
@repos.delete(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_repo?(repo)
|
32
|
+
name = normalize_repo_name(repo)
|
33
|
+
@repos.has_key?(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_repo(repo)
|
37
|
+
name = normalize_repo_name(repo)
|
38
|
+
@repos[name]
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_group(group, overwrite = false)
|
42
|
+
raise ArgumentError, "Group must be of type Gitolite::Config::Group!" unless group.instance_of? Gitolite::Config::Group
|
43
|
+
@groups[group.name] = group
|
44
|
+
end
|
45
|
+
|
46
|
+
def rm_group(group)
|
47
|
+
name = normalize_group_name(group)
|
48
|
+
@groups.delete(name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_group?(group)
|
52
|
+
name = normalize_group_name(group)
|
53
|
+
@groups.has_key?(name)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_group(group)
|
57
|
+
name = normalize_group_name(group)
|
58
|
+
@groups[name]
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_file(path=".", filename=@filename)
|
62
|
+
raise ArgumentError, "Path contains a filename or does not exist" unless File.directory?(path)
|
63
|
+
|
64
|
+
new_conf = File.join(path, filename)
|
65
|
+
File.open(new_conf, "w") do |f|
|
66
|
+
#Output groups
|
67
|
+
dep_order = build_groups_depgraph
|
68
|
+
dep_order.each {|group| f.write group.to_s }
|
69
|
+
|
70
|
+
gitweb_descs = []
|
71
|
+
@repos.sort.each do |k, v|
|
72
|
+
f.write "\n"
|
73
|
+
f.write v.to_s
|
74
|
+
|
75
|
+
gwd = v.gitweb_description
|
76
|
+
gitweb_descs.push(gwd) unless gwd.nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
f.write "\n"
|
80
|
+
f.write gitweb_descs.join("\n")
|
81
|
+
end
|
82
|
+
|
83
|
+
new_conf
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
#Based on
|
88
|
+
#https://github.com/sitaramc/gitolite/blob/pu/src/gl-compile-conf#cleanup_conf_line
|
89
|
+
def cleanup_config_line(line)
|
90
|
+
#remove comments, even those that happen inline
|
91
|
+
line.gsub!(/^((".*?"|[^#"])*)#.*/) {|m| m=$1}
|
92
|
+
|
93
|
+
#fix whitespace
|
94
|
+
line.gsub!('=', ' = ')
|
95
|
+
line.gsub!(/\s+/, ' ')
|
96
|
+
line.strip
|
97
|
+
end
|
98
|
+
|
99
|
+
def process_config(config)
|
100
|
+
|
101
|
+
|
102
|
+
context = [] #will store our context for permissions or config declarations
|
103
|
+
|
104
|
+
#Read each line of our config
|
105
|
+
config.data.lines.each do |l|
|
106
|
+
|
107
|
+
line = cleanup_config_line(l)
|
108
|
+
next if line.empty? #lines are empty if we killed a comment
|
109
|
+
|
110
|
+
case line
|
111
|
+
#found a repo definition
|
112
|
+
when /^repo (.*)/
|
113
|
+
#Empty our current context
|
114
|
+
context = []
|
115
|
+
|
116
|
+
repos = $1.split
|
117
|
+
repos.each do |r|
|
118
|
+
context << r
|
119
|
+
|
120
|
+
@repos[r] = Repo.new(r) unless has_repo?(r)
|
121
|
+
end
|
122
|
+
#repo permissions
|
123
|
+
when /^(-|C|R|RW\+?(?:C?D?|D?C?)M?) (.* )?= (.+)/
|
124
|
+
perm = $1
|
125
|
+
refex = $2 || ""
|
126
|
+
users = $3.split
|
127
|
+
|
128
|
+
context.each do |c|
|
129
|
+
@repos[c].add_permission(perm, refex, users)
|
130
|
+
end
|
131
|
+
#repo git config
|
132
|
+
when /^config (.+) = ?(.*)/
|
133
|
+
key = $1
|
134
|
+
value = $2
|
135
|
+
|
136
|
+
context.each do |c|
|
137
|
+
@repos[c].set_git_config(key, value)
|
138
|
+
end
|
139
|
+
#group definition
|
140
|
+
when /^#{Group::PREPEND_CHAR}(\S+) = ?(.*)/
|
141
|
+
group = $1
|
142
|
+
users = $2.split
|
143
|
+
|
144
|
+
@groups[group] = Group.new(group) unless has_group?(group)
|
145
|
+
@groups[group].add_users(users)
|
146
|
+
#gitweb definition
|
147
|
+
when /^(\S+)(?: "(.*?)")? = "(.*)"$/
|
148
|
+
repo = $1
|
149
|
+
owner = $2
|
150
|
+
description = $3
|
151
|
+
|
152
|
+
#Check for missing description
|
153
|
+
raise ParseError, "Missing Gitweb description for repo: #{repo}" if description.nil?
|
154
|
+
|
155
|
+
#Check for groups
|
156
|
+
raise ParseError, "Gitweb descriptions cannot be set for groups" if repo =~ /@.+/
|
157
|
+
|
158
|
+
if has_repo? repo
|
159
|
+
r = @repos[repo]
|
160
|
+
else
|
161
|
+
r = Repo.new(repo)
|
162
|
+
add_repo(r)
|
163
|
+
end
|
164
|
+
|
165
|
+
r.owner = owner
|
166
|
+
r.description = description
|
167
|
+
when /^include "(.+)"/
|
168
|
+
#TODO: implement includes
|
169
|
+
#ignore includes for now
|
170
|
+
when /^subconf (\S+)$/
|
171
|
+
#TODO: implement subconfs
|
172
|
+
#ignore subconfs for now
|
173
|
+
else
|
174
|
+
raise ParseError, "'#{line}' cannot be processed"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def flatten_groups
|
180
|
+
@groups.each_value do |group|
|
181
|
+
@flat_groups[group.name]=flatten_group(group)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def flatten_group(group)
|
186
|
+
users = []
|
187
|
+
group.users.each do |user|
|
188
|
+
if user[0,1]=='@'
|
189
|
+
gname = user.gsub('@', '')
|
190
|
+
if ((@special_groups.include? gname) == false)
|
191
|
+
grp = @groups[gname]
|
192
|
+
gusr = flatten_group(grp)
|
193
|
+
if gusr != nil
|
194
|
+
users.concat(gusr)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
else
|
198
|
+
users.push(user)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
return users
|
202
|
+
end
|
203
|
+
|
204
|
+
# Normalizes the various different input objects to Strings
|
205
|
+
def normalize_name(context, constant = nil)
|
206
|
+
case context
|
207
|
+
when constant
|
208
|
+
context.name
|
209
|
+
when Symbol
|
210
|
+
context.to_s
|
211
|
+
else
|
212
|
+
context
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def method_missing(meth, *args, &block)
|
217
|
+
if meth.to_s =~ /normalize_(\w+)_name/
|
218
|
+
#Could use Object.const_get to figure out the constant here
|
219
|
+
#but for only two cases, this is more readable
|
220
|
+
case $1
|
221
|
+
when "repo"
|
222
|
+
normalize_name(args[0], Gitolite::Dtg::Config::Repo)
|
223
|
+
when "group"
|
224
|
+
normalize_name(args[0], Gitolite::Dtg::Config::Group)
|
225
|
+
end
|
226
|
+
else
|
227
|
+
super
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Builds a dependency tree from the groups in order to ensure all groups
|
232
|
+
# are defined before they are used
|
233
|
+
def build_groups_depgraph
|
234
|
+
dp = ::GRATR::Digraph.new
|
235
|
+
|
236
|
+
# Add each group to the graph
|
237
|
+
@groups.each_value do |group|
|
238
|
+
dp.add_vertex! group
|
239
|
+
|
240
|
+
# Select group names from the users
|
241
|
+
subgroups = group.users.select {|u| u =~ /^#{Group::PREPEND_CHAR}.*$/}.map{|g| get_group g.gsub(Group::PREPEND_CHAR, '') }
|
242
|
+
|
243
|
+
subgroups.each do |subgroup|
|
244
|
+
dp.add_edge! subgroup, group
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Figure out if we have a good depedency graph
|
249
|
+
dep_order = dp.topsort
|
250
|
+
|
251
|
+
if dep_order.empty?
|
252
|
+
raise GroupDependencyError unless @groups.empty?
|
253
|
+
end
|
254
|
+
|
255
|
+
dep_order
|
256
|
+
end
|
257
|
+
|
258
|
+
#Raised when something in a config fails to parse properly
|
259
|
+
class ParseError < RuntimeError
|
260
|
+
end
|
261
|
+
|
262
|
+
# Raised when group dependencies cannot be suitably resolved for output
|
263
|
+
class GroupDependencyError < RuntimeError
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|