fpm 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,7 +23,7 @@ class FPM::Package::Deb < FPM::Package
23
23
  # The list of supported compression types. Default is gz (gzip)
24
24
  COMPRESSION_TYPES = [ "gz", "bzip2", "xz" ]
25
25
 
26
- option "--ignore-iteration-in-dependencies", :flag,
26
+ option "--ignore-iteration-in-dependencies", :flag,
27
27
  "For '=' (equal) dependencies, allow iterations on the specified " \
28
28
  "version. Default is to be specific. This option allows the same " \
29
29
  "version of a package but any iteration is permitted"
@@ -73,10 +73,10 @@ class FPM::Package::Deb < FPM::Package
73
73
  value.to_i
74
74
  end
75
75
 
76
- option "--priority", "PRIORITY",
76
+ option "--priority", "PRIORITY",
77
77
  "The debian package 'priority' value.", :default => "extra"
78
78
 
79
- option "--use-file-permissions", :flag,
79
+ option "--use-file-permissions", :flag,
80
80
  "Use existing file permissions when defining ownership and modes"
81
81
 
82
82
  option "--user", "USER", "The owner of files in this package", :default => 'root'
@@ -124,6 +124,10 @@ class FPM::Package::Deb < FPM::Package
124
124
  next @custom_fields
125
125
  end
126
126
 
127
+ option "--no-default-config-files", :flag,
128
+ "Do not add all files in /etc as configuration files by default for Debian packages.",
129
+ :default => false
130
+
127
131
  option "--shlibs", "SHLIBS", "Include control/shlibs content. This flag " \
128
132
  "expects a string that is used as the contents of the shlibs file. " \
129
133
  "See the following url for a description of this file and its format: " \
@@ -162,7 +166,7 @@ class FPM::Package::Deb < FPM::Package
162
166
  @architecture = %x{dpkg --print-architecture 2> /dev/null}.chomp
163
167
  if $?.exitstatus != 0 or @architecture.empty?
164
168
  # if dpkg fails or emits nothing, revert back to uname -m
165
- @architecture = %x{uname -m}.chomp
169
+ @architecture = %x{uname -m}.chomp
166
170
  end
167
171
  else
168
172
  @architecture = %x{uname -m}.chomp
@@ -209,7 +213,7 @@ class FPM::Package::Deb < FPM::Package
209
213
 
210
214
  return @name
211
215
  end # def name
212
-
216
+
213
217
  def prefix
214
218
  return (attributes[:prefix] or "/")
215
219
  end # def prefix
@@ -227,7 +231,7 @@ class FPM::Package::Deb < FPM::Package
227
231
 
228
232
  control = File.read(File.join(path, "control"))
229
233
 
230
- parse = lambda do |field|
234
+ parse = lambda do |field|
231
235
  value = control[/^#{field.capitalize}: .*/]
232
236
  if value.nil?
233
237
  return nil
@@ -267,6 +271,22 @@ class FPM::Package::Deb < FPM::Package
267
271
  #self.config_files = config_files
268
272
 
269
273
  self.dependencies += parse_depends(parse.call("Depends")) if !attributes[:no_auto_depends?]
274
+
275
+ if File.file?(File.join(path, "preinst"))
276
+ self.scripts[:before_install] = File.read(File.join(path, "preinst"))
277
+ end
278
+ if File.file?(File.join(path, "postinst"))
279
+ self.scripts[:after_install] = File.read(File.join(path, "postinst"))
280
+ end
281
+ if File.file?(File.join(path, "prerm"))
282
+ self.scripts[:before_remove] = File.read(File.join(path, "prerm"))
283
+ end
284
+ if File.file?(File.join(path, "postrm"))
285
+ self.scripts[:after_remove] = File.read(File.join(path, "postrm"))
286
+ end
287
+ if File.file?(File.join(path, "conffiles"))
288
+ self.config_files = File.read(File.join(path, "conffiles")).split("\n")
289
+ end
270
290
  end
271
291
  end # def extract_info
272
292
 
@@ -289,7 +309,7 @@ class FPM::Package::Deb < FPM::Package
289
309
  m = dep_re.match(dep)
290
310
  if m
291
311
  name, op, version = m.captures
292
- # deb uses ">>" and "<<" for greater and less than respectively.
312
+ # deb uses ">>" and "<<" for greater and less than respectively.
293
313
  # fpm wants just ">" and "<"
294
314
  op = "<" if op == "<<"
295
315
  op = ">" if op == ">>"
@@ -309,10 +329,10 @@ class FPM::Package::Deb < FPM::Package
309
329
  when "gz"
310
330
  datatar = "data.tar.gz"
311
331
  compression = "-z"
312
- when "bzip2"
332
+ when "bzip2"
313
333
  datatar = "data.tar.bz2"
314
334
  compression = "-j"
315
- when "xz"
335
+ when "xz"
316
336
  datatar = "data.tar.xz"
317
337
  compression = "-J"
318
338
  else
@@ -373,7 +393,7 @@ class FPM::Package::Deb < FPM::Package
373
393
  when "gz", nil
374
394
  datatar = build_path("data.tar.gz")
375
395
  compression = "-z"
376
- when "bzip2"
396
+ when "bzip2"
377
397
  datatar = build_path("data.tar.bz2")
378
398
  compression = "-j"
379
399
  when "xz"
@@ -577,7 +597,7 @@ class FPM::Package::Deb < FPM::Package
577
597
  with(build_path("control.tar.gz")) do |controltar|
578
598
  logger.info("Creating", :path => controltar, :from => control_path)
579
599
 
580
- args = [ tar_cmd, "-C", control_path, "-zcf", controltar,
600
+ args = [ tar_cmd, "-C", control_path, "-zcf", controltar,
581
601
  "--owner=0", "--group=0", "--numeric-owner", "." ]
582
602
  safesystem(*args)
583
603
  end
@@ -637,33 +657,48 @@ class FPM::Package::Deb < FPM::Package
637
657
  # deb maintainer scripts are required to be executable
638
658
  File.chmod(0755, controlscript)
639
659
  end
640
- end
660
+ end
641
661
  end # def write_scripts
642
662
 
643
663
  def write_conffiles
644
- return unless config_files.any?
645
-
646
- # scan all conf file paths for files and add them
647
664
  allconfigs = []
648
- config_files.each do |path|
665
+
666
+ # expand recursively a given path to be put in allconfigs
667
+ def add_path(path, allconfigs)
649
668
  # Strip leading /
650
669
  path = path[1..-1] if path[0,1] == "/"
651
670
  cfg_path = File.expand_path(path, staging_path)
671
+ Find.find(cfg_path).select { |p| File.file?(p) }.each do |p|
672
+ allconfigs << p.gsub("#{staging_path}/", '')
673
+ end
674
+ end
675
+
676
+ # scan all conf file paths for files and add them
677
+ config_files.each do |path|
652
678
  begin
653
- Find.find(cfg_path) do |p|
654
- allconfigs << p.gsub("#{staging_path}/", '') if File.file? p
655
- end
656
- rescue Errno::ENOENT => e
679
+ add_path(path, allconfigs)
680
+ rescue Errno::ENOENT
657
681
  raise FPM::InvalidPackageConfiguration,
658
- "Error trying to use '#{cfg_path}' as a config file in the package. Does it exist?"
682
+ "Error trying to use '#{path}' as a config file in the package. Does it exist?"
683
+ end
684
+ end
685
+
686
+ # Also add everything in /etc
687
+ begin
688
+ if !attributes[:deb_no_default_config_files?]
689
+ logger.warn("Debian packaging tools generally labels all files in /etc as config files, " \
690
+ "as mandated by policy, so fpm defaults to this behavior for deb packages. " \
691
+ "You can disable this default behavior with --deb-no-default-config-files flag")
692
+ add_path("/etc", allconfigs)
659
693
  end
694
+ rescue Errno::ENOENT
660
695
  end
696
+
661
697
  allconfigs.sort!.uniq!
698
+ return unless allconfigs.any?
662
699
 
663
700
  with(control_path("conffiles")) do |conffiles|
664
701
  File.open(conffiles, "w") do |out|
665
- # 'config_files' comes from FPM::Package and is usually set with
666
- # FPM::Command's --config-files flag
667
702
  allconfigs.each do |cf|
668
703
  # We need to put the leading / back. Stops lintian relative-conffile error.
669
704
  out.puts("/" + cf)
@@ -679,6 +714,7 @@ class FPM::Package::Deb < FPM::Package
679
714
  File.open(control_path("shlibs"), "w") do |out|
680
715
  out.write(attributes[:deb_shlibs])
681
716
  end
717
+ File.chmod(0644, control_path("shlibs"))
682
718
  end # def write_shlibs
683
719
 
684
720
  def write_debconf
@@ -75,7 +75,7 @@ class FPM::Package::Dir < FPM::Package
75
75
  end
76
76
  end
77
77
  rescue Errno::ENOENT => e
78
- raise FPM::InvalidPackageConfiguration,
78
+ raise FPM::InvalidPackageConfiguration,
79
79
  "Cannot chdir to '#{chdir}'. Does it exist?"
80
80
  end
81
81
 
@@ -133,14 +133,14 @@ class FPM::Package::Dir < FPM::Package
133
133
 
134
134
  # For single file copies, permit file destinations
135
135
  fileinfo = File.lstat(source)
136
- if fileinfo.file? && !File.directory?(destination)
136
+ if fileinfo.file? && !File.directory?(destination)
137
137
  if destination[-1,1] == "/"
138
138
  copy(source, File.join(destination, source))
139
139
  else
140
140
  copy(source, destination)
141
141
  end
142
142
  elsif fileinfo.symlink?
143
- copy(source, destination)
143
+ copy(source, File.join(destination, source))
144
144
  else
145
145
  # Copy all files from 'path' into staging_path
146
146
  Find.find(source) do |path|
@@ -157,8 +157,17 @@ class FPM::Package::Dir < FPM::Package
157
157
  def copy(source, destination)
158
158
  logger.debug("Copying path", :source => source, :destination => destination)
159
159
  directory = File.dirname(destination)
160
- if !File.directory?(directory)
160
+ # lstat to follow symlinks
161
+ dstat = File.stat(directory) rescue nil
162
+ if dstat.nil?
161
163
  FileUtils.mkdir_p(directory)
164
+ elsif dstat.directory?
165
+ # do nothing, it's already a directory!
166
+ else
167
+ # It exists and is not a directory. This is probably a user error or a bug.
168
+ readable_path = directory.gsub(staging_path, "")
169
+ logger.error("You wanted to copy a file into a directory, but that's not a directory, it's a file!", :path => readable_path, :stat => dstat)
170
+ raise FPM::InvalidPackageConfiguration, "Tried to treat #{readable_path} like a directory, but it's a file!"
162
171
  end
163
172
 
164
173
  if File.directory?(source)
@@ -194,27 +203,5 @@ class FPM::Package::Dir < FPM::Package
194
203
  copy_metadata(source, destination)
195
204
  end # def copy
196
205
 
197
- def copy_metadata(source, destination)
198
- source_stat = File::lstat(source)
199
- dest_stat = File::lstat(destination)
200
-
201
- # If this is a hard-link, there's no metadata to copy.
202
- # If this is a symlink, what it points to hasn't been copied yet.
203
- return if source_stat.ino == dest_stat.ino || dest_stat.symlink?
204
-
205
- File.utime(source_stat.atime, source_stat.mtime, destination)
206
- mode = source_stat.mode
207
- begin
208
- File.lchown(source_stat.uid, source_stat.gid, destination)
209
- rescue Errno::EPERM
210
- # clear setuid/setgid
211
- mode &= 01777
212
- end
213
-
214
- unless source_stat.symlink?
215
- File.chmod(mode, destination)
216
- end
217
- end # def copy_metadata
218
-
219
206
  public(:input, :output)
220
207
  end # class FPM::Package::Dir
@@ -4,4 +4,10 @@ require "backports"
4
4
  # Empty Package type. For strict/meta/virtual package creation
5
5
 
6
6
  class FPM::Package::Empty < FPM::Package
7
+ def output(output_path)
8
+ logger.warn("Your package has gone into the void.")
9
+ end
10
+ def to_s(fmt)
11
+ return ""
12
+ end
7
13
  end
@@ -58,7 +58,7 @@ class FPM::Package::Gem < FPM::Package
58
58
 
59
59
  def download_if_necessary(gem, gem_version)
60
60
  path = gem
61
- if !File.exists?(path)
61
+ if !File.exist?(path)
62
62
  path = download(gem, gem_version)
63
63
  end
64
64
 
@@ -114,7 +114,7 @@ class FPM::Package::Gem < FPM::Package
114
114
  # where missing 'build' number prevents correct dependency resolution by target
115
115
  # package manager. Ie. for dpkg 1.1 != 1.1.0
116
116
  m = spec.version.to_s.scan(/(\d+)\.?/)
117
- self.version = m.flatten.fill('0', m.length..2).join('.')
117
+ self.version = m.flatten.fill('0', m.length..2).join('.')
118
118
 
119
119
  self.vendor = spec.author
120
120
  self.url = spec.homepage
@@ -132,7 +132,7 @@ class FPM::Package::Gem < FPM::Package
132
132
  self.description = description_options.find { |d| !(d.nil? or d.strip.empty?) }
133
133
 
134
134
  # Upstream rpms seem to do this, might as well share.
135
- # TODO(sissel): Figure out how to hint this only to rpm?
135
+ # TODO(sissel): Figure out how to hint this only to rpm?
136
136
  # maybe something like attributes[:rpm_provides] for rpm specific stuff?
137
137
  # Or just ignore it all together.
138
138
  #self.provides << "rubygem(#{self.name})"
@@ -213,7 +213,7 @@ class FPM::Package::Gem < FPM::Package
213
213
  end
214
214
  end
215
215
  end # def install_to_staging
216
-
216
+
217
217
  # Sanitize package name.
218
218
  # This prefixes the package name with 'rubygem' (but depends on the attribute
219
219
  # :gem_package_name_prefix
@@ -72,7 +72,7 @@ class FPM::Package::NPM < FPM::Package
72
72
  end
73
73
 
74
74
  self.description = info["description"]
75
- # Supposedly you can upload a package for npm with no author/author email
75
+ # Supposedly you can upload a package for npm with no author/author email
76
76
  # so I'm being safer with this. Author can also be a hash or a string
77
77
  self.vendor = "Unknown <unknown@unknown.unknown>"
78
78
  if info.include?("author")
@@ -91,7 +91,7 @@ class FPM::Package::NPM < FPM::Package
91
91
  # 'npm install express' it installs dependencies (like 'connect')
92
92
  # to: node_modules/express/node_modules/connect/...
93
93
  #
94
- # To that end, I don't think we necessarily need to include
94
+ # To that end, I don't think we necessarily need to include
95
95
  # any automatic dependency information since every 'npm install'
96
96
  # is fully self-contained. That's why you don't see any bother, yet,
97
97
  # to include the package's dependencies in here.
@@ -21,7 +21,7 @@ class FPM::Package::OSXpkg < FPM::Package
21
21
  POSTINSTALL_ACTIONS = [ "logout", "restart", "shutdown" ]
22
22
  OWNERSHIP_OPTIONS = ["recommended", "preserve", "preserve-other"]
23
23
 
24
- option "--identifier-prefix", "IDENTIFIER_PREFIX",
24
+ option "--identifier-prefix", "IDENTIFIER_PREFIX",
25
25
  "Reverse domain prefix prepended to package identifier, " \
26
26
  "ie. 'org.great.my'. If this is omitted, the identifer " \
27
27
  "will be the package name."
@@ -87,12 +87,12 @@ class FPM::Package::OSXpkg < FPM::Package
87
87
  # scripts are required to be executable
88
88
  File.chmod(0755, pkgscript)
89
89
  end
90
- end
90
+ end
91
91
  end # def write_scripts
92
92
 
93
93
  # Returns path of a processed template PackageInfo given to 'pkgbuild --info'
94
94
  # note: '--info' is undocumented:
95
- # http://managingosx.wordpress.com/2012/07/05/stupid-tricks-with-pkgbuild
95
+ # http://managingosx.wordpress.com/2012/07/05/stupid-tricks-with-pkgbuild
96
96
  def pkginfo_template_path
97
97
  pkginfo_template = Tempfile.open("fpm-PackageInfo")
98
98
  pkginfo_data = template("osxpkg.erb").result(binding)
@@ -0,0 +1,124 @@
1
+ require "erb"
2
+ require "fpm/namespace"
3
+ require "fpm/package"
4
+ require "fpm/errors"
5
+ require "fpm/util"
6
+
7
+ class FPM::Package::P5P < FPM::Package
8
+
9
+ option "--user", "USER",
10
+ "Set the user to USER in the prototype files.",
11
+ :default => 'root'
12
+
13
+ option "--group", "GROUP",
14
+ "Set the group to GROUP in the prototype file.",
15
+ :default => 'root'
16
+
17
+ option "--zonetype", "ZONETYPE",
18
+ "Set the allowed zone types (global, nonglobal, both)",
19
+ :default => 'value=global value=nonglobal' do |value|
20
+ case @value
21
+ when "both"
22
+ value = "value=global value=nonglobal"
23
+ else
24
+ value = "value=#{value}"
25
+ end
26
+ end # value
27
+
28
+ option "--publisher", "PUBLISHER",
29
+ "Set the publisher name for the repository",
30
+ :default => 'FPM'
31
+
32
+ option "--lint" , :flag, "Check manifest with pkglint",
33
+ :default => true
34
+
35
+ option "--validate", :flag, "Validate with pkg install",
36
+ :default => true
37
+
38
+ def architecture
39
+ case @architecture
40
+ when nil, "native"
41
+ @architecture = %x{uname -p}.chomp
42
+ when "all"
43
+ @architecture = 'i386 value=sparc'
44
+ end
45
+
46
+ return @architecture
47
+ end # def architecture
48
+
49
+ def output(output_path)
50
+
51
+ # Fixup the category to an acceptable solaris category
52
+ case @category
53
+ when nil, "default"
54
+ @category = 'Applications/System Utilities'
55
+ end
56
+
57
+ # Manifest Filename
58
+ manifest_fn = build_path("#{name}.p5m")
59
+
60
+ # Generate a package manifest.
61
+ pkg_generate = safesystemout("pkgsend", "generate", "#{staging_path}")
62
+ File.write(build_path("#{name}.p5m.1"), pkg_generate)
63
+
64
+ # Add necessary metadata to the generated manifest.
65
+ metadata_template = template("p5p_metadata.erb").result(binding)
66
+ File.write(build_path("#{name}.mog"), metadata_template)
67
+
68
+ # Combine template and filelist; allow user to edit before proceeding
69
+ File.open(manifest_fn, "w") do |manifest|
70
+ manifest.write metadata_template
71
+ manifest.write pkg_generate
72
+ end
73
+ edit_file(manifest_fn) if attributes[:edit?]
74
+
75
+ # Execute the transmogrification on the manifest
76
+ pkg_mogrify = safesystemout("pkgmogrify", manifest_fn)
77
+ File.write(build_path("#{name}.p5m.2"), pkg_mogrify)
78
+ safesystem("cp", build_path("#{name}.p5m.2"), manifest_fn)
79
+
80
+ # Evaluate dependencies.
81
+ if !attributes[:no_auto_depends?]
82
+ pkgdepend_gen = safesystemout("pkgdepend", "generate", "-md", "#{staging_path}", manifest_fn)
83
+ File.write(build_path("#{name}.p5m.3"), pkgdepend_gen)
84
+
85
+ # Allow user to review added dependencies
86
+ edit_file(build_path("#{name}.p5m.3")) if attributes[:edit?]
87
+
88
+ safesystem("pkgdepend", "resolve", "-m", build_path("#{name}.p5m.3"))
89
+ safesystem("cp", build_path("#{name}.p5m.3.res"), manifest_fn)
90
+ end
91
+
92
+ # Final format of manifest
93
+ safesystem("pkgfmt", manifest_fn)
94
+
95
+ # Final edit for lint check and packaging
96
+ edit_file(manifest_fn) if attributes[:edit?]
97
+
98
+ # Add any facets or actuators that are needed.
99
+ # TODO(jcraig): add manpage actuator to enable install wo/ man pages
100
+
101
+ # Verify the package.
102
+ if attributes[:p5p_lint] then
103
+ safesystem("pkglint", manifest_fn)
104
+ end
105
+
106
+ # Publish the package.
107
+ repo_path = build_path("#{name}_repo")
108
+ safesystem("pkgrepo", "create", repo_path)
109
+ safesystem("pkgrepo", "set", "-s", repo_path, "publisher/prefix=#{attributes[:p5p_publisher]}")
110
+ safesystem("pkgsend", "-s", repo_path,
111
+ "publish", "-d", "#{staging_path}", "#{build_path}/#{name}.p5m")
112
+ safesystem("pkgrecv", "-s", repo_path, "-a",
113
+ "-d", build_path("#{name}.p5p"), name)
114
+
115
+ # Test the package
116
+ if attributes[:p5p_validate] then
117
+ safesystem("pkg", "install", "-nvg", build_path("#{name}.p5p"), name)
118
+ end
119
+
120
+ # Cleanup
121
+ safesystem("mv", build_path("#{name}.p5p"), output_path)
122
+
123
+ end # def output
124
+ end # class FPM::Package::IPS