drupid 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|