##// END OF EJS Templates
Capture scm CLI stderr to log/scm.stderr.log when running in dev environment...
Jean-Philippe Lang -
r2126:a37f4b9cf699
parent child
Show More
@@ -1,296 +1,300
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 class << self
28 28 # Returns the version of the scm client
29 29 # Eg: [1, 5, 0] or [] if unknown
30 30 def client_version
31 31 []
32 32 end
33 33
34 34 # Returns the version string of the scm client
35 35 # Eg: '1.5.0' or 'Unknown version' if unknown
36 36 def client_version_string
37 37 v = client_version || 'Unknown version'
38 38 v.is_a?(Array) ? v.join('.') : v.to_s
39 39 end
40 40
41 41 # Returns true if the current client version is above
42 42 # or equals the given one
43 43 # If option is :unknown is set to true, it will return
44 44 # true if the client version is unknown
45 45 def client_version_above?(v, options={})
46 46 ((client_version <=> v) >= 0) || (client_version.empty? && options[:unknown])
47 47 end
48 48 end
49 49
50 50 def initialize(url, root_url=nil, login=nil, password=nil)
51 51 @url = url
52 52 @login = login if login && !login.empty?
53 53 @password = (password || "") if @login
54 54 @root_url = root_url.blank? ? retrieve_root_url : root_url
55 55 end
56 56
57 57 def adapter_name
58 58 'Abstract'
59 59 end
60 60
61 61 def supports_cat?
62 62 true
63 63 end
64 64
65 65 def supports_annotate?
66 66 respond_to?('annotate')
67 67 end
68 68
69 69 def root_url
70 70 @root_url
71 71 end
72 72
73 73 def url
74 74 @url
75 75 end
76 76
77 77 # get info about the svn repository
78 78 def info
79 79 return nil
80 80 end
81 81
82 82 # Returns the entry identified by path and revision identifier
83 83 # or nil if entry doesn't exist in the repository
84 84 def entry(path=nil, identifier=nil)
85 85 parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?}
86 86 search_path = parts[0..-2].join('/')
87 87 search_name = parts[-1]
88 88 if search_path.blank? && search_name.blank?
89 89 # Root entry
90 90 Entry.new(:path => '', :kind => 'dir')
91 91 else
92 92 # Search for the entry in the parent directory
93 93 es = entries(search_path, identifier)
94 94 es ? es.detect {|e| e.name == search_name} : nil
95 95 end
96 96 end
97 97
98 98 # Returns an Entries collection
99 99 # or nil if the given path doesn't exist in the repository
100 100 def entries(path=nil, identifier=nil)
101 101 return nil
102 102 end
103 103
104 104 def properties(path, identifier=nil)
105 105 return nil
106 106 end
107 107
108 108 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
109 109 return nil
110 110 end
111 111
112 112 def diff(path, identifier_from, identifier_to=nil)
113 113 return nil
114 114 end
115 115
116 116 def cat(path, identifier=nil)
117 117 return nil
118 118 end
119 119
120 120 def with_leading_slash(path)
121 121 path ||= ''
122 122 (path[0,1]!="/") ? "/#{path}" : path
123 123 end
124 124
125 125 def with_trailling_slash(path)
126 126 path ||= ''
127 127 (path[-1,1] == "/") ? path : "#{path}/"
128 128 end
129 129
130 130 def without_leading_slash(path)
131 131 path ||= ''
132 132 path.gsub(%r{^/+}, '')
133 133 end
134 134
135 135 def without_trailling_slash(path)
136 136 path ||= ''
137 137 (path[-1,1] == "/") ? path[0..-2] : path
138 138 end
139 139
140 140 def shell_quote(str)
141 141 if Redmine::Platform.mswin?
142 142 '"' + str.gsub(/"/, '\\"') + '"'
143 143 else
144 144 "'" + str.gsub(/'/, "'\"'\"'") + "'"
145 145 end
146 146 end
147 147
148 148 private
149 149 def retrieve_root_url
150 150 info = self.info
151 151 info ? info.root_url : nil
152 152 end
153 153
154 154 def target(path)
155 155 path ||= ''
156 156 base = path.match(/^\//) ? root_url : url
157 157 shell_quote("#{base}/#{path}".gsub(/[?<>\*]/, ''))
158 158 end
159 159
160 160 def logger
161 161 self.class.logger
162 162 end
163 163
164 164 def shellout(cmd, &block)
165 165 self.class.shellout(cmd, &block)
166 166 end
167 167
168 168 def self.logger
169 169 RAILS_DEFAULT_LOGGER
170 170 end
171 171
172 172 def self.shellout(cmd, &block)
173 173 logger.debug "Shelling out: #{cmd}" if logger && logger.debug?
174 if Rails.env == 'development'
175 # Capture stderr when running in dev environment
176 cmd = "#{cmd} 2>>#{RAILS_ROOT}/log/scm.stderr.log"
177 end
174 178 begin
175 179 IO.popen(cmd, "r+") do |io|
176 180 io.close_write
177 181 block.call(io) if block_given?
178 182 end
179 183 rescue Errno::ENOENT => e
180 184 msg = strip_credential(e.message)
181 185 # The command failed, log it and re-raise
182 186 logger.error("SCM command failed, make sure that your SCM binary (eg. svn) is in PATH (#{ENV['PATH']}): #{strip_credential(cmd)}\n with: #{msg}")
183 187 raise CommandFailed.new(msg)
184 188 end
185 189 end
186 190
187 191 # Hides username/password in a given command
188 192 def self.strip_credential(cmd)
189 193 q = (Redmine::Platform.mswin? ? '"' : "'")
190 194 cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
191 195 end
192 196
193 197 def strip_credential(cmd)
194 198 self.class.strip_credential(cmd)
195 199 end
196 200 end
197 201
198 202 class Entries < Array
199 203 def sort_by_name
200 204 sort {|x,y|
201 205 if x.kind == y.kind
202 206 x.name <=> y.name
203 207 else
204 208 x.kind <=> y.kind
205 209 end
206 210 }
207 211 end
208 212
209 213 def revisions
210 214 revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
211 215 end
212 216 end
213 217
214 218 class Info
215 219 attr_accessor :root_url, :lastrev
216 220 def initialize(attributes={})
217 221 self.root_url = attributes[:root_url] if attributes[:root_url]
218 222 self.lastrev = attributes[:lastrev]
219 223 end
220 224 end
221 225
222 226 class Entry
223 227 attr_accessor :name, :path, :kind, :size, :lastrev
224 228 def initialize(attributes={})
225 229 self.name = attributes[:name] if attributes[:name]
226 230 self.path = attributes[:path] if attributes[:path]
227 231 self.kind = attributes[:kind] if attributes[:kind]
228 232 self.size = attributes[:size].to_i if attributes[:size]
229 233 self.lastrev = attributes[:lastrev]
230 234 end
231 235
232 236 def is_file?
233 237 'file' == self.kind
234 238 end
235 239
236 240 def is_dir?
237 241 'dir' == self.kind
238 242 end
239 243
240 244 def is_text?
241 245 Redmine::MimeType.is_type?('text', name)
242 246 end
243 247 end
244 248
245 249 class Revisions < Array
246 250 def latest
247 251 sort {|x,y|
248 252 unless x.time.nil? or y.time.nil?
249 253 x.time <=> y.time
250 254 else
251 255 0
252 256 end
253 257 }.last
254 258 end
255 259 end
256 260
257 261 class Revision
258 262 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
259 263 def initialize(attributes={})
260 264 self.identifier = attributes[:identifier]
261 265 self.scmid = attributes[:scmid]
262 266 self.name = attributes[:name] || self.identifier
263 267 self.author = attributes[:author]
264 268 self.time = attributes[:time]
265 269 self.message = attributes[:message] || ""
266 270 self.paths = attributes[:paths]
267 271 self.revision = attributes[:revision]
268 272 self.branch = attributes[:branch]
269 273 end
270 274
271 275 end
272 276
273 277 class Annotate
274 278 attr_reader :lines, :revisions
275 279
276 280 def initialize
277 281 @lines = []
278 282 @revisions = []
279 283 end
280 284
281 285 def add_line(line, revision)
282 286 @lines << line
283 287 @revisions << revision
284 288 end
285 289
286 290 def content
287 291 content = lines.join("\n")
288 292 end
289 293
290 294 def empty?
291 295 lines.empty?
292 296 end
293 297 end
294 298 end
295 299 end
296 300 end
General Comments 0
You need to be logged in to leave comments. Login now