jbox-gitolite 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +7 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.markdown +190 -0
- data/Rakefile +33 -0
- data/gitolite.gemspec +32 -0
- data/lib/gitolite.rb +8 -0
- data/lib/gitolite/config.rb +253 -0
- data/lib/gitolite/config/group.rb +51 -0
- data/lib/gitolite/config/repo.rb +94 -0
- data/lib/gitolite/dirty_proxy.rb +29 -0
- data/lib/gitolite/gitolite_admin.rb +206 -0
- data/lib/gitolite/ssh_key.rb +87 -0
- data/lib/gitolite/version.rb +3 -0
- data/spec/concurrency_spec.rb +2 -0
- data/spec/config_spec.rb +491 -0
- data/spec/dirty_proxy_spec.rb +63 -0
- data/spec/gitolite_admin_spec.rb +4 -0
- data/spec/group_spec.rb +126 -0
- data/spec/keys/bob-ins@zilla-site.com@desktop.pub +1 -0
- data/spec/keys/bob.joe@test.zilla.com@desktop.pub +1 -0
- data/spec/keys/bob.pub +1 -0
- data/spec/keys/bob@desktop.pub +1 -0
- data/spec/keys/bob@foo-bar.pub +1 -0
- data/spec/keys/bob@zilla.com.pub +1 -0
- data/spec/keys/bob@zilla.com@desktop.pub +1 -0
- data/spec/keys/jakub123.pub +1 -0
- data/spec/keys/jakub123@foo.net.pub +1 -0
- data/spec/keys/joe-bob@god-zilla.com@desktop.pub +1 -0
- data/spec/keys/joe@sch.ool.edu.pub +1 -0
- data/spec/keys/joe@sch.ool.edu@desktop.pub +1 -0
- data/spec/repo_spec.rb +204 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/ssh_key_spec.rb +335 -0
- metadata +198 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f37bb126327d01abd1e3fb270b1da512d823c68f
|
4
|
+
data.tar.gz: 0d8485bf1ac13abebfeba9329cb2a31829941d69
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6beb1f436fabad483f7e88c0a9633a7d24c1cf2393fa61046b32c2fd678da7c2f0abde8200624aa48f52aac9235e962df0dc037afde248df562493da437dba41
|
7
|
+
data.tar.gz: dee2cb5e1f9c97988cb9ccea6ad0b63e1eecb45a08a19c16365450228ce3f310016a277027dacc1edcfecddaddc71c79baa1226539f472390b7ab17fdc22174c
|
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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,190 @@
|
|
1
|
+
# gitolite [![Build Status](https://secure.travis-ci.org/wingrunr21/gitolite.png)](http://travis-ci.org/wingrunr21/gitolite)
|
2
|
+
|
3
|
+
This gem is designed to provide a Ruby interface to the [gitolite](https://github.com/sitaramc/gitolite) git backend system. I am aiming to provide all management functionality that is available via the gitolite-admin repository (like SSH keys, repository permissions, etc)
|
4
|
+
|
5
|
+
This gem can still have problems. Please file an issue if you encounter a bug. If you have a feature request, file one please.
|
6
|
+
|
7
|
+
## Features ##
|
8
|
+
* Allows for the creation and management of repos within gitolite
|
9
|
+
* Allows for the creation and deletion of SSH keys within gitolite
|
10
|
+
* Allows for the bootstrapping of a gitolite-admin repository
|
11
|
+
|
12
|
+
## Requirements ##
|
13
|
+
* Ruby 1.8.x or 1.9.x
|
14
|
+
* a working [gitolite](https://github.com/sitaramc/gitolite) installation
|
15
|
+
* the <tt>gitolite-admin</tt> repository checked out locally
|
16
|
+
|
17
|
+
## Installation ##
|
18
|
+
|
19
|
+
gem install gitolite
|
20
|
+
|
21
|
+
## Usage ##
|
22
|
+
|
23
|
+
### Load a gitolite-admin repo ###
|
24
|
+
|
25
|
+
require 'gitolite'
|
26
|
+
ga_repo = Gitolite::GitoliteAdmin.new("/path/to/gitolite/admin/repo")
|
27
|
+
|
28
|
+
This method can only be called on an existing gitolite-admin repo. If you need to create a new gitolite-admin repo, see "Bootstrapping".
|
29
|
+
|
30
|
+
### Configuration Files ###
|
31
|
+
|
32
|
+
conf = ga_repo.config
|
33
|
+
|
34
|
+
#Empty configs can also be initialized
|
35
|
+
conf2 = Config.init # => defaults to a filename of gitolite.conf
|
36
|
+
conf2 = Config.init("new_config.conf")
|
37
|
+
|
38
|
+
#Filename is set to whatever the filename was when the config was created
|
39
|
+
conf.filename # => "gitolite.conf"
|
40
|
+
conf2.filename # => "new_config.conf")
|
41
|
+
|
42
|
+
#filename can be changed via the setter
|
43
|
+
conf2.filename = "new_config.conf"
|
44
|
+
|
45
|
+
#to_file will write the config out to the file system
|
46
|
+
#using the value of the filename attribute. An alternative
|
47
|
+
#filename can also be specified
|
48
|
+
conf.to_file("/new/config/path") # => writes /new/config/path/gitolite.conf
|
49
|
+
conf.to_file("/new/config/path", "test.conf") # => writes /new/config/path/test.conf
|
50
|
+
|
51
|
+
### Repo management ###
|
52
|
+
|
53
|
+
repo = Gitolite::Config::Repo.new("AwesomeRepo")
|
54
|
+
|
55
|
+
#For a list of permissions, see http://sitaramc.github.com/gitolite/conf.html#gitolite
|
56
|
+
repo.add_permission("RW+", "", "bob", "joe", "susan")
|
57
|
+
|
58
|
+
#Set a git config option to the repo
|
59
|
+
repo.set_git_config("hooks.mailinglist", "gitolite-commits@example.tld") # => "gitolite-commits@example.tld"
|
60
|
+
|
61
|
+
#Unset a git config option from the repo
|
62
|
+
repo.unset_git_config("hooks.mailinglist") # => "gitolite-commits@example.tld"
|
63
|
+
|
64
|
+
#Set a gitolite option to the repo
|
65
|
+
repo.set_gitolite_option("mirroring.master", "kenobi") # => "kenobi"
|
66
|
+
|
67
|
+
#Remove a gitolite option from the repo
|
68
|
+
repo.unset_gitolite_option("mirroring.master") # => "kenobi"
|
69
|
+
|
70
|
+
#Add repo to config
|
71
|
+
conf.add_repo(repo)
|
72
|
+
|
73
|
+
#Delete repo by object
|
74
|
+
conf.rm_repo(repo)
|
75
|
+
|
76
|
+
#Delete a repo by name
|
77
|
+
conf.rm_repo("AwesomeRepo")
|
78
|
+
conf.rm_repo(:AwesomeRepo)
|
79
|
+
|
80
|
+
#Test if repo exists by name
|
81
|
+
conf.has_repo?('cool_repo') # => false
|
82
|
+
conf.has_repo?(:cool_repo) # => false
|
83
|
+
|
84
|
+
#Can also pass a Gitolite::Config::Repo object
|
85
|
+
repo = Gitolite::Config::Repo.new('cool_repo')
|
86
|
+
conf.has_repo?(repo) # => true
|
87
|
+
|
88
|
+
#Get a repo object from the config
|
89
|
+
repo = conf.get_repo('cool_repo')
|
90
|
+
repo = conf.get_repo(:cool_repo)
|
91
|
+
|
92
|
+
### SSH Key Management ###
|
93
|
+
|
94
|
+
#Three ways to create keys: manually, from an existing key, or from a string representing a key
|
95
|
+
key = Gitolite::SSHKey.new("ssh-rsa", "big-public-key-blob", "email")
|
96
|
+
key2 = Gitolite::SSHKey.from_file("/path/to/ssh/key.pub")
|
97
|
+
|
98
|
+
key_string = File.read("/path/to/ssh/key.pub")
|
99
|
+
key3 = Gitolite::SSHKey.from_string(key_string, "owner")
|
100
|
+
|
101
|
+
#Add the keys
|
102
|
+
ga_repo.add_key(key)
|
103
|
+
ga_repo.add_key(key2)
|
104
|
+
ga_repo.add_key(key3)
|
105
|
+
|
106
|
+
#Remove key2
|
107
|
+
ga_repo.rm_key(key2)
|
108
|
+
|
109
|
+
### Save changes ###
|
110
|
+
|
111
|
+
ga_repo.save
|
112
|
+
|
113
|
+
When this method is called, all changes get written to the file system and staged in git. For the time being, gitolite assumes full control of the gitolite-admin repository. This means that any keys in the keydir that are not being tracked will be removed and any human changes to gitolite.conf will be erased.
|
114
|
+
|
115
|
+
### Apply changes ###
|
116
|
+
ga_repo.apply
|
117
|
+
|
118
|
+
This method will commit all changes with a generic message (will be improved upon later) and push to <tt>origin master</tt>.
|
119
|
+
|
120
|
+
### Save and apply ###
|
121
|
+
ga_repo.save_and_apply
|
122
|
+
|
123
|
+
### Updating remote changes ###
|
124
|
+
#In order to avoid conflicts, this will perform a reset! by default
|
125
|
+
#pass :reset => false to disable the reset (Git conflicts will have to be manually fixed)
|
126
|
+
ga_repo.update
|
127
|
+
ga_repo.update(:reset => false)
|
128
|
+
|
129
|
+
#Update while performing a rebase
|
130
|
+
ga_repo.update(:rebase => true)
|
131
|
+
|
132
|
+
### Reloading from the file system ###
|
133
|
+
ga_repo.reload!
|
134
|
+
|
135
|
+
### Resetting to HEAD, destroying all local changes (including untracked files) ###
|
136
|
+
#This will also perform a reload!
|
137
|
+
ga_repo.reset!
|
138
|
+
|
139
|
+
### Bootstrapping ###
|
140
|
+
ga_repo = GitoliteAdmin.bootstrap("/path/to/new/gitolite/repo")
|
141
|
+
|
142
|
+
This will create the folders <tt>conf</tt> and <tt>keydir</tt> in the supplied path. A config file will also be created in the conf directory. The default configuration supplies RW+ permissions to a user named git for a repo named <tt>gitolite-admin</tt>. You can specify an options hash to change some values:
|
143
|
+
|
144
|
+
ga_repo = GitoliteAdmin.bootstrap("/path/to/new/gitolite/repo", {:user => "admin", :perm => "RW"})
|
145
|
+
|
146
|
+
You can also pass a message to be used for the initial bootstrap commit:
|
147
|
+
|
148
|
+
ga_repo = GitoliteAdmin.bootstrap("/path/to/new/gitolite/repo", {:message => "Bootstrapped new repo"})
|
149
|
+
|
150
|
+
Please note that while bootstrapping is supported, I highly recommend that the initial gitolite-admin repo be created by gitolite itself.
|
151
|
+
|
152
|
+
## Caveats ##
|
153
|
+
### Windows compatibility ###
|
154
|
+
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.
|
155
|
+
|
156
|
+
### Group Ordering ###
|
157
|
+
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.
|
158
|
+
|
159
|
+
For example:
|
160
|
+
|
161
|
+
@groupa = bob joe sue
|
162
|
+
@groupb = jim @groupa
|
163
|
+
@groupa = sam
|
164
|
+
|
165
|
+
Group b will contain the users <tt>jim, bob, joe, and sue</tt>
|
166
|
+
|
167
|
+
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:
|
168
|
+
|
169
|
+
@groupa = bob joe sue sam
|
170
|
+
@groupb = jim @groupa
|
171
|
+
|
172
|
+
## Issues ##
|
173
|
+
* Gem is not thread safe. For now, the gem will change directories in order to perform git operations. It will, however, return to the old working directory once it is finished. I am looking into making the gem thread safe. Note that this is only an issue on Rubies that do not have a GIL (ex jRuby or Rubinius)
|
174
|
+
|
175
|
+
# Contributing #
|
176
|
+
* Tests! If you ask me to pull changes that are not adequately tested, I'm not going to do it.
|
177
|
+
* If you introduce new features/public methods on objects, you must update the README.
|
178
|
+
|
179
|
+
### Contributors ###
|
180
|
+
* Alexander Simonov - [simonoff](https://github.com/simonoff)
|
181
|
+
* Pierre Guinoiseau - [peikk0](https://github.com/peikk0)
|
182
|
+
|
183
|
+
## Documentation ##
|
184
|
+
* Rdoc is coming eventually
|
185
|
+
|
186
|
+
## Future ##
|
187
|
+
* support folders in the keydir
|
188
|
+
* support include tags
|
189
|
+
* cleanup methods to make adding and removing easier (like add_key should accept an array of keys)
|
190
|
+
* Make the gem thread safe
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
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
|
+
RSpec::Core::RakeTask.new(:spec)
|
19
|
+
task :test => :spec
|
20
|
+
task :default => :spec
|
21
|
+
|
22
|
+
require 'rdoc/task'
|
23
|
+
Rake::RDocTask.new do |rdoc|
|
24
|
+
rdoc.rdoc_dir = 'rdoc'
|
25
|
+
rdoc.title = "#{name} #{version}"
|
26
|
+
rdoc.rdoc_files.include('README*')
|
27
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Open an irb session preloaded with this library"
|
31
|
+
task :console do
|
32
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
33
|
+
end
|
data/gitolite.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "gitolite/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "jbox-gitolite"
|
7
|
+
s.version = Gitolite::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Nicolas Rodriguez"]
|
10
|
+
s.email = ["nrodriguez@jbox-web.com"]
|
11
|
+
s.homepage = "https://github.com/n-rodriguez/gitolite"
|
12
|
+
s.summary = %q{A Ruby gem for manipulating the gitolite git backend via the gitolite-admin repository.}
|
13
|
+
s.description = %q{This gem is designed to provide a Ruby interface to the gitolite git backend system. This gem aims to provide all management functionality that is available via the gitolite-admin repository (like SSH keys, repository permissions, etc)}
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.rubyforge_project = "jbox-gitolite"
|
17
|
+
|
18
|
+
s.add_development_dependency "rspec", "~> 2.9.0"
|
19
|
+
s.add_development_dependency "forgery", "~> 0.5.0"
|
20
|
+
s.add_development_dependency "rdoc", "~> 3.12"
|
21
|
+
s.add_development_dependency "simplecov", "~> 0.6.2"
|
22
|
+
s.add_development_dependency "rake", "~> 10.0.2"
|
23
|
+
|
24
|
+
s.add_dependency "gitlab-grit", "~> 2.6.0"
|
25
|
+
s.add_dependency "hashery", "~> 1.5.0"
|
26
|
+
s.add_dependency "gratr19", "~> 0.4.4.1"
|
27
|
+
|
28
|
+
s.files = `git ls-files`.split("\n")
|
29
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
30
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
31
|
+
s.require_paths = ["lib"]
|
32
|
+
end
|
data/lib/gitolite.rb
ADDED
@@ -0,0 +1,253 @@
|
|
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
|
+
class Config
|
7
|
+
attr_accessor :repos, :groups, :filename
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@repos = {}
|
11
|
+
@groups = {}
|
12
|
+
@filename = File.basename(config)
|
13
|
+
process_config(config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.init(filename = "gitolite.conf")
|
17
|
+
file = Tempfile.new(filename)
|
18
|
+
conf = self.new(file.path)
|
19
|
+
conf.filename = filename #kill suffix added by Tempfile
|
20
|
+
file.close(unlink_now = true)
|
21
|
+
conf
|
22
|
+
end
|
23
|
+
|
24
|
+
#TODO: merge repo unless overwrite = true
|
25
|
+
def add_repo(repo, overwrite = false)
|
26
|
+
raise ArgumentError, "Repo must be of type Gitolite::Config::Repo!" unless repo.instance_of? Gitolite::Config::Repo
|
27
|
+
@repos[repo.name] = repo
|
28
|
+
end
|
29
|
+
|
30
|
+
def rm_repo(repo)
|
31
|
+
name = normalize_repo_name(repo)
|
32
|
+
@repos.delete(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_repo?(repo)
|
36
|
+
name = normalize_repo_name(repo)
|
37
|
+
@repos.has_key?(name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_repo(repo)
|
41
|
+
name = normalize_repo_name(repo)
|
42
|
+
@repos[name]
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_group(group, overwrite = false)
|
46
|
+
raise ArgumentError, "Group must be of type Gitolite::Config::Group!" unless group.instance_of? Gitolite::Config::Group
|
47
|
+
@groups[group.name] = group
|
48
|
+
end
|
49
|
+
|
50
|
+
def rm_group(group)
|
51
|
+
name = normalize_group_name(group)
|
52
|
+
@groups.delete(name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_group?(group)
|
56
|
+
name = normalize_group_name(group)
|
57
|
+
@groups.has_key?(name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_group(group)
|
61
|
+
name = normalize_group_name(group)
|
62
|
+
@groups[name]
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_file(path=".", filename=@filename)
|
66
|
+
raise ArgumentError, "Path contains a filename or does not exist" unless File.directory?(path)
|
67
|
+
|
68
|
+
new_conf = File.join(path, filename)
|
69
|
+
File.open(new_conf, "w") do |f|
|
70
|
+
#Output groups
|
71
|
+
dep_order = build_groups_depgraph
|
72
|
+
dep_order.each {|group| f.write group.to_s }
|
73
|
+
|
74
|
+
gitweb_descs = []
|
75
|
+
@repos.sort.each do |k, v|
|
76
|
+
f.write "\n"
|
77
|
+
f.write v.to_s
|
78
|
+
|
79
|
+
gwd = v.gitweb_description
|
80
|
+
gitweb_descs.push(gwd) unless gwd.nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
f.write "\n"
|
84
|
+
f.write gitweb_descs.join("\n")
|
85
|
+
end
|
86
|
+
|
87
|
+
new_conf
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
#Based on
|
92
|
+
#https://github.com/sitaramc/gitolite/blob/pu/src/gl-compile-conf#cleanup_conf_line
|
93
|
+
def cleanup_config_line(line)
|
94
|
+
#remove comments, even those that happen inline
|
95
|
+
line.gsub!(/^((".*?"|[^#"])*)#.*/) {|m| m=$1}
|
96
|
+
|
97
|
+
#fix whitespace
|
98
|
+
line.gsub!('=', ' = ')
|
99
|
+
line.gsub!(/\s+/, ' ')
|
100
|
+
line.strip
|
101
|
+
end
|
102
|
+
|
103
|
+
def process_config(config)
|
104
|
+
context = [] #will store our context for permissions or config declarations
|
105
|
+
|
106
|
+
#Read each line of our config
|
107
|
+
File.open(config, 'r').each do |l|
|
108
|
+
|
109
|
+
line = cleanup_config_line(l)
|
110
|
+
next if line.empty? #lines are empty if we killed a comment
|
111
|
+
|
112
|
+
case line
|
113
|
+
#found a repo definition
|
114
|
+
when /^repo (.*)/
|
115
|
+
#Empty our current context
|
116
|
+
context = []
|
117
|
+
|
118
|
+
repos = $1.split
|
119
|
+
repos.each do |r|
|
120
|
+
context << r
|
121
|
+
|
122
|
+
@repos[r] = Repo.new(r) unless has_repo?(r)
|
123
|
+
end
|
124
|
+
#repo permissions
|
125
|
+
when /^(-|C|R|RW\+?(?:C?D?|D?C?)M?) (.* )?= (.+)/
|
126
|
+
perm = $1
|
127
|
+
refex = $2 || ""
|
128
|
+
users = $3.split
|
129
|
+
|
130
|
+
context.each do |c|
|
131
|
+
@repos[c].add_permission(perm, refex, users)
|
132
|
+
end
|
133
|
+
#repo git config
|
134
|
+
when /^config (.+) = ?(.*)/
|
135
|
+
key = $1
|
136
|
+
value = $2
|
137
|
+
|
138
|
+
context.each do |c|
|
139
|
+
@repos[c].set_git_config(key, value)
|
140
|
+
end
|
141
|
+
#repo gitolite option
|
142
|
+
when /^option (.+) = (.*)/
|
143
|
+
key = $1
|
144
|
+
value = $2
|
145
|
+
|
146
|
+
raise ParseError, "Missing gitolite option value for repo: #{repo} and key: #{key}" if value.nil?
|
147
|
+
|
148
|
+
context.each do |c|
|
149
|
+
@repos[c].set_gitolite_option(key, value)
|
150
|
+
end
|
151
|
+
#group definition
|
152
|
+
when /^#{Group::PREPEND_CHAR}(\S+) = ?(.*)/
|
153
|
+
group = $1
|
154
|
+
users = $2.split
|
155
|
+
|
156
|
+
@groups[group] = Group.new(group) unless has_group?(group)
|
157
|
+
@groups[group].add_users(users)
|
158
|
+
#gitweb definition
|
159
|
+
when /^(\S+)(?: "(.*?)")? = "(.*)"$/
|
160
|
+
repo = $1
|
161
|
+
owner = $2
|
162
|
+
description = $3
|
163
|
+
|
164
|
+
#Check for missing description
|
165
|
+
raise ParseError, "Missing Gitweb description for repo: #{repo}" if description.nil?
|
166
|
+
|
167
|
+
#Check for groups
|
168
|
+
raise ParseError, "Gitweb descriptions cannot be set for groups" if repo =~ /@.+/
|
169
|
+
|
170
|
+
if has_repo? repo
|
171
|
+
r = @repos[repo]
|
172
|
+
else
|
173
|
+
r = Repo.new(repo)
|
174
|
+
add_repo(r)
|
175
|
+
end
|
176
|
+
|
177
|
+
r.owner = owner
|
178
|
+
r.description = description
|
179
|
+
when /^include "(.+)"/
|
180
|
+
#TODO: implement includes
|
181
|
+
#ignore includes for now
|
182
|
+
when /^subconf (\S+)$/
|
183
|
+
#TODO: implement subconfs
|
184
|
+
#ignore subconfs for now
|
185
|
+
else
|
186
|
+
raise ParseError, "'#{line}' cannot be processed"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Normalizes the various different input objects to Strings
|
192
|
+
def normalize_name(context, constant = nil)
|
193
|
+
case context
|
194
|
+
when constant
|
195
|
+
context.name
|
196
|
+
when Symbol
|
197
|
+
context.to_s
|
198
|
+
else
|
199
|
+
context
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def method_missing(meth, *args, &block)
|
204
|
+
if meth.to_s =~ /normalize_(\w+)_name/
|
205
|
+
#Could use Object.const_get to figure out the constant here
|
206
|
+
#but for only two cases, this is more readable
|
207
|
+
case $1
|
208
|
+
when "repo"
|
209
|
+
normalize_name(args[0], Gitolite::Config::Repo)
|
210
|
+
when "group"
|
211
|
+
normalize_name(args[0], Gitolite::Config::Group)
|
212
|
+
end
|
213
|
+
else
|
214
|
+
super
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Builds a dependency tree from the groups in order to ensure all groups
|
219
|
+
# are defined before they are used
|
220
|
+
def build_groups_depgraph
|
221
|
+
dp = ::GRATR::Digraph.new
|
222
|
+
|
223
|
+
# Add each group to the graph
|
224
|
+
@groups.each_value do |group|
|
225
|
+
dp.add_vertex! group
|
226
|
+
|
227
|
+
# Select group names from the users
|
228
|
+
subgroups = group.users.select {|u| u =~ /^#{Group::PREPEND_CHAR}.*$/}.map{|g| get_group g.gsub(Group::PREPEND_CHAR, '') }
|
229
|
+
|
230
|
+
subgroups.each do |subgroup|
|
231
|
+
dp.add_edge! subgroup, group
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Figure out if we have a good depedency graph
|
236
|
+
dep_order = dp.topsort
|
237
|
+
|
238
|
+
if dep_order.empty?
|
239
|
+
raise GroupDependencyError unless @groups.empty?
|
240
|
+
end
|
241
|
+
|
242
|
+
dep_order
|
243
|
+
end
|
244
|
+
|
245
|
+
#Raised when something in a config fails to parse properly
|
246
|
+
class ParseError < RuntimeError
|
247
|
+
end
|
248
|
+
|
249
|
+
# Raised when group dependencies cannot be suitably resolved for output
|
250
|
+
class GroupDependencyError < RuntimeError
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|