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