##// END OF EJS Templates
More detailed error message in log when scm command fails (#1682)....
Jean-Philippe Lang -
r1756:dbf4438ddac0
parent child
Show More
@@ -1,296 +1,296
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 begin
174 begin
175 IO.popen(cmd, "r+") do |io|
175 IO.popen(cmd, "r+") do |io|
176 io.close_write
176 io.close_write
177 block.call(io) if block_given?
177 block.call(io) if block_given?
178 end
178 end
179 rescue Errno::ENOENT => e
179 rescue Errno::ENOENT => e
180 msg = strip_credential(e.message)
180 msg = strip_credential(e.message)
181 # The command failed, log it and re-raise
181 # The command failed, log it and re-raise
182 logger.error("SCM command failed: #{strip_credential(cmd)}\n with: #{msg}")
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}")
183 raise CommandFailed.new(msg)
183 raise CommandFailed.new(msg)
184 end
184 end
185 end
185 end
186
186
187 # Hides username/password in a given command
187 # Hides username/password in a given command
188 def self.strip_credential(cmd)
188 def self.strip_credential(cmd)
189 q = (Redmine::Platform.mswin? ? '"' : "'")
189 q = (Redmine::Platform.mswin? ? '"' : "'")
190 cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
190 cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
191 end
191 end
192
192
193 def strip_credential(cmd)
193 def strip_credential(cmd)
194 self.class.strip_credential(cmd)
194 self.class.strip_credential(cmd)
195 end
195 end
196 end
196 end
197
197
198 class Entries < Array
198 class Entries < Array
199 def sort_by_name
199 def sort_by_name
200 sort {|x,y|
200 sort {|x,y|
201 if x.kind == y.kind
201 if x.kind == y.kind
202 x.name <=> y.name
202 x.name <=> y.name
203 else
203 else
204 x.kind <=> y.kind
204 x.kind <=> y.kind
205 end
205 end
206 }
206 }
207 end
207 end
208
208
209 def revisions
209 def revisions
210 revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
210 revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
211 end
211 end
212 end
212 end
213
213
214 class Info
214 class Info
215 attr_accessor :root_url, :lastrev
215 attr_accessor :root_url, :lastrev
216 def initialize(attributes={})
216 def initialize(attributes={})
217 self.root_url = attributes[:root_url] if attributes[:root_url]
217 self.root_url = attributes[:root_url] if attributes[:root_url]
218 self.lastrev = attributes[:lastrev]
218 self.lastrev = attributes[:lastrev]
219 end
219 end
220 end
220 end
221
221
222 class Entry
222 class Entry
223 attr_accessor :name, :path, :kind, :size, :lastrev
223 attr_accessor :name, :path, :kind, :size, :lastrev
224 def initialize(attributes={})
224 def initialize(attributes={})
225 self.name = attributes[:name] if attributes[:name]
225 self.name = attributes[:name] if attributes[:name]
226 self.path = attributes[:path] if attributes[:path]
226 self.path = attributes[:path] if attributes[:path]
227 self.kind = attributes[:kind] if attributes[:kind]
227 self.kind = attributes[:kind] if attributes[:kind]
228 self.size = attributes[:size].to_i if attributes[:size]
228 self.size = attributes[:size].to_i if attributes[:size]
229 self.lastrev = attributes[:lastrev]
229 self.lastrev = attributes[:lastrev]
230 end
230 end
231
231
232 def is_file?
232 def is_file?
233 'file' == self.kind
233 'file' == self.kind
234 end
234 end
235
235
236 def is_dir?
236 def is_dir?
237 'dir' == self.kind
237 'dir' == self.kind
238 end
238 end
239
239
240 def is_text?
240 def is_text?
241 Redmine::MimeType.is_type?('text', name)
241 Redmine::MimeType.is_type?('text', name)
242 end
242 end
243 end
243 end
244
244
245 class Revisions < Array
245 class Revisions < Array
246 def latest
246 def latest
247 sort {|x,y|
247 sort {|x,y|
248 unless x.time.nil? or y.time.nil?
248 unless x.time.nil? or y.time.nil?
249 x.time <=> y.time
249 x.time <=> y.time
250 else
250 else
251 0
251 0
252 end
252 end
253 }.last
253 }.last
254 end
254 end
255 end
255 end
256
256
257 class Revision
257 class Revision
258 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
258 attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
259 def initialize(attributes={})
259 def initialize(attributes={})
260 self.identifier = attributes[:identifier]
260 self.identifier = attributes[:identifier]
261 self.scmid = attributes[:scmid]
261 self.scmid = attributes[:scmid]
262 self.name = attributes[:name] || self.identifier
262 self.name = attributes[:name] || self.identifier
263 self.author = attributes[:author]
263 self.author = attributes[:author]
264 self.time = attributes[:time]
264 self.time = attributes[:time]
265 self.message = attributes[:message] || ""
265 self.message = attributes[:message] || ""
266 self.paths = attributes[:paths]
266 self.paths = attributes[:paths]
267 self.revision = attributes[:revision]
267 self.revision = attributes[:revision]
268 self.branch = attributes[:branch]
268 self.branch = attributes[:branch]
269 end
269 end
270
270
271 end
271 end
272
272
273 class Annotate
273 class Annotate
274 attr_reader :lines, :revisions
274 attr_reader :lines, :revisions
275
275
276 def initialize
276 def initialize
277 @lines = []
277 @lines = []
278 @revisions = []
278 @revisions = []
279 end
279 end
280
280
281 def add_line(line, revision)
281 def add_line(line, revision)
282 @lines << line
282 @lines << line
283 @revisions << revision
283 @revisions << revision
284 end
284 end
285
285
286 def content
286 def content
287 content = lines.join("\n")
287 content = lines.join("\n")
288 end
288 end
289
289
290 def empty?
290 def empty?
291 lines.empty?
291 lines.empty?
292 end
292 end
293 end
293 end
294 end
294 end
295 end
295 end
296 end
296 end
General Comments 0
You need to be logged in to leave comments. Login now