##// END OF EJS Templates
Handle spaces in development scm stderr log file path....
Etienne Massip -
r8009:f385a3e3da51
parent child
Show More
@@ -1,385 +1,385
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'cgi'
19 19
20 20 module Redmine
21 21 module Scm
22 22 module Adapters
23 23 class CommandFailed < StandardError #:nodoc:
24 24 end
25 25
26 26 class AbstractAdapter #:nodoc:
27 27
28 28 # raised if scm command exited with error, e.g. unknown revision.
29 29 class ScmCommandAborted < CommandFailed; end
30 30
31 31 class << self
32 32 def client_command
33 33 ""
34 34 end
35 35
36 36 def shell_quote_command
37 37 if Redmine::Platform.mswin? && RUBY_PLATFORM == 'java'
38 38 client_command
39 39 else
40 40 shell_quote(client_command)
41 41 end
42 42 end
43 43
44 44 # Returns the version of the scm client
45 45 # Eg: [1, 5, 0] or [] if unknown
46 46 def client_version
47 47 []
48 48 end
49 49
50 50 # Returns the version string of the scm client
51 51 # Eg: '1.5.0' or 'Unknown version' if unknown
52 52 def client_version_string
53 53 v = client_version || 'Unknown version'
54 54 v.is_a?(Array) ? v.join('.') : v.to_s
55 55 end
56 56
57 57 # Returns true if the current client version is above
58 58 # or equals the given one
59 59 # If option is :unknown is set to true, it will return
60 60 # true if the client version is unknown
61 61 def client_version_above?(v, options={})
62 62 ((client_version <=> v) >= 0) || (client_version.empty? && options[:unknown])
63 63 end
64 64
65 65 def client_available
66 66 true
67 67 end
68 68
69 69 def shell_quote(str)
70 70 if Redmine::Platform.mswin?
71 71 '"' + str.gsub(/"/, '\\"') + '"'
72 72 else
73 73 "'" + str.gsub(/'/, "'\"'\"'") + "'"
74 74 end
75 75 end
76 76 end
77 77
78 78 def initialize(url, root_url=nil, login=nil, password=nil,
79 79 path_encoding=nil)
80 80 @url = url
81 81 @login = login if login && !login.empty?
82 82 @password = (password || "") if @login
83 83 @root_url = root_url.blank? ? retrieve_root_url : root_url
84 84 end
85 85
86 86 def adapter_name
87 87 'Abstract'
88 88 end
89 89
90 90 def supports_cat?
91 91 true
92 92 end
93 93
94 94 def supports_annotate?
95 95 respond_to?('annotate')
96 96 end
97 97
98 98 def root_url
99 99 @root_url
100 100 end
101 101
102 102 def url
103 103 @url
104 104 end
105 105
106 106 def path_encoding
107 107 nil
108 108 end
109 109
110 110 # get info about the svn repository
111 111 def info
112 112 return nil
113 113 end
114 114
115 115 # Returns the entry identified by path and revision identifier
116 116 # or nil if entry doesn't exist in the repository
117 117 def entry(path=nil, identifier=nil)
118 118 parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?}
119 119 search_path = parts[0..-2].join('/')
120 120 search_name = parts[-1]
121 121 if search_path.blank? && search_name.blank?
122 122 # Root entry
123 123 Entry.new(:path => '', :kind => 'dir')
124 124 else
125 125 # Search for the entry in the parent directory
126 126 es = entries(search_path, identifier)
127 127 es ? es.detect {|e| e.name == search_name} : nil
128 128 end
129 129 end
130 130
131 131 # Returns an Entries collection
132 132 # or nil if the given path doesn't exist in the repository
133 133 def entries(path=nil, identifier=nil, options={})
134 134 return nil
135 135 end
136 136
137 137 def branches
138 138 return nil
139 139 end
140 140
141 141 def tags
142 142 return nil
143 143 end
144 144
145 145 def default_branch
146 146 return nil
147 147 end
148 148
149 149 def properties(path, identifier=nil)
150 150 return nil
151 151 end
152 152
153 153 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
154 154 return nil
155 155 end
156 156
157 157 def diff(path, identifier_from, identifier_to=nil)
158 158 return nil
159 159 end
160 160
161 161 def cat(path, identifier=nil)
162 162 return nil
163 163 end
164 164
165 165 def with_leading_slash(path)
166 166 path ||= ''
167 167 (path[0,1]!="/") ? "/#{path}" : path
168 168 end
169 169
170 170 def with_trailling_slash(path)
171 171 path ||= ''
172 172 (path[-1,1] == "/") ? path : "#{path}/"
173 173 end
174 174
175 175 def without_leading_slash(path)
176 176 path ||= ''
177 177 path.gsub(%r{^/+}, '')
178 178 end
179 179
180 180 def without_trailling_slash(path)
181 181 path ||= ''
182 182 (path[-1,1] == "/") ? path[0..-2] : path
183 183 end
184 184
185 185 def shell_quote(str)
186 186 self.class.shell_quote(str)
187 187 end
188 188
189 189 private
190 190 def retrieve_root_url
191 191 info = self.info
192 192 info ? info.root_url : nil
193 193 end
194 194
195 195 def target(path, sq=true)
196 196 path ||= ''
197 197 base = path.match(/^\//) ? root_url : url
198 198 str = "#{base}/#{path}".gsub(/[?<>\*]/, '')
199 199 if sq
200 200 str = shell_quote(str)
201 201 end
202 202 str
203 203 end
204 204
205 205 def logger
206 206 self.class.logger
207 207 end
208 208
209 209 def shellout(cmd, &block)
210 210 self.class.shellout(cmd, &block)
211 211 end
212 212
213 213 def self.logger
214 214 Rails.logger
215 215 end
216 216
217 217 def self.shellout(cmd, &block)
218 218 if logger && logger.debug?
219 219 logger.debug "Shelling out: #{strip_credential(cmd)}"
220 220 end
221 221 if Rails.env == 'development'
222 222 # Capture stderr when running in dev environment
223 cmd = "#{cmd} 2>>#{Rails.root}/log/scm.stderr.log"
223 cmd = "#{cmd} 2>>#{shell_quote(Rails.root.join('log/scm.stderr.log').to_s)}"
224 224 end
225 225 begin
226 226 if RUBY_VERSION < '1.9'
227 227 mode = "r+"
228 228 else
229 229 mode = "r+:ASCII-8BIT"
230 230 end
231 231 IO.popen(cmd, mode) do |io|
232 232 io.close_write
233 233 block.call(io) if block_given?
234 234 end
235 235 ## If scm command does not exist,
236 236 ## Linux JRuby 1.6.2 (ruby-1.8.7-p330) raises java.io.IOException
237 237 ## in production environment.
238 238 # rescue Errno::ENOENT => e
239 239 rescue Exception => e
240 240 msg = strip_credential(e.message)
241 241 # The command failed, log it and re-raise
242 242 logmsg = "SCM command failed, "
243 243 logmsg += "make sure that your SCM command (e.g. svn) is "
244 244 logmsg += "in PATH (#{ENV['PATH']})\n"
245 245 logmsg += "You can configure your scm commands in config/configuration.yml.\n"
246 246 logmsg += "#{strip_credential(cmd)}\n"
247 247 logmsg += "with: #{msg}"
248 248 logger.error(logmsg)
249 249 raise CommandFailed.new(msg)
250 250 end
251 251 end
252 252
253 253 # Hides username/password in a given command
254 254 def self.strip_credential(cmd)
255 255 q = (Redmine::Platform.mswin? ? '"' : "'")
256 256 cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
257 257 end
258 258
259 259 def strip_credential(cmd)
260 260 self.class.strip_credential(cmd)
261 261 end
262 262
263 263 def scm_iconv(to, from, str)
264 264 return nil if str.nil?
265 265 return str if to == from
266 266 begin
267 267 Iconv.conv(to, from, str)
268 268 rescue Iconv::Failure => err
269 269 logger.error("failed to convert from #{from} to #{to}. #{err}")
270 270 nil
271 271 end
272 272 end
273 273 end
274 274
275 275 class Entries < Array
276 276 def sort_by_name
277 277 sort {|x,y|
278 278 if x.kind == y.kind
279 279 x.name.to_s <=> y.name.to_s
280 280 else
281 281 x.kind <=> y.kind
282 282 end
283 283 }
284 284 end
285 285
286 286 def revisions
287 287 revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
288 288 end
289 289 end
290 290
291 291 class Info
292 292 attr_accessor :root_url, :lastrev
293 293 def initialize(attributes={})
294 294 self.root_url = attributes[:root_url] if attributes[:root_url]
295 295 self.lastrev = attributes[:lastrev]
296 296 end
297 297 end
298 298
299 299 class Entry
300 300 attr_accessor :name, :path, :kind, :size, :lastrev
301 301 def initialize(attributes={})
302 302 self.name = attributes[:name] if attributes[:name]
303 303 self.path = attributes[:path] if attributes[:path]
304 304 self.kind = attributes[:kind] if attributes[:kind]
305 305 self.size = attributes[:size].to_i if attributes[:size]
306 306 self.lastrev = attributes[:lastrev]
307 307 end
308 308
309 309 def is_file?
310 310 'file' == self.kind
311 311 end
312 312
313 313 def is_dir?
314 314 'dir' == self.kind
315 315 end
316 316
317 317 def is_text?
318 318 Redmine::MimeType.is_type?('text', name)
319 319 end
320 320 end
321 321
322 322 class Revisions < Array
323 323 def latest
324 324 sort {|x,y|
325 325 unless x.time.nil? or y.time.nil?
326 326 x.time <=> y.time
327 327 else
328 328 0
329 329 end
330 330 }.last
331 331 end
332 332 end
333 333
334 334 class Revision
335 335 attr_accessor :scmid, :name, :author, :time, :message,
336 336 :paths, :revision, :branch, :identifier,
337 337 :parents
338 338
339 339 def initialize(attributes={})
340 340 self.identifier = attributes[:identifier]
341 341 self.scmid = attributes[:scmid]
342 342 self.name = attributes[:name] || self.identifier
343 343 self.author = attributes[:author]
344 344 self.time = attributes[:time]
345 345 self.message = attributes[:message] || ""
346 346 self.paths = attributes[:paths]
347 347 self.revision = attributes[:revision]
348 348 self.branch = attributes[:branch]
349 349 self.parents = attributes[:parents]
350 350 end
351 351
352 352 # Returns the readable identifier.
353 353 def format_identifier
354 354 self.identifier.to_s
355 355 end
356 356 end
357 357
358 358 class Annotate
359 359 attr_reader :lines, :revisions
360 360
361 361 def initialize
362 362 @lines = []
363 363 @revisions = []
364 364 end
365 365
366 366 def add_line(line, revision)
367 367 @lines << line
368 368 @revisions << revision
369 369 end
370 370
371 371 def content
372 372 content = lines.join("\n")
373 373 end
374 374
375 375 def empty?
376 376 lines.empty?
377 377 end
378 378 end
379 379
380 380 class Branch < String
381 381 attr_accessor :revision, :scmid
382 382 end
383 383 end
384 384 end
385 385 end
General Comments 0
You need to be logged in to leave comments. Login now