drupid 1.0.0
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.
- data/bin/drupid +270 -0
- data/lib/drupid/component.rb +236 -0
- data/lib/drupid/download_strategy.rb +585 -0
- data/lib/drupid/drush.rb +185 -0
- data/lib/drupid/extend/pathname.rb +114 -0
- data/lib/drupid/library.rb +52 -0
- data/lib/drupid/makefile.rb +423 -0
- data/lib/drupid/patch.rb +92 -0
- data/lib/drupid/platform.rb +234 -0
- data/lib/drupid/platform_project.rb +91 -0
- data/lib/drupid/project.rb +563 -0
- data/lib/drupid/updater.rb +683 -0
- data/lib/drupid/utils.rb +301 -0
- data/lib/drupid/version.rb +230 -0
- data/lib/drupid.rb +56 -0
- metadata +76 -0
data/bin/drupid
ADDED
@@ -0,0 +1,270 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Copyright (c) 2012 Lifepillar
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
14
|
+
# copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
|
24
|
+
require 'drupid'
|
25
|
+
|
26
|
+
module Drupid
|
27
|
+
|
28
|
+
class Fools
|
29
|
+
include Drupid::Utils
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@options = { # Defaults
|
33
|
+
:command => :sync,
|
34
|
+
:directory => nil,
|
35
|
+
:dry => false,
|
36
|
+
:edit => nil,
|
37
|
+
:force => false,
|
38
|
+
:nocore => false,
|
39
|
+
:nodeps => false,
|
40
|
+
:nolibs => false,
|
41
|
+
:out => nil,
|
42
|
+
:site => nil
|
43
|
+
}
|
44
|
+
@updater = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def preflight_dependencies
|
48
|
+
odie 'Drupid requires Ruby 1.8.7 or later' if RUBY_VERSION < '1.8.7'
|
49
|
+
['diff','curl','git','drush','mktemp','patch','rsync'].each do |cmd|
|
50
|
+
if `which #{cmd} 2>/dev/null`.chomp.empty?
|
51
|
+
odie "Drupid requires '#{cmd}', but '#{cmd}' was not found in your PATH."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_options
|
57
|
+
require 'optparse'
|
58
|
+
|
59
|
+
begin
|
60
|
+
OptionParser.new do |o|
|
61
|
+
o.banner = "Drupid synchronizes a Drush makefile" +
|
62
|
+
" with a Drupal platform, and more!\n" +
|
63
|
+
"Usage:\n" +
|
64
|
+
"drupid -s <MAKEFILE> -p <DIRECTORY> [-cdDflnSv]\n" +
|
65
|
+
"drupid --clear-cache [-nv]\n" +
|
66
|
+
"drupid --edit [<URL>] [-v] [-o <FILE>]\n" +
|
67
|
+
"drupid --graph -p <DIRECTORY> [-Sv]\n" +
|
68
|
+
"drupid --help\n" +
|
69
|
+
"drupid --version"
|
70
|
+
o.on('-s', '--spec MAKEFILE', 'Path to a drush .make file.') do |p|
|
71
|
+
begin
|
72
|
+
@options[:makefile] = Pathname.new(p).realpath
|
73
|
+
rescue
|
74
|
+
odie "#{p} does not exist."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
o.on('-c', '--no-core', 'Do not synchronize Drupal core.') { @options[:nocore] = true }
|
78
|
+
o.on('-C', '--clear-cache', 'Clear Drupid\'s cache and exit.') { @options[:command] = :clear }
|
79
|
+
o.on('-d', '--no-deps', 'Do not follow dependencies...',
|
80
|
+
'...and miss one of the coolest features of Drupid :)') { @options[:nodeps] = true }
|
81
|
+
o.on('-D', '--debug', 'Enable debugging.') { $DEBUG = true; $VERBOSE = true }
|
82
|
+
o.on('-e', '--edit [URL]', 'Create patches interactively.',
|
83
|
+
'With no URL, edit the current directory.') { |u| @options[:command] = :edit; @options[:edit] = u }
|
84
|
+
o.on('-f', '--force', 'Force completion, even if there are errors.') { |b| @options[:force] = b }
|
85
|
+
o.on('-g', '--graph', 'Generate a dependency graph and exit.') { @options[:command] = :graph }
|
86
|
+
o.on('-h', '--help', 'Print help and exit.') {
|
87
|
+
puts o
|
88
|
+
puts "\nGleefully brought to you by Lifepillar! http://lifepillar.com"
|
89
|
+
exit 0
|
90
|
+
}
|
91
|
+
o.on('-l', '--no-libs', 'Do not synchronize libraries.') { @options[:nolibs] = true }
|
92
|
+
o.on('-n', '--dry', 'Dry run.') { |b| @options[:dry] = b }
|
93
|
+
o.on('-o', '--out FILE', 'Name of the output patch.') { |o| @options[:out] = o }
|
94
|
+
o.on('-p', '--path DIRECTORY', 'Path to a Drupal platform.') do |p|
|
95
|
+
@options[:directory] = Pathname.new(p).expand_path
|
96
|
+
@options[:directory].mkpath
|
97
|
+
end
|
98
|
+
o.on('-S', '--site NAME', 'Process the given site.',
|
99
|
+
'(For multi-site platforms.)') { |s| @options[:site] = s }
|
100
|
+
o.on('-v', '--verbose', 'Be verbose.') { $VERBOSE = true }
|
101
|
+
o.on('-V', '--version', 'Print version and exit.') { puts DRUPID_USER_AGENT; exit 0 }
|
102
|
+
o.parse!
|
103
|
+
end
|
104
|
+
rescue OptionParser::InvalidOption, OptionParser::AmbiguousOption => ex
|
105
|
+
odie "#{ex}\nTry 'drupid --help' to see the available options."
|
106
|
+
rescue OptionParser::MissingArgument, OptionParser::NeedlessArgument => ex
|
107
|
+
odie "#{ex}\nTry 'drupid --help' for the correct syntax."
|
108
|
+
end
|
109
|
+
case @options[:command]
|
110
|
+
when :graph
|
111
|
+
odie "Please specify the path to a Drupal platform." unless @options[:directory]
|
112
|
+
when :clear
|
113
|
+
when :edit
|
114
|
+
when :sync
|
115
|
+
odie "Please specify a makefile." unless @options[:makefile]
|
116
|
+
odie "Please specify a destination." unless @options[:directory]
|
117
|
+
else
|
118
|
+
odie "Unknown command: #{@options[:command]}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def sync!
|
123
|
+
blah "Caching files in #{Drupid.cache_path}"
|
124
|
+
begin
|
125
|
+
mf = Drupid::Makefile.new(@options[:makefile])
|
126
|
+
rescue ParseMakefileError => ex
|
127
|
+
odie "Could not parse makefile: #{ex}"
|
128
|
+
end
|
129
|
+
begin
|
130
|
+
pl = Drupid::Platform.new(@options[:directory])
|
131
|
+
pl.contrib_path = pl.sites_dir + @options[:site] if @options[:site]
|
132
|
+
rescue => ex
|
133
|
+
odie "Could not analyze platform: #{ex}"
|
134
|
+
end
|
135
|
+
@updater = Drupid::Updater.new(mf, pl, @options[:site])
|
136
|
+
blah 'Syncing (this may take a while)...'
|
137
|
+
ohai 'Preflighting changes...'
|
138
|
+
@updater.sync(
|
139
|
+
:nocore => @options[:nocore],
|
140
|
+
:nofollow => @options[:nodeps],
|
141
|
+
:nolibs => @options[:nolibs]
|
142
|
+
)
|
143
|
+
|
144
|
+
# Check outcome and apply changes
|
145
|
+
failed = @updater.log.errors?
|
146
|
+
if failed
|
147
|
+
puts
|
148
|
+
ohai "The following errors should be fixed for a successful update:"
|
149
|
+
@updater.log.errors.each { |e| ofail e }
|
150
|
+
end
|
151
|
+
|
152
|
+
if @updater.pending_actions?
|
153
|
+
if (failed and (not @options[:force])) or @options[:dry]
|
154
|
+
ohai 'No changes applied.'
|
155
|
+
else
|
156
|
+
ohai 'Applying changes' + (failed ? ' despite errors...' : '...')
|
157
|
+
@updater.apply_changes(:force => @options[:force])
|
158
|
+
ohai 'Success!'
|
159
|
+
end
|
160
|
+
else
|
161
|
+
ohai "The platform is in sync with the makefile." unless failed
|
162
|
+
end
|
163
|
+
# Write .lock makefile
|
164
|
+
if !(@options[:no_lockfile] or @options[:dry]) and
|
165
|
+
(!(failed) or @options[:force]) and
|
166
|
+
'.lock' != @updater.makefile.path.extname
|
167
|
+
@updater.makefile.save(@updater.makefile.path.sub_ext('.make.lock'))
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def clear_cache
|
172
|
+
FileUtils.rmtree Drupid.cache_path.to_s, :noop => @options[:dry], :verbose => $VERBOSE
|
173
|
+
ohai "Cache cleared."
|
174
|
+
end
|
175
|
+
|
176
|
+
def graph
|
177
|
+
platform = Drupid::Platform.new(@options[:directory])
|
178
|
+
platform.contrib_path = platform.sites_dir + @options[:site] if @options[:site]
|
179
|
+
outfile = platform.dependency_graph
|
180
|
+
ohai "#{outfile} created in the current directory."
|
181
|
+
end
|
182
|
+
|
183
|
+
def patch_interactive
|
184
|
+
patch = ''
|
185
|
+
if @options[:edit]
|
186
|
+
begin
|
187
|
+
tmp = Pathname.new `mktemp -d /tmp/temp_item-XXXXXX`.strip
|
188
|
+
dl = Drupid.makeDownloader @options[:edit], tmp, File.basename(@options[:edit])
|
189
|
+
ohai "Fetching #{@options[:edit]}"
|
190
|
+
dl.fetch
|
191
|
+
dl.stage
|
192
|
+
wd = dl.staged_path
|
193
|
+
rescue => ex
|
194
|
+
odie "Error retrieving #{@options[:edit]}: #{ex}"
|
195
|
+
end
|
196
|
+
else
|
197
|
+
wd = Pathname.pwd
|
198
|
+
end
|
199
|
+
git_repo = (wd + '.git').exist?
|
200
|
+
Dir.chdir wd.to_s do
|
201
|
+
if git_repo
|
202
|
+
blah "This directory appears to be a git repo"
|
203
|
+
unless git('status', '-s').empty?
|
204
|
+
odie "This git repo is not in a clean state"
|
205
|
+
end
|
206
|
+
else
|
207
|
+
begin
|
208
|
+
blah "Initializing temporary git repo inside #{wd}"
|
209
|
+
git 'init'
|
210
|
+
git 'add', '-A'
|
211
|
+
git 'commit', '-m', 'Temporary commit'
|
212
|
+
rescue
|
213
|
+
odie "Unable to create temporary git repo."
|
214
|
+
end
|
215
|
+
end
|
216
|
+
begin
|
217
|
+
ohai 'Make any changes you wish, then exit from the shell.'
|
218
|
+
interactive_shell
|
219
|
+
git 'add', '-A'
|
220
|
+
patch = git 'diff', '--binary', 'HEAD'
|
221
|
+
ensure
|
222
|
+
git 'reset', '--hard'
|
223
|
+
FileUtils.rmtree '.git' unless git_repo
|
224
|
+
end
|
225
|
+
end
|
226
|
+
# Show/write patch
|
227
|
+
if patch.empty?
|
228
|
+
ohai "No changes made"
|
229
|
+
else
|
230
|
+
if @options[:out]
|
231
|
+
writeFile @options[:out], patch
|
232
|
+
ohai "Patch written to #{@options[:out]}"
|
233
|
+
else
|
234
|
+
ohai 'May I interest you in a patch?'
|
235
|
+
puts patch
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def go!
|
241
|
+
preflight_dependencies
|
242
|
+
parse_options
|
243
|
+
ohai "Dry run" if @options[:dry]
|
244
|
+
case @options[:command]
|
245
|
+
when :clear then clear_cache
|
246
|
+
when :graph then graph
|
247
|
+
when :edit then patch_interactive
|
248
|
+
else sync!
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.rush_in!
|
253
|
+
begin
|
254
|
+
drupid = Fools.new
|
255
|
+
drupid.go!
|
256
|
+
rescue Interrupt
|
257
|
+
puts
|
258
|
+
drupid.ohai "Drupid interrupted"
|
259
|
+
rescue => ex
|
260
|
+
puts
|
261
|
+
drupid.debug 'Backtrace:', ex.backtrace.join("\n")
|
262
|
+
drupid.odie "Unexpected exception raised:\n#{ex}"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
end # Fools
|
267
|
+
|
268
|
+
Fools.rush_in!
|
269
|
+
|
270
|
+
end # Drupid
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2012 Lifepillar
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
module Drupid
|
24
|
+
|
25
|
+
if RUBY_PLATFORM =~ /darwin/
|
26
|
+
@@cache_path = Pathname.new(ENV['HOME']) + 'Library/Caches/Drupid'
|
27
|
+
else
|
28
|
+
@@cache_path = Pathname.new(ENV['HOME']) + '.drupid_cache'
|
29
|
+
end
|
30
|
+
|
31
|
+
def Drupid.cache_path
|
32
|
+
@@cache_path
|
33
|
+
end
|
34
|
+
|
35
|
+
def Drupid.cache_path=(new_path)
|
36
|
+
raise "Invalid cache path" unless new_path.to_s =~ /cache/i
|
37
|
+
@@cache_path = Pathname.new(new_path).realpath # must exist
|
38
|
+
end
|
39
|
+
|
40
|
+
class Component
|
41
|
+
include Drupid::Utils
|
42
|
+
|
43
|
+
attr :name
|
44
|
+
attr_accessor :download_url
|
45
|
+
attr_accessor :download_type
|
46
|
+
attr_accessor :download_specs
|
47
|
+
attr_accessor :overwrite
|
48
|
+
attr_accessor :local_path
|
49
|
+
attr :ignore_paths
|
50
|
+
|
51
|
+
def initialize name
|
52
|
+
@name = name
|
53
|
+
@download_url = nil
|
54
|
+
@download_type = nil
|
55
|
+
@download_specs = Hash.new
|
56
|
+
@overwrite = false
|
57
|
+
@subdir = nil
|
58
|
+
@directory_name = nil
|
59
|
+
@local_path = nil
|
60
|
+
@ignore_paths = Array.new
|
61
|
+
@patches = Array.new
|
62
|
+
end
|
63
|
+
|
64
|
+
# Performs a deep copy of this object.
|
65
|
+
def clone
|
66
|
+
Marshal.load(Marshal.dump(self))
|
67
|
+
end
|
68
|
+
|
69
|
+
def extended_name
|
70
|
+
@name
|
71
|
+
end
|
72
|
+
|
73
|
+
# A synonym for #extended_name.
|
74
|
+
def to_s
|
75
|
+
extended_name
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns a path to a subdirectory where this component
|
79
|
+
# should be installed. The path is meant to be relative
|
80
|
+
# to the 'default' installation path (e.g., 'sites/all/modules' for modules,
|
81
|
+
# 'sites/all/themes' for themes, 'profiles' for profiles, etc...).
|
82
|
+
# For example, if a module 'foobar' must be installed under 'sites/all/modules'
|
83
|
+
# and this property is set, say, to 'contrib', then the module will be installed
|
84
|
+
# at 'sites/all/modules/contrib/foobar'.
|
85
|
+
def subdir
|
86
|
+
(@subdir) ? Pathname.new(@subdir) : Pathname.new('.')
|
87
|
+
end
|
88
|
+
|
89
|
+
# Sets the path to a subdirectory where this component should be installed,
|
90
|
+
# relative to the default installation path.
|
91
|
+
def subdir=(d)
|
92
|
+
@subdir = d
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the directory name for this component.
|
96
|
+
def directory_name
|
97
|
+
return @directory_name.to_s if @directory_name
|
98
|
+
return local_path.basename.to_s if exist?
|
99
|
+
return name
|
100
|
+
end
|
101
|
+
|
102
|
+
# Sets the directory name for this component.
|
103
|
+
def directory_name=(d)
|
104
|
+
@directory_name = d
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns true if this project is associated to a local copy on disk;
|
108
|
+
# returns false otherwise.
|
109
|
+
def exist?
|
110
|
+
@local_path and @local_path.exist?
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_download_spec(spec, ref)
|
114
|
+
@download_specs.merge!({spec => ref})
|
115
|
+
end
|
116
|
+
|
117
|
+
# Downloads to local cache.
|
118
|
+
def fetch
|
119
|
+
if cached_location.exist?
|
120
|
+
@local_path = cached_location
|
121
|
+
debug "#{extended_name} is cached"
|
122
|
+
else
|
123
|
+
raise "No download URL specified for #{extended_name}" unless download_url
|
124
|
+
blah "Fetching #{extended_name}"
|
125
|
+
downloader = Drupid.makeDownloader download_url.to_s, cached_location.dirname.to_s, cached_location.basename.to_s, download_specs
|
126
|
+
downloader.fetch
|
127
|
+
downloader.stage
|
128
|
+
@local_path = downloader.staged_path
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Applies the patches associated to this component.
|
133
|
+
# Raises an exception if a patch cannot be applied.
|
134
|
+
def patch
|
135
|
+
fetch unless exist?
|
136
|
+
return unless has_patches?
|
137
|
+
patched_location.rmtree if patched_location.exist? # Ensure no previous patched copy exists
|
138
|
+
@local_path.ditto patched_location
|
139
|
+
@local_path = patched_location
|
140
|
+
# Download patches
|
141
|
+
patched_location.dirname.cd do
|
142
|
+
each_patch do |p|
|
143
|
+
p.fetch
|
144
|
+
end
|
145
|
+
end
|
146
|
+
# Apply patches
|
147
|
+
patched_location.cd do
|
148
|
+
each_patch do |p|
|
149
|
+
p.apply
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Removes all the patches from this component.
|
155
|
+
def clear_patches
|
156
|
+
@patches.clear
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns true if this component has been patched;
|
160
|
+
# returns false otherwise.
|
161
|
+
def patched?
|
162
|
+
@local_path == patched_location
|
163
|
+
end
|
164
|
+
|
165
|
+
# Iterates over each patch associated to this component,
|
166
|
+
# yielding a Drupid::Patch object.
|
167
|
+
def each_patch
|
168
|
+
@patches.each do |p|
|
169
|
+
yield p
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns true if patches are associated to this component,
|
174
|
+
# returns false otherwise.
|
175
|
+
def has_patches?
|
176
|
+
!@patches.empty?
|
177
|
+
end
|
178
|
+
|
179
|
+
def add_patch(url, descr, md5 = nil)
|
180
|
+
@patches << Patch.new(url, descr, md5)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns the first patch with the given description, or
|
184
|
+
# nil if no such patch exists.
|
185
|
+
def get_patch descr
|
186
|
+
@patches.each do |p|
|
187
|
+
return p if descr == p.descr
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Full path to the location where a cached copy of this component is located.
|
192
|
+
def cached_location
|
193
|
+
dlt = (download_type) ? download_type : 'default'
|
194
|
+
Drupid.cache_path + self.class.to_s.split(/::/).last + extended_name + dlt + name
|
195
|
+
end
|
196
|
+
|
197
|
+
# Full path to the directory where a patched copy of this component is located.
|
198
|
+
def patched_location
|
199
|
+
cached_location.dirname + '__patches' + name
|
200
|
+
end
|
201
|
+
|
202
|
+
# Ignores the given path relative to this component's path.
|
203
|
+
# This is useful, for example, when an external library is installed
|
204
|
+
# inside a module's folder (rather than in the libraries folder).
|
205
|
+
def ignore_path(relative_path)
|
206
|
+
@ignore_paths << Pathname.new(relative_path)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Performs a file-by-file comparison of this component with another.
|
210
|
+
# Returns a list of files that are different between the two copies.
|
211
|
+
# If the directories of the two projects look the same, returns an empty array.
|
212
|
+
# Local copies must exist for both projects, otherwise this method raises an error.
|
213
|
+
#
|
214
|
+
# If one of the projects has a makefile, the content of the following directories
|
215
|
+
# is ignored: libraries, modules, themes.
|
216
|
+
# Version control directories (.git) are always ignored.
|
217
|
+
def file_level_compare_with tgt, additional_rsync_args = []
|
218
|
+
raise "#{extended_name} does not exist at #{local_path}" unless exist?
|
219
|
+
raise "#{tgt.extended_name} does not exist at #{tgt.local_path}" unless tgt.exist?
|
220
|
+
args = Array.new
|
221
|
+
default_exclusions = [
|
222
|
+
'.DS_Store',
|
223
|
+
'.git/',
|
224
|
+
'.bzr/',
|
225
|
+
'.hg/',
|
226
|
+
'.svn/'
|
227
|
+
]
|
228
|
+
default_exclusions.each { |e| args << "--exclude=#{e}" }
|
229
|
+
ignore_paths.each { |p| args << "--exclude=#{p}" }
|
230
|
+
tgt.ignore_paths.each { |p| args << "--exclude=#{p}" }
|
231
|
+
args += additional_rsync_args
|
232
|
+
compare_paths local_path, tgt.local_path, args
|
233
|
+
end
|
234
|
+
|
235
|
+
end # Component
|
236
|
+
end # Drupid
|