cure-fpm 1.3.3b → 1.6.0b

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELIST +73 -0
  3. data/CONTRIBUTORS +1 -1
  4. data/LICENSE +1 -1
  5. data/lib/fpm.rb +2 -0
  6. data/lib/fpm/command.rb +100 -50
  7. data/lib/fpm/package.rb +42 -28
  8. data/lib/fpm/package/apk.rb +510 -0
  9. data/lib/fpm/package/cpan.rb +50 -25
  10. data/lib/fpm/package/deb.rb +211 -47
  11. data/lib/fpm/package/dir.rb +29 -28
  12. data/lib/fpm/package/empty.rb +6 -0
  13. data/lib/fpm/package/freebsd.rb +144 -0
  14. data/lib/fpm/package/gem.rb +5 -5
  15. data/lib/fpm/package/npm.rb +2 -2
  16. data/lib/fpm/package/osxpkg.rb +8 -7
  17. data/lib/fpm/package/p5p.rb +124 -0
  18. data/lib/fpm/package/pacman.rb +399 -0
  19. data/lib/fpm/package/pleaserun.rb +63 -0
  20. data/lib/fpm/package/pyfpm/get_metadata.py +9 -1
  21. data/lib/fpm/package/python.rb +19 -7
  22. data/lib/fpm/package/rpm.rb +58 -18
  23. data/lib/fpm/package/sh.rb +1 -7
  24. data/lib/fpm/package/solaris.rb +1 -1
  25. data/lib/fpm/package/tar.rb +14 -2
  26. data/lib/fpm/package/virtualenv.rb +145 -0
  27. data/lib/fpm/package/zip.rb +1 -1
  28. data/lib/fpm/rake_task.rb +60 -0
  29. data/lib/fpm/util.rb +176 -48
  30. data/lib/fpm/util/tar_writer.rb +80 -0
  31. data/lib/fpm/version.rb +1 -1
  32. data/templates/deb/postinst_upgrade.sh.erb +33 -2
  33. data/templates/deb/postrm_upgrade.sh.erb +10 -1
  34. data/templates/deb/preinst_upgrade.sh.erb +11 -2
  35. data/templates/deb/prerm_upgrade.sh.erb +14 -2
  36. data/templates/p5p_metadata.erb +12 -0
  37. data/templates/pacman.erb +47 -0
  38. data/templates/pacman/INSTALL.erb +41 -0
  39. data/templates/pleaserun/generate-cleanup.sh +17 -0
  40. data/templates/pleaserun/install-path.sh +17 -0
  41. data/templates/pleaserun/install.sh +117 -0
  42. data/templates/pleaserun/scripts/after-install.sh +4 -0
  43. data/templates/pleaserun/scripts/before-remove.sh +12 -0
  44. data/templates/rpm.erb +38 -6
  45. data/templates/sh.erb +38 -3
  46. metadata +81 -9
@@ -0,0 +1,399 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "fpm/package"
3
+ require "fpm/util"
4
+ require "backports"
5
+ require "fileutils"
6
+ require "find"
7
+
8
+ class FPM::Package::Pacman < FPM::Package
9
+
10
+ option "--optional-depends", "PACKAGE",
11
+ "Add an optional dependency to the pacman package.", :multivalued => true
12
+
13
+ option "--use-file-permissions", :flag,
14
+ "Use existing file permissions when defining ownership and modes"
15
+
16
+ option "--user", "USER", "The owner of files in this package", :default => 'root'
17
+
18
+ option "--group", "GROUP", "The group owner of files in this package", :default => 'root'
19
+
20
+ # The list of supported compression types. Default is xz (LZMA2)
21
+ COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "none" ]
22
+
23
+ option "--compression", "COMPRESSION", "The compression type to use, must " \
24
+ "be one of #{COMPRESSION_TYPES.join(", ")}.", :default => "xz" do |value|
25
+ if !COMPRESSION_TYPES.include?(value)
26
+ raise ArgumentError, "Pacman compression value of '#{value}' is invalid. " \
27
+ "Must be one of #{COMPRESSION_TYPES.join(", ")}"
28
+ end
29
+ value
30
+ end
31
+
32
+ def initialize(*args)
33
+ super(*args)
34
+ attributes[:pacman_optional_depends] = []
35
+ end # def initialize
36
+
37
+ def architecture
38
+ case @architecture
39
+ when nil
40
+ return %x{uname -m}.chomp # default to current arch
41
+ when "amd64" # debian and pacman disagree on architecture names
42
+ return "x86_64"
43
+ when "native"
44
+ return %x{uname -m}.chomp # 'native' is current arch
45
+ when "all", "any", "noarch"
46
+ return "any"
47
+ else
48
+ return @architecture
49
+ end
50
+ end # def architecture
51
+
52
+ def iteration
53
+ return @iteration || 1
54
+ end # def iteration
55
+
56
+ def config_files
57
+ return @config_files || []
58
+ end # def config_files
59
+
60
+ def dependencies
61
+ bogus_regex = /[^\sA-Za-z0-9><=-]/
62
+ # Actually modifies depencies if they are not right
63
+ bogus_dependencies = @dependencies.grep bogus_regex
64
+ if bogus_dependencies.any?
65
+ @dependencies.reject! { |a| a =~ bogus_regex }
66
+ logger.warn("Some of the dependencies looked like they weren't package " \
67
+ "names. Such dependency entries only serve to confuse arch. " \
68
+ "I am removing them.",
69
+ :removed_dependencies => bogus_dependencies,
70
+ :fixed_dependencies => @dependencies)
71
+ end
72
+ return @dependencies
73
+ end
74
+
75
+
76
+ # This method is invoked on a package when it has been converted to a new
77
+ # package format. The purpose of this method is to do any extra conversion
78
+ # steps, like translating dependency conditions, etc.
79
+ #def converted_from(origin)
80
+ # nothing to do by default. Subclasses may implement this.
81
+ # See the RPM package class for an example.
82
+ #end # def converted_from
83
+
84
+ # Add a new source to this package.
85
+ # The exact behavior depends on the kind of package being managed.
86
+ #
87
+ # For instance:
88
+ #
89
+ # * for FPM::Package::Dir, << expects a path to a directory or files.
90
+ # * for FPM::Package::RPM, << expects a path to an rpm.
91
+ #
92
+ # The idea is that you can keep pumping in new things to a package
93
+ # for later conversion or output.
94
+ #
95
+ # Implementations are expected to put files relevant to the 'input' in the
96
+ # staging_path
97
+ def input(pacman_pkg_path)
98
+ control = {}
99
+ # Unpack the control tarball
100
+ safesystem(tar_cmd, "-C", staging_path, "-xf", pacman_pkg_path)
101
+ pkginfo = staging_path(".PKGINFO")
102
+ mtree = staging_path(".MTREE")
103
+ install = staging_path(".INSTALL")
104
+
105
+ control_contents = File.read(pkginfo)
106
+ FileUtils.rm(pkginfo)
107
+ FileUtils.rm(mtree)
108
+
109
+
110
+ control_lines = control_contents.split("\n")
111
+ control_lines.each do |line|
112
+ key, val = line.split(/ += +/, 2)
113
+ if control.has_key? key
114
+ control[key].push(val)
115
+ else
116
+ control[key] = [val]
117
+ end
118
+ end
119
+
120
+ self.name = control["pkgname"][0]
121
+
122
+ # Parse 'epoch:version-iteration' in the version string
123
+ version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
124
+ m = version_re.match(control["pkgver"][0])
125
+ if !m
126
+ raise "Unsupported version string '#{control["pkgver"][0]}'"
127
+ end
128
+ self.epoch, self.version, self.iteration = m.captures
129
+
130
+ self.maintainer = control["packager"][0]
131
+
132
+ # Arch has no notion of vendor, so...
133
+ #self.vendor =
134
+
135
+ self.url = control["url"][0]
136
+ # Groups could include more than one.
137
+ # Speaking of just taking the first entry of the field:
138
+ # A crude thing to do, but I suppose it's better than nothing.
139
+ # -- Daniel Haskin, 3/24/2015
140
+ self.category = control["group"][0] || self.category
141
+
142
+ # Licenses could include more than one.
143
+ # Speaking of just taking the first entry of the field:
144
+ # A crude thing to do, but I suppose it's better than nothing.
145
+ # -- Daniel Haskin, 3/24/2015
146
+ self.license = control["license"][0] || self.license
147
+
148
+ self.architecture = control["arch"][0]
149
+
150
+ self.dependencies = control["depend"] || self.dependencies
151
+
152
+ self.provides = control["provides"] || self.provides
153
+
154
+ self.conflicts = control["conflict"] || self.conflicts
155
+
156
+ self.replaces = control["replaces"] || self.replaces
157
+
158
+ self.description = control["pkgdesc"][0]
159
+
160
+ if control.include? "backup"
161
+ self.config_files = control["backup"].map{|file| "/" + file}
162
+ else
163
+ self.config_files = []
164
+ end
165
+
166
+ self.dependencies = control["depend"] || self.dependencies
167
+
168
+ self.attributes[:pacman_optional_depends] = control["optdepend"] || []
169
+ # There are other available attributes, but I didn't include them because:
170
+ # - makedepend: deps needed to make the arch package. But it's already
171
+ # made. It just needs to be converted at this point
172
+ # - checkdepend: See above
173
+ # - makepkgopt: See above
174
+ # - size: can be dynamically generated
175
+ # - builddate: Should be changed to time of package conversion in the new
176
+ # package, so this value should be thrown away.
177
+
178
+ if File.exist?(install)
179
+ functions = parse_install_script(install)
180
+ if functions.include?("pre_install")
181
+ self.scripts[:before_install] = functions["pre_install"].join("\n")
182
+ end
183
+ if functions.include?("post_install")
184
+ self.scripts[:after_install] = functions["post_install"].join("\n")
185
+ end
186
+ if functions.include?("pre_upgrade")
187
+ self.scripts[:before_upgrade] = functions["pre_upgrade"].join("\n")
188
+ end
189
+ if functions.include?("post_upgrade")
190
+ self.scripts[:after_upgrade] = functions["post_upgrade"].join("\n")
191
+ end
192
+ if functions.include?("pre_remove")
193
+ self.scripts[:before_remove] = functions["pre_remove"].join("\n")
194
+ end
195
+ if functions.include?("post_remove")
196
+ self.scripts[:after_remove] = functions["post_remove"].join("\n")
197
+ end
198
+ FileUtils.rm(install)
199
+ end
200
+
201
+ # Note: didn't use `self.directories`.
202
+ # Pacman doesn't really record that information, to my knowledge.
203
+
204
+ end # def input
205
+
206
+ def compression_option
207
+ case self.attributes[:pacman_compression]
208
+ when nil, "xz"
209
+ return "--xz"
210
+ when "none"
211
+ return ""
212
+ when "gz"
213
+ return "-z"
214
+ when "bzip2"
215
+ return "-j"
216
+ else
217
+ return "--xz"
218
+ end
219
+ end
220
+
221
+ def compression_ending
222
+ case self.attributes[:pacman_compression]
223
+ when nil, "xz"
224
+ return ".xz"
225
+ when "none"
226
+ return ""
227
+ when "gz"
228
+ return ".gz"
229
+ when "bzip2"
230
+ return ".bz2"
231
+ else
232
+ return ".xz"
233
+ end
234
+ end
235
+
236
+ # Output this package to the given path.
237
+ def output(output_path)
238
+ output_check(output_path)
239
+
240
+ # Copy all files from staging to BUILD dir
241
+ Find.find(staging_path) do |path|
242
+ src = path.gsub(/^#{staging_path}/, '')
243
+ dst = build_path(src)
244
+ copy_entry(path, dst, preserve=true, remove_destination=true)
245
+ copy_metadata(path, dst)
246
+ end
247
+
248
+ # This value is used later in the template for PKGINFO
249
+ size = safesystemout("du", "-sk", build_path).split(/\s+/)[0].to_i * 1024
250
+ builddate = Time.new.to_i
251
+
252
+ pkginfo = template("pacman.erb").result(binding)
253
+ pkginfo_file = build_path(".PKGINFO")
254
+ File.write(pkginfo_file, pkginfo)
255
+
256
+ if script?(:before_install) or script?(:after_install) or \
257
+ script?(:before_upgrade) or script?(:after_upgrade) or \
258
+ script?(:before_remove) or script?(:after_remove)
259
+ install_script = template("pacman/INSTALL.erb").result(binding)
260
+ install_script_file = build_path(".INSTALL")
261
+ File.write(install_script_file, install_script)
262
+ end
263
+
264
+ generate_mtree
265
+
266
+ File.expand_path(output_path).tap do |path|
267
+ ::Dir.chdir(build_path) do
268
+ safesystem(*([tar_cmd,
269
+ compression_option,
270
+ "-cf",
271
+ path] + data_tar_flags + \
272
+ ::Dir.entries(".").reject{|entry| entry =~ /^\.{1,2}$/ }))
273
+ end
274
+ end
275
+ end # def output
276
+
277
+ def data_tar_flags
278
+ data_tar_flags = []
279
+ if attributes[:pacman_use_file_permissions?].nil?
280
+ if !attributes[:pacman_user].nil?
281
+ if attributes[:pacman_user] == 'root'
282
+ data_tar_flags += [ "--numeric-owner", "--owner", "0" ]
283
+ else
284
+ data_tar_flags += [ "--owner", attributes[:deb_user] ]
285
+ end
286
+ end
287
+
288
+ if !attributes[:pacman_group].nil?
289
+ if attributes[:pacman_group] == 'root'
290
+ data_tar_flags += [ "--numeric-owner", "--group", "0" ]
291
+ else
292
+ data_tar_flags += [ "--group", attributes[:deb_group] ]
293
+ end
294
+ end
295
+ end
296
+ return data_tar_flags
297
+ end # def data_tar_flags
298
+
299
+ def default_output
300
+ v = version
301
+ v = "#{epoch}:#{v}" if epoch
302
+ if iteration
303
+ "#{name}_#{v}-#{iteration}_#{architecture}.#{type}"
304
+ else
305
+ "#{name}_#{v}_#{architecture}.#{type}"
306
+ end
307
+ end # def default_output
308
+
309
+ def to_s_extension; "pkg.tar#{compression_ending}"; end
310
+
311
+ def to_s(format=nil)
312
+ # Default format if nil
313
+ # git_1.7.9.3-1-amd64.pkg.tar.xz
314
+ return super(format.nil? ? "NAME-FULLVERSION-ARCH.EXTENSION" : format)
315
+ end # def to_s
316
+
317
+ private
318
+
319
+ def generate_mtree
320
+ ::Dir.chdir(build_path) do
321
+ cmd = "LANG=C bsdtar "
322
+ cmd += "-czf .MTREE "
323
+ cmd += "--format=mtree "
324
+ cmd += "--options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' "
325
+ cmd += ::Dir.entries(".").reject{|entry| entry =~ /^\.{1,2}$/ }.join(" ")
326
+ safesystem(cmd)
327
+ end
328
+ end # def generate_mtree
329
+
330
+ # KNOWN ISSUE:
331
+ # If an un-matched bracket is used in valid bash, as in
332
+ # `echo "{"`, this function will choke.
333
+ # However, to cover this case basically
334
+ # requires writing almost half a bash parser,
335
+ # and it is a very small corner case.
336
+ # Otherwise, this approach is very robust.
337
+ def gobble_function(cons,prod)
338
+ level = 1
339
+ while level > 0
340
+ line = cons.next
341
+ # Not the best, but pretty good
342
+ # short of writing an *actual* sh
343
+ # parser
344
+ level += line.count "{"
345
+ level -= line.count "}"
346
+ if level > 0
347
+ prod.push(line.rstrip())
348
+ else
349
+ fine = line.sub(/\s*[}]\s*$/, "")
350
+ if !(fine =~ /^\s*$/)
351
+ prod.push(fine.rstrip())
352
+ end
353
+ end
354
+ end
355
+ end # def gobble_function
356
+
357
+ FIND_SCRIPT_FUNCTION_LINE =
358
+ /^\s*(\w+)\s*\(\s*\)\s*\{\s*([^}]+?)?\s*(\})?\s*$/
359
+
360
+ def parse_install_script(path)
361
+ global_lines = []
362
+ look_for = Set.new(["pre_install", "post_install",
363
+ "pre_upgrade", "post_upgrade",
364
+ "pre_remove", "post_remove"])
365
+ functions = {}
366
+ look_for.each do |fname|
367
+ functions[fname] = []
368
+ end
369
+
370
+ open(path, "r") do |iscript|
371
+ lines = iscript.each
372
+ begin
373
+ while true
374
+ line = lines.next
375
+ # This regex picks up beginning names of posix shell
376
+ # functions
377
+ # Examples:
378
+ # fname() {
379
+ # fname() { echo hi }
380
+ m = FIND_SCRIPT_FUNCTION_LINE.match(line)
381
+ if not m.nil? and look_for.include? m[1]
382
+ if not m[2].nil?
383
+ functions[m[1]].push(m[2].rstrip())
384
+ end
385
+ gobble_function(lines, functions[m[1]])
386
+ else
387
+ global_lines.push(line.rstrip())
388
+ end
389
+ end
390
+ rescue StopIteration
391
+ end
392
+ end
393
+ look_for.each do |name|
394
+ # Add global lines to each function to preserve global variables, etc.
395
+ functions[name] = global_lines + functions[name]
396
+ end
397
+ return functions
398
+ end # def parse_install_script
399
+ end # class FPM::Package::Pacman
@@ -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