Mercurial > hg > soundsoftware-site
comparison extra/svn/reposman.rb @ 0:513646585e45
* Import Redmine trunk SVN rev 3859
author | Chris Cannam |
---|---|
date | Fri, 23 Jul 2010 15:52:44 +0100 |
parents | |
children | 80433603a2cd 94944d00e43c |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:513646585e45 |
---|---|
1 #!/usr/bin/env ruby | |
2 | |
3 # == Synopsis | |
4 # | |
5 # reposman: manages your repositories with Redmine | |
6 # | |
7 # == Usage | |
8 # | |
9 # reposman [OPTIONS...] -s [DIR] -r [HOST] | |
10 # | |
11 # Examples: | |
12 # reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion | |
13 # reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git | |
14 # | |
15 # == Arguments (mandatory) | |
16 # | |
17 # -s, --svn-dir=DIR use DIR as base directory for svn repositories | |
18 # -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples: | |
19 # -r redmine.example.net | |
20 # -r http://redmine.example.net | |
21 # -r https://example.net/redmine | |
22 # -k, --key=KEY use KEY as the Redmine API key | |
23 # | |
24 # == Options | |
25 # | |
26 # -o, --owner=OWNER owner of the repository. using the rails login | |
27 # allow user to browse the repository within | |
28 # Redmine even for private project. If you want to | |
29 # share repositories through Redmine.pm, you need | |
30 # to use the apache owner. | |
31 # -g, --group=GROUP group of the repository. (default: root) | |
32 # --scm=SCM the kind of SCM repository you want to create (and | |
33 # register) in Redmine (default: Subversion). | |
34 # reposman is able to create Git and Subversion | |
35 # repositories. For all other kind, you must specify | |
36 # a --command option | |
37 # -u, --url=URL the base url Redmine will use to access your | |
38 # repositories. This option is used to automatically | |
39 # register the repositories in Redmine. The project | |
40 # identifier will be appended to this url. Examples: | |
41 # -u https://example.net/svn | |
42 # -u file:///var/svn/ | |
43 # if this option isn't set, reposman won't register | |
44 # the repositories in Redmine | |
45 # -c, --command=COMMAND use this command instead of "svnadmin create" to | |
46 # create a repository. This option can be used to | |
47 # create repositories other than subversion and git | |
48 # kind. | |
49 # This command override the default creation for git | |
50 # and subversion. | |
51 # -f, --force force repository creation even if the project | |
52 # repository is already declared in Redmine | |
53 # -t, --test only show what should be done | |
54 # -h, --help show help and exit | |
55 # -v, --verbose verbose | |
56 # -V, --version print version and exit | |
57 # -q, --quiet no log | |
58 # | |
59 # == References | |
60 # | |
61 # You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos | |
62 | |
63 | |
64 require 'getoptlong' | |
65 require 'rdoc/usage' | |
66 require 'find' | |
67 require 'etc' | |
68 | |
69 Version = "1.3" | |
70 SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem ) | |
71 | |
72 opts = GetoptLong.new( | |
73 ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT], | |
74 ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT], | |
75 ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT], | |
76 ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT], | |
77 ['--group', '-g', GetoptLong::REQUIRED_ARGUMENT], | |
78 ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT], | |
79 ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT], | |
80 ['--scm', GetoptLong::REQUIRED_ARGUMENT], | |
81 ['--test', '-t', GetoptLong::NO_ARGUMENT], | |
82 ['--force', '-f', GetoptLong::NO_ARGUMENT], | |
83 ['--verbose', '-v', GetoptLong::NO_ARGUMENT], | |
84 ['--version', '-V', GetoptLong::NO_ARGUMENT], | |
85 ['--help' , '-h', GetoptLong::NO_ARGUMENT], | |
86 ['--quiet' , '-q', GetoptLong::NO_ARGUMENT] | |
87 ) | |
88 | |
89 $verbose = 0 | |
90 $quiet = false | |
91 $redmine_host = '' | |
92 $repos_base = '' | |
93 $svn_owner = 'root' | |
94 $svn_group = 'root' | |
95 $use_groupid = true | |
96 $svn_url = false | |
97 $test = false | |
98 $force = false | |
99 $scm = 'Subversion' | |
100 | |
101 def log(text, options={}) | |
102 level = options[:level] || 0 | |
103 puts text unless $quiet or level > $verbose | |
104 exit 1 if options[:exit] | |
105 end | |
106 | |
107 def system_or_raise(command) | |
108 raise "\"#{command}\" failed" unless system command | |
109 end | |
110 | |
111 module SCM | |
112 | |
113 module Subversion | |
114 def self.create(path) | |
115 system_or_raise "svnadmin create #{path}" | |
116 end | |
117 end | |
118 | |
119 module Git | |
120 def self.create(path) | |
121 Dir.mkdir path | |
122 Dir.chdir(path) do | |
123 system_or_raise "git --bare init --shared" | |
124 system_or_raise "git update-server-info" | |
125 end | |
126 end | |
127 end | |
128 | |
129 end | |
130 | |
131 begin | |
132 opts.each do |opt, arg| | |
133 case opt | |
134 when '--svn-dir'; $repos_base = arg.dup | |
135 when '--redmine-host'; $redmine_host = arg.dup | |
136 when '--key'; $api_key = arg.dup | |
137 when '--owner'; $svn_owner = arg.dup; $use_groupid = false; | |
138 when '--group'; $svn_group = arg.dup; $use_groupid = false; | |
139 when '--url'; $svn_url = arg.dup | |
140 when '--scm'; $scm = arg.dup.capitalize; log("Invalid SCM: #{$scm}", :exit => true) unless SUPPORTED_SCM.include?($scm) | |
141 when '--command'; $command = arg.dup | |
142 when '--verbose'; $verbose += 1 | |
143 when '--test'; $test = true | |
144 when '--force'; $force = true | |
145 when '--version'; puts Version; exit | |
146 when '--help'; RDoc::usage | |
147 when '--quiet'; $quiet = true | |
148 end | |
149 end | |
150 rescue | |
151 exit 1 | |
152 end | |
153 | |
154 if $test | |
155 log("running in test mode") | |
156 end | |
157 | |
158 # Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git) | |
159 if $command.nil? | |
160 begin | |
161 scm_module = SCM.const_get($scm) | |
162 rescue | |
163 log("Please use --command option to specify how to create a #{$scm} repository.", :exit => true) | |
164 end | |
165 end | |
166 | |
167 $svn_url += "/" if $svn_url and not $svn_url.match(/\/$/) | |
168 | |
169 if ($redmine_host.empty? or $repos_base.empty?) | |
170 RDoc::usage | |
171 end | |
172 | |
173 unless File.directory?($repos_base) | |
174 log("directory '#{$repos_base}' doesn't exists", :exit => true) | |
175 end | |
176 | |
177 begin | |
178 require 'active_resource' | |
179 rescue LoadError | |
180 log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true) | |
181 end | |
182 | |
183 class Project < ActiveResource::Base; end | |
184 | |
185 log("querying Redmine for projects...", :level => 1); | |
186 | |
187 $redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://") | |
188 $redmine_host.gsub!(/\/$/, '') | |
189 | |
190 Project.site = "#{$redmine_host}/sys"; | |
191 | |
192 begin | |
193 # Get all active projects that have the Repository module enabled | |
194 projects = Project.find(:all, :params => {:key => $api_key}) | |
195 rescue => e | |
196 log("Unable to connect to #{Project.site}: #{e}", :exit => true) | |
197 end | |
198 | |
199 if projects.nil? | |
200 log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true) | |
201 end | |
202 | |
203 log("retrieved #{projects.size} projects", :level => 1) | |
204 | |
205 def set_owner_and_rights(project, repos_path, &block) | |
206 if RUBY_PLATFORM =~ /mswin/ | |
207 yield if block_given? | |
208 else | |
209 uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid) | |
210 right = project.is_public ? 0775 : 0770 | |
211 yield if block_given? | |
212 Find.find(repos_path) do |f| | |
213 File.chmod right, f | |
214 File.chown uid, gid, f | |
215 end | |
216 end | |
217 end | |
218 | |
219 def other_read_right?(file) | |
220 (File.stat(file).mode & 0007).zero? ? false : true | |
221 end | |
222 | |
223 def owner_name(file) | |
224 mswin? ? | |
225 $svn_owner : | |
226 Etc.getpwuid( File.stat(file).uid ).name | |
227 end | |
228 | |
229 def mswin? | |
230 (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i) | |
231 end | |
232 | |
233 projects.each do |project| | |
234 log("treating project #{project.name}", :level => 1) | |
235 | |
236 if project.identifier.empty? | |
237 log("\tno identifier for project #{project.name}") | |
238 next | |
239 elsif not project.identifier.match(/^[a-z0-9\-]+$/) | |
240 log("\tinvalid identifier for project #{project.name} : #{project.identifier}"); | |
241 next; | |
242 end | |
243 | |
244 repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR) | |
245 | |
246 if File.directory?(repos_path) | |
247 | |
248 # we must verify that repository has the good owner and the good | |
249 # rights before leaving | |
250 other_read = other_read_right?(repos_path) | |
251 owner = owner_name(repos_path) | |
252 next if project.is_public == other_read and owner == $svn_owner | |
253 | |
254 if $test | |
255 log("\tchange mode on #{repos_path}") | |
256 next | |
257 end | |
258 | |
259 begin | |
260 set_owner_and_rights(project, repos_path) | |
261 rescue Errno::EPERM => e | |
262 log("\tunable to change mode on #{repos_path} : #{e}\n") | |
263 next | |
264 end | |
265 | |
266 log("\tmode change on #{repos_path}"); | |
267 | |
268 else | |
269 # if repository is already declared in redmine, we don't create | |
270 # unless user use -f with reposman | |
271 if $force == false and project.respond_to?(:repository) | |
272 log("\trepository for project #{project.identifier} already exists in Redmine", :level => 1) | |
273 next | |
274 end | |
275 | |
276 project.is_public ? File.umask(0002) : File.umask(0007) | |
277 | |
278 if $test | |
279 log("\tcreate repository #{repos_path}") | |
280 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url; | |
281 next | |
282 end | |
283 | |
284 begin | |
285 set_owner_and_rights(project, repos_path) do | |
286 if scm_module.nil? | |
287 system_or_raise "#{$command} #{repos_path}" | |
288 else | |
289 scm_module.create(repos_path) | |
290 end | |
291 end | |
292 rescue => e | |
293 log("\tunable to create #{repos_path} : #{e}\n") | |
294 next | |
295 end | |
296 | |
297 if $svn_url | |
298 begin | |
299 project.post(:repository, :vendor => $scm, :repository => {:url => "#{$svn_url}#{project.identifier}"}, :key => $api_key) | |
300 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}"); | |
301 rescue => e | |
302 log("\trepository #{repos_path} not registered in Redmine: #{e.message}"); | |
303 end | |
304 end | |
305 | |
306 log("\trepository #{repos_path} created"); | |
307 end | |
308 | |
309 end | |
310 |