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