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
@@ -0,0 +1,234 @@
|
|
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
|
+
class Platform
|
25
|
+
include Drupid::Utils
|
26
|
+
|
27
|
+
# The absolute path to this platform.
|
28
|
+
attr :local_path
|
29
|
+
# A Drupid::Platform::Project object with information about Drupal core,
|
30
|
+
# or nil if this platform does not contain Drupal core.
|
31
|
+
attr :drupal_project
|
32
|
+
# The path for contrib modules and themes (e.g., 'sites/all'),
|
33
|
+
# relative to #path.
|
34
|
+
attr_accessor :contrib_path
|
35
|
+
# The path to the sites directory (default: 'sites'), relative
|
36
|
+
# relative to #path.
|
37
|
+
attr :sites_dir
|
38
|
+
|
39
|
+
# Creates a new platform object for the Drupal installation at the
|
40
|
+
# specified path.
|
41
|
+
def initialize(pathname)
|
42
|
+
@local_path = Pathname.new(pathname).realpath # must exist
|
43
|
+
@sites_dir = Pathname.new('sites')
|
44
|
+
@contrib_path = @sites_dir + 'all'
|
45
|
+
@drupal_project = nil # Project
|
46
|
+
@projects = Hash.new # String -> PlatformProject
|
47
|
+
@libraries = Hash.new # String -> Library
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the version of Drupal core in this platform, or
|
51
|
+
# nil if the version cannot be determined.
|
52
|
+
def version
|
53
|
+
if @drupal_project and @drupal_project.has_version?
|
54
|
+
return @drupal_project.version
|
55
|
+
end
|
56
|
+
return nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the full path to the sites directory in this platform, e.g.,
|
60
|
+
# '/path/to/drupal/sites', as obtained by joining #local_path and #sites_dir.
|
61
|
+
def sites_path
|
62
|
+
@local_path + @sites_dir
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the (possibly empty) list of sites in this platform.
|
66
|
+
def site_names
|
67
|
+
return [] unless sites_path.exist?
|
68
|
+
Pathname.glob(sites_path + '*/').map { |s| s.basename.to_s }.reject { |s| s =~ /^all$/ }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the full path to the libraries folder.
|
72
|
+
def libraries_path
|
73
|
+
@local_path + @contrib_path + 'libraries'
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
# Returns the relative path where the given component should be placed
|
78
|
+
# in this platform.
|
79
|
+
def dest_path(component)
|
80
|
+
if component.instance_of?(String) # assume it is the name of a platform project
|
81
|
+
c = get_project(component)
|
82
|
+
raise "No project called #{component} exists in this platform" if c.nil?
|
83
|
+
else
|
84
|
+
c = component
|
85
|
+
end
|
86
|
+
if c.core_project? or c.profile?
|
87
|
+
return c.target_path
|
88
|
+
else
|
89
|
+
return contrib_path + c.target_path
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns a list of the names of the profiles that exist in this platform.
|
94
|
+
# For profiles to be found, they must be located inside
|
95
|
+
# the subdirectory of #local_path named 'profiles'.
|
96
|
+
def profiles
|
97
|
+
Pathname.glob(local_path + 'profiles/*/*.profile').map { |p| p.basename('.profile').to_s }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the Drupid::PlatformProject object with the specified name,
|
101
|
+
# or nil if this platform does not contain a project with the given name.
|
102
|
+
def get_project(project_name)
|
103
|
+
return @drupal_project if @drupal_project and project_name == @drupal_project.name
|
104
|
+
return @projects[project_name]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns true if this platform contains a project with the specified name.
|
108
|
+
def has_project?(project_name)
|
109
|
+
@projects.has_key?(project_name) or (@drupal_project and project_name == @drupal_project.name)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns true if the specified site in this platform is bootstrapped.
|
113
|
+
# If no site is specified, returns true if the platform contains at least
|
114
|
+
# one bootstrapped site. Returns false otherwise. Example:
|
115
|
+
# platform.bootstrapped?('default')
|
116
|
+
def bootstrapped?(site = nil)
|
117
|
+
sites_list = (site) ? [site] : site_names
|
118
|
+
sites_list.each do |s|
|
119
|
+
p = sites_path + s
|
120
|
+
next unless p.exist?
|
121
|
+
return true if Drupid::Drush.bootstrapped?(p)
|
122
|
+
end
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
126
|
+
# Analyzes this platform.
|
127
|
+
def analyze
|
128
|
+
blah "Analyzing #{local_path}"
|
129
|
+
analyze_drupal_core
|
130
|
+
analyze_projects
|
131
|
+
analyze_libraries
|
132
|
+
return self
|
133
|
+
end
|
134
|
+
|
135
|
+
# Retrieves information about Drupal core in this platform.
|
136
|
+
def analyze_drupal_core
|
137
|
+
@drupal_project = nil
|
138
|
+
vers = Drupid::Drush.drupal_version(local_path)
|
139
|
+
if vers
|
140
|
+
core = vers.match(/(\d+)\./)[1].to_i
|
141
|
+
@drupal_project = Drupid::Project.new('drupal', core, vers)
|
142
|
+
@drupal_project.local_path = self.local_path
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Extracts information about the projects in this platform.
|
147
|
+
# This method is invoked automatically by Drupid::Platform.analyze.
|
148
|
+
# In general, it does not need to be called by the user.
|
149
|
+
def analyze_projects
|
150
|
+
@projects = Hash.new
|
151
|
+
count = 0
|
152
|
+
search_paths = Array.new
|
153
|
+
search_paths << local_path+'modules/**/*.info'
|
154
|
+
search_paths << local_path+'themes/**/*.info'
|
155
|
+
search_paths << local_path+'profiles/*/*.info'
|
156
|
+
search_paths << local_path+contrib_path+'modules/**/*.info'
|
157
|
+
search_paths << local_path+contrib_path+'themes/**/*.info'
|
158
|
+
search_paths.uniq! # contrib_path may be ''
|
159
|
+
search_paths.each do |sp|
|
160
|
+
Dir[sp].each do |p|
|
161
|
+
pp = Drupid::PlatformProject.new(self, p)
|
162
|
+
@projects[pp.name] = pp
|
163
|
+
count += 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
count
|
167
|
+
end
|
168
|
+
|
169
|
+
# TODO: implement method
|
170
|
+
def analyze_libraries
|
171
|
+
end
|
172
|
+
|
173
|
+
# TODO: implement or throw away?
|
174
|
+
def to_makefile()
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns a list of the names of all contrib projects in this platform.
|
178
|
+
def project_names
|
179
|
+
@projects.values.reject { |p| p.core_project? }.map { |p| p.name }
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns a list of the names of all core projects.
|
183
|
+
def core_project_names
|
184
|
+
@projects.values.select { |p| p.core_project? }.map { |p| p.name }
|
185
|
+
end
|
186
|
+
|
187
|
+
# Iterates over all contrib projects in this platform.
|
188
|
+
def each_project
|
189
|
+
@projects.values.reject { |p| p.core_project? }.each { |p| yield p }
|
190
|
+
end
|
191
|
+
|
192
|
+
# Iterates over all core projects in this platform.
|
193
|
+
def each_core_project
|
194
|
+
@projects.values.select { |p| p.core_project? }.each { |p| yield p }
|
195
|
+
end
|
196
|
+
|
197
|
+
# Creates a PNG image depicting the relationships
|
198
|
+
# among the projects in this platform.
|
199
|
+
def dependency_graph
|
200
|
+
silence_warnings do
|
201
|
+
begin
|
202
|
+
require 'rgl/adjacency'
|
203
|
+
require 'rgl/dot'
|
204
|
+
rescue LoadError
|
205
|
+
odie "Please install the RGL gem with 'gem install rgl'"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
if `which dot 2>/dev/null`.chomp.empty?
|
209
|
+
owarn "The 'dot' program is required to get an SVG image."
|
210
|
+
owarn "Without it you will only get a .dot file."
|
211
|
+
end
|
212
|
+
analyze
|
213
|
+
# We use this instead of a dag, because there may be circular dependencies...
|
214
|
+
graph = ::RGL::DirectedAdjacencyGraph.new
|
215
|
+
each_project do |p|
|
216
|
+
graph.add_vertex(p.name)
|
217
|
+
p.dependencies(:subprojects => false).each do |depname|
|
218
|
+
graph.add_vertex(depname) # does nothing if depname already exists
|
219
|
+
graph.add_edge(p.name, depname)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
each_core_project do |p|
|
223
|
+
next if p.name.match(/test/) # Skip test modules
|
224
|
+
graph.add_vertex('node' == p.name ? '"node"' : p.name) # 'node' is a Dot keyword
|
225
|
+
p.dependencies.each do |depname|
|
226
|
+
graph.add_vertex(depname)
|
227
|
+
graph.add_edge(p.name, depname)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
graph.write_to_graphic_file('svg')
|
231
|
+
end
|
232
|
+
|
233
|
+
end # Platform
|
234
|
+
end # Drupid
|
@@ -0,0 +1,91 @@
|
|
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
|
+
class PlatformProject < Project
|
25
|
+
attr :platform
|
26
|
+
|
27
|
+
def initialize proj_platform, project_or_info_path
|
28
|
+
pi = ProjectInfo.new(project_or_info_path)
|
29
|
+
super(pi.project_name, pi.project_core)
|
30
|
+
@info_file = pi.info_file
|
31
|
+
@local_path = pi.project_dir
|
32
|
+
@version = pi.project_version if pi.project_version
|
33
|
+
@proj_type = pi.project_type
|
34
|
+
@core_project = pi.core_project?
|
35
|
+
@platform = proj_platform
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the path of this project relative to the path of the containing
|
39
|
+
# platform.
|
40
|
+
def relative_path
|
41
|
+
@local_path.relative_path_from(@platform.local_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
# A subdirectory where this project is installed.
|
45
|
+
# For example, for 'sites/all/modules/specialmodules/mymodule',
|
46
|
+
# this method returns 'specialmodules';
|
47
|
+
# for 'profiles/custom_profiles/myprofile', this method returns 'custom_profiles';
|
48
|
+
# for 'modules/node/tests/', returns 'node'.
|
49
|
+
def subdir
|
50
|
+
if core_project? or 'profile' == proj_type
|
51
|
+
return @local_path.parent.relative_path_from(@platform.local_path + (proj_type + 's'))
|
52
|
+
else
|
53
|
+
return @local_path.parent.relative_path_from(@platform.local_path + @platform.contrib_path + (proj_type + 's'))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns true if this is an enabled theme
|
58
|
+
# or an installed (enabled or disabled) module in the specified site,
|
59
|
+
# or in at least one site in the platform if no site is specified;
|
60
|
+
# returns false otherwise.
|
61
|
+
# Note that the project's path must be defined, because this method uses
|
62
|
+
# the path to determine whether the project is installed in a site.
|
63
|
+
# Consider, for example, a Drupal platform that
|
64
|
+
# contains two distinct copies of a module called Foo, one
|
65
|
+
# in ./sites/www.mysite.org/modules/foo
|
66
|
+
# and another in ./profiles/fooprofile/modules/foo. Suppose that the former is used by
|
67
|
+
# the site www.mysite.org, and the latter is used by another site, say
|
68
|
+
# www.othersite.org, installed using the fooprofile installation profile.
|
69
|
+
# If p is a Drupid::PlatformProject object associated to ./sites/www.mysite.org/modules/foo
|
70
|
+
# and q is another Drupid::PlatformProject object associated to
|
71
|
+
# ./profiles/fooprofile/modules/foo and both modules are installed in their
|
72
|
+
# respective sites,
|
73
|
+
# then the result of invoking this method will be as follows:
|
74
|
+
# p.installed?('www.mysite.org') # true
|
75
|
+
# p.installed?('www.othersite.org') # false
|
76
|
+
# q.installed?('www.mysite.org') # false
|
77
|
+
# q.installed?('www.othersite.org') # true
|
78
|
+
# p.installed? # true
|
79
|
+
# q.installed? # true
|
80
|
+
def installed? site = nil
|
81
|
+
site_list = (site) ? [site] : platform.site_names
|
82
|
+
site_list.each do |s|
|
83
|
+
site_path = platform.sites_path + s
|
84
|
+
next unless site_path.exist?
|
85
|
+
return true if Drush.installed?(site_path, name, relative_path)
|
86
|
+
end
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
|
90
|
+
end # Project
|
91
|
+
end # Drupid
|