cure-fpm 1.3.3b → 1.6.0b
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELIST +73 -0
- data/CONTRIBUTORS +1 -1
- data/LICENSE +1 -1
- data/lib/fpm.rb +2 -0
- data/lib/fpm/command.rb +100 -50
- data/lib/fpm/package.rb +42 -28
- data/lib/fpm/package/apk.rb +510 -0
- data/lib/fpm/package/cpan.rb +50 -25
- data/lib/fpm/package/deb.rb +211 -47
- data/lib/fpm/package/dir.rb +29 -28
- data/lib/fpm/package/empty.rb +6 -0
- data/lib/fpm/package/freebsd.rb +144 -0
- data/lib/fpm/package/gem.rb +5 -5
- data/lib/fpm/package/npm.rb +2 -2
- data/lib/fpm/package/osxpkg.rb +8 -7
- data/lib/fpm/package/p5p.rb +124 -0
- data/lib/fpm/package/pacman.rb +399 -0
- data/lib/fpm/package/pleaserun.rb +63 -0
- data/lib/fpm/package/pyfpm/get_metadata.py +9 -1
- data/lib/fpm/package/python.rb +19 -7
- data/lib/fpm/package/rpm.rb +58 -18
- data/lib/fpm/package/sh.rb +1 -7
- data/lib/fpm/package/solaris.rb +1 -1
- data/lib/fpm/package/tar.rb +14 -2
- data/lib/fpm/package/virtualenv.rb +145 -0
- data/lib/fpm/package/zip.rb +1 -1
- data/lib/fpm/rake_task.rb +60 -0
- data/lib/fpm/util.rb +176 -48
- data/lib/fpm/util/tar_writer.rb +80 -0
- data/lib/fpm/version.rb +1 -1
- data/templates/deb/postinst_upgrade.sh.erb +33 -2
- data/templates/deb/postrm_upgrade.sh.erb +10 -1
- data/templates/deb/preinst_upgrade.sh.erb +11 -2
- data/templates/deb/prerm_upgrade.sh.erb +14 -2
- 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/rpm.erb +38 -6
- data/templates/sh.erb +38 -3
- metadata +81 -9
data/lib/fpm/package/zip.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
require "fpm/namespace"
|
2
|
+
require "ostruct"
|
3
|
+
require "rake"
|
4
|
+
require "rake/tasklib"
|
5
|
+
|
6
|
+
class FPM::RakeTask < Rake::TaskLib
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(package_name, opts = {}, &block)
|
10
|
+
@options = OpenStruct.new(:name => package_name.to_s)
|
11
|
+
@source, @target = opts.values_at(:source, :target).map(&:to_s)
|
12
|
+
@directory = File.expand_path(opts[:directory].to_s)
|
13
|
+
|
14
|
+
(@source.empty? || @target.empty? || options.name.empty?) &&
|
15
|
+
abort("Must specify package name, source and output")
|
16
|
+
|
17
|
+
desc "Package #{@name}" unless ::Rake.application.last_comment
|
18
|
+
|
19
|
+
task(options.name) do |_, task_args|
|
20
|
+
block.call(*[options, task_args].first(block.arity)) if block_given?
|
21
|
+
abort("Must specify args") unless options.respond_to?(:args)
|
22
|
+
@args = options.delete_field(:args)
|
23
|
+
run_cli
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def parsed_options
|
30
|
+
options.to_h.map do |option, value|
|
31
|
+
opt = option.to_s.tr("_", "-")
|
32
|
+
|
33
|
+
case
|
34
|
+
when value.is_a?(String), value.is_a?(Symbol)
|
35
|
+
%W(--#{opt} #{value})
|
36
|
+
when value.is_a?(Array)
|
37
|
+
value.map { |v| %W(--#{opt} #{v}) }
|
38
|
+
when value.is_a?(TrueClass)
|
39
|
+
"--#{opt}"
|
40
|
+
when value.is_a?(FalseClass)
|
41
|
+
"--no-#{opt}"
|
42
|
+
else
|
43
|
+
fail TypeError, "Unexpected type: #{value.class}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_cli
|
49
|
+
require "fpm"
|
50
|
+
require "fpm/command"
|
51
|
+
|
52
|
+
args = %W(-t #{@target} -s #{@source} -C #{@directory})
|
53
|
+
args << parsed_options
|
54
|
+
args << @args
|
55
|
+
|
56
|
+
args.flatten!.compact!
|
57
|
+
|
58
|
+
abort 'FPM failed!' unless FPM::Command.new("fpm").run(args) == 0
|
59
|
+
end
|
60
|
+
end
|
data/lib/fpm/util.rb
CHANGED
@@ -24,6 +24,8 @@ module FPM::Util
|
|
24
24
|
|
25
25
|
# Is the given program in the system's PATH?
|
26
26
|
def program_in_path?(program)
|
27
|
+
# return false if path is not set
|
28
|
+
return false unless ENV['PATH']
|
27
29
|
# Scan path to find the executable
|
28
30
|
# Do this to help the user get a better error message.
|
29
31
|
envpath = ENV["PATH"].split(":")
|
@@ -33,52 +35,168 @@ module FPM::Util
|
|
33
35
|
def program_exists?(program)
|
34
36
|
# Scan path to find the executable
|
35
37
|
# Do this to help the user get a better error message.
|
36
|
-
return program_in_path?(program) if !program.include?("/")
|
38
|
+
return program_in_path?(program) if !program.include?("/")
|
37
39
|
return File.executable?(program)
|
38
40
|
end # def program_exists?
|
39
41
|
|
40
42
|
def default_shell
|
41
|
-
shell = ENV["SHELL"]
|
43
|
+
shell = ENV["SHELL"]
|
42
44
|
return "/bin/sh" if shell.nil? || shell.empty?
|
43
45
|
return shell
|
44
46
|
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
############################################################################
|
49
|
+
# execmd([env,] cmd [,opts])
|
50
|
+
#
|
51
|
+
# Execute a command as a child process. The function allows to:
|
52
|
+
#
|
53
|
+
# - pass environment variables to child process,
|
54
|
+
# - communicate with stdin, stdout and stderr of the child process via pipes,
|
55
|
+
# - retrieve execution's status code.
|
56
|
+
#
|
57
|
+
# ---- EXAMPLE 1 (simple execution)
|
58
|
+
#
|
59
|
+
# if execmd(['which', 'python']) == 0
|
60
|
+
# p "Python is installed"
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# ---- EXAMPLE 2 (custom environment variables)
|
64
|
+
#
|
65
|
+
# execmd({:PYTHONPATH=>'/home/me/foo'}, [ 'python', '-m', 'bar'])
|
66
|
+
#
|
67
|
+
# ---- EXAMPLE 3 (communicating via stdin, stdout and stderr)
|
68
|
+
#
|
69
|
+
# script = <<PYTHON
|
70
|
+
# import sys
|
71
|
+
# sys.stdout.write("normal output\n")
|
72
|
+
# sys.stdout.write("narning or error\n")
|
73
|
+
# PYTHON
|
74
|
+
# status = execmd('python') do |stdin,stdout,stderr|
|
75
|
+
# stdin.write(script)
|
76
|
+
# stdin.close
|
77
|
+
# p "STDOUT: #{stdout.read}"
|
78
|
+
# p "STDERR: #{stderr.read}"
|
79
|
+
# end
|
80
|
+
# p "STATUS: #{status}"
|
81
|
+
#
|
82
|
+
# ---- EXAMPLE 4 (additional options)
|
83
|
+
#
|
84
|
+
# execmd(['which', 'python'], :process=>true, :stdin=>false, :stderr=>false) do |process,stdout|
|
85
|
+
# p = stdout.read.chomp
|
86
|
+
# process.wait
|
87
|
+
# if (x = process.exit_code) == 0
|
88
|
+
# p "PYTHON: #{p}"
|
89
|
+
# else
|
90
|
+
# p "ERROR: #{x}"
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
#
|
95
|
+
# OPTIONS:
|
96
|
+
#
|
97
|
+
# :process (default: false) -- pass process object as the first argument the to block,
|
98
|
+
# :stdin (default: true) -- pass stdin object of the child process to the block for writting,
|
99
|
+
# :stdout (default: true) -- pass stdout object of the child process to the block for reading,
|
100
|
+
# :stderr (default: true) -- pass stderr object of the child process to the block for reading,
|
101
|
+
#
|
102
|
+
def execmd(*args)
|
103
|
+
i = 0
|
104
|
+
if i < args.size
|
105
|
+
if args[i].kind_of?(Hash)
|
106
|
+
# args[0] may contain environment variables
|
107
|
+
env = args[i]
|
108
|
+
i += 1
|
109
|
+
else
|
110
|
+
env = Hash[]
|
111
|
+
end
|
52
112
|
end
|
53
|
-
program = args[0]
|
54
113
|
|
55
|
-
if
|
114
|
+
if i < args.size
|
115
|
+
if args[i].kind_of?(Array)
|
116
|
+
args2 = args[i]
|
117
|
+
else
|
118
|
+
args2 = [ args[i] ]
|
119
|
+
end
|
120
|
+
program = args2[0]
|
121
|
+
i += 1
|
122
|
+
else
|
123
|
+
raise ArgumentError.new("missing argument: cmd")
|
124
|
+
end
|
125
|
+
|
126
|
+
if i < args.size
|
127
|
+
if args[i].kind_of?(Hash)
|
128
|
+
opts = Hash[args[i].map {|k,v| [k.to_sym, v]} ]
|
129
|
+
i += 1
|
130
|
+
end
|
131
|
+
else
|
132
|
+
opts = Hash[]
|
133
|
+
end
|
134
|
+
|
135
|
+
opts[:process] = false unless opts.include?(:process)
|
136
|
+
opts[:stdin] = true unless opts.include?(:stdin)
|
137
|
+
opts[:stdout] = true unless opts.include?(:stdout)
|
138
|
+
opts[:stderr] = true unless opts.include?(:stderr)
|
139
|
+
|
140
|
+
if !program.include?("/") and !program_in_path?(program)
|
56
141
|
raise ExecutableNotFound.new(program)
|
57
142
|
end
|
58
143
|
|
59
|
-
logger.debug("Running command", :args =>
|
144
|
+
logger.debug("Running command", :args => args2)
|
60
145
|
|
61
|
-
# Create a pair of pipes to connect the
|
62
|
-
# invoked process to the cabin logger
|
63
146
|
stdout_r, stdout_w = IO.pipe
|
64
147
|
stderr_r, stderr_w = IO.pipe
|
65
148
|
|
66
|
-
process
|
149
|
+
process = ChildProcess.build(*args2)
|
150
|
+
process.environment.merge!(env)
|
151
|
+
|
67
152
|
process.io.stdout = stdout_w
|
68
153
|
process.io.stderr = stderr_w
|
69
154
|
|
155
|
+
if block_given? and opts[:stdin]
|
156
|
+
process.duplex = true
|
157
|
+
end
|
158
|
+
|
70
159
|
process.start
|
160
|
+
|
71
161
|
stdout_w.close; stderr_w.close
|
72
|
-
logger.debug(
|
73
|
-
|
74
|
-
|
75
|
-
|
162
|
+
logger.debug("Process is running", :pid => process.pid)
|
163
|
+
if block_given?
|
164
|
+
args3 = []
|
165
|
+
args3.push(process) if opts[:process]
|
166
|
+
args3.push(process.io.stdin) if opts[:stdin]
|
167
|
+
args3.push(stdout_r) if opts[:stdout]
|
168
|
+
args3.push(stderr_r) if opts[:stderr]
|
169
|
+
|
170
|
+
yield(*args3)
|
171
|
+
|
172
|
+
process.io.stdin.close if opts[:stdin] and not process.io.stdin.closed?
|
173
|
+
stdout_r.close unless stdout_r.closed?
|
174
|
+
stderr_r.close unless stderr_r.closed?
|
175
|
+
else
|
176
|
+
# Log both stdout and stderr as 'info' because nobody uses stderr for
|
177
|
+
# actually reporting errors and as a result 'stderr' is a misnomer.
|
178
|
+
logger.pipe(stdout_r => :info, stderr_r => :info)
|
179
|
+
end
|
76
180
|
|
77
|
-
process.wait
|
78
|
-
|
181
|
+
process.wait if process.alive?
|
182
|
+
|
183
|
+
return process.exit_code
|
184
|
+
end # def execmd
|
185
|
+
|
186
|
+
# Run a command safely in a way that gets reports useful errors.
|
187
|
+
def safesystem(*args)
|
188
|
+
# ChildProcess isn't smart enough to run a $SHELL if there's
|
189
|
+
# spaces in the first arg and there's only 1 arg.
|
190
|
+
if args.size == 1
|
191
|
+
args = [ default_shell, "-c", args[0] ]
|
192
|
+
end
|
193
|
+
program = args[0]
|
194
|
+
|
195
|
+
exit_code = execmd(args)
|
196
|
+
success = (exit_code == 0)
|
79
197
|
|
80
198
|
if !success
|
81
|
-
raise ProcessFailed.new("#{program} failed (exit code #{
|
199
|
+
raise ProcessFailed.new("#{program} failed (exit code #{exit_code})" \
|
82
200
|
". Full command was:#{args.inspect}")
|
83
201
|
end
|
84
202
|
return success
|
@@ -95,29 +213,16 @@ module FPM::Util
|
|
95
213
|
raise ExecutableNotFound.new(program)
|
96
214
|
end
|
97
215
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
process = ChildProcess.build(*args)
|
104
|
-
process.io.stdout = stdout_w
|
105
|
-
process.io.stderr = stderr_w
|
106
|
-
|
107
|
-
process.start
|
108
|
-
stdout_w.close; stderr_w.close
|
109
|
-
stdout_r_str = stdout_r.read
|
110
|
-
stdout_r.close; stderr_r.close
|
111
|
-
logger.debug("Process is running", :pid => process.pid)
|
112
|
-
|
113
|
-
process.wait
|
114
|
-
success = (process.exit_code == 0)
|
216
|
+
stdout_r_str = nil
|
217
|
+
exit_code = execmd(args, :stdin=>false, :stderr=>false) do |stdout|
|
218
|
+
stdout_r_str = stdout.read
|
219
|
+
end
|
220
|
+
success = (exit_code == 0)
|
115
221
|
|
116
222
|
if !success
|
117
|
-
raise ProcessFailed.new("#{program} failed (exit code #{
|
223
|
+
raise ProcessFailed.new("#{program} failed (exit code #{exit_code})" \
|
118
224
|
". Full command was:#{args.inspect}")
|
119
225
|
end
|
120
|
-
|
121
226
|
return stdout_r_str
|
122
227
|
end # def safesystemout
|
123
228
|
|
@@ -133,17 +238,14 @@ module FPM::Util
|
|
133
238
|
system("#{tar} > /dev/null 2> /dev/null")
|
134
239
|
return tar unless $?.exitstatus == 127
|
135
240
|
end
|
241
|
+
when "FreeBSD"
|
242
|
+
# use gnutar instead
|
243
|
+
return "gtar"
|
136
244
|
else
|
137
245
|
return "tar"
|
138
246
|
end
|
139
247
|
end # def tar_cmd
|
140
248
|
|
141
|
-
# Run a block with a value.
|
142
|
-
# Useful in lieu of assigning variables
|
143
|
-
def with(value, &block)
|
144
|
-
block.call(value)
|
145
|
-
end # def with
|
146
|
-
|
147
249
|
# wrapper around mknod ffi calls
|
148
250
|
def mknod_w(path, mode, dev)
|
149
251
|
rc = -1
|
@@ -157,7 +259,30 @@ module FPM::Util
|
|
157
259
|
rc
|
158
260
|
end
|
159
261
|
|
160
|
-
def
|
262
|
+
def copy_metadata(source, destination)
|
263
|
+
source_stat = File::lstat(source)
|
264
|
+
dest_stat = File::lstat(destination)
|
265
|
+
|
266
|
+
# If this is a hard-link, there's no metadata to copy.
|
267
|
+
# If this is a symlink, what it points to hasn't been copied yet.
|
268
|
+
return if source_stat.ino == dest_stat.ino || dest_stat.symlink?
|
269
|
+
|
270
|
+
File.utime(source_stat.atime, source_stat.mtime, destination)
|
271
|
+
mode = source_stat.mode
|
272
|
+
begin
|
273
|
+
File.lchown(source_stat.uid, source_stat.gid, destination)
|
274
|
+
rescue Errno::EPERM
|
275
|
+
# clear setuid/setgid
|
276
|
+
mode &= 01777
|
277
|
+
end
|
278
|
+
|
279
|
+
unless source_stat.symlink?
|
280
|
+
File.chmod(mode, destination)
|
281
|
+
end
|
282
|
+
end # def copy_metadata
|
283
|
+
|
284
|
+
|
285
|
+
def copy_entry(src, dst, preserve=false, remove_destination=false)
|
161
286
|
case File.ftype(src)
|
162
287
|
when 'fifo', 'characterSpecial', 'blockSpecial', 'socket'
|
163
288
|
st = File.stat(src)
|
@@ -173,7 +298,8 @@ module FPM::Util
|
|
173
298
|
if known_entry
|
174
299
|
FileUtils.ln(known_entry, dst)
|
175
300
|
else
|
176
|
-
FileUtils.copy_entry(src, dst
|
301
|
+
FileUtils.copy_entry(src, dst, preserve=preserve,
|
302
|
+
remove_destination=remove_destination)
|
177
303
|
copied_entries[[st.dev, st.ino]] = dst
|
178
304
|
end
|
179
305
|
end # else...
|
@@ -228,3 +354,5 @@ module FPM::Util
|
|
228
354
|
@logger ||= Cabin::Channel.get
|
229
355
|
end # def logger
|
230
356
|
end # module FPM::Util
|
357
|
+
|
358
|
+
require 'fpm/util/tar_writer'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
|
3
|
+
module FPM
|
4
|
+
module Issues
|
5
|
+
module TarWriter
|
6
|
+
# See https://github.com/rubygems/rubygems/issues/1608
|
7
|
+
def self.has_issue_1608?
|
8
|
+
name, prefix = nil,nil
|
9
|
+
io = StringIO.new
|
10
|
+
::Gem::Package::TarWriter.new(io) do |tw|
|
11
|
+
name, prefix = tw.split_name('/123456789'*9 + '/1234567890') # abs name 101 chars long
|
12
|
+
end
|
13
|
+
return prefix.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.has_issues_with_split_name?
|
17
|
+
return false unless ::Gem::Package::TarWriter.method_defined?(:split_name)
|
18
|
+
return has_issue_1608?
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.has_issues_with_add_symlink?
|
22
|
+
return !::Gem::Package::TarWriter.public_instance_methods.include?(:add_symlink)
|
23
|
+
end
|
24
|
+
end # module TarWriter
|
25
|
+
end # module Issues
|
26
|
+
end # module FPM
|
27
|
+
|
28
|
+
module FPM; module Util; end; end
|
29
|
+
|
30
|
+
# Like the ::Gem::Package::TarWriter but contains some backports and bug fixes
|
31
|
+
class FPM::Util::TarWriter < ::Gem::Package::TarWriter
|
32
|
+
if FPM::Issues::TarWriter.has_issues_with_split_name?
|
33
|
+
def split_name(name)
|
34
|
+
if name.bytesize > 256 then
|
35
|
+
raise ::Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)")
|
36
|
+
end
|
37
|
+
|
38
|
+
prefix = ''
|
39
|
+
if name.bytesize > 100 then
|
40
|
+
parts = name.split('/', -1) # parts are never empty here
|
41
|
+
name = parts.pop # initially empty for names with a trailing slash ("foo/.../bar/")
|
42
|
+
prefix = parts.join('/') # if empty, then it's impossible to split (parts is empty too)
|
43
|
+
while !parts.empty? && (prefix.bytesize > 155 || name.empty?)
|
44
|
+
name = parts.pop + '/' + name
|
45
|
+
prefix = parts.join('/')
|
46
|
+
end
|
47
|
+
|
48
|
+
if name.bytesize > 100 or prefix.empty? then
|
49
|
+
raise ::Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)")
|
50
|
+
end
|
51
|
+
|
52
|
+
if prefix.bytesize > 155 then
|
53
|
+
raise ::Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long base path (should be 155 or less)")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
return name, prefix
|
58
|
+
end
|
59
|
+
end # if FPM::Issues::TarWriter.spit_name_has_issues?
|
60
|
+
|
61
|
+
if FPM::Issues::TarWriter.has_issues_with_add_symlink?
|
62
|
+
# Backport Symlink Support to TarWriter
|
63
|
+
# https://github.com/rubygems/rubygems/blob/4a778c9c2489745e37bcc2d0a8f12c601a9c517f/lib/rubygems/package/tar_writer.rb#L239-L253
|
64
|
+
def add_symlink(name, target, mode)
|
65
|
+
check_closed
|
66
|
+
|
67
|
+
name, prefix = split_name name
|
68
|
+
|
69
|
+
header = ::Gem::Package::TarHeader.new(:name => name, :mode => mode,
|
70
|
+
:size => 0, :typeflag => "2",
|
71
|
+
:linkname => target,
|
72
|
+
:prefix => prefix,
|
73
|
+
:mtime => Time.now).to_s
|
74
|
+
|
75
|
+
@io.write header
|
76
|
+
|
77
|
+
self
|
78
|
+
end # def add_symlink
|
79
|
+
end
|
80
|
+
end
|