fpm 0.4.10 → 0.4.11

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.
data/CHANGELIST CHANGED
@@ -1,3 +1,24 @@
1
+ 0.4.11 (August 7, 2012)
2
+ - Support different compression types on deb packages (default gzip,
3
+ alternates xz and bzip2)
4
+ - Fix some symlink handling to prevent links from being followed during
5
+ cleanup (#228, patch by sbuss)
6
+ - rpm: 'vendor' in rpm spec is now omitted if empty or nil. This fixes a bug
7
+ where rpmbuild fails due to empty 'Vendor' tag if you convert rpm to rpm.
8
+ - internal: remove empty directories marked by --exclude (#205, patch by
9
+ jimbrowne)
10
+ - dir: don't try to set utime on symlinks (#234, #240, patch by ctgswallow)
11
+ - rpm: relocatable rpms now supported when using the '--prefix' flag.
12
+ Example: fpm -s dir -t rpm --prefix /usr/local -n example /etc/motd
13
+ (patch by jkoppe)
14
+ - deb: --deb-compression flag: Support different compression methods.
15
+ Default continues to be gzip.
16
+ - new flag: --template-scripts. This lets you write script templates for
17
+ --after-install, etc. Templates are ERB, so you can do things like
18
+ '<%= name %>' to get the package name in the script, etc.
19
+ - warn on command invocations that appear to have stray flags to try and
20
+ help users who have complex command lines that are failling.
21
+
1
22
  0.4.10 (May 25, 2012)
2
23
  - Fix python package support for python3 (#212, patch by Slezhuk Evgeniy)
3
24
  - Preserve file metadata (time, owner, etc) when copying with the dir
@@ -16,3 +16,9 @@ Luke Macken (github: lmacken)
16
16
  Matt Blair (github: mblair)
17
17
  Thomas Meson (github: zllak)
18
18
  Oliver Hookins (github: ohookins)
19
+ llasram
20
+ sbuss
21
+
22
+ If you have contributed (bug reports, feature requests, help in IRC, blog
23
+ posts, code, etc) and aren't listed here, please let me know if you wish to be
24
+ added!
@@ -145,6 +145,12 @@ class FPM::Command < Clamp::Command
145
145
  "a script to be run before package removal" do |val|
146
146
  File.expand_path(val) # Get the full path to the script
147
147
  end # --before-remove
148
+ option "--template-scripts", :flag,
149
+ "Allow scripts to be templated. This lets you use ERB to template your " \
150
+ "packaging scripts (for --after-install, etc). For example, you can do " \
151
+ "things like <%= name %> to get the package name. For more information, " \
152
+ "see the fpm wiki: " \
153
+ "https://github.com/jordansissel/fpm/wiki/Script-Templates"
148
154
 
149
155
  parameter "[ARGS] ...",
150
156
  "Inputs to the source package type. For the 'dir' type, this is the files" \
@@ -183,6 +189,11 @@ class FPM::Command < Clamp::Command
183
189
  @logger.subscribe(STDOUT)
184
190
  @logger.level = :warn
185
191
 
192
+ if (stray_flags = args.grep(/^-/); stray_flags.any?)
193
+ @logger.warn("All flags should be before the first argument " \
194
+ "(stray flags found: #{stray_flags}")
195
+ end
196
+
186
197
  # Some older behavior, if you specify:
187
198
  # 'fpm -s dir -t ... -C somepath'
188
199
  # fpm would assume you meant to add '.' to the end of the commandline.
@@ -296,15 +307,18 @@ class FPM::Command < Clamp::Command
296
307
  input.config_files += config_files
297
308
 
298
309
  setscript = proc do |scriptname|
310
+ # 'self.send(scriptname) == self.before_install == --before-install
311
+ # Gets the path to the script
299
312
  path = self.send(scriptname)
300
313
  # Skip scripts not set
301
314
  next if path.nil?
302
315
 
303
- # 'self.send(scriptname) == self.before_install == --before-install
304
316
  if !File.exists?(path)
305
317
  @logger.error("No such file (for #{scriptname.to_s}): #{path.inspect}")
306
318
  return 1
307
319
  end
320
+
321
+ # Load the script into memory.
308
322
  input.scripts[scriptname] = File.read(path)
309
323
  end
310
324
 
@@ -336,6 +350,9 @@ class FPM::Command < Clamp::Command
336
350
  rescue FPM::Util::ExecutableNotFound => e
337
351
  @logger.error("Need executable '#{e}' to convert #{input_type} to #{output_type}")
338
352
  return 1
353
+ rescue FPM::InvalidPackageConfiguration => e
354
+ @logger.error("Invalid package configuration: #{e}")
355
+ return 1
339
356
  ensure
340
357
  input.cleanup unless input.nil?
341
358
  output.cleanup unless output.nil?
@@ -1,9 +1,11 @@
1
1
  require "fpm/namespace" # local
2
2
  require "fpm/util" # local
3
+ require "pathname" # stdlib
3
4
  require "tmpdir" # stdlib
4
5
  require "backports" # gem 'backports'
5
6
  require "socket" # stdlib, for Socket.gethostname
6
7
  require "shellwords" # stdlib, for Shellwords.escape
8
+ require "erb" # stdlib, for template processing
7
9
  require "cabin" # gem "cabin"
8
10
 
9
11
  # This class is the parent of all packages.
@@ -299,7 +301,6 @@ class FPM::Package
299
301
  end # def files
300
302
 
301
303
  def template(path)
302
- require "erb"
303
304
  template_dir = File.join(File.dirname(__FILE__), "..", "..", "templates")
304
305
  template_path = File.join(template_dir, path)
305
306
  template_code = File.read(template_path)
@@ -334,6 +335,8 @@ class FPM::Package
334
335
  # This method removes excluded files from the staging_path. Subclasses can
335
336
  # remove the files during the input phase rather than deleting them here
336
337
  def exclude
338
+ return if attributes[:excludes].nil?
339
+
337
340
  if @attributes.include?(:prefix)
338
341
  installdir = staging_path(@attributes[:prefix])
339
342
  else
@@ -346,6 +349,14 @@ class FPM::Package
346
349
  if File.fnmatch(wildcard, file)
347
350
  @logger.info("Removing excluded file", :path => file, :matches => wildcard)
348
351
  FileUtils.remove_entry_secure(staging_path(file))
352
+ Pathname.new(staging_path(file)).parent.ascend do |d|
353
+ if (::Dir.entries(d) - %w[ . .. ]).empty?
354
+ ::Dir.rmdir(d)
355
+ @logger.info("Deleting empty directory left by removing exluded file", :path => d)
356
+ else
357
+ break
358
+ end
359
+ end
349
360
  end
350
361
  end
351
362
  end
@@ -436,9 +447,29 @@ class FPM::Package
436
447
  return nil
437
448
  end # def version
438
449
 
450
+ # Does this package have the given script?
451
+ def script?(name)
452
+ return scripts.include?(name)
453
+ end # def script?
454
+
455
+ # Get the contents of the script by a given name.
456
+ #
457
+ # If template_scripts? is set in attributes (often by the --template-scripts
458
+ # flag), then apply it as an ERB template.
459
+ def script(script_name)
460
+ if attributes[:template_scripts?]
461
+ erb = ERB.new(scripts[script_name], nil, "-")
462
+ # TODO(sissel): find the original file name for the file.
463
+ erb.filename = "script(#{script_name})"
464
+ return erb.result(binding)
465
+ else
466
+ return scripts[script_name]
467
+ end
468
+ end # def script
469
+
439
470
  # General public API
440
471
  public(:type, :initialize, :convert, :input, :output, :to_s, :cleanup, :files,
441
- :version)
472
+ :version, :script)
442
473
 
443
474
  # Package internal public api
444
475
  public(:cleanup_staging, :cleanup_build, :staging_path, :converted_from,
@@ -4,13 +4,13 @@ require "fpm/package"
4
4
  require "fpm/errors"
5
5
  require "fpm/util"
6
6
  require "backports"
7
- require "fpm/monkeypatches" # until backports > 2.3.0
8
7
  require "fileutils"
9
8
 
10
9
  # Support for debian packages (.deb files)
11
10
  #
12
11
  # This class supports both input and output of packages.
13
12
  class FPM::Package::Deb < FPM::Package
13
+
14
14
  # Map of what scripts are named.
15
15
  SCRIPT_MAP = {
16
16
  :before_install => "preinst",
@@ -19,6 +19,9 @@ class FPM::Package::Deb < FPM::Package
19
19
  :after_remove => "postrm",
20
20
  } unless defined?(SCRIPT_MAP)
21
21
 
22
+ # The list of supported compression types. Default is gz (gzip)
23
+ COMPRESSION_TYPES = [ "gz", "bzip2", "xz" ]
24
+
22
25
  option "--ignore-iteration-in-dependencies", :flag,
23
26
  "For '=' (equal) dependencies, allow iterations on the specified " \
24
27
  "version. Default is to be specific. This option allows the same " \
@@ -30,6 +33,15 @@ class FPM::Package::Deb < FPM::Package
30
33
  @pre_depends << dep
31
34
  end
32
35
 
36
+ option "--compression", "COMPRESSION", "The compression type to use, must " \
37
+ "be one of #{COMPRESSION_TYPES.join(", ")}.", :default => "gzip" do |value|
38
+ if !COMPRESSION_TYPES.include?(value)
39
+ raise ArgumentError, "deb compression value of '#{value}' is invalid. " \
40
+ "Must be one of #{COMPRESSION_TYPES.join(", ")}"
41
+ end
42
+ value
43
+ end
44
+
33
45
  # Take care about the case when we want custom control file but still use fpm ...
34
46
  option "--custom-control", "FILEPATH",
35
47
  "Custom version of the Debian control file." do |control|
@@ -159,6 +171,7 @@ class FPM::Package::Deb < FPM::Package
159
171
  #
160
172
  # parse_depends("foo (>= 3), bar (= 5), baz")
161
173
  def parse_depends(data)
174
+ return [] if data.nil? or data.empty?
162
175
  # parse dependencies. Debian dependencies come in one of two forms:
163
176
  # * name
164
177
  # * name (op version)
@@ -183,8 +196,28 @@ class FPM::Package::Deb < FPM::Package
183
196
  end # def parse_depends
184
197
 
185
198
  def extract_files(package)
186
- # unpack the data.tar.gz from the deb package into staging_path
187
- safesystem("ar p #{package} data.tar.gz | tar -zxf - -C #{staging_path}")
199
+ # Find out the compression type
200
+ p `ar t #{package}`
201
+ compression = `ar t #{package}`.split("\n").grep(/data.tar/).first.split(".").last
202
+ case compression
203
+ when "gz"
204
+ datatar = "data.tar.gz"
205
+ compression = "-z"
206
+ when "bzip2"
207
+ datatar = "data.tar.bz2"
208
+ compression = "-j"
209
+ when "xz"
210
+ datatar = "data.tar.xz"
211
+ compression = "-J"
212
+ else
213
+ raise FPM::InvalidPackageConfiguration,
214
+ "Unknown compression type '#{self.attributes[:deb_compression]}' "
215
+ "in deb source package #{package}"
216
+ end
217
+
218
+ # unpack the data.tar.{gz,bz2,xz} from the deb package into staging_path
219
+ safesystem("ar p #{package} #{datatar} " \
220
+ "| tar #{compression} -xf - -C #{staging_path}")
188
221
  end # def extract_files
189
222
 
190
223
  def output(output_path)
@@ -196,9 +229,22 @@ class FPM::Package::Deb < FPM::Package
196
229
 
197
230
  write_control_tarball
198
231
 
199
- # Tar up the staging_path and call it 'data.tar.gz'
200
- datatar = build_path("data.tar.gz")
201
- safesystem(tar_cmd, "-C", staging_path, "-zcf", datatar, ".")
232
+ # Tar up the staging_path into data.tar.{compression type}
233
+ case self.attributes[:deb_compression]
234
+ when "gzip", nil
235
+ datatar = build_path("data.tar.gz")
236
+ compression = "-z"
237
+ when "bzip2"
238
+ datatar = build_path("data.tar.bz2")
239
+ compression = "-j"
240
+ when "xz"
241
+ datatar = build_path("data.tar.xz")
242
+ compression = "-J"
243
+ else
244
+ raise FPM::InvalidPackageConfiguration,
245
+ "Unknown compression type '#{self.attributes[:deb_compression]}'"
246
+ end
247
+ safesystem(tar_cmd, "-C", staging_path, compression, "-cf", datatar, ".")
202
248
 
203
249
  # pack up the .deb, which is just an 'ar' archive with 3 files
204
250
  # the 'debian-binary' file has to be first
@@ -348,11 +394,11 @@ class FPM::Package::Deb < FPM::Package
348
394
  # 'post_install' names
349
395
  def write_scripts
350
396
  SCRIPT_MAP.each do |scriptname, filename|
351
- next if scripts[scriptname].nil?
397
+ next if script?(scriptname).nil?
352
398
 
353
399
  with(control_path(filename)) do |controlscript|
354
400
  @logger.debug("Writing control script", :source => filename, :target => controlscript)
355
- File.write(controlscript, scripts[scriptname])
401
+ File.write(controlscript, script(scriptname))
356
402
  # deb maintainer scripts are required to be executable
357
403
  File.chmod(0755, controlscript)
358
404
  end
@@ -90,11 +90,20 @@ class FPM::Package::Dir < FPM::Package
90
90
  FileUtils.mkdir_p(directory)
91
91
  end
92
92
 
93
- # Create a directory if this path is a directory
94
- if File.directory?(source) and !File.symlink?(source)
95
- @logger.debug("Creating", :directory => destination)
96
- if !File.directory?(destination)
97
- FileUtils.mkdir(destination)
93
+ if File.directory?(source)
94
+ if !File.symlink?(source)
95
+ # Create a directory if this path is a directory
96
+ @logger.debug("Creating", :directory => destination)
97
+ if !File.directory?(destination)
98
+ FileUtils.mkdir(destination)
99
+ end
100
+ else
101
+ # Linking symlinked directories causes a hardlink to be created, which
102
+ # results in the source directory being wiped out during cleanup,
103
+ # so copy the symlink.
104
+ @logger.debug("Copying symlinked directory", :source => source,
105
+ :destination => destination)
106
+ FileUtils.copy_entry(source, destination)
98
107
  end
99
108
  else
100
109
  # Otherwise try copying the file.
@@ -116,16 +125,20 @@ class FPM::Package::Dir < FPM::Package
116
125
  dest_stat = File::lstat(destination)
117
126
 
118
127
  # If this is a hard-link, there's no metadata to copy.
119
- return if source_stat.ino == dest_stat.ino
128
+ # If this is a symlink, what it points to hasn't been copied yet.
129
+ return if source_stat.ino == dest_stat.ino || dest_stat.symlink?
120
130
 
121
131
  File.utime(source_stat.atime, source_stat.mtime, destination)
132
+ mode = source_stat.mode
122
133
  begin
123
- File.chown(source_stat.uid, source_stat.gid, destination)
134
+ File.lchown(source_stat.uid, source_stat.gid, destination)
124
135
  rescue Errno::EPERM
125
136
  # clear setuid/setgid
126
- File.chmod(source_stat.mode & 01777, destination)
127
- else
128
- File.chmod(source_stat.mode, destination)
137
+ mode &= 01777
138
+ end
139
+
140
+ unless source_stat.symlink?
141
+ File.chmod(mode, destination)
129
142
  end
130
143
  end # def copy_metadata
131
144
 
@@ -21,7 +21,8 @@ class FPM::Package::Gem < FPM::Package
21
21
  option "--package-prefix", "NAMEPREFIX",
22
22
  "(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
23
23
  "name with." do |value|
24
- @logger.warn("Using deprecated flag: --package-prefix. Please use " \
24
+ logger = Cabin::Channel.get
25
+ logger.warn("Using deprecated flag: --package-prefix. Please use " \
25
26
  "--package-name-prefix")
26
27
  value
27
28
  end
@@ -2,7 +2,6 @@ require "fpm/package"
2
2
  require "backports"
3
3
  require "fileutils"
4
4
  require "find"
5
- require "fpm/monkeypatches" # until backports > 2.3.0
6
5
  require "arr-pm/file" # gem 'arr-pm'
7
6
 
8
7
  # RPM Package type.
@@ -204,6 +203,16 @@ class FPM::Package::RPM < FPM::Package
204
203
  @logger.log("Created rpm", :path => output_path)
205
204
  end # def output
206
205
 
206
+ def prefix
207
+ return (attributes[:prefix] or "/")
208
+ end # def prefix
209
+
210
+ def build_sub_dir
211
+ return "BUILD"
212
+ #return File.join("BUILD", prefix)
213
+ end # def prefix
214
+
215
+
207
216
  def to_s(format=nil)
208
217
  return super("NAME-VERSION-ITERATION.ARCH.TYPE") if format.nil?
209
218
  return super(format)
@@ -218,5 +227,5 @@ class FPM::Package::RPM < FPM::Package
218
227
  end # def digest_algorithm
219
228
 
220
229
  public(:input, :output, :converted_from, :architecture, :to_s, :iteration,
221
- :payload_compression, :digest_algorithm)
230
+ :payload_compression, :digest_algorithm, :prefix, :build_sub_dir)
222
231
  end # class FPM::Package::RPM
@@ -27,10 +27,15 @@ AutoReqProv: no
27
27
  # Seems specifying BuildRoot is required on older rpmbuild (like on CentOS 5)
28
28
  # fpm passes '--define buildroot ...' on the commandline, so just reuse that.
29
29
  BuildRoot: %buildroot
30
+ <% if !prefix.nil? and !prefix.empty? %>
31
+ Prefix: <%= prefix %>
32
+ <% end -%>
30
33
 
31
34
  Group: <%= category %>
32
35
  License: <%= license %>
36
+ <% if !vendor.nil? and !vendor.empty? -%>
33
37
  Vendor: <%= vendor %>
38
+ <% end -%>
34
39
  URL: <%= url or "http://nourlgiven.example.com/" %>
35
40
  Packager: <%= maintainer %>
36
41
 
@@ -62,7 +67,7 @@ Obsoletes: <%= repl %>
62
67
  <% files.each do |path| -%>
63
68
  <% source = Shellwords.shellescape(File.join(staging_path, path)) -%>
64
69
  <% # Copy to the build_path/BUILD/ to make rpmbuild happy -%>
65
- <% target = Shellwords.shellescape(File.join(build_path, "BUILD", path)) -%>
70
+ <% target = Shellwords.shellescape(File.join(build_path, build_sub_dir, path)) -%>
66
71
  <% dir = File.dirname(target) %>
67
72
  mkdir -p <%= dir %>
68
73
  if [ -f <%= source %> ] || [ -h <%= source %> ] ; then
@@ -75,31 +80,28 @@ fi
75
80
  %clean
76
81
  # noop
77
82
 
78
- <% if scripts[:before_install] -%>
79
- %pre
80
- <%= scripts[:before_install] %>
81
-
82
- <% end -%>
83
- <% if scripts[:after_install] -%>
84
- %post
85
- <%= scripts[:after_install] %>
86
-
87
- <% end -%>
88
- <% if scripts[:before_remove] -%>
89
- %preun
90
- <%= scripts[:before_remove] %>
91
-
83
+ <%# This next section puts any %pre, %post, %preun, or %postun scripts %>
84
+ <%
85
+ scriptmap = {
86
+ :before_install => "pre",
87
+ :after_install => "post",
88
+ :before_remove => "preun",
89
+ :after_remove => "postun",
90
+ }
91
+ scriptmap.each do |name, rpmname|
92
+ -%>
93
+ <% if script?(name) -%>
94
+ %<%= rpmname %>
95
+ <%= script(name) %>
96
+
97
+ <% end -%>
92
98
  <% end -%>
93
- <% if scripts[:after_remove] -%>
94
- %postun
95
- <%= scripts[:after_remove] %>
96
99
 
97
- <% end -%>
98
100
  %files
99
101
  %defattr(-,<%= attributes[:rpm_user] %>,<%= attributes[:rpm_group] %>,-)
100
102
  <%# Output config files and then regular files. -%>
101
103
  <% config_files.each do |path| -%>
102
- %config(noreplace) <%= path %>
104
+ %config(noreplace) <%= File.join(prefix, path) %>
103
105
  <% end -%>
104
106
  <%# list only files, not directories? -%>
105
107
  <%=
@@ -110,12 +112,15 @@ fi
110
112
  # The 'files' section of rpm can be
111
113
  # Replace [ with [\[] to make rpm not use globs
112
114
  # Replace * with [*] to make rpm not use globs
115
+ # Replace ? with [?] to make rpm not use globs
113
116
  files.collect { |f| "/#{f}" } \
114
117
  .reject { |f| config_files.include?(f) } \
115
118
  .collect { |f| f[/\s/] and "\"#{f}\"" or f } \
116
119
  .collect { |f| f.gsub("[", "[\\[]") } \
117
120
  .collect { |f| f.gsub("*", "[*]") } \
118
- .join("\n")
121
+ .collect { |f| f.gsub("?", "[?]") } \
122
+ .join("\n")
123
+ #.collect { |f| File.join(prefix, f) } \
119
124
  %>
120
125
 
121
126
  %changelog
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.10
4
+ version: 0.4.11
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-25 00:00:00.000000000 Z
12
+ date: 2012-08-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -50,7 +50,7 @@ dependencies:
50
50
  requirements:
51
51
  - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: 2.3.0
53
+ version: 2.6.2
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,7 +58,7 @@ dependencies:
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 2.3.0
61
+ version: 2.6.2
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: arr-pm
64
64
  requirement: !ruby/object:Gem::Requirement
@@ -166,7 +166,6 @@ files:
166
166
  - lib/fpm/package/dir.rb
167
167
  - lib/fpm/package/puppet.rb
168
168
  - lib/fpm/package/solaris.rb
169
- - lib/fpm/monkeypatches.rb
170
169
  - lib/fpm/package.rb
171
170
  - lib/fpm/namespace.rb
172
171
  - lib/fpm.rb
@@ -1,11 +0,0 @@
1
- # Copied from https://raw.github.com/marcandre/backports/master/lib/backports/1.9.3/io.rb
2
- # Also Hacked just to make it work
3
- # This is necessary until a newer version of backports (> 2.3.0) is available
4
- class << IO
5
- # Standard in Ruby 1.9.3 See official documentation[http://ruby-doc.org/core-1.9.3/IO.html#method-c-write]
6
- def write(name, string, offset = nil, options = Backports::Undefined)
7
- File.open(name, "w") do |fd|
8
- fd.write(string)
9
- end
10
- end unless method_defined? :write
11
- end