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