fpm 1.6.1 → 1.14.1

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.
@@ -1,9 +1,12 @@
1
1
  require "fpm/package"
2
- require "backports"
2
+ require "backports/latest"
3
3
  require "fileutils"
4
4
  require "find"
5
5
  require "arr-pm/file" # gem 'arr-pm'
6
6
 
7
+ # For conversion handling
8
+ require "fpm/package/gem"
9
+
7
10
  # RPM Package type.
8
11
  #
9
12
  # Build RPMs without having to waste hours reading Maximum-RPM.
@@ -24,9 +27,10 @@ class FPM::Package::RPM < FPM::Package
24
27
 
25
28
  COMPRESSION_MAP = {
26
29
  "none" => "w0.gzdio",
27
- "xz" => "w9.xzdio",
28
- "gzip" => "w9.gzdio",
29
- "bzip2" => "w9.bzdio"
30
+ "xz" => ".xzdio",
31
+ "xzmt" => "T.xzdio",
32
+ "gzip" => ".gzdio",
33
+ "bzip2" => ".bzdio"
30
34
  } unless defined?(COMPRESSION_MAP)
31
35
 
32
36
  option "--use-file-permissions", :flag,
@@ -67,6 +71,15 @@ class FPM::Package::RPM < FPM::Package
67
71
  value.downcase
68
72
  end
69
73
 
74
+ option "--compression-level", "[0-9]", "Select a compression level. 0 is store-only. 9 is max compression.",
75
+ :default => "9" do |value|
76
+ valint = value.to_i
77
+ unless value =~ /^\d$/ && valint >= 0 && valint <= 9
78
+ raise "Invalid compression level '#{value}'. Valid values are integers between 0 and 9 inclusive."
79
+ end
80
+ valint
81
+ end
82
+
70
83
  option "--compression", COMPRESSION_MAP.keys.join("|"),
71
84
  "Select a compression method. gzip works on the most platforms.",
72
85
  :default => "gzip" do |value|
@@ -141,6 +154,10 @@ class FPM::Package::RPM < FPM::Package
141
154
  "names in rpm requires instead of the redhat style " \
142
155
  "rubygem(foo).", :default => false
143
156
 
157
+ option "--macro-expansion", :flag,
158
+ "install-time macro expansion in %pre %post %preun %postun scripts " \
159
+ "(see: https://rpm.org/user_doc/scriptlet_expansion.html)", :default => false
160
+
144
161
  option "--verifyscript", "FILE",
145
162
  "a script to be run on verification" do |val|
146
163
  File.expand_path(val) # Get the full path to the script
@@ -176,14 +193,15 @@ class FPM::Package::RPM < FPM::Package
176
193
  # Replace ? with [?] to make rpm not use globs
177
194
  # Replace % with [%] to make rpm not expand macros
178
195
  def rpm_fix_name(name)
179
- name = name.gsub(/(\ |\[|\]|\*|\?|\%|\$)/, {
196
+ name = name.gsub(/(\ |\[|\]|\*|\?|\%|\$|')/, {
180
197
  ' ' => '?',
181
198
  '%' => '[%]',
182
199
  '$' => '[$]',
183
200
  '?' => '[?]',
184
201
  '*' => '[*]',
185
202
  '[' => '[\[]',
186
- ']' => '[\]]'
203
+ ']' => '[\]]',
204
+ "'" => "\\'",
187
205
  })
188
206
  end
189
207
 
@@ -225,6 +243,8 @@ class FPM::Package::RPM < FPM::Package
225
243
  return %x{uname -m}.chomp # default to current arch
226
244
  when "amd64" # debian and redhat disagree on architecture names
227
245
  return "x86_64"
246
+ when "arm64" # debian and redhat disagree on architecture names
247
+ return "aarch64"
228
248
  when "native"
229
249
  return %x{uname -m}.chomp # 'native' is current arch
230
250
  when "all"
@@ -237,6 +257,12 @@ class FPM::Package::RPM < FPM::Package
237
257
 
238
258
  # This method ensures a default value for iteration if none is provided.
239
259
  def iteration
260
+ if @iteration.kind_of?(String) and @iteration.include?("-")
261
+ logger.warn("Package iteration '#{@iteration}' includes dashes, converting" \
262
+ " to underscores. rpmbuild does not allow the dashes in the package iteration (called 'Release' in rpm)")
263
+ @iteration = @iteration.gsub(/-/, "_")
264
+ end
265
+
240
266
  return @iteration ? @iteration : 1
241
267
  end # def iteration
242
268
 
@@ -472,6 +498,22 @@ class FPM::Package::RPM < FPM::Package
472
498
  self.directories = alldirs
473
499
  end
474
500
 
501
+ # include external config files
502
+ (attributes[:config_files] or []).each do |conf|
503
+ dest_conf = File.join(staging_path, conf)
504
+
505
+ if File.exist?(dest_conf)
506
+ logger.debug("Using --config-file from staging area", :path => conf)
507
+ elsif File.exist?(conf)
508
+ logger.info("Copying --config-file from local path", :path => conf)
509
+ FileUtils.mkdir_p(File.dirname(dest_conf))
510
+ FileUtils.cp_r conf, dest_conf
511
+ else
512
+ logger.error("Failed to find given --config-file", :path => conf)
513
+ raise "Could not find config file '#{conf}' in staging area or on host. This can happen if you specify `--config-file '#{conf}'` but this file does not exist in the source package and also does not exist in filesystem."
514
+ end
515
+ end
516
+
475
517
  # scan all conf file paths for files and add them
476
518
  allconfigs = []
477
519
  self.config_files.each do |path|
@@ -499,10 +541,11 @@ class FPM::Package::RPM < FPM::Package
499
541
  end
500
542
 
501
543
  # copy all files from staging to BUILD dir
544
+ # [#1538] Be sure to preserve the original timestamps.
502
545
  Find.find(staging_path) do |path|
503
546
  src = path.gsub(/^#{staging_path}/, '')
504
547
  dst = File.join(build_path, build_sub_dir, src)
505
- copy_entry(path, dst)
548
+ copy_entry(path, dst, preserve=true)
506
549
  end
507
550
 
508
551
  rpmspec = template("rpm.erb").result(binding)
@@ -523,7 +566,11 @@ class FPM::Package::RPM < FPM::Package
523
566
  end # def output
524
567
 
525
568
  def prefix
526
- return (attributes[:prefix] or "/")
569
+ if attributes[:prefix] and attributes[:prefix] != '/'
570
+ return attributes[:prefix].chomp('/')
571
+ else
572
+ return "/"
573
+ end
527
574
  end # def prefix
528
575
 
529
576
  def build_sub_dir
@@ -533,7 +580,7 @@ class FPM::Package::RPM < FPM::Package
533
580
 
534
581
  def summary
535
582
  if !attributes[:rpm_summary]
536
- return @description.split("\n").first || "_"
583
+ return @description.split("\n").find { |line| !line.strip.empty? } || "_"
537
584
  end
538
585
 
539
586
  return attributes[:rpm_summary]
@@ -576,7 +623,12 @@ class FPM::Package::RPM < FPM::Package
576
623
  end # def to_s
577
624
 
578
625
  def payload_compression
579
- return COMPRESSION_MAP[attributes[:rpm_compression]]
626
+ if attributes[:rpm_compression] == 'none'
627
+ # when 'none' ignore any compression level and return w0.gzdio
628
+ return COMPRESSION_MAP[attributes[:rpm_compression]]
629
+ else
630
+ return "w#{attributes[:rpm_compression_level]}" + COMPRESSION_MAP[attributes[:rpm_compression]]
631
+ end
580
632
  end # def payload_compression
581
633
 
582
634
  def digest_algorithm
@@ -3,7 +3,7 @@ require "fpm/namespace"
3
3
  require "fpm/package"
4
4
  require "fpm/errors"
5
5
  require "fpm/util"
6
- require "backports"
6
+ require "backports/latest"
7
7
  require "fileutils"
8
8
  require "digest"
9
9
 
@@ -0,0 +1,130 @@
1
+ require "yaml"
2
+
3
+ require "fpm/package"
4
+ require "fpm/util"
5
+ require "fileutils"
6
+ require "fpm/package/dir"
7
+
8
+ # Support for snaps (.snap files).
9
+ #
10
+ # This supports the input and output of snaps.
11
+ class FPM::Package::Snap < FPM::Package
12
+
13
+ option "--yaml", "FILEPATH",
14
+ "Custom version of the snap.yaml file." do | snap_yaml |
15
+ File.expand_path(snap_yaml)
16
+ end
17
+
18
+ option "--confinement", "CONFINEMENT",
19
+ "Type of confinement to use for this snap.",
20
+ default: "devmode" do | confinement |
21
+ if ['strict', 'devmode', 'classic'].include? confinement
22
+ confinement
23
+ else
24
+ raise "Unsupported confinement type '#{confinement}'"
25
+ end
26
+ end
27
+
28
+ option "--grade", "GRADE", "Grade of this snap.",
29
+ default: "devel" do | grade |
30
+ if ['stable', 'devel'].include? grade
31
+ grade
32
+ else
33
+ raise "Unsupported grade type '#{grade}'"
34
+ end
35
+ end
36
+
37
+ # Input a snap
38
+ def input(input_snap)
39
+ extract_snap_to_staging input_snap
40
+ extract_snap_metadata_from_staging
41
+ end # def input
42
+
43
+ # Output a snap.
44
+ def output(output_snap)
45
+ output_check(output_snap)
46
+
47
+ write_snap_yaml
48
+
49
+ # Create the snap from the staging path
50
+ safesystem("mksquashfs", staging_path, output_snap, "-noappend", "-comp",
51
+ "xz", "-no-xattrs", "-no-fragments", "-all-root")
52
+ end # def output
53
+
54
+ def to_s(format=nil)
55
+ # Default format if nil
56
+ # name_version_arch.snap
57
+ return super(format.nil? ? "NAME_FULLVERSION_ARCH.EXTENSION" : format)
58
+ end # def to_s
59
+
60
+ private
61
+
62
+ def extract_snap_to_staging(snap_path)
63
+ safesystem("unsquashfs", "-f", "-d", staging_path, snap_path)
64
+ end
65
+
66
+ def extract_snap_metadata_from_staging
67
+ metadata = YAML.safe_load(File.read(
68
+ staging_path(File.join("meta", "snap.yaml"))))
69
+
70
+ self.name = metadata["name"]
71
+ self.version = metadata["version"]
72
+ self.description = metadata["summary"] + "\n" + metadata["description"]
73
+ self.architecture = metadata["architectures"][0]
74
+ self.attributes[:snap_confinement] = metadata["confinement"]
75
+ self.attributes[:snap_grade] = metadata["grade"]
76
+
77
+ if metadata["apps"].nil?
78
+ attributes[:snap_apps] = []
79
+ else
80
+ attributes[:snap_apps] = metadata["apps"]
81
+ end
82
+
83
+ if metadata["hooks"].nil?
84
+ attributes[:snap_hooks] = []
85
+ else
86
+ attributes[:snap_hooks] = metadata["hooks"]
87
+ end
88
+ end
89
+
90
+ def write_snap_yaml
91
+ # Write the snap.yaml
92
+ if attributes[:snap_yaml]
93
+ logger.debug("Using '#{attributes[:snap_yaml]}' as the snap.yaml")
94
+ yaml_data = File.read(attributes[:snap_yaml])
95
+ else
96
+ summary, *remainder = (self.description or "no summary given").split("\n")
97
+ description = "no description given"
98
+ if remainder.any?
99
+ description = remainder.join("\n")
100
+ end
101
+
102
+ yaml_data = {
103
+ "name" => self.name,
104
+ "version" => self.version,
105
+ "summary" => summary,
106
+ "description" => description,
107
+ "architectures" => [self.architecture],
108
+ "confinement" => self.attributes[:snap_confinement],
109
+ "grade" => self.attributes[:snap_grade],
110
+ }
111
+
112
+ unless attributes[:snap_apps].nil? or attributes[:snap_apps].empty?
113
+ yaml_data["apps"] = attributes[:snap_apps]
114
+ end
115
+
116
+ unless attributes[:snap_hooks].nil? or attributes[:snap_hooks].empty?
117
+ yaml_data["hooks"] = attributes[:snap_hooks]
118
+ end
119
+
120
+ yaml_data = yaml_data.to_yaml
121
+ end
122
+
123
+ FileUtils.mkdir_p(staging_path("meta"))
124
+ snap_yaml_path = staging_path(File.join("meta", "snap.yaml"))
125
+ logger.debug("Writing snap.yaml", :path => snap_yaml_path)
126
+ File.write(snap_yaml_path, yaml_data)
127
+ File.chmod(0644, snap_yaml_path)
128
+ edit_file(snap_yaml_path) if attributes[:edit?]
129
+ end # def write_snap_yaml
130
+ end # class FPM::Package::Snap
@@ -1,4 +1,4 @@
1
- require "backports" # gem backports
1
+ require "backports/latest" # gem backports/latest
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
4
  require "fileutils"
@@ -50,15 +50,7 @@ class FPM::Package::Tar < FPM::Package
50
50
  output_check(output_path)
51
51
 
52
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
53
+ write_scripts
62
54
 
63
55
  # Unpack the tarball to the staging path
64
56
  args = ["-cf", output_path, "-C", staging_path]
@@ -2,6 +2,8 @@ require "fpm/namespace"
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
4
 
5
+ require "fpm/package/dir"
6
+
5
7
  # Support for python virtualenv packages.
6
8
  #
7
9
  # This supports input, but not output.
@@ -15,8 +17,11 @@ class FPM::Package::Virtualenv < FPM::Package
15
17
  option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
16
18
  "name with.", :default => "virtualenv"
17
19
 
18
- option "--install-location", "DIRECTORY", "Location to which to " \
19
- "install the virtualenv by default.", :default => "/usr/share/python" do |path|
20
+ option "--install-location", "DIRECTORY", "DEPRECATED: Use --prefix instead." \
21
+ " Location to which to install the virtualenv by default.",
22
+ :default => "/usr/share/python" do |path|
23
+ logger.warn("Using deprecated flag: --install-location. Please use " \
24
+ "--prefix instead.")
20
25
  File.expand_path(path)
21
26
  end
22
27
 
@@ -30,6 +35,19 @@ class FPM::Package::Virtualenv < FPM::Package
30
35
  :multivalued => true, :attribute_name => :virtualenv_pypi_extra_index_urls,
31
36
  :default => nil
32
37
 
38
+ option "--setup-install", :flag, "After building virtualenv run setup.py install "\
39
+ "useful when building a virtualenv for packages and including their requirements from "
40
+ "requirements.txt"
41
+
42
+ option "--system-site-packages", :flag, "Give the virtual environment access to the "\
43
+ "global site-packages"
44
+
45
+ option "--find-links", "PIP_FIND_LINKS", "If a url or path to an html file, then parse for "\
46
+ "links to archives. If a local path or file:// url that's a directory, then look "\
47
+ "for archives in the directory listing.",
48
+ :multivalued => true, :attribute_name => :virtualenv_find_links_urls,
49
+ :default => nil
50
+
33
51
  private
34
52
 
35
53
  # Input a package.
@@ -40,7 +58,18 @@ class FPM::Package::Virtualenv < FPM::Package
40
58
  m = /^([^=]+)==([^=]+)$/.match(package)
41
59
  package_version = nil
42
60
 
43
- if m
61
+ is_requirements_file = (File.basename(package) == "requirements.txt")
62
+
63
+ if is_requirements_file
64
+ if !File.file?(package)
65
+ raise FPM::InvalidPackageConfiguration, "Path looks like a requirements.txt, but it doesn't exist: #{package}"
66
+ end
67
+
68
+ package = File.join(::Dir.pwd, package) if File.dirname(package) == "."
69
+ package_name = File.basename(File.dirname(package))
70
+ logger.info("No name given. Using the directory's name", :name => package_name)
71
+ package_version = nil
72
+ elsif m
44
73
  package_name = m[1]
45
74
  package_version = m[2]
46
75
  self.version ||= package_version
@@ -58,23 +87,33 @@ class FPM::Package::Virtualenv < FPM::Package
58
87
  self.name].join("-")
59
88
  end
60
89
 
90
+ # prefix wins over previous virtual_install_location behaviour
61
91
  virtualenv_folder =
62
- File.join(installdir,
63
- virtualenv_name)
92
+ if self.attributes[:prefix]
93
+ self.attributes[:prefix]
94
+ else
95
+ File.join(installdir,
96
+ virtualenv_name)
97
+ end
64
98
 
65
99
  virtualenv_build_folder = build_path(virtualenv_folder)
66
100
 
67
101
  ::FileUtils.mkdir_p(virtualenv_build_folder)
68
102
 
69
- safesystem("virtualenv", virtualenv_build_folder)
103
+ if self.attributes[:virtualenv_system_site_packages?]
104
+ logger.info("Creating virtualenv with --system-site-packages")
105
+ safesystem("virtualenv", "--system-site-packages", virtualenv_build_folder)
106
+ else
107
+ safesystem("virtualenv", virtualenv_build_folder)
108
+ end
109
+
70
110
  pip_exe = File.join(virtualenv_build_folder, "bin", "pip")
71
111
  python_exe = File.join(virtualenv_build_folder, "bin", "python")
72
112
 
73
113
  # Why is this hack here? It looks important, so I'll keep it in.
74
- safesystem(pip_exe, "install", "-U", "-i",
114
+ safesystem(python_exe, pip_exe, "install", "-U", "-i",
75
115
  attributes[:virtualenv_pypi],
76
- "pip", "distribute")
77
- safesystem(pip_exe, "uninstall", "-y", "distribute")
116
+ "pip")
78
117
 
79
118
  extra_index_url_args = []
80
119
  if attributes[:virtualenv_pypi_extra_index_urls]
@@ -82,12 +121,34 @@ class FPM::Package::Virtualenv < FPM::Package
82
121
  extra_index_url_args << "--extra-index-url" << extra_url
83
122
  end
84
123
  end
85
- pip_args = [pip_exe, "install", "-i", attributes[:virtualenv_pypi]] << extra_index_url_args << package
124
+
125
+ find_links_url_args = []
126
+ if attributes[:virtualenv_find_links_urls]
127
+ attributes[:virtualenv_find_links_urls].each do |links_url|
128
+ find_links_url_args << "--find-links" << links_url
129
+ end
130
+ end
131
+
132
+ target_args = []
133
+ if is_requirements_file
134
+ target_args << "-r" << package
135
+ else
136
+ target_args << package
137
+ end
138
+
139
+ pip_args = [python_exe, pip_exe, "install", "-i", attributes[:virtualenv_pypi]] << extra_index_url_args << find_links_url_args << target_args
86
140
  safesystem(*pip_args.flatten)
87
141
 
88
- if package_version.nil?
89
- frozen = safesystemout(pip_exe, "freeze")
90
- package_version = frozen[/#{package}==[^=]+$/].split("==")[1].chomp!
142
+ if attributes[:virtualenv_setup_install?]
143
+ logger.info("Running PACKAGE setup.py")
144
+ setup_args = [python_exe, "setup.py", "install"]
145
+ safesystem(*setup_args.flatten)
146
+ end
147
+
148
+ if ! is_requirements_file && package_version.nil?
149
+ frozen = safesystemout(python_exe, pip_exe, "freeze")
150
+ frozen_version = frozen[/#{package}==[^=]+$/]
151
+ package_version = frozen_version && frozen_version.split("==")[1].chomp!
91
152
  self.version ||= package_version
92
153
  end
93
154
 
@@ -116,7 +177,8 @@ class FPM::Package::Virtualenv < FPM::Package
116
177
  # use dir to set stuff up properly, mainly so I don't have to reimplement
117
178
  # the chdir/prefix stuff special for tar.
118
179
  dir = convert(FPM::Package::Dir)
119
-
180
+ # don't double prefix the files
181
+ dir.attributes[:prefix] = nil
120
182
  if attributes[:chdir]
121
183
  dir.attributes[:chdir] = File.join(build_path, attributes[:chdir])
122
184
  else
@@ -1,4 +1,4 @@
1
- require "backports" # gem backports
1
+ require "backports/latest" # gem backports/latest
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
4
  require "fileutils"
@@ -36,28 +36,12 @@ class FPM::Package::Zip < FPM::Package
36
36
  dir.cleanup_build
37
37
  end # def input
38
38
 
39
- # Output a tarball.
40
- #
41
- # If the output path ends predictably (like in .tar.gz) it will try to obey
42
- # the compression type.
39
+ # Output a zipfile.
43
40
  def output(output_path)
44
41
  output_check(output_path)
45
-
46
- files = Find.find(staging_path).to_a
47
- safesystem("zip", output_path, *files)
48
- end # def output
49
-
50
- # Generate the proper tar flags based on the path name.
51
- def tar_compression_flag(path)
52
- case path
53
- when /\.tar\.bz2$/
54
- return "-j"
55
- when /\.tar\.gz$|\.tgz$/
56
- return "-z"
57
- when /\.tar\.xz$/
58
- return "-J"
59
- else
60
- return nil
42
+ realpath = Pathname.new(output_path).realdirpath.to_s
43
+ ::Dir.chdir(staging_path) do
44
+ safesystem("zip", "-9r", realpath, ".")
61
45
  end
62
- end # def tar_compression_flag
46
+ end # def output
63
47
  end # class FPM::Package::Tar
data/lib/fpm/package.rb CHANGED
@@ -3,11 +3,13 @@ require "fpm/util" # local
3
3
  require "pathname" # stdlib
4
4
  require "find"
5
5
  require "tmpdir" # stdlib
6
- require "backports" # gem 'backports'
6
+ require "ostruct"
7
+ require "backports/latest"
7
8
  require "socket" # stdlib, for Socket.gethostname
8
9
  require "shellwords" # stdlib, for Shellwords.escape
9
10
  require "erb" # stdlib, for template processing
10
11
  require "cabin" # gem "cabin"
12
+ require "stud/temporary"
11
13
 
12
14
  # This class is the parent of all packages.
13
15
  # If you want to implement an FPM package type, you'll inherit from this.
@@ -117,7 +119,10 @@ class FPM::Package
117
119
 
118
120
  def initialize
119
121
  # Attributes for this specific package
120
- @attributes = {}
122
+ @attributes = {
123
+ # Default work location
124
+ :workdir => ::Dir.tmpdir
125
+ }
121
126
 
122
127
  # Reference
123
128
  # http://www.debian.org/doc/manuals/maint-guide/first.en.html
@@ -174,8 +179,8 @@ class FPM::Package
174
179
  @directories = []
175
180
  @attrs = {}
176
181
 
177
- staging_path
178
182
  build_path
183
+ # Dont' initialize staging_path just yet, do it lazily so subclass can get a word in.
179
184
  end # def initialize
180
185
 
181
186
  # Get the 'type' for this instance.
@@ -249,7 +254,7 @@ class FPM::Package
249
254
  end # def output
250
255
 
251
256
  def staging_path(path=nil)
252
- @staging_path ||= ::Dir.mktmpdir("package-#{type}-staging") #, ::Dir.pwd)
257
+ @staging_path ||= Stud::Temporary.directory("package-#{type}-staging")
253
258
 
254
259
  if path.nil?
255
260
  return @staging_path
@@ -259,7 +264,7 @@ class FPM::Package
259
264
  end # def staging_path
260
265
 
261
266
  def build_path(path=nil)
262
- @build_path ||= ::Dir.mktmpdir("package-#{type}-build") #, ::Dir.pwd)
267
+ @build_path ||= Stud::Temporary.directory("package-#{type}-build")
263
268
 
264
269
  if path.nil?
265
270
  return @build_path
@@ -311,7 +316,7 @@ class FPM::Package
311
316
  # the path before returning.
312
317
  #
313
318
  # Wrapping Find.find in an Enumerator is required for sane operation in ruby 1.8.7,
314
- # but requires the 'backports' gem (which is used in other places in fpm)
319
+ # but requires the 'backports/latest' gem (which is used in other places in fpm)
315
320
  return Enumerator.new { |y| Find.find(staging_path) { |path| y << path } } \
316
321
  .select { |path| path != staging_path } \
317
322
  .select { |path| is_leaf.call(path) } \
@@ -491,6 +496,22 @@ class FPM::Package
491
496
  return scripts.include?(name)
492
497
  end # def script?
493
498
 
499
+ # write all scripts to .scripts (tar and dir)
500
+ def write_scripts
501
+ scripts_path = File.join(staging_path, ".scripts")
502
+ target_scripts = [:before_install, :after_install, :before_remove, :after_remove]
503
+ if target_scripts.any? {|name| script?(name)}
504
+ ::Dir.mkdir(scripts_path)
505
+ target_scripts.each do |name|
506
+ next unless script?(name)
507
+ out = File.join(scripts_path, name.to_s)
508
+ logger.debug('Writing script', :source => name, :target => out)
509
+ File.write(out, script(name))
510
+ File.chmod(0755, out)
511
+ end
512
+ end
513
+ end
514
+
494
515
  # Get the contents of the script by a given name.
495
516
  #
496
517
  # If template_scripts? is set in attributes (often by the --template-scripts
@@ -1,5 +1,7 @@
1
1
  require 'rubygems/package'
2
2
 
3
+ require 'stringio'
4
+
3
5
  module FPM
4
6
  module Issues
5
7
  module TarWriter
@@ -27,7 +29,7 @@ end # module FPM
27
29
 
28
30
  module FPM; module Util; end; end
29
31
 
30
- # Like the ::Gem::Package::TarWriter but contains some backports and bug fixes
32
+ # Like the ::Gem::Package::TarWriter but contains some backports/latest and bug fixes
31
33
  class FPM::Util::TarWriter < ::Gem::Package::TarWriter
32
34
  if FPM::Issues::TarWriter.has_issues_with_split_name?
33
35
  def split_name(name)