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/lib/drupid/drush.rb
ADDED
@@ -0,0 +1,185 @@
|
|
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
|
+
# A wrapper around drush.
|
26
|
+
module Drush extend Drupid::Utils
|
27
|
+
|
28
|
+
DRUSH = `which drush`.strip
|
29
|
+
|
30
|
+
def self.drush *args
|
31
|
+
runBabyRun DRUSH, args
|
32
|
+
end
|
33
|
+
|
34
|
+
# Executes 'drush core-status' at the given path and
|
35
|
+
# returns the output of the command.
|
36
|
+
def self.status path, options = {}
|
37
|
+
output = nil
|
38
|
+
FileUtils.cd(path) do
|
39
|
+
output = drush 'core-status', '--pipe'
|
40
|
+
end
|
41
|
+
output
|
42
|
+
end
|
43
|
+
|
44
|
+
# Executes 'drush pm-releases --all' for the given project and
|
45
|
+
# returns the output of the command.
|
46
|
+
def self.pm_releases project_name, options = {}
|
47
|
+
drush 'pm-releases', '--all', project_name
|
48
|
+
end
|
49
|
+
|
50
|
+
# Parses the output of 'drush pm-releases' and returns
|
51
|
+
# the version of the latest recommended release, or nil
|
52
|
+
# if no such release exists.
|
53
|
+
def self.recommended_release pm_releases_output
|
54
|
+
pm_releases_output.each_line do |l|
|
55
|
+
return $~[1] if (l.match(/^\s*([^\s]+).*Recommended/))
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Parses the output of 'drush pm-releases' and returns
|
61
|
+
# the version of the latest supported release, or nil
|
62
|
+
# if no such release exists.
|
63
|
+
def self.supported_release pm_releases_output
|
64
|
+
pm_releases_output.each_line do |l|
|
65
|
+
return $~[1] if (l.match(/^\s*([^\s]+).*Supported/))
|
66
|
+
end
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
# Executes 'drush pm-download <project_name>' and
|
71
|
+
# returns the output of the command.
|
72
|
+
# If :working_dir is set to an existing directory, move into that directory before
|
73
|
+
# executing the command.
|
74
|
+
#
|
75
|
+
# Options: source, destination, working_dir
|
76
|
+
def self.pm_download project_name, options = {}
|
77
|
+
args = ['pm-download', '-y']
|
78
|
+
args << "--source=#{options[:source]}" if options[:source]
|
79
|
+
args << "--destination\=#{options[:destination]}" if options[:destination]
|
80
|
+
args << "--variant=profile-only"
|
81
|
+
args << "--verbose" if $VERBOSE
|
82
|
+
args << project_name
|
83
|
+
if options[:working_dir] and File.directory?(options[:working_dir])
|
84
|
+
wd = options[:working_dir]
|
85
|
+
else
|
86
|
+
wd = FileUtils.pwd
|
87
|
+
end
|
88
|
+
output = nil
|
89
|
+
FileUtils.cd(wd) do
|
90
|
+
output = runBabyRun DRUSH, args, :redirect_stderr_to_stdout => true
|
91
|
+
end
|
92
|
+
output
|
93
|
+
end
|
94
|
+
|
95
|
+
# Parses the output of 'drush pm-download' and returns
|
96
|
+
# the path to the downloaded project, or nil
|
97
|
+
# if the path cannot be determined.
|
98
|
+
def self.download_path pm_download_output
|
99
|
+
pm_download_output.match(/^\s*Project.+downloaded to *(.\[.+[\n\r])?\s*(.+)\./)
|
100
|
+
return File.expand_path($~[2]) if $~
|
101
|
+
return nil
|
102
|
+
end
|
103
|
+
|
104
|
+
# Parses the output of 'drush pm-download' and returns
|
105
|
+
# the version of the downloaded project, or nil
|
106
|
+
# if the version cannot be determined.
|
107
|
+
def self.downloaded_version pm_download_output
|
108
|
+
pm_download_output.match(/^\s*Project.+\((.+)\) +downloaded +to/)
|
109
|
+
return $~[1] if $~
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# Executes 'drush pm-info' at the given path and with an
|
114
|
+
# optional list of projects.
|
115
|
+
# Returns the output of the command.
|
116
|
+
def self.pm_info path, projects = [], options = {}
|
117
|
+
output = nil
|
118
|
+
args = Array.new
|
119
|
+
args << 'pm-info'
|
120
|
+
args << projects.join(' ') unless nil != projects and projects.empty?
|
121
|
+
FileUtils.cd(path) do
|
122
|
+
output = runBabyRun DRUSH, args, :redirect_stderr_to_stdout => true
|
123
|
+
end
|
124
|
+
output
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns the version of Drupal (e.g., '7.12')
|
128
|
+
# if the given path contains Drupal;
|
129
|
+
# returns nil otherwise.
|
130
|
+
def self.drupal_version path, options = {}
|
131
|
+
st = self.status(path, options)
|
132
|
+
return nil unless st.match(/drupal_version=(\d+\.\d+)/)
|
133
|
+
return $~[1]
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns true if a Drupal's site is bootstrapped at the given path;
|
137
|
+
# returns false otherwise.
|
138
|
+
def self.bootstrapped?(path, options = {})
|
139
|
+
st = self.status(path, options)
|
140
|
+
st.match(/drupal_bootstrap=Successful/) ? true : false
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns true if the project at the given path is an enabled theme
|
144
|
+
# or an installed (enabled or disabled) module in the given site;
|
145
|
+
# returns false otherwise. The project's path must be relative
|
146
|
+
# to the Drupal installation (e.g., 'sites/all/modules').
|
147
|
+
# Note that the project path is necessary because, in general,
|
148
|
+
# there may be several copies of the same modules at different locations
|
149
|
+
# within a platform (in 'sites/all', in 'profiles/' and in site-specific locations).
|
150
|
+
#
|
151
|
+
# Options: verbose
|
152
|
+
def self.installed?(site_path, project_name, project_path, options = {})
|
153
|
+
st = ''
|
154
|
+
begin
|
155
|
+
st = self.pm_info(site_path, [project_name], options)
|
156
|
+
rescue # site not fully bootstrapped
|
157
|
+
return false
|
158
|
+
end
|
159
|
+
return false unless st.match(/Path +: +#{project_path}/)
|
160
|
+
return false unless st.match(/Type +: +([^\s]+)/)
|
161
|
+
type = $~[1]
|
162
|
+
return false unless st.match(/Status +: +(.+)$/)
|
163
|
+
project_status = $~[1]
|
164
|
+
('module' == type and project_status !~ /not installed/) or
|
165
|
+
('theme' == type and project_status =~ /^enabled/)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Executes 'drush make' and returns the output of the command.
|
169
|
+
#
|
170
|
+
# Options: contrib_path, nocore, dry, verbose
|
171
|
+
def self.make(makefile, target, options = {})
|
172
|
+
args = Array.new
|
173
|
+
args << 'make'
|
174
|
+
args << '-y'
|
175
|
+
args << '--no-core' if options[:nocore]
|
176
|
+
args << "--contrib-destination=#{options[:contrib_path]}" if options[:contrib_path]
|
177
|
+
args << "--no-patch-txt"
|
178
|
+
args << makefile
|
179
|
+
args << target
|
180
|
+
drush(*args)
|
181
|
+
end
|
182
|
+
|
183
|
+
end # module Drush
|
184
|
+
|
185
|
+
end # Drupid
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# Copyright (c) 2012 Lifepillar
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
11
|
+
# copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
# SOFTWARE.
|
20
|
+
|
21
|
+
# Portions Copyright 2009-2011 Max Howell and other contributors.
|
22
|
+
#
|
23
|
+
# Redistribution and use in source and binary forms, with or without
|
24
|
+
# modification, are permitted provided that the following conditions
|
25
|
+
# are met:
|
26
|
+
#
|
27
|
+
# 1. Redistributions of source code must retain the above copyright
|
28
|
+
# notice, this list of conditions and the following disclaimer.
|
29
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
30
|
+
# notice, this list of conditions and the following disclaimer in the
|
31
|
+
# documentation and/or other materials provided with the distribution.
|
32
|
+
#
|
33
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
34
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
35
|
+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
36
|
+
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
37
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
38
|
+
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
39
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
40
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
41
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
42
|
+
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
43
|
+
|
44
|
+
require 'pathname'
|
45
|
+
|
46
|
+
# Code borrowed from Homebrew ;)
|
47
|
+
class Pathname
|
48
|
+
def compression_type
|
49
|
+
return nil if self.directory?
|
50
|
+
# Don't treat jars or wars as compressed
|
51
|
+
return nil if self.extname == '.jar'
|
52
|
+
return nil if self.extname == '.war'
|
53
|
+
|
54
|
+
# OS X installer package
|
55
|
+
return :pkg if self.extname == '.pkg'
|
56
|
+
|
57
|
+
# Get enough of the file to detect common file types
|
58
|
+
# POSIX tar magic has a 257 byte offset
|
59
|
+
magic_bytes = nil
|
60
|
+
File.open(self) { |f| magic_bytes = f.read(262) }
|
61
|
+
|
62
|
+
# magic numbers stolen from /usr/share/file/magic/
|
63
|
+
case magic_bytes
|
64
|
+
when /^PK\003\004/ then :zip
|
65
|
+
when /^\037\213/ then :gzip
|
66
|
+
when /^BZh/ then :bzip2
|
67
|
+
when /^\037\235/ then :compress
|
68
|
+
when /^.{257}ustar/ then :tar
|
69
|
+
when /^\xFD7zXZ\x00/ then :xz
|
70
|
+
when /^Rar!/ then :rar
|
71
|
+
else
|
72
|
+
# Assume it is not an archive
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def cd
|
78
|
+
Dir.chdir(self) { yield }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Copies a file to another location or
|
82
|
+
# the content of a directory into the specified directory.
|
83
|
+
def ditto dst
|
84
|
+
d = Pathname.new(dst)
|
85
|
+
if file?
|
86
|
+
FileUtils.cp to_s, d.to_s, :verbose => $DEBUG
|
87
|
+
return (d.directory?) ? d+basename : d
|
88
|
+
else
|
89
|
+
d.mkpath
|
90
|
+
FileUtils.cp_r to_s + '/.', d.to_s, :verbose => $DEBUG
|
91
|
+
return d
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# extended to support common double extensions
|
96
|
+
alias extname_old extname
|
97
|
+
def extname
|
98
|
+
/(\.(tar|cpio)\.(gz|bz2|xz|Z))$/.match to_s
|
99
|
+
return $1 if $1
|
100
|
+
return File.extname(to_s)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Pathname in Ruby 1.8.x does not define #sub_ext.
|
104
|
+
unless self.method_defined? :sub_ext
|
105
|
+
# Return a pathname which the extension of the basename is substituted by
|
106
|
+
# <i>repl</i>.
|
107
|
+
#
|
108
|
+
# If self has no extension part, <i>repl</i> is appended.
|
109
|
+
def sub_ext(repl)
|
110
|
+
ext = File.extname(@path)
|
111
|
+
self.class.new(@path.chomp(ext) + repl)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end # Pathname
|
@@ -0,0 +1,52 @@
|
|
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 Library < Component
|
25
|
+
|
26
|
+
def initialize name
|
27
|
+
super
|
28
|
+
self.download_type = 'file' # Default download type
|
29
|
+
@destination = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def destination
|
33
|
+
return Pathname.new('libraries') unless @destination
|
34
|
+
return Pathname.new(@destination)
|
35
|
+
end
|
36
|
+
|
37
|
+
def destination=(d)
|
38
|
+
@destination = d
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the relative path where this library should be installed
|
42
|
+
# within a platform. This is 'libraries/#name' by default.
|
43
|
+
def target_path
|
44
|
+
return destination + subdir + directory_name
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch
|
48
|
+
cached_location.rmtree if cached_location.exist?
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end # Library
|
52
|
+
end # Drupid
|