fpm 0.4.10 → 0.4.11

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