##// END OF EJS Templates
reposman: allow underscores in project identifiers (#1363)...
Toshi MARUYAMA -
r9143:5c9dd1629527
parent child
Show More
@@ -1,323 +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 210 rescue ActiveResource::ForbiddenAccess
211 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.")
212 212 rescue => e
213 213 log("Unable to connect to #{Project.site}: #{e}", :exit => true)
214 214 end
215 215
216 216 if projects.nil?
217 217 log('No project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
218 218 end
219 219
220 220 log("retrieved #{projects.size} projects", :level => 1)
221 221
222 222 def set_owner_and_rights(project, repos_path, &block)
223 223 if mswin?
224 224 yield if block_given?
225 225 else
226 226 uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid)
227 227 right = project.is_public ? 0775 : 0770
228 228 yield if block_given?
229 229 Find.find(repos_path) do |f|
230 230 File.chmod right, f
231 231 File.chown uid, gid, f
232 232 end
233 233 end
234 234 end
235 235
236 236 def other_read_right?(file)
237 237 (File.stat(file).mode & 0007).zero? ? false : true
238 238 end
239 239
240 240 def owner_name(file)
241 241 mswin? ?
242 242 $svn_owner :
243 243 Etc.getpwuid( File.stat(file).uid ).name
244 244 end
245 245
246 246 def mswin?
247 247 (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
248 248 end
249 249
250 250 projects.each do |project|
251 251 log("treating project #{project.name}", :level => 1)
252 252
253 253 if project.identifier.empty?
254 254 log("\tno identifier for project #{project.name}")
255 255 next
256 elsif not project.identifier.match(/^[a-z0-9\-]+$/)
256 elsif not project.identifier.match(/^[a-z0-9\-_]+$/)
257 257 log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
258 258 next;
259 259 end
260 260
261 261 repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
262 262
263 263 if File.directory?(repos_path)
264 264 # we must verify that repository has the good owner and the good
265 265 # rights before leaving
266 266 other_read = other_read_right?(repos_path)
267 267 owner = owner_name(repos_path)
268 268 next if project.is_public == other_read and owner == $svn_owner
269 269
270 270 if $test
271 271 log("\tchange mode on #{repos_path}")
272 272 next
273 273 end
274 274
275 275 begin
276 276 set_owner_and_rights(project, repos_path)
277 277 rescue Errno::EPERM => e
278 278 log("\tunable to change mode on #{repos_path} : #{e}\n")
279 279 next
280 280 end
281 281
282 282 log("\tmode change on #{repos_path}");
283 283
284 284 else
285 285 # if repository is already declared in redmine, we don't create
286 286 # unless user use -f with reposman
287 287 if $force == false and project.respond_to?(:repository)
288 288 log("\trepository for project #{project.identifier} already exists in Redmine", :level => 1)
289 289 next
290 290 end
291 291
292 292 project.is_public ? File.umask(0002) : File.umask(0007)
293 293
294 294 if $test
295 295 log("\tcreate repository #{repos_path}")
296 296 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url;
297 297 next
298 298 end
299 299
300 300 begin
301 301 set_owner_and_rights(project, repos_path) do
302 302 if scm_module.nil?
303 303 system_or_raise "#{$command} #{repos_path}"
304 304 else
305 305 scm_module.create(repos_path)
306 306 end
307 307 end
308 308 rescue => e
309 309 log("\tunable to create #{repos_path} : #{e}\n")
310 310 next
311 311 end
312 312
313 313 if $svn_url
314 314 begin
315 315 project.post(:repository, :vendor => $scm, :repository => {:url => "#{$svn_url}#{project.identifier}"}, :key => $api_key)
316 316 log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}");
317 317 rescue => e
318 318 log("\trepository #{repos_path} not registered in Redmine: #{e.message}");
319 319 end
320 320 end
321 321 log("\trepository #{repos_path} created");
322 322 end
323 323 end
General Comments 0
You need to be logged in to leave comments. Login now