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