fpm 1.5.0 → 1.6.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.
@@ -297,7 +297,7 @@ class FPM::Package::CPAN < FPM::Package
297
297
  release_response = httpfetch(metacpan_release_url)
298
298
  rescue Net::HTTPServerException => e
299
299
  logger.error("metacpan release query failed.", :error => e.message,
300
- :module => package, :url => metacpan_release_url)
300
+ :url => metacpan_release_url)
301
301
  raise FPM::InvalidPackageConfiguration, "metacpan release query failed"
302
302
  end
303
303
 
@@ -494,7 +494,8 @@ class FPM::Package::Deb < FPM::Package
494
494
 
495
495
  attributes.fetch(:deb_upstart_list, []).each do |upstart|
496
496
  name = File.basename(upstart, ".upstart")
497
- dest_upstart = staging_path("etc/init/#{name}.conf")
497
+ name = "#{name}.conf" if !(name =~ /\.conf$/)
498
+ dest_upstart = staging_path("etc/init/#{name}")
498
499
  mkdir_p(File.dirname(dest_upstart))
499
500
  FileUtils.cp(upstart, dest_upstart)
500
501
  File.chmod(0644, dest_upstart)
@@ -905,8 +906,7 @@ class FPM::Package::Deb < FPM::Package
905
906
  def to_s(format=nil)
906
907
  # Default format if nil
907
908
  # git_1.7.9.3-1_amd64.deb
908
- return super("NAME_FULLVERSION_ARCH.TYPE") if format.nil?
909
- return super(format)
909
+ return super(format.nil? ? "NAME_FULLVERSION_ARCH.EXTENSION" : format)
910
910
  end # def to_s
911
911
 
912
912
  def data_tar_flags
@@ -99,6 +99,20 @@ class FPM::Package::Dir < FPM::Package
99
99
  logger["method"] = "output"
100
100
  clone(".", output_path)
101
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
+
102
116
  ensure
103
117
  logger.remove("method")
104
118
  end # def output
@@ -3,9 +3,7 @@ require "fpm/package"
3
3
  require "fpm/util"
4
4
  require "digest"
5
5
  require "fileutils"
6
- require "rubygems/package"
7
6
  require "xz"
8
- require "corefines"
9
7
 
10
8
  class FPM::Package::FreeBSD < FPM::Package
11
9
  SCRIPT_MAP = {
@@ -50,7 +48,11 @@ class FPM::Package::FreeBSD < FPM::Package
50
48
  pkg_origin = "fpm/#{name}"
51
49
  end
52
50
 
53
- pkg_version = "#{version}-#{iteration || 1}"
51
+ # Follow similar rules to these used in ``to_s_fullversion`` method.
52
+ # FIXME: maybe epoch should also be introduced somehow ("#{version},#{epoch})?
53
+ # should it go to pkgdata["version"] or to another place?
54
+ # https://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html
55
+ pkg_version = (iteration and (iteration.to_i > 0)) ? "#{version}-#{iteration}" : "#{version}"
54
56
 
55
57
  pkgdata = {
56
58
  "abi" => attributes[:freebsd_abi],
@@ -90,7 +92,7 @@ class FPM::Package::FreeBSD < FPM::Package
90
92
  # Create the .txz package archive from the files in staging_path.
91
93
  File.open(output_path, "wb") do |file|
92
94
  XZ::StreamWriter.new(file) do |xz|
93
- ::Gem::Package::TarWriter.new(xz) do |tar|
95
+ FPM::Util::TarWriter.new(xz) do |tar|
94
96
  # The manifests must come first for pkg.
95
97
  add_path(tar, "+COMPACT_MANIFEST",
96
98
  File.join(staging_path, "+COMPACT_MANIFEST"))
@@ -128,38 +130,15 @@ class FPM::Package::FreeBSD < FPM::Package
128
130
  end
129
131
  end # def add_path
130
132
 
133
+ def to_s_extension; "txz"; end
134
+
135
+ def to_s_fullversion()
136
+ # iteration (PORTREVISION on FreeBSD) shall be appended only(?) if non-zero.
137
+ # https://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html
138
+ (iteration and (iteration.to_i > 0)) ? "#{version}_#{iteration}" : "#{version}"
139
+ end
140
+
131
141
  def to_s(format=nil)
132
- return "#{name}-#{version}_#{iteration || 1}.txz"
133
- return super(format)
142
+ return super(format.nil? ? "NAME-FULLVERSION.EXTENSION" : format)
134
143
  end # def to_s
135
144
  end # class FPM::Package::FreeBSD
136
-
137
- # Backport Symlink Support to TarWriter
138
- # https://github.com/rubygems/rubygems/blob/4a778c9c2489745e37bcc2d0a8f12c601a9c517f/lib/rubygems/package/tar_writer.rb#L239-L253
139
- module TarWriterAddSymlink
140
- refine Gem::Package::TarWriter do
141
- def add_symlink(name, target, mode)
142
- check_closed
143
-
144
- name, prefix = split_name name
145
-
146
- header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
147
- :size => 0, :typeflag => "2",
148
- :linkname => target,
149
- :prefix => prefix,
150
- :mtime => Time.now).to_s
151
-
152
- @io.write header
153
-
154
- self
155
- end # def add_symlink
156
- end # refine Gem::Package::TarWriter
157
- end # module TarWriterAddSymlink
158
-
159
- module Util
160
- module Tar
161
- unless Gem::Package::TarWriter.public_instance_methods.include? :add_symlink
162
- using TarWriterAddSymlink
163
- end
164
- end # module Tar
165
- end # module Util
@@ -178,7 +178,7 @@ class FPM::Package::Gem < FPM::Package
178
178
  ::FileUtils.mkdir_p(installdir)
179
179
  # TODO(sissel): Allow setting gem tool path
180
180
  args = [attributes[:gem_gem], "install", "--quiet", "--no-ri", "--no-rdoc",
181
- "--install-dir", installdir, "--ignore-dependencies"]
181
+ "--no-user-install", "--install-dir", installdir, "--ignore-dependencies"]
182
182
  if attributes[:gem_env_shebang?]
183
183
  args += ["-E"]
184
184
  end
@@ -154,9 +154,10 @@ class FPM::Package::OSXpkg < FPM::Package
154
154
  FileUtils.remove_file(temp_info)
155
155
  end # def output
156
156
 
157
+ def to_s_extension; "pkg"; end
158
+
157
159
  def to_s(format=nil)
158
- return super("NAME-VERSION.pkg") if format.nil?
159
- return super(format)
160
+ return super(format.nil? ? "NAME-VERSION.EXTENSION" : format)
160
161
  end # def to_s
161
162
 
162
163
  public(:input, :output, :identifier, :to_s)
@@ -306,11 +306,12 @@ class FPM::Package::Pacman < FPM::Package
306
306
  end
307
307
  end # def default_output
308
308
 
309
+ def to_s_extension; "pkg.tar#{compression_ending}"; end
310
+
309
311
  def to_s(format=nil)
310
312
  # Default format if nil
311
- # git_1.7.9.3-1_amd64.deb
312
- return super("NAME-FULLVERSION-ARCH.pkg.tar#{compression_ending}") if format.nil?
313
- return super(format)
313
+ # git_1.7.9.3-1-amd64.pkg.tar.xz
314
+ return super(format.nil? ? "NAME-FULLVERSION-ARCH.EXTENSION" : format)
314
315
  end # def to_s
315
316
 
316
317
  private
@@ -0,0 +1,63 @@
1
+ require "fpm/namespace"
2
+ require "fpm/package"
3
+ require "fpm/util"
4
+ require "fileutils"
5
+
6
+ require "pleaserun/cli"
7
+
8
+ # A pleaserun package.
9
+ #
10
+ # This does not currently support 'output'
11
+ class FPM::Package::PleaseRun < FPM::Package
12
+ # TODO(sissel): Implement flags.
13
+
14
+ require "pleaserun/platform/systemd"
15
+ require "pleaserun/platform/upstart"
16
+ require "pleaserun/platform/launchd"
17
+ require "pleaserun/platform/sysv"
18
+
19
+ option "--name", "SERVICE_NAME", "The name of the service you are creating"
20
+
21
+ private
22
+ def input(command)
23
+ platforms = [
24
+ ::PleaseRun::Platform::Systemd.new("default"), # RHEL 7, Fedora 19+, Debian 8, Ubuntu 16.04
25
+ ::PleaseRun::Platform::Upstart.new("1.5"), # Recent Ubuntus
26
+ ::PleaseRun::Platform::Upstart.new("0.6.5"), # CentOS 6
27
+ ::PleaseRun::Platform::Launchd.new("10.9"), # OS X
28
+ ::PleaseRun::Platform::SYSV.new("lsb-3.1") # Ancient stuff
29
+ ]
30
+
31
+ attributes[:pleaserun_name] ||= File.basename(command.first)
32
+ attributes[:prefix] ||= "/usr/share/pleaserun/#{attributes[:pleaserun_name]}"
33
+
34
+ platforms.each do |platform|
35
+ logger.info("Generating service manifest.", :platform => platform.class.name)
36
+ platform.program = command.first
37
+ platform.name = attributes[:pleaserun_name]
38
+ platform.args = command[1..-1]
39
+ platform.description = if attributes[:description_given?]
40
+ attributes[:description]
41
+ else
42
+ platform.name
43
+ end
44
+ base = staging_path(File.join(attributes[:prefix], "#{platform.platform}/#{platform.target_version || "default"}"))
45
+ target = File.join(base, "files")
46
+ actions_script = File.join(base, "install_actions.sh")
47
+ ::PleaseRun::Installer.install_files(platform, target, false)
48
+ ::PleaseRun::Installer.write_actions(platform, actions_script)
49
+ end
50
+
51
+ libs = [ "install.sh", "install-path.sh", "generate-cleanup.sh" ]
52
+ libs.each do |file|
53
+ base = staging_path(File.join(attributes[:prefix]))
54
+ File.write(File.join(base, file), template(File.join("pleaserun", file)).result(binding))
55
+ File.chmod(0755, File.join(base, file))
56
+ end
57
+
58
+ scripts[:after_install] = template(File.join("pleaserun", "scripts", "after-install.sh")).result(binding)
59
+ scripts[:before_remove] = template(File.join("pleaserun", "scripts", "before-remove.sh")).result(binding)
60
+ end # def input
61
+
62
+ public(:input)
63
+ end # class FPM::Package::PleaseRun
@@ -131,7 +131,8 @@ class FPM::Package::Python < FPM::Package
131
131
  "--build-directory", target, want_pkg)
132
132
  else
133
133
  logger.debug("using pip", :pip => attributes[:python_pip])
134
- safesystem(attributes[:python_pip], "install", "--no-deps", "--no-install", "--no-use-wheel", "-i", attributes[:python_pypi], "-U", "--build", target, want_pkg)
134
+ # TODO: Support older versions of pip
135
+ safesystem(attributes[:python_pip], "download", "--no-clean", "--no-deps", "--no-binary", ":all:", "-i", attributes[:python_pypi], "--build", target, want_pkg)
135
136
  end
136
137
 
137
138
  # easy_install will put stuff in @tmpdir/packagename/, so find that:
@@ -560,12 +560,19 @@ class FPM::Package::RPM < FPM::Package
560
560
  return @epoch
561
561
  end # def epoch
562
562
 
563
+ def to_s_dist;
564
+ attributes[:rpm_dist] ? "#{attributes[:rpm_dist]}" : "DIST";
565
+ end
566
+
563
567
  def to_s(format=nil)
564
568
  if format.nil?
565
- return super("NAME-VERSION-ITERATION.DIST.ARCH.TYPE").gsub('DIST', attributes[:rpm_dist]) if attributes[:rpm_dist]
566
- return super("NAME-VERSION-ITERATION.ARCH.TYPE")
569
+ format = if attributes[:rpm_dist]
570
+ "NAME-VERSION-ITERATION.DIST.ARCH.EXTENSION"
571
+ else
572
+ "NAME-VERSION-ITERATION.ARCH.EXTENSION"
573
+ end
567
574
  end
568
- return super(format)
575
+ return super(format.gsub("DIST", to_s_dist))
569
576
  end # def to_s
570
577
 
571
578
  def payload_compression
@@ -48,6 +48,18 @@ class FPM::Package::Tar < FPM::Package
48
48
  # the compression type.
49
49
  def output(output_path)
50
50
  output_check(output_path)
51
+
52
+ # Write the scripts, too.
53
+ scripts_path = File.join(staging_path, ".scripts")
54
+ ::Dir.mkdir(scripts_path)
55
+ [:before_install, :after_install, :before_remove, :after_remove].each do |name|
56
+ next unless script?(name)
57
+ out = File.join(scripts_path, name.to_s)
58
+ logger.debug("Writing script", :source => name, :target => out)
59
+ File.write(out, script(name))
60
+ File.chmod(0755, out)
61
+ end
62
+
51
63
  # Unpack the tarball to the staging path
52
64
  args = ["-cf", output_path, "-C", staging_path]
53
65
  tar_compression_flag(output_path).tap do |flag|
@@ -92,7 +92,7 @@ class FPM::Package::Virtualenv < FPM::Package
92
92
  end
93
93
 
94
94
  ::Dir[build_path + "/**/*"].each do |f|
95
- if ! File.world_readable? f
95
+ if ! File.readable? f
96
96
  File.lchmod(File.stat(f).mode | 444)
97
97
  end
98
98
  end
@@ -1,3 +1,4 @@
1
+ require "fpm/namespace"
1
2
  require "ostruct"
2
3
  require "rake"
3
4
  require "rake/tasklib"
@@ -54,6 +55,6 @@ class FPM::RakeTask < Rake::TaskLib
54
55
 
55
56
  args.flatten!.compact!
56
57
 
57
- exit(FPM::Command.new("fpm").run(args) || 0)
58
+ abort 'FPM failed!' unless FPM::Command.new("fpm").run(args) == 0
58
59
  end
59
60
  end
@@ -45,42 +45,158 @@ module FPM::Util
45
45
  return shell
46
46
  end
47
47
 
48
- # Run a command safely in a way that gets reports useful errors.
49
- def safesystem(*args)
50
- # ChildProcess isn't smart enough to run a $SHELL if there's
51
- # spaces in the first arg and there's only 1 arg.
52
- if args.size == 1
53
- args = [ default_shell, "-c", args[0] ]
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
112
+ end
113
+
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")
54
124
  end
55
- program = args[0]
56
125
 
57
- if !program_exists?(program)
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)
58
141
  raise ExecutableNotFound.new(program)
59
142
  end
60
143
 
61
- logger.debug("Running command", :args => args)
144
+ logger.debug("Running command", :args => args2)
62
145
 
63
- # Create a pair of pipes to connect the
64
- # invoked process to the cabin logger
65
146
  stdout_r, stdout_w = IO.pipe
66
147
  stderr_r, stderr_w = IO.pipe
67
148
 
68
- process = ChildProcess.build(*args)
149
+ process = ChildProcess.build(*args2)
150
+ process.environment.merge!(env)
151
+
69
152
  process.io.stdout = stdout_w
70
153
  process.io.stderr = stderr_w
71
154
 
155
+ if block_given? and opts[:stdin]
156
+ process.duplex = true
157
+ end
158
+
72
159
  process.start
160
+
73
161
  stdout_w.close; stderr_w.close
74
- logger.debug('Process is running', :pid => process.pid)
75
- # Log both stdout and stderr as 'info' because nobody uses stderr for
76
- # actually reporting errors and as a result 'stderr' is a misnomer.
77
- logger.pipe(stdout_r => :info, stderr_r => :info)
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
180
+
181
+ process.wait if process.alive?
78
182
 
79
- process.wait
80
- success = (process.exit_code == 0)
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)
81
197
 
82
198
  if !success
83
- raise ProcessFailed.new("#{program} failed (exit code #{process.exit_code})" \
199
+ raise ProcessFailed.new("#{program} failed (exit code #{exit_code})" \
84
200
  ". Full command was:#{args.inspect}")
85
201
  end
86
202
  return success
@@ -97,29 +213,16 @@ module FPM::Util
97
213
  raise ExecutableNotFound.new(program)
98
214
  end
99
215
 
100
- logger.debug("Running command", :args => args)
101
-
102
- stdout_r, stdout_w = IO.pipe
103
- stderr_r, stderr_w = IO.pipe
104
-
105
- process = ChildProcess.build(*args)
106
- process.io.stdout = stdout_w
107
- process.io.stderr = stderr_w
108
-
109
- process.start
110
- stdout_w.close; stderr_w.close
111
- stdout_r_str = stdout_r.read
112
- stdout_r.close; stderr_r.close
113
- logger.debug("Process is running", :pid => process.pid)
114
-
115
- process.wait
116
- 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)
117
221
 
118
222
  if !success
119
- raise ProcessFailed.new("#{program} failed (exit code #{process.exit_code})" \
223
+ raise ProcessFailed.new("#{program} failed (exit code #{exit_code})" \
120
224
  ". Full command was:#{args.inspect}")
121
225
  end
122
-
123
226
  return stdout_r_str
124
227
  end # def safesystemout
125
228
 
@@ -251,3 +354,5 @@ module FPM::Util
251
354
  @logger ||= Cabin::Channel.get
252
355
  end # def logger
253
356
  end # module FPM::Util
357
+
358
+ require 'fpm/util/tar_writer'