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