right_scraper 3.2.6 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/right_scraper.rb +16 -34
- data/lib/right_scraper/builders.rb +32 -0
- data/lib/right_scraper/builders/base.rb +19 -20
- data/lib/right_scraper/builders/filesystem.rb +8 -6
- data/lib/right_scraper/builders/union.rb +4 -1
- data/lib/right_scraper/loggers.rb +31 -0
- data/lib/right_scraper/loggers/base.rb +113 -0
- data/lib/right_scraper/loggers/default.rb +98 -0
- data/lib/right_scraper/{scraper.rb → main.rb} +53 -9
- data/lib/right_scraper/processes.rb +33 -0
- data/lib/right_scraper/processes/shell.rb +227 -0
- data/lib/right_scraper/processes/{ssh.rb → ssh_agent.rb} +4 -0
- data/lib/right_scraper/processes/svn_client.rb +117 -0
- data/lib/right_scraper/processes/warden.rb +358 -0
- data/lib/right_scraper/registered_base.rb +154 -0
- data/lib/right_scraper/repositories.rb +33 -0
- data/lib/right_scraper/repositories/base.rb +271 -232
- data/lib/right_scraper/repositories/download.rb +8 -6
- data/lib/right_scraper/repositories/git.rb +8 -9
- data/lib/right_scraper/repositories/svn.rb +8 -8
- data/lib/right_scraper/resources.rb +32 -0
- data/lib/right_scraper/resources/base.rb +5 -1
- data/lib/right_scraper/resources/cookbook.rb +34 -27
- data/lib/right_scraper/resources/workflow.rb +27 -28
- data/lib/right_scraper/retrievers.rb +34 -0
- data/lib/right_scraper/retrievers/base.rb +80 -84
- data/lib/right_scraper/retrievers/checkout_base.rb +178 -0
- data/lib/right_scraper/retrievers/download.rb +125 -117
- data/lib/right_scraper/retrievers/git.rb +377 -223
- data/lib/right_scraper/retrievers/svn.rb +102 -62
- data/lib/right_scraper/scanners.rb +37 -0
- data/lib/right_scraper/scanners/base.rb +77 -80
- data/lib/right_scraper/scanners/cookbook_manifest.rb +31 -30
- data/lib/right_scraper/scanners/cookbook_metadata.rb +380 -35
- data/lib/right_scraper/scanners/cookbook_s3_upload.rb +56 -53
- data/lib/right_scraper/scanners/union.rb +61 -58
- data/lib/right_scraper/scanners/workflow_manifest.rb +55 -54
- data/lib/right_scraper/scanners/workflow_metadata.rb +41 -39
- data/lib/right_scraper/scanners/workflow_s3_upload.rb +59 -55
- data/lib/right_scraper/scrapers.rb +32 -0
- data/lib/right_scraper/scrapers/base.rb +217 -205
- data/lib/right_scraper/scrapers/cookbook.rb +42 -40
- data/lib/right_scraper/scrapers/workflow.rb +57 -58
- data/lib/right_scraper/version.rb +3 -0
- data/right_scraper.gemspec +12 -16
- metadata +57 -163
- data/Gemfile +0 -15
- data/Rakefile +0 -89
- data/lib/right_scraper/logger.rb +0 -107
- data/lib/right_scraper/loggers/noisy.rb +0 -85
- data/lib/right_scraper/repositories/mock.rb +0 -70
- data/lib/right_scraper/retrievers/checkout.rb +0 -79
- data/lib/right_scraper/scraper_logger.rb +0 -66
- data/lib/right_scraper/svn_client.rb +0 -164
- data/right_scraper.rconf +0 -13
- data/spec/builder_spec.rb +0 -50
- data/spec/cookbook_helper.rb +0 -73
- data/spec/cookbook_manifest_spec.rb +0 -93
- data/spec/cookbook_s3_upload_spec.rb +0 -159
- data/spec/download/download_retriever_spec.rb +0 -118
- data/spec/download/download_retriever_spec_helper.rb +0 -72
- data/spec/download/download_spec.rb +0 -128
- data/spec/download/multi_dir_spec.rb +0 -106
- data/spec/download/multi_dir_spec_helper.rb +0 -40
- data/spec/git/cookbook_spec.rb +0 -165
- data/spec/git/demokey +0 -27
- data/spec/git/demokey.pub +0 -1
- data/spec/git/password_key +0 -30
- data/spec/git/password_key.pub +0 -1
- data/spec/git/repository_spec.rb +0 -110
- data/spec/git/retriever_spec.rb +0 -553
- data/spec/git/retriever_spec_helper.rb +0 -112
- data/spec/git/scraper_spec.rb +0 -151
- data/spec/git/ssh_spec.rb +0 -174
- data/spec/git/url_spec.rb +0 -103
- data/spec/logger_spec.rb +0 -185
- data/spec/repository_spec.rb +0 -111
- data/spec/retriever_spec_helper.rb +0 -146
- data/spec/scanner_spec.rb +0 -61
- data/spec/scraper_helper.rb +0 -88
- data/spec/scraper_spec.rb +0 -147
- data/spec/spec_helper.rb +0 -185
- data/spec/svn/cookbook_spec.rb +0 -96
- data/spec/svn/multi_svn_spec.rb +0 -64
- data/spec/svn/multi_svn_spec_helper.rb +0 -40
- data/spec/svn/repository_spec.rb +0 -72
- data/spec/svn/retriever_spec.rb +0 -266
- data/spec/svn/scraper_spec.rb +0 -90
- data/spec/svn/svn_retriever_spec_helper.rb +0 -90
- data/spec/svn/url_spec.rb +0 -47
- data/spec/url_spec.rb +0 -164
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright: Copyright (c) 2010-
|
2
|
+
# Copyright: Copyright (c) 2010-2013 RightScale, Inc.
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -20,18 +20,26 @@
|
|
20
20
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
21
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
|
-
|
23
|
+
|
24
|
+
# ancestor
|
25
|
+
require 'right_scraper'
|
26
|
+
|
27
|
+
require 'fileutils'
|
24
28
|
|
25
29
|
module RightScraper
|
26
30
|
|
27
31
|
# Library main entry point. Instantiate this class and call the scrape
|
28
32
|
# method to download or update a remote repository to the local disk and
|
29
33
|
# run a scraper on the resulting files.
|
30
|
-
|
34
|
+
#
|
35
|
+
# Note that this class was known as Scraper in v1-3 but the name was confusing
|
36
|
+
# due to the Scrapers module performing only a subset of the main Scraper
|
37
|
+
# class functionality.
|
38
|
+
class Main
|
31
39
|
|
32
|
-
# (Array):: Scraped resources
|
40
|
+
# (Array):: Scraped resources
|
33
41
|
attr_reader :resources
|
34
|
-
|
42
|
+
|
35
43
|
# Initialize scrape destination directory
|
36
44
|
#
|
37
45
|
# === Options
|
@@ -40,11 +48,27 @@ module RightScraper
|
|
40
48
|
# <tt>:max_bytes</tt>:: Maximum number of bytes to read from remote repo, unlimited if nil
|
41
49
|
# <tt>:max_seconds</tt>:: Maximum number of seconds to spend reading from remote repo, unlimited if nil
|
42
50
|
def initialize(options={})
|
51
|
+
options = {
|
52
|
+
:kind => nil,
|
53
|
+
:basedir => nil,
|
54
|
+
:max_bytes => nil,
|
55
|
+
:max_seconds => nil,
|
56
|
+
:callback => nil,
|
57
|
+
:logger => nil,
|
58
|
+
:s3_key => nil,
|
59
|
+
:s3_secret => nil,
|
60
|
+
:s3_bucket => nil,
|
61
|
+
:errors => nil,
|
62
|
+
:warnings => nil,
|
63
|
+
:scanners => nil,
|
64
|
+
:builders => nil,
|
65
|
+
}.merge(options)
|
43
66
|
@temporary = !options.has_key?(:basedir)
|
44
67
|
options[:basedir] ||= Dir.mktmpdir
|
45
|
-
|
46
|
-
@
|
68
|
+
options[:logger] ||= ::RightScraper::Loggers::Default.new
|
69
|
+
@logger = options[:logger]
|
47
70
|
@resources = []
|
71
|
+
@options = options
|
48
72
|
end
|
49
73
|
|
50
74
|
# Scrape given repository, depositing files into the scrape
|
@@ -80,11 +104,32 @@ module RightScraper
|
|
80
104
|
begin
|
81
105
|
# 1. Retrieve the files
|
82
106
|
retriever = nil
|
107
|
+
repo_dir_changed = false
|
83
108
|
@logger.operation(:retrieving, "from #{repo}") do
|
109
|
+
# note that the retriever type may be unavailable but allow the
|
110
|
+
# retrieve method to raise any such error.
|
84
111
|
retriever = repo.retriever(@options)
|
85
|
-
|
112
|
+
repo_dir_changed = retriever.retrieve
|
86
113
|
end
|
87
114
|
|
115
|
+
# TEAL FIX: Note that retrieve will now return true iff there has been
|
116
|
+
# a change to the last scraped repository directory for efficiency
|
117
|
+
# reasons and only for retreiver types that support this behavior.
|
118
|
+
#
|
119
|
+
# Even if the retrieval is skipped due to already having the data on
|
120
|
+
# disk we still need to scrape its resources only because of the case
|
121
|
+
# of the metadata scraper daemon, which updates multiple repositories
|
122
|
+
# of similar criteria.
|
123
|
+
#
|
124
|
+
# The issue is that a new repo can appear later with the same criteria
|
125
|
+
# as an already-scraped repo and will need it's own copy of the
|
126
|
+
# scraped resources. The easiest (but not most efficient) way to
|
127
|
+
# deliver these is to rescrape the already-seen resources. This
|
128
|
+
# becomes more expensive as we rely on generating "metadata.json" from
|
129
|
+
# "metadata.rb" for cookbooks but is likely not expensive enough to
|
130
|
+
# need to improve this logic.
|
131
|
+
|
132
|
+
|
88
133
|
# 2. Now scrape if there is a scraper in the options
|
89
134
|
@logger.operation(:scraping, retriever.repo_dir) do
|
90
135
|
if @options[:kind]
|
@@ -139,4 +184,3 @@ module RightScraper
|
|
139
184
|
|
140
185
|
end
|
141
186
|
end
|
142
|
-
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
# ancestor
|
24
|
+
require 'right_scraper'
|
25
|
+
|
26
|
+
module RightScraper
|
27
|
+
module Processes
|
28
|
+
autoload :Shell, 'right_scraper/processes/shell'
|
29
|
+
autoload :SSHAgent, 'right_scraper/processes/ssh_agent'
|
30
|
+
autoload :SvnClient, 'right_scraper/processes/svn_client'
|
31
|
+
autoload :Warden, 'right_scraper/processes/warden'
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright: Copyright (c) 2010-2013 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
# ancestor
|
25
|
+
require 'right_scraper/processes'
|
26
|
+
|
27
|
+
require 'right_git'
|
28
|
+
require 'right_popen'
|
29
|
+
require 'right_popen/safe_output_buffer'
|
30
|
+
|
31
|
+
module RightScraper
|
32
|
+
module Processes
|
33
|
+
|
34
|
+
# provides a shell with configurable properties that satisfies the interface
|
35
|
+
# for a shell for right_git but can be used for other scraper actions.
|
36
|
+
class Shell
|
37
|
+
include ::RightGit::Shell::Interface
|
38
|
+
|
39
|
+
# exceptions.
|
40
|
+
class LimitError < ::RightGit::Shell::ShellError; end
|
41
|
+
|
42
|
+
class SizeLimitError < LimitError; end
|
43
|
+
class TimeLimitError < LimitError; end
|
44
|
+
|
45
|
+
MAX_SAFE_BUFFER_LINE_COUNT = 10
|
46
|
+
MAX_SAFE_BUFFER_LINE_LENGTH = 128
|
47
|
+
|
48
|
+
attr_accessor :initial_directory, :max_seconds, :max_bytes
|
49
|
+
attr_accessor :stop_timestamp, :watch_directory
|
50
|
+
|
51
|
+
# @param [RightScraper::Repositories::Base] retriever associated with shell
|
52
|
+
# @param [Hash] options for shell
|
53
|
+
# @option options [Integer] :initial_directory for child process (Default = use current directory)
|
54
|
+
# @option options [Integer] :max_bytes for interruption (Default = no byte limit)
|
55
|
+
# @option options [Integer] :max_seconds for interruption (Default = no time limit)
|
56
|
+
# @option options [Integer] :watch_directory for interruption (Default = no byte limit)
|
57
|
+
def initialize(options = {})
|
58
|
+
options = {
|
59
|
+
:initial_directory => nil,
|
60
|
+
:max_bytes => nil,
|
61
|
+
:max_seconds => nil,
|
62
|
+
:watch_directory => nil,
|
63
|
+
}.merge(options)
|
64
|
+
|
65
|
+
@initial_directory = options[:initial_directory]
|
66
|
+
@max_bytes = options[:max_bytes]
|
67
|
+
@max_seconds = options[:max_seconds]
|
68
|
+
@watch_directory = options[:watch_directory]
|
69
|
+
|
70
|
+
# set stop time once for the lifetime of this shell object.
|
71
|
+
@stop_timestamp = (::Time.now + @max_seconds).to_i if @max_seconds
|
72
|
+
end
|
73
|
+
|
74
|
+
# Implements execute interface.
|
75
|
+
#
|
76
|
+
# @param [String] cmd the shell command to run
|
77
|
+
# @param [Hash] options for execution
|
78
|
+
#
|
79
|
+
# @return [Integer] exitstatus of the command
|
80
|
+
#
|
81
|
+
# @raise [ShellError] on failure only if :raise_on_failure is true
|
82
|
+
def execute(cmd, options = {})
|
83
|
+
inner_execute(cmd, :safe_output_handler, options)
|
84
|
+
ensure
|
85
|
+
@output = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Implements output_for interface.
|
89
|
+
#
|
90
|
+
# @param [String] cmd command to execute
|
91
|
+
# @param [Hash] options for execution
|
92
|
+
#
|
93
|
+
# @return [String] entire output (stdout) of the command
|
94
|
+
#
|
95
|
+
# @raise [ShellError] on failure only if :raise_on_failure is true
|
96
|
+
def output_for(cmd, options = {})
|
97
|
+
inner_execute(cmd, :unsafe_output_handler, options)
|
98
|
+
@output.display_text
|
99
|
+
ensure
|
100
|
+
@output = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# Buffers output safely.
|
104
|
+
#
|
105
|
+
# @param [String] data
|
106
|
+
#
|
107
|
+
# @return [TrueClass] always true
|
108
|
+
def safe_output_handler(data)
|
109
|
+
@output.safe_buffer_data(data)
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
# Buffers output unsafely but completely.
|
114
|
+
#
|
115
|
+
# @param [String] data
|
116
|
+
#
|
117
|
+
# @return [TrueClass] always true
|
118
|
+
def unsafe_output_handler(data)
|
119
|
+
@output.buffer << data.chomp
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
# Raises size limit error.
|
124
|
+
#
|
125
|
+
# @raise [SizeLimitError] always
|
126
|
+
def size_limit_handler
|
127
|
+
message =
|
128
|
+
"Exceeded size limit of #{@max_bytes / (1024 * 1024)} MB on " +
|
129
|
+
"repository directory. Hidden file and directory sizes are not " +
|
130
|
+
"included in the total."
|
131
|
+
raise SizeLimitError, message
|
132
|
+
end
|
133
|
+
|
134
|
+
# Raises timeout error.
|
135
|
+
#
|
136
|
+
# @raise [TimeLimitError] always
|
137
|
+
def timeout_handler
|
138
|
+
raise TimeLimitError, "Timed-out after #{@max_seconds} seconds"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Handles exit status.
|
142
|
+
#
|
143
|
+
# @param [Status] status after execution
|
144
|
+
#
|
145
|
+
# @return [TrueClass] always true
|
146
|
+
#
|
147
|
+
# @raise [ShellError] on execution failure
|
148
|
+
def exit_handler(status)
|
149
|
+
@exit_code = status.exitstatus
|
150
|
+
if @raise_on_failure && !status.success?
|
151
|
+
@output.buffer << "Exit code = #{@exit_code}"
|
152
|
+
raise ::RightGit::Shell::ShellError, "Execution failed: #{@output.display_text}"
|
153
|
+
end
|
154
|
+
true
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def inner_execute(cmd, output_handler, options)
|
160
|
+
options = {
|
161
|
+
:directory => nil,
|
162
|
+
:logger => nil,
|
163
|
+
:outstream => nil,
|
164
|
+
:raise_on_failure => true,
|
165
|
+
:set_env_vars => nil,
|
166
|
+
:clear_env_vars => nil,
|
167
|
+
}.merge(options)
|
168
|
+
if options[:outstream]
|
169
|
+
raise ::ArgumentError, ':outstream is not currently supported'
|
170
|
+
end
|
171
|
+
@raise_on_failure = options[:raise_on_failure]
|
172
|
+
|
173
|
+
# max seconds decreases over lifetime of shell until no more commands
|
174
|
+
# can be executed due to initial time constraint.
|
175
|
+
if @stop_timestamp
|
176
|
+
remaining_seconds = @stop_timestamp - ::Time.now.to_i
|
177
|
+
min_seconds = 5 # process start, a network gesture, etc.
|
178
|
+
timeout_handler if remaining_seconds < min_seconds
|
179
|
+
else
|
180
|
+
remaining_seconds = nil
|
181
|
+
end
|
182
|
+
|
183
|
+
# set/clear env vars, if requested.
|
184
|
+
environment = {}
|
185
|
+
if cev = options[:clear_env_vars]
|
186
|
+
cev.each { |k| environment[k] = nil }
|
187
|
+
end
|
188
|
+
if sev = options[:set_env_vars]
|
189
|
+
environment.merge!(sev)
|
190
|
+
end
|
191
|
+
environment = nil if environment.empty?
|
192
|
+
|
193
|
+
# use safe buffer (allows both safe and unsafe buffering) with limited
|
194
|
+
# buffering for output that is only seen on error.
|
195
|
+
@output = ::RightScale::RightPopen::SafeOutputBuffer.new(
|
196
|
+
buffer = [],
|
197
|
+
max_line_count = MAX_SAFE_BUFFER_LINE_COUNT,
|
198
|
+
max_line_length = MAX_SAFE_BUFFER_LINE_LENGTH)
|
199
|
+
|
200
|
+
# directory may be provided, else use initial directory.
|
201
|
+
working_directory = options[:directory] || @initial_directory
|
202
|
+
|
203
|
+
# synchronous popen with watchers, etc.
|
204
|
+
if logger = options[:logger]
|
205
|
+
logger.info("+ #{cmd}")
|
206
|
+
end
|
207
|
+
@exit_code = nil
|
208
|
+
::RightScale::RightPopen.popen3_sync(
|
209
|
+
cmd,
|
210
|
+
:target => self,
|
211
|
+
:directory => working_directory,
|
212
|
+
:environment => environment,
|
213
|
+
:timeout_handler => :timeout_handler,
|
214
|
+
:size_limit_handler => :size_limit_handler,
|
215
|
+
:exit_handler => :exit_handler,
|
216
|
+
:stderr_handler => output_handler,
|
217
|
+
:stdout_handler => output_handler,
|
218
|
+
:inherit_io => true, # avoid killing any rails connection
|
219
|
+
:watch_directory => @watch_directory,
|
220
|
+
:size_limit_bytes => @max_bytes,
|
221
|
+
:timeout_seconds => remaining_seconds)
|
222
|
+
@exit_code
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright: Copyright (c) 2010-2013 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require 'right_popen'
|
25
|
+
require 'right_popen/safe_output_buffer'
|
26
|
+
|
27
|
+
module RightScraper::Processes
|
28
|
+
|
29
|
+
# Simplified interface to the process of creating SVN client
|
30
|
+
# contexts.
|
31
|
+
#
|
32
|
+
# SVN client contexts are needed for almost every nontrivial SVN
|
33
|
+
# operation, and the authentication procedure is truly baroque.
|
34
|
+
# Thus, when you need a client context, do something like this:
|
35
|
+
# client = SvnClient.new(repository)
|
36
|
+
# client.with_context do |ctx|
|
37
|
+
# ...
|
38
|
+
# end
|
39
|
+
class SvnClient
|
40
|
+
|
41
|
+
class SvnClientError < StandardError; end
|
42
|
+
|
43
|
+
attr_reader :repository, :shell
|
44
|
+
|
45
|
+
# @param [RightScraper::Repositories::Base] repository to associate
|
46
|
+
# @param [Object] shell for execution
|
47
|
+
def initialize(repository, logger, shell)
|
48
|
+
unless @repository = repository
|
49
|
+
raise ::ArgumentError, 'repository is required'
|
50
|
+
end
|
51
|
+
unless @logger = logger
|
52
|
+
raise ::ArgumentError, 'logger is required'
|
53
|
+
end
|
54
|
+
unless @shell = shell
|
55
|
+
raise ::ArgumentError, 'shell is required'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.calculate_version
|
60
|
+
unless @svn_version
|
61
|
+
begin
|
62
|
+
cmd = 'svn --version --quiet'
|
63
|
+
out = `#{cmd}`
|
64
|
+
if $?.success?
|
65
|
+
@svn_version = out.chomp.split('.').map {|e| e.to_i}
|
66
|
+
else
|
67
|
+
raise SvnClientError, "Unable to determine svn version: #{cmd.inspect} exited with #{$?.exitstatus}"
|
68
|
+
end
|
69
|
+
rescue Errno::ENOENT => e
|
70
|
+
raise SvnClientError, "Unable to determine svn version: #{e.message}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@svn_version
|
74
|
+
end
|
75
|
+
|
76
|
+
# Executes svn using the given arguments.
|
77
|
+
#
|
78
|
+
# @param [Array] args for svn
|
79
|
+
#
|
80
|
+
# @return [TrueClass] always true
|
81
|
+
def execute(*args)
|
82
|
+
shell.execute(svn_command_for(args), :logger => @logger)
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
# Executes and returns output for svn using the given arguments.
|
87
|
+
#
|
88
|
+
# @param [Array] args for svn
|
89
|
+
#
|
90
|
+
# @return [String] output text
|
91
|
+
def output_for(*args)
|
92
|
+
shell.output_for(svn_command_for(args), :logger => @logger)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def svn_command_for(*args)
|
98
|
+
version = self.class.calculate_version
|
99
|
+
svn_args = ['svn', args, '--no-auth-cache', '--non-interactive']
|
100
|
+
case
|
101
|
+
when (version[0] != 1 || version[1] < 4)
|
102
|
+
raise SvnClientError, 'SVN client version is unsupported (~> 1.4)'
|
103
|
+
when version[1] < 6
|
104
|
+
# --trust-server-cert is a 1.6ism
|
105
|
+
else
|
106
|
+
svn_args << '--trust-server-cert'
|
107
|
+
end
|
108
|
+
if @repository.first_credential && @repository.second_credential
|
109
|
+
svn_args << "--username"
|
110
|
+
svn_args << @repository.first_credential
|
111
|
+
svn_args << "--password"
|
112
|
+
svn_args << @repository.second_credential
|
113
|
+
end
|
114
|
+
svn_args.flatten.join(' ')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|