fpm-aeppert 1.6.2
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/CHANGELIST +661 -0
- data/CONTRIBUTORS +26 -0
- data/LICENSE +21 -0
- data/bin/fpm +8 -0
- data/lib/fpm.rb +20 -0
- data/lib/fpm/command.rb +648 -0
- data/lib/fpm/errors.rb +4 -0
- data/lib/fpm/namespace.rb +4 -0
- data/lib/fpm/package.rb +539 -0
- data/lib/fpm/package/apk.rb +510 -0
- data/lib/fpm/package/cpan.rb +405 -0
- data/lib/fpm/package/deb.rb +935 -0
- data/lib/fpm/package/dir.rb +221 -0
- data/lib/fpm/package/empty.rb +13 -0
- data/lib/fpm/package/freebsd.rb +147 -0
- data/lib/fpm/package/gem.rb +243 -0
- data/lib/fpm/package/npm.rb +120 -0
- data/lib/fpm/package/osxpkg.rb +165 -0
- data/lib/fpm/package/p5p.rb +124 -0
- data/lib/fpm/package/pacman.rb +403 -0
- data/lib/fpm/package/pear.rb +117 -0
- data/lib/fpm/package/pkgin.rb +35 -0
- data/lib/fpm/package/pleaserun.rb +63 -0
- data/lib/fpm/package/puppet.rb +120 -0
- data/lib/fpm/package/pyfpm/__init__.py +1 -0
- data/lib/fpm/package/pyfpm/get_metadata.py +104 -0
- data/lib/fpm/package/python.rb +318 -0
- data/lib/fpm/package/rpm.rb +593 -0
- data/lib/fpm/package/sh.rb +69 -0
- data/lib/fpm/package/solaris.rb +95 -0
- data/lib/fpm/package/tar.rb +86 -0
- data/lib/fpm/package/virtualenv.rb +164 -0
- data/lib/fpm/package/zip.rb +63 -0
- data/lib/fpm/rake_task.rb +60 -0
- data/lib/fpm/util.rb +358 -0
- data/lib/fpm/util/tar_writer.rb +80 -0
- data/lib/fpm/version.rb +3 -0
- data/templates/deb.erb +52 -0
- data/templates/deb/changelog.erb +5 -0
- data/templates/deb/ldconfig.sh.erb +13 -0
- data/templates/deb/postinst_upgrade.sh.erb +62 -0
- data/templates/deb/postrm_upgrade.sh.erb +46 -0
- data/templates/deb/preinst_upgrade.sh.erb +41 -0
- data/templates/deb/prerm_upgrade.sh.erb +39 -0
- data/templates/osxpkg.erb +11 -0
- data/templates/p5p_metadata.erb +12 -0
- data/templates/pacman.erb +47 -0
- data/templates/pacman/INSTALL.erb +41 -0
- data/templates/pleaserun/generate-cleanup.sh +17 -0
- data/templates/pleaserun/install-path.sh +17 -0
- data/templates/pleaserun/install.sh +117 -0
- data/templates/pleaserun/scripts/after-install.sh +4 -0
- data/templates/pleaserun/scripts/before-remove.sh +12 -0
- data/templates/puppet/package.pp.erb +34 -0
- data/templates/puppet/package/remove.pp.erb +13 -0
- data/templates/rpm.erb +260 -0
- data/templates/rpm/filesystem_list +14514 -0
- data/templates/sh.erb +369 -0
- data/templates/solaris.erb +15 -0
- metadata +322 -0
@@ -0,0 +1,221 @@
|
|
1
|
+
require "fpm/package"
|
2
|
+
require "fpm/util"
|
3
|
+
require "backports"
|
4
|
+
require "fileutils"
|
5
|
+
require "find"
|
6
|
+
require "socket"
|
7
|
+
|
8
|
+
# A directory package.
|
9
|
+
#
|
10
|
+
# This class supports both input and output. As a note, 'output' will
|
11
|
+
# only emit the files, not any metadata. This is an effective way
|
12
|
+
# to extract another package type.
|
13
|
+
class FPM::Package::Dir < FPM::Package
|
14
|
+
private
|
15
|
+
|
16
|
+
# Add a new path to this package.
|
17
|
+
#
|
18
|
+
# A special handling of the path occurs if it includes a '=' symbol.
|
19
|
+
# You can say "source=destination" and it will copy files from that source
|
20
|
+
# to the given destination in the package.
|
21
|
+
#
|
22
|
+
# This lets you take a local directory and map it to the desired location at
|
23
|
+
# packaging time. Such as: "./src/redis-server=/usr/local/bin" will make
|
24
|
+
# the local file ./src/redis-server appear as /usr/local/bin/redis-server in
|
25
|
+
# your package.
|
26
|
+
#
|
27
|
+
# If the path is a directory, it is copied recursively. The behavior
|
28
|
+
# of the copying is modified by the :chdir and :prefix attributes.
|
29
|
+
#
|
30
|
+
# If :prefix is set, the destination path is prefixed with that value.
|
31
|
+
# If :chdir is set, the current directory is changed to that value
|
32
|
+
# during the copy.
|
33
|
+
#
|
34
|
+
# Example: Copy /etc/X11 into this package as /opt/xorg/X11:
|
35
|
+
#
|
36
|
+
# package.attributes[:prefix] = "/opt/xorg"
|
37
|
+
# package.attributes[:chdir] = "/etc"
|
38
|
+
# package.input("X11")
|
39
|
+
def input(path)
|
40
|
+
chdir = attributes[:chdir] || "."
|
41
|
+
|
42
|
+
# Support mapping source=dest
|
43
|
+
# This mapping should work the same way 'rsync -a' does
|
44
|
+
# Meaning 'rsync -a source dest'
|
45
|
+
# and 'source=dest' in fpm work the same as the above rsync
|
46
|
+
if path =~ /.=./ && !File.exists?(chdir == '.' ? path : File.join(chdir, path))
|
47
|
+
origin, destination = path.split("=", 2)
|
48
|
+
|
49
|
+
if File.directory?(origin) && origin[-1,1] == "/"
|
50
|
+
chdir = chdir == '.' ? origin : File.join(chdir, origin)
|
51
|
+
source = "."
|
52
|
+
else
|
53
|
+
origin_dir = File.dirname(origin)
|
54
|
+
chdir = chdir == '.' ? origin_dir : File.join(chdir, origin_dir)
|
55
|
+
source = File.basename(origin)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
source, destination = path, "/"
|
59
|
+
end
|
60
|
+
|
61
|
+
if attributes[:prefix]
|
62
|
+
destination = File.join(attributes[:prefix], destination)
|
63
|
+
end
|
64
|
+
|
65
|
+
destination = File.join(staging_path, destination)
|
66
|
+
|
67
|
+
logger["method"] = "input"
|
68
|
+
begin
|
69
|
+
::Dir.chdir(chdir) do
|
70
|
+
begin
|
71
|
+
clone(source, destination)
|
72
|
+
rescue Errno::ENOENT => e
|
73
|
+
raise FPM::InvalidPackageConfiguration,
|
74
|
+
"Cannot package the path '#{File.join(chdir, source)}', does it exist?"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
rescue Errno::ENOENT => e
|
78
|
+
raise FPM::InvalidPackageConfiguration,
|
79
|
+
"Cannot chdir to '#{chdir}'. Does it exist?"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set some defaults. This is useful because other package types
|
83
|
+
# can include license data from themselves (rpms, gems, etc),
|
84
|
+
# but to make sure a simple dir -> rpm works without having
|
85
|
+
# to specify a license.
|
86
|
+
self.license = "unknown"
|
87
|
+
self.vendor = [ENV["USER"], Socket.gethostname].join("@")
|
88
|
+
ensure
|
89
|
+
# Clean up any logger context we added.
|
90
|
+
logger.remove("method")
|
91
|
+
end # def input
|
92
|
+
|
93
|
+
# Output this package to the given directory.
|
94
|
+
def output(output_path)
|
95
|
+
output_check(output_path)
|
96
|
+
|
97
|
+
output_path = File.expand_path(output_path)
|
98
|
+
::Dir.chdir(staging_path) do
|
99
|
+
logger["method"] = "output"
|
100
|
+
clone(".", output_path)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Write the scripts, too.
|
104
|
+
scripts_path = File.join(output_path, ".scripts")
|
105
|
+
::Dir.mkdir(scripts_path)
|
106
|
+
[:before_install, :after_install, :before_remove, :after_remove].each do |name|
|
107
|
+
next unless script?(name)
|
108
|
+
out = File.join(scripts_path, name.to_s)
|
109
|
+
logger.debug("Writing script", :source => name, :target => out)
|
110
|
+
File.write(out, script(name))
|
111
|
+
require "pry"
|
112
|
+
binding.pry
|
113
|
+
File.chmod(0755, out)
|
114
|
+
end
|
115
|
+
|
116
|
+
ensure
|
117
|
+
logger.remove("method")
|
118
|
+
end # def output
|
119
|
+
|
120
|
+
private
|
121
|
+
# Copy a file or directory to a destination
|
122
|
+
#
|
123
|
+
# This is special because it respects the full path of the source.
|
124
|
+
# Aditionally, hardlinks will be used instead of copies.
|
125
|
+
#
|
126
|
+
# Example:
|
127
|
+
#
|
128
|
+
# clone("/tmp/hello/world", "/tmp/example")
|
129
|
+
#
|
130
|
+
# The above will copy, recursively, /tmp/hello/world into
|
131
|
+
# /tmp/example/hello/world
|
132
|
+
def clone(source, destination)
|
133
|
+
logger.debug("Cloning path", :source => source, :destination => destination)
|
134
|
+
# Edge case check; abort if the temporary directory is the source.
|
135
|
+
# If the temporary dir is the same path as the source, it causes
|
136
|
+
# fpm to recursively (and forever) copy the staging directory by
|
137
|
+
# accident (#542).
|
138
|
+
if File.expand_path(source) == File.expand_path(::Dir.tmpdir)
|
139
|
+
raise FPM::InvalidPackageConfiguration,
|
140
|
+
"A source directory cannot be the root of your temporary " \
|
141
|
+
"directory (#{::Dir.tmpdir}). fpm uses the temporary directory " \
|
142
|
+
"to stage files during packaging, so this setting would have " \
|
143
|
+
"caused fpm to loop creating staging directories and copying " \
|
144
|
+
"them into your package! Oops! If you are confused, maybe you could " \
|
145
|
+
"check your TMPDIR, TMP, or TEMP environment variables?"
|
146
|
+
end
|
147
|
+
|
148
|
+
# For single file copies, permit file destinations
|
149
|
+
fileinfo = File.lstat(source)
|
150
|
+
if fileinfo.file? && !File.directory?(destination)
|
151
|
+
if destination[-1,1] == "/"
|
152
|
+
copy(source, File.join(destination, source))
|
153
|
+
else
|
154
|
+
copy(source, destination)
|
155
|
+
end
|
156
|
+
elsif fileinfo.symlink?
|
157
|
+
copy(source, File.join(destination, source))
|
158
|
+
else
|
159
|
+
# Copy all files from 'path' into staging_path
|
160
|
+
Find.find(source) do |path|
|
161
|
+
target = File.join(destination, path)
|
162
|
+
copy(path, target)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end # def clone
|
166
|
+
|
167
|
+
# Copy a path.
|
168
|
+
#
|
169
|
+
# Files will be hardlinked if possible, but copied otherwise.
|
170
|
+
# Symlinks should be copied as symlinks.
|
171
|
+
def copy(source, destination)
|
172
|
+
logger.debug("Copying path", :source => source, :destination => destination)
|
173
|
+
directory = File.dirname(destination)
|
174
|
+
# lstat to follow symlinks
|
175
|
+
dstat = File.stat(directory) rescue nil
|
176
|
+
if dstat.nil?
|
177
|
+
FileUtils.mkdir_p(directory, :mode => 0755)
|
178
|
+
elsif dstat.directory?
|
179
|
+
# do nothing, it's already a directory!
|
180
|
+
else
|
181
|
+
# It exists and is not a directory. This is probably a user error or a bug.
|
182
|
+
readable_path = directory.gsub(staging_path, "")
|
183
|
+
logger.error("You wanted to copy a file into a directory, but that's not a directory, it's a file!", :path => readable_path, :stat => dstat)
|
184
|
+
raise FPM::InvalidPackageConfiguration, "Tried to treat #{readable_path} like a directory, but it's a file!"
|
185
|
+
end
|
186
|
+
|
187
|
+
if File.directory?(source)
|
188
|
+
if !File.symlink?(source)
|
189
|
+
# Create a directory if this path is a directory
|
190
|
+
logger.debug("Creating", :directory => destination)
|
191
|
+
if !File.directory?(destination)
|
192
|
+
FileUtils.mkdir(destination)
|
193
|
+
end
|
194
|
+
else
|
195
|
+
# Linking symlinked directories causes a hardlink to be created, which
|
196
|
+
# results in the source directory being wiped out during cleanup,
|
197
|
+
# so copy the symlink.
|
198
|
+
logger.debug("Copying symlinked directory", :source => source,
|
199
|
+
:destination => destination)
|
200
|
+
FileUtils.copy_entry(source, destination)
|
201
|
+
end
|
202
|
+
else
|
203
|
+
# Otherwise try copying the file.
|
204
|
+
begin
|
205
|
+
logger.debug("Linking", :source => source, :destination => destination)
|
206
|
+
File.link(source, destination)
|
207
|
+
rescue Errno::ENOENT, Errno::EXDEV, Errno::EPERM
|
208
|
+
# Hardlink attempt failed, copy it instead
|
209
|
+
logger.debug("Copying", :source => source, :destination => destination)
|
210
|
+
copy_entry(source, destination)
|
211
|
+
rescue Errno::EEXIST
|
212
|
+
sane_path = destination.gsub(staging_path, "")
|
213
|
+
logger.error("Cannot copy file, the destination path is probably a directory and I attempted to write a file.", :path => sane_path, :staging => staging_path)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
copy_metadata(source, destination)
|
218
|
+
end # def copy
|
219
|
+
|
220
|
+
public(:input, :output)
|
221
|
+
end # class FPM::Package::Dir
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "fpm/package"
|
2
|
+
require "backports"
|
3
|
+
|
4
|
+
# Empty Package type. For strict/meta/virtual package creation
|
5
|
+
|
6
|
+
class FPM::Package::Empty < FPM::Package
|
7
|
+
def output(output_path)
|
8
|
+
logger.warn("Your package has gone into the void.")
|
9
|
+
end
|
10
|
+
def to_s(fmt)
|
11
|
+
return ""
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "backports" # gem backports
|
2
|
+
require "fpm/package"
|
3
|
+
require "fpm/util"
|
4
|
+
require "digest"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
class FPM::Package::FreeBSD < FPM::Package
|
8
|
+
SCRIPT_MAP = {
|
9
|
+
:before_install => "pre-install",
|
10
|
+
:after_install => "post-install",
|
11
|
+
:before_remove => "pre-deinstall",
|
12
|
+
:after_remove => "post-deinstall",
|
13
|
+
} unless defined?(SCRIPT_MAP)
|
14
|
+
|
15
|
+
def self.default_abi
|
16
|
+
abi_name = %x{uname -s}.chomp
|
17
|
+
abi_version = %x{uname -r}.chomp.split(".")[0]
|
18
|
+
abi_arch = %x{uname -m}.chomp
|
19
|
+
|
20
|
+
[abi_name, abi_version, abi_arch].join(":")
|
21
|
+
end
|
22
|
+
|
23
|
+
option "--abi", "ABI",
|
24
|
+
"Sets the FreeBSD abi pkg field to specify binary compatibility.",
|
25
|
+
:default => default_abi
|
26
|
+
|
27
|
+
option "--origin", "ABI",
|
28
|
+
"Sets the FreeBSD 'origin' pkg field",
|
29
|
+
:default => "fpm/<name>"
|
30
|
+
|
31
|
+
def output(output_path)
|
32
|
+
# See https://github.com/jordansissel/fpm/issues/1090
|
33
|
+
# require xz later, because this triggers a load of liblzma.so.5 that is
|
34
|
+
# unavailable on older CentOS/RH distros.
|
35
|
+
require "xz"
|
36
|
+
output_check(output_path)
|
37
|
+
|
38
|
+
# Build the packaging metadata files.
|
39
|
+
checksums = {}
|
40
|
+
self.files.each do |f|
|
41
|
+
path = staging_path(f)
|
42
|
+
if File.symlink?(path)
|
43
|
+
checksums[f] = "-"
|
44
|
+
elsif File.file?(path)
|
45
|
+
checksums[f] = Digest::SHA256.file(path).hexdigest
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
pkg_origin = attributes[:freebsd_origin]
|
50
|
+
if pkg_origin == "fpm/<name>" # fill in default
|
51
|
+
pkg_origin = "fpm/#{name}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Follow similar rules to these used in ``to_s_fullversion`` method.
|
55
|
+
# FIXME: maybe epoch should also be introduced somehow ("#{version},#{epoch})?
|
56
|
+
# should it go to pkgdata["version"] or to another place?
|
57
|
+
# https://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html
|
58
|
+
pkg_version = (iteration and (iteration.to_i > 0)) ? "#{version}-#{iteration}" : "#{version}"
|
59
|
+
|
60
|
+
pkgdata = {
|
61
|
+
"abi" => attributes[:freebsd_abi],
|
62
|
+
"name" => name,
|
63
|
+
"version" => pkg_version,
|
64
|
+
"comment" => description,
|
65
|
+
"desc" => description,
|
66
|
+
"origin" => pkg_origin,
|
67
|
+
"maintainer" => maintainer,
|
68
|
+
"www" => url,
|
69
|
+
# prefix is required, but it doesn't seem to matter
|
70
|
+
"prefix" => "/",
|
71
|
+
}
|
72
|
+
|
73
|
+
# Write +COMPACT_MANIFEST, without the "files" section.
|
74
|
+
File.open(staging_path("+COMPACT_MANIFEST"), "w+") do |file|
|
75
|
+
file.write(pkgdata.to_json + "\n")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Populate files + checksums, then write +MANIFEST.
|
79
|
+
pkgdata["files"] = {}
|
80
|
+
checksums.each do |f, shasum|
|
81
|
+
# pkg expands % URL-style escapes, so make sure to escape % as %25
|
82
|
+
pkgdata["files"]["/" + f.gsub("%", "%25")] = shasum
|
83
|
+
end
|
84
|
+
|
85
|
+
# Populate scripts
|
86
|
+
pkgdata["scripts"] = {}
|
87
|
+
scripts.each do |name, data|
|
88
|
+
pkgdata["scripts"][SCRIPT_MAP[name]] = data
|
89
|
+
end
|
90
|
+
|
91
|
+
File.open(staging_path("+MANIFEST"), "w+") do |file|
|
92
|
+
file.write(pkgdata.to_json + "\n")
|
93
|
+
end
|
94
|
+
|
95
|
+
# Create the .txz package archive from the files in staging_path.
|
96
|
+
File.open(output_path, "wb") do |file|
|
97
|
+
XZ::StreamWriter.new(file) do |xz|
|
98
|
+
FPM::Util::TarWriter.new(xz) do |tar|
|
99
|
+
# The manifests must come first for pkg.
|
100
|
+
add_path(tar, "+COMPACT_MANIFEST",
|
101
|
+
File.join(staging_path, "+COMPACT_MANIFEST"))
|
102
|
+
add_path(tar, "+MANIFEST",
|
103
|
+
File.join(staging_path, "+MANIFEST"))
|
104
|
+
|
105
|
+
checksums.keys.each do |path|
|
106
|
+
add_path(tar, "/" + path, File.join(staging_path, path))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end # def output
|
112
|
+
|
113
|
+
def add_path(tar, tar_path, path)
|
114
|
+
stat = File.lstat(path)
|
115
|
+
if stat.directory?
|
116
|
+
tar.mkdir(tar_path, stat.mode)
|
117
|
+
elsif stat.symlink?
|
118
|
+
tar.add_symlink(tar_path, File.readlink(path), stat.mode)
|
119
|
+
else
|
120
|
+
tar.add_file_simple(tar_path, stat.mode, stat.size) do |io|
|
121
|
+
File.open(path) do |fd|
|
122
|
+
chunk = nil
|
123
|
+
size = 0
|
124
|
+
while chunk = fd.read(16384) do
|
125
|
+
size += io.write(chunk)
|
126
|
+
end
|
127
|
+
if size != stat.size
|
128
|
+
raise "Failed to add #{path} to the archive; expected to " +
|
129
|
+
"write #{stat.size} bytes, only wrote #{size}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end # tar.tar.add_file_simple
|
133
|
+
end
|
134
|
+
end # def add_path
|
135
|
+
|
136
|
+
def to_s_extension; "txz"; end
|
137
|
+
|
138
|
+
def to_s_fullversion()
|
139
|
+
# iteration (PORTREVISION on FreeBSD) shall be appended only(?) if non-zero.
|
140
|
+
# https://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html
|
141
|
+
(iteration and (iteration.to_i > 0)) ? "#{version}_#{iteration}" : "#{version}"
|
142
|
+
end
|
143
|
+
|
144
|
+
def to_s(format=nil)
|
145
|
+
return super(format.nil? ? "NAME-FULLVERSION.EXTENSION" : format)
|
146
|
+
end # def to_s
|
147
|
+
end # class FPM::Package::FreeBSD
|
@@ -0,0 +1,243 @@
|
|
1
|
+
require "fpm/namespace"
|
2
|
+
require "fpm/package"
|
3
|
+
require "rubygems"
|
4
|
+
require "fileutils"
|
5
|
+
require "fpm/util"
|
6
|
+
require "yaml"
|
7
|
+
|
8
|
+
# A rubygems package.
|
9
|
+
#
|
10
|
+
# This does not currently support 'output'
|
11
|
+
#
|
12
|
+
# The following attributes are supported:
|
13
|
+
#
|
14
|
+
# * :gem_bin_path
|
15
|
+
# * :gem_package_name_prefix
|
16
|
+
# * :gem_gem
|
17
|
+
class FPM::Package::Gem < FPM::Package
|
18
|
+
# Flags '--foo' will be accessable as attributes[:gem_foo]
|
19
|
+
option "--bin-path", "DIRECTORY", "The directory to install gem executables"
|
20
|
+
option "--package-prefix", "NAMEPREFIX",
|
21
|
+
"(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
|
22
|
+
"name with." do |value|
|
23
|
+
logger = Cabin::Channel.get
|
24
|
+
logger.warn("Using deprecated flag: --package-prefix. Please use " \
|
25
|
+
"--package-name-prefix")
|
26
|
+
value
|
27
|
+
end
|
28
|
+
option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
|
29
|
+
"name with.", :default => "rubygem"
|
30
|
+
option "--gem", "PATH_TO_GEM",
|
31
|
+
"The path to the 'gem' tool (defaults to 'gem' and searches " \
|
32
|
+
"your $PATH)", :default => "gem"
|
33
|
+
option "--shebang", "SHEBANG",
|
34
|
+
"Replace the shebang in the executables in the bin path with a " \
|
35
|
+
"custom string", :default => nil
|
36
|
+
option "--fix-name", :flag, "Should the target package name be prefixed?",
|
37
|
+
:default => true
|
38
|
+
option "--fix-dependencies", :flag, "Should the package dependencies be " \
|
39
|
+
"prefixed?", :default => true
|
40
|
+
option "--env-shebang", :flag, "Should the target package have the " \
|
41
|
+
"shebang rewritten to use env?", :default => true
|
42
|
+
|
43
|
+
option "--prerelease", :flag, "Allow prerelease versions of a gem", :default => false
|
44
|
+
option "--disable-dependency", "gem_name",
|
45
|
+
"The gem name to remove from dependency list",
|
46
|
+
:multivalued => true, :attribute_name => :gem_disable_dependencies
|
47
|
+
|
48
|
+
option "--version-bins", :flag, "Append the version to the bins", :default => false
|
49
|
+
|
50
|
+
def input(gem)
|
51
|
+
# 'arg' is the name of the rubygem we should unpack.
|
52
|
+
path_to_gem = download_if_necessary(gem, version)
|
53
|
+
|
54
|
+
# Got a good gem now (downloaded or otherwise)
|
55
|
+
#
|
56
|
+
# 1. unpack it into staging_path
|
57
|
+
# 2. take the metadata from it and update our wonderful package with it.
|
58
|
+
load_package_info(path_to_gem)
|
59
|
+
install_to_staging(path_to_gem)
|
60
|
+
end # def input
|
61
|
+
|
62
|
+
def download_if_necessary(gem, gem_version)
|
63
|
+
path = gem
|
64
|
+
if !File.exist?(path)
|
65
|
+
path = download(gem, gem_version)
|
66
|
+
end
|
67
|
+
|
68
|
+
logger.info("Using gem file", :path => path)
|
69
|
+
return path
|
70
|
+
end # def download_if_necessary
|
71
|
+
|
72
|
+
def download(gem_name, gem_version=nil)
|
73
|
+
|
74
|
+
logger.info("Trying to download", :gem => gem_name, :version => gem_version)
|
75
|
+
|
76
|
+
gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
|
77
|
+
|
78
|
+
gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
|
79
|
+
gem_fetch += ["--version", gem_version] if gem_version
|
80
|
+
|
81
|
+
download_dir = build_path(gem_name)
|
82
|
+
FileUtils.mkdir(download_dir) unless File.directory?(download_dir)
|
83
|
+
|
84
|
+
::Dir.chdir(download_dir) do |dir|
|
85
|
+
logger.debug("Downloading in directory #{dir}")
|
86
|
+
safesystem(*gem_fetch)
|
87
|
+
end
|
88
|
+
|
89
|
+
gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
|
90
|
+
|
91
|
+
if gem_files.length != 1
|
92
|
+
raise "Unexpected number of gem files in #{download_dir}, #{gem_files.length} should be 1"
|
93
|
+
end
|
94
|
+
|
95
|
+
return gem_files.first
|
96
|
+
end # def download
|
97
|
+
|
98
|
+
def load_package_info(gem_path)
|
99
|
+
|
100
|
+
spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml})
|
101
|
+
|
102
|
+
if !attributes[:gem_package_prefix].nil?
|
103
|
+
attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix]
|
104
|
+
end
|
105
|
+
|
106
|
+
# name prefixing is optional, if enabled, a name 'foo' will become
|
107
|
+
# 'rubygem-foo' (depending on what the gem_package_name_prefix is)
|
108
|
+
self.name = spec.name
|
109
|
+
if attributes[:gem_fix_name?]
|
110
|
+
self.name = fix_name(spec.name)
|
111
|
+
end
|
112
|
+
|
113
|
+
#self.name = [attributes[:gem_package_name_prefix], spec.name].join("-")
|
114
|
+
self.license = (spec.license or "no license listed in #{File.basename(gem_path)}")
|
115
|
+
|
116
|
+
# expand spec's version to match RationalVersioningPolicy to prevent cases
|
117
|
+
# where missing 'build' number prevents correct dependency resolution by target
|
118
|
+
# package manager. Ie. for dpkg 1.1 != 1.1.0
|
119
|
+
m = spec.version.to_s.scan(/(\d+)\.?/)
|
120
|
+
self.version = m.flatten.fill('0', m.length..2).join('.')
|
121
|
+
|
122
|
+
self.vendor = spec.author
|
123
|
+
self.url = spec.homepage
|
124
|
+
self.category = "Languages/Development/Ruby"
|
125
|
+
|
126
|
+
# if the gemspec has C extensions defined, then this should be a 'native' arch.
|
127
|
+
if !spec.extensions.empty?
|
128
|
+
self.architecture = "native"
|
129
|
+
else
|
130
|
+
self.architecture = "all"
|
131
|
+
end
|
132
|
+
|
133
|
+
# make sure we have a description
|
134
|
+
description_options = [ spec.description, spec.summary, "#{spec.name} - no description given" ]
|
135
|
+
self.description = description_options.find { |d| !(d.nil? or d.strip.empty?) }
|
136
|
+
|
137
|
+
# Upstream rpms seem to do this, might as well share.
|
138
|
+
# TODO(sissel): Figure out how to hint this only to rpm?
|
139
|
+
# maybe something like attributes[:rpm_provides] for rpm specific stuff?
|
140
|
+
# Or just ignore it all together.
|
141
|
+
#self.provides << "rubygem(#{self.name})"
|
142
|
+
|
143
|
+
# By default, we'll usually automatically provide this, but in the case that we are
|
144
|
+
# composing multiple packages, it's best to explicitly include it in the provides list.
|
145
|
+
self.provides << "#{self.name} = #{self.version}"
|
146
|
+
|
147
|
+
if !attributes[:no_auto_depends?]
|
148
|
+
spec.runtime_dependencies.map do |dep|
|
149
|
+
# rubygems 1.3.5 doesn't have 'Gem::Dependency#requirement'
|
150
|
+
if dep.respond_to?(:requirement)
|
151
|
+
reqs = dep.requirement.to_s
|
152
|
+
else
|
153
|
+
reqs = dep.version_requirements
|
154
|
+
end
|
155
|
+
|
156
|
+
# Some reqs can be ">= a, < b" versions, let's handle that.
|
157
|
+
reqs.to_s.split(/, */).each do |req|
|
158
|
+
if attributes[:gem_disable_dependencies]
|
159
|
+
next if attributes[:gem_disable_dependencies].include?(dep.name)
|
160
|
+
end
|
161
|
+
|
162
|
+
if attributes[:gem_fix_dependencies?]
|
163
|
+
name = fix_name(dep.name)
|
164
|
+
else
|
165
|
+
name = dep.name
|
166
|
+
end
|
167
|
+
self.dependencies << "#{name} #{req}"
|
168
|
+
end
|
169
|
+
end # runtime_dependencies
|
170
|
+
end #no_auto_depends
|
171
|
+
end # def load_package_info
|
172
|
+
|
173
|
+
def install_to_staging(gem_path)
|
174
|
+
if attributes.include?(:prefix) && ! attributes[:prefix].nil?
|
175
|
+
installdir = "#{staging_path}/#{attributes[:prefix]}"
|
176
|
+
else
|
177
|
+
gemdir = safesystemout(*[attributes[:gem_gem], 'env', 'gemdir']).chomp
|
178
|
+
installdir = File.join(staging_path, gemdir)
|
179
|
+
end
|
180
|
+
|
181
|
+
::FileUtils.mkdir_p(installdir)
|
182
|
+
# TODO(sissel): Allow setting gem tool path
|
183
|
+
args = [attributes[:gem_gem], "install", "--quiet", "--no-ri", "--no-rdoc",
|
184
|
+
"--no-user-install", "--install-dir", installdir, "--ignore-dependencies"]
|
185
|
+
if attributes[:gem_env_shebang?]
|
186
|
+
args += ["-E"]
|
187
|
+
end
|
188
|
+
|
189
|
+
if attributes.include?(:gem_bin_path) && ! attributes[:gem_bin_path].nil?
|
190
|
+
bin_path = File.join(staging_path, attributes[:gem_bin_path])
|
191
|
+
else
|
192
|
+
gem_env = safesystemout(*[attributes[:gem_gem], 'env']).split("\n")
|
193
|
+
gem_bin = gem_env.select{ |line| line =~ /EXECUTABLE DIRECTORY/ }.first.split(': ').last
|
194
|
+
bin_path = File.join(staging_path, gem_bin)
|
195
|
+
end
|
196
|
+
|
197
|
+
args += ["--bindir", bin_path]
|
198
|
+
::FileUtils.mkdir_p(bin_path)
|
199
|
+
args << gem_path
|
200
|
+
safesystem(*args)
|
201
|
+
|
202
|
+
# Replace the shebangs in the executables
|
203
|
+
if attributes[:gem_shebang]
|
204
|
+
::Dir.entries(bin_path).each do |file_name|
|
205
|
+
# exclude . and ..
|
206
|
+
next if ['.', '..'].include?(file_name)
|
207
|
+
# exclude everything which is not a file
|
208
|
+
file_path = File.join(bin_path, file_name)
|
209
|
+
next unless File.ftype(file_path) == 'file'
|
210
|
+
# replace shebang in files if there is one
|
211
|
+
file = File.read(file_path)
|
212
|
+
if file.gsub!(/\A#!.*$/, "#!#{attributes[:gem_shebang]}")
|
213
|
+
File.open(file_path, 'w'){|f| f << file}
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Delete bin_path if it's empty, and any empty parents (#612)
|
219
|
+
# Above, we mkdir_p bin_path because rubygems aborts if the parent
|
220
|
+
# directory doesn't exist, for example:
|
221
|
+
# ERROR: While executing gem ... (Errno::ENOENT)
|
222
|
+
# No such file or directory - /tmp/something/weird/bin
|
223
|
+
tmp = bin_path
|
224
|
+
while ::Dir.entries(tmp).size == 2 || tmp == "/" # just [ "..", "." ] is an empty directory
|
225
|
+
logger.info("Deleting empty bin_path", :path => tmp)
|
226
|
+
::Dir.rmdir(tmp)
|
227
|
+
tmp = File.dirname(tmp)
|
228
|
+
end
|
229
|
+
if attributes[:gem_version_bins?] and File.directory?(bin_path)
|
230
|
+
(::Dir.entries(bin_path) - ['.','..']).each do |bin|
|
231
|
+
FileUtils.mv("#{bin_path}/#{bin}", "#{bin_path}/#{bin}-#{self.version}")
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end # def install_to_staging
|
235
|
+
|
236
|
+
# Sanitize package name.
|
237
|
+
# This prefixes the package name with 'rubygem' (but depends on the attribute
|
238
|
+
# :gem_package_name_prefix
|
239
|
+
def fix_name(name)
|
240
|
+
return [attributes[:gem_package_name_prefix], name].join("-")
|
241
|
+
end # def fix_name
|
242
|
+
public(:input, :output)
|
243
|
+
end # class FPM::Package::Gem
|