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