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