fpm 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'