fpm-itchio 1.4.0

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELIST +629 -0
  3. data/CONTRIBUTORS +26 -0
  4. data/LICENSE +21 -0
  5. data/bin/fpm +8 -0
  6. data/lib/fpm.rb +18 -0
  7. data/lib/fpm/command.rb +642 -0
  8. data/lib/fpm/errors.rb +4 -0
  9. data/lib/fpm/namespace.rb +4 -0
  10. data/lib/fpm/package.rb +524 -0
  11. data/lib/fpm/package/cpan.rb +378 -0
  12. data/lib/fpm/package/deb.rb +887 -0
  13. data/lib/fpm/package/dir.rb +207 -0
  14. data/lib/fpm/package/empty.rb +13 -0
  15. data/lib/fpm/package/gem.rb +224 -0
  16. data/lib/fpm/package/npm.rb +120 -0
  17. data/lib/fpm/package/osxpkg.rb +164 -0
  18. data/lib/fpm/package/p5p.rb +124 -0
  19. data/lib/fpm/package/pacman.rb +397 -0
  20. data/lib/fpm/package/pear.rb +117 -0
  21. data/lib/fpm/package/pkgin.rb +35 -0
  22. data/lib/fpm/package/puppet.rb +120 -0
  23. data/lib/fpm/package/pyfpm/__init__.py +1 -0
  24. data/lib/fpm/package/pyfpm/get_metadata.py +104 -0
  25. data/lib/fpm/package/python.rb +317 -0
  26. data/lib/fpm/package/rpm.rb +583 -0
  27. data/lib/fpm/package/sh.rb +69 -0
  28. data/lib/fpm/package/solaris.rb +95 -0
  29. data/lib/fpm/package/tar.rb +74 -0
  30. data/lib/fpm/package/virtualenv.rb +145 -0
  31. data/lib/fpm/package/zip.rb +63 -0
  32. data/lib/fpm/rake_task.rb +59 -0
  33. data/lib/fpm/util.rb +253 -0
  34. data/lib/fpm/version.rb +3 -0
  35. data/templates/deb.erb +52 -0
  36. data/templates/deb/changelog.erb +5 -0
  37. data/templates/deb/ldconfig.sh.erb +13 -0
  38. data/templates/deb/postinst_upgrade.sh.erb +62 -0
  39. data/templates/deb/postrm_upgrade.sh.erb +46 -0
  40. data/templates/deb/preinst_upgrade.sh.erb +41 -0
  41. data/templates/deb/prerm_upgrade.sh.erb +39 -0
  42. data/templates/osxpkg.erb +11 -0
  43. data/templates/p5p_metadata.erb +12 -0
  44. data/templates/pacman.erb +47 -0
  45. data/templates/pacman/INSTALL.erb +41 -0
  46. data/templates/puppet/package.pp.erb +34 -0
  47. data/templates/puppet/package/remove.pp.erb +13 -0
  48. data/templates/rpm.erb +261 -0
  49. data/templates/rpm/filesystem_list +14514 -0
  50. data/templates/sh.erb +367 -0
  51. data/templates/solaris.erb +15 -0
  52. metadata +265 -0
@@ -0,0 +1,4 @@
1
+ require "fpm/namespace"
2
+
3
+ # Raised if a package is configured in an unsupported way
4
+ class FPM::InvalidPackageConfiguration < StandardError; end
@@ -0,0 +1,4 @@
1
+ # The FPM namespace
2
+ module FPM
3
+ class Package; end
4
+ end
@@ -0,0 +1,524 @@
1
+ require "fpm/namespace" # local
2
+ require "fpm/util" # local
3
+ require "pathname" # stdlib
4
+ require "find"
5
+ require "tmpdir" # stdlib
6
+ require "backports" # gem 'backports'
7
+ require "socket" # stdlib, for Socket.gethostname
8
+ require "shellwords" # stdlib, for Shellwords.escape
9
+ require "erb" # stdlib, for template processing
10
+ require "cabin" # gem "cabin"
11
+
12
+ # This class is the parent of all packages.
13
+ # If you want to implement an FPM package type, you'll inherit from this.
14
+ class FPM::Package
15
+ include FPM::Util
16
+ include Cabin::Inspectable
17
+
18
+ # This class is raised if there's something wrong with a setting in the package.
19
+ class InvalidArgument < StandardError; end
20
+
21
+ # This class is raised when a file already exists when trying to write.
22
+ class FileAlreadyExists < StandardError
23
+ # Get a human-readable error message
24
+ def to_s
25
+ return "File already exists, refusing to continue: #{super}"
26
+ end # def to_s
27
+ end # class FileAlreadyExists
28
+
29
+ # This class is raised when you try to output a package to a path
30
+ # whose containing directory does not exist.
31
+ class ParentDirectoryMissing < StandardError
32
+ def to_s
33
+ return "Parent directory does not exist: #{File.dirname(super)} - cannot write to #{super}"
34
+ end # def to_s
35
+ end # class ParentDirectoryMissing
36
+
37
+ # The name of this package
38
+ attr_accessor :name
39
+
40
+ # The version of this package (the upstream version)
41
+ attr_accessor :version
42
+
43
+ # The epoch version of this package
44
+ # This is used most when an upstream package changes it's versioning
45
+ # style so standard comparisions wouldn't work.
46
+ attr_accessor :epoch
47
+
48
+ # The iteration of this package.
49
+ # Debian calls this 'release' and is the last '-NUMBER' in the version
50
+ # RedHat has this as 'Release' in the .spec file
51
+ # FreeBSD calls this 'PORTREVISION'
52
+ #
53
+ # Iteration can be nil. If nil, the fpm package implementation is expected
54
+ # to handle any default value that should be instead.
55
+ attr_accessor :iteration
56
+
57
+ # Who maintains this package? This could be the upstream author
58
+ # or the package maintainer. You pick.
59
+ attr_accessor :maintainer
60
+
61
+ # A identifier representing the vendor. Any string is fine.
62
+ # This is usually who produced the software.
63
+ attr_accessor :vendor
64
+
65
+ # URL for this package.
66
+ # Could be the homepage. Could be the download url. You pick.
67
+ attr_accessor :url
68
+
69
+ # The category of this package.
70
+ # RedHat calls this 'Group'
71
+ # Debian calls this 'Section'
72
+ # FreeBSD would put this in /usr/ports/<category>/...
73
+ attr_accessor :category
74
+
75
+ # A identifier representing the license. Any string is fine.
76
+ attr_accessor :license
77
+
78
+ # What architecture is this package for?
79
+ attr_accessor :architecture
80
+
81
+ # Array of dependencies.
82
+ attr_accessor :dependencies
83
+
84
+ # Array of things this package provides.
85
+ # (Not all packages support this)
86
+ attr_accessor :provides
87
+
88
+ # Array of things this package conflicts with.
89
+ # (Not all packages support this)
90
+ attr_accessor :conflicts
91
+
92
+ # Array of things this package replaces.
93
+ # (Not all packages support this)
94
+ attr_accessor :replaces
95
+
96
+ # a summary or description of the package
97
+ attr_accessor :description
98
+
99
+ # hash of scripts for maintainer/package scripts (postinstall, etc)
100
+ #
101
+ # The keys are :before_install, etc
102
+ # The values are the text to use in the script.
103
+ attr_accessor :scripts
104
+
105
+ # Array of configuration files
106
+ attr_accessor :config_files
107
+
108
+ attr_accessor :directories
109
+
110
+ # Any other attributes specific to this package.
111
+ # This is where you'd put rpm, deb, or other specific attributes.
112
+ attr_accessor :attributes
113
+
114
+ attr_accessor :attrs
115
+
116
+ private
117
+
118
+ def initialize
119
+ # Attributes for this specific package
120
+ @attributes = {}
121
+
122
+ # Reference
123
+ # http://www.debian.org/doc/manuals/maint-guide/first.en.html
124
+ # http://wiki.debian.org/DeveloperConfiguration
125
+ # https://github.com/jordansissel/fpm/issues/37
126
+ if ENV.include?("DEBEMAIL") and ENV.include?("DEBFULLNAME")
127
+ # Use DEBEMAIL and DEBFULLNAME as the default maintainer if available.
128
+ @maintainer = "#{ENV["DEBFULLNAME"]} <#{ENV["DEBEMAIL"]}>"
129
+ else
130
+ # TODO(sissel): Maybe support using 'git config' for a default as well?
131
+ # git config --get user.name, etc can be useful.
132
+ #
133
+ # Otherwise default to user@currenthost
134
+ @maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
135
+ end
136
+
137
+ # Set attribute defaults based on flags
138
+ # This allows you to define command line options with default values
139
+ # that also are obeyed if fpm is used programmatically.
140
+ self.class.default_attributes do |attribute, value|
141
+ attributes[attribute] = value
142
+ end
143
+
144
+ @name = nil
145
+ @architecture = "native"
146
+ @description = "no description given"
147
+ @version = nil
148
+ @epoch = nil
149
+ @iteration = nil
150
+ @url = nil
151
+ @category = "default"
152
+ @license = "unknown"
153
+ @vendor = "none"
154
+
155
+ # Iterate over all the options and set defaults
156
+ if self.class.respond_to?(:declared_options)
157
+ self.class.declared_options.each do |option|
158
+ option.attribute_name.tap do |attr|
159
+ # clamp makes option attributes available as accessor methods
160
+ # do --foo-bar is available as 'foo_bar'
161
+ # make these available as package attributes.
162
+ attr = "#{attr}?" if !respond_to?(attr)
163
+ input.attributes[attr.to_sym] = send(attr) if respond_to?(attr)
164
+ end
165
+ end
166
+ end
167
+
168
+ @provides = []
169
+ @conflicts = []
170
+ @replaces = []
171
+ @dependencies = []
172
+ @scripts = {}
173
+ @config_files = []
174
+ @directories = []
175
+ @attrs = {}
176
+
177
+ staging_path
178
+ build_path
179
+ end # def initialize
180
+
181
+ # Get the 'type' for this instance.
182
+ #
183
+ # For FPM::Package::ABC, this returns 'abc'
184
+ def type
185
+ self.class.type
186
+ end # def type
187
+
188
+ # Convert this package to a new package type
189
+ def convert(klass)
190
+ logger.info("Converting #{self.type} to #{klass.type}")
191
+
192
+ exclude
193
+
194
+ pkg = klass.new
195
+ pkg.cleanup_staging # purge any directories that may have been created by klass.new
196
+
197
+ # copy other bits
198
+ ivars = [
199
+ :@architecture, :@category, :@config_files, :@conflicts,
200
+ :@dependencies, :@description, :@epoch, :@iteration, :@license, :@maintainer,
201
+ :@name, :@provides, :@replaces, :@scripts, :@url, :@vendor, :@version,
202
+ :@directories, :@staging_path, :@attrs
203
+ ]
204
+ ivars.each do |ivar|
205
+ #logger.debug("Copying ivar", :ivar => ivar, :value => instance_variable_get(ivar),
206
+ #:from => self.type, :to => pkg.type)
207
+ pkg.instance_variable_set(ivar, instance_variable_get(ivar))
208
+ end
209
+
210
+ # Attributes are special! We do not want to remove the default values of
211
+ # the destination package type unless their value is specified on the
212
+ # source package object.
213
+ pkg.attributes.merge!(self.attributes)
214
+
215
+ pkg.converted_from(self.class)
216
+ return pkg
217
+ end # def convert
218
+
219
+ # This method is invoked on a package when it has been converted to a new
220
+ # package format. The purpose of this method is to do any extra conversion
221
+ # steps, like translating dependency conditions, etc.
222
+ def converted_from(origin)
223
+ # nothing to do by default. Subclasses may implement this.
224
+ # See the RPM package class for an example.
225
+ end # def converted
226
+
227
+ # Add a new source to this package.
228
+ # The exact behavior depends on the kind of package being managed.
229
+ #
230
+ # For instance:
231
+ #
232
+ # * for FPM::Package::Dir, << expects a path to a directory or files.
233
+ # * for FPM::Package::RPM, << expects a path to an rpm.
234
+ #
235
+ # The idea is that you can keep pumping in new things to a package
236
+ # for later conversion or output.
237
+ #
238
+ # Implementations are expected to put files relevant to the 'input' in the
239
+ # staging_path
240
+ def input(thing_to_input)
241
+ raise NotImplementedError.new("#{self.class.name} does not yet support " \
242
+ "reading #{self.type} packages")
243
+ end # def input
244
+
245
+ # Output this package to the given path.
246
+ def output(path)
247
+ raise NotImplementedError.new("#{self.class.name} does not yet support " \
248
+ "creating #{self.type} packages")
249
+ end # def output
250
+
251
+ def staging_path(path=nil)
252
+ @staging_path ||= ::Dir.mktmpdir("package-#{type}-staging") #, ::Dir.pwd)
253
+
254
+ if path.nil?
255
+ return @staging_path
256
+ else
257
+ return File.join(@staging_path, path)
258
+ end
259
+ end # def staging_path
260
+
261
+ def build_path(path=nil)
262
+ @build_path ||= ::Dir.mktmpdir("package-#{type}-build") #, ::Dir.pwd)
263
+
264
+ if path.nil?
265
+ return @build_path
266
+ else
267
+ return File.join(@build_path, path)
268
+ end
269
+ end # def build_path
270
+
271
+ # Clean up any temporary storage used by this class.
272
+ def cleanup
273
+ cleanup_staging
274
+ cleanup_build
275
+ end # def cleanup
276
+
277
+ def cleanup_staging
278
+ if File.directory?(staging_path)
279
+ logger.debug("Cleaning up staging path", :path => staging_path)
280
+ FileUtils.rm_r(staging_path)
281
+ end
282
+ end # def cleanup_staging
283
+
284
+ def cleanup_build
285
+ if File.directory?(build_path)
286
+ logger.debug("Cleaning up build path", :path => build_path)
287
+ FileUtils.rm_r(build_path)
288
+ end
289
+ end # def cleanup_build
290
+
291
+ # List all files in the staging_path
292
+ #
293
+ # The paths will all be relative to staging_path and will not include that
294
+ # path.
295
+ #
296
+ # This method will emit 'leaf' paths. Files, symlinks, and other file-like
297
+ # things are emitted. Intermediate directories are ignored, but
298
+ # empty directories are emitted.
299
+ def files
300
+ is_leaf = lambda do |path|
301
+ # True if this is a file/symlink/etc, but not a plain directory
302
+ return true if !(File.directory?(path) and !File.symlink?(path))
303
+ # Empty directories are leafs as well.
304
+ return true if ::Dir.entries(path).sort == [".", ".."]
305
+ # False otherwise (non-empty directory, etc)
306
+ return false
307
+ end # is_leaf
308
+
309
+ # Find all leaf-like paths (files, symlink, empty directories, etc)
310
+ # Also trim the leading path such that '#{staging_path}/' is removed from
311
+ # the path before returning.
312
+ #
313
+ # 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)
315
+ return Enumerator.new { |y| Find.find(staging_path) { |path| y << path } } \
316
+ .select { |path| path != staging_path } \
317
+ .select { |path| is_leaf.call(path) } \
318
+ .collect { |path| path[staging_path.length + 1.. -1] }
319
+ end # def files
320
+
321
+ def template_dir
322
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "templates"))
323
+ end
324
+
325
+ def template(path)
326
+ template_path = File.join(template_dir, path)
327
+ template_code = File.read(template_path)
328
+ logger.info("Reading template", :path => template_path)
329
+ erb = ERB.new(template_code, nil, "-")
330
+ erb.filename = template_path
331
+ return erb
332
+ end # def template
333
+
334
+ def to_s(fmt="NAME.TYPE")
335
+ fmt = "NAME.TYPE" if fmt.nil?
336
+ fullversion = version.to_s
337
+ fullversion += "-#{iteration}" if iteration
338
+ return fmt.gsub("ARCH", architecture.to_s) \
339
+ .gsub("NAME", name.to_s) \
340
+ .gsub("FULLVERSION", fullversion) \
341
+ .gsub("VERSION", version.to_s) \
342
+ .gsub("ITERATION", iteration.to_s) \
343
+ .gsub("EPOCH", epoch.to_s) \
344
+ .gsub("TYPE", type.to_s)
345
+ end # def to_s
346
+
347
+ def edit_file(path)
348
+ editor = ENV['FPM_EDITOR'] || ENV['EDITOR'] || 'vi'
349
+ logger.info("Launching editor", :file => path)
350
+ command = "#{editor} #{Shellwords.escape(path)}"
351
+ system("#{editor} #{Shellwords.escape(path)}")
352
+ if !$?.success?
353
+ raise ProcessFailed.new("'#{editor}' failed (exit code " \
354
+ "#{$?.exitstatus}) Full command was: " \
355
+ "#{command}");
356
+ end
357
+
358
+ if File.size(path) == 0
359
+ raise "Empty file after editing: #{path.inspect}"
360
+ end
361
+ end # def edit_file
362
+
363
+ # This method removes excluded files from the staging_path. Subclasses can
364
+ # remove the files during the input phase rather than deleting them here
365
+ def exclude
366
+ return if attributes[:excludes].nil?
367
+
368
+ if @attributes.include?(:prefix)
369
+ installdir = staging_path(@attributes[:prefix])
370
+ else
371
+ installdir = staging_path
372
+ end
373
+
374
+ Find.find(installdir) do |path|
375
+ match_path = path.sub("#{installdir.chomp('/')}/", '')
376
+
377
+ attributes[:excludes].each do |wildcard|
378
+ logger.debug("Checking path against wildcard", :path => match_path, :wildcard => wildcard)
379
+
380
+ if File.fnmatch(wildcard, match_path)
381
+ logger.info("Removing excluded path", :path => match_path, :matches => wildcard)
382
+ FileUtils.rm_r(path)
383
+ Find.prune
384
+ break
385
+ end
386
+ end
387
+ end
388
+ end # def exclude
389
+
390
+
391
+ class << self
392
+ # This method is invoked when subclass occurs.
393
+ #
394
+ # Lets us track all known FPM::Package subclasses
395
+ def inherited(klass)
396
+ @subclasses ||= {}
397
+ @subclasses[klass.name.gsub(/.*:/, "").downcase] = klass
398
+ end # def self.inherited
399
+
400
+ # Get a list of all known package subclasses
401
+ def types
402
+ return @subclasses
403
+ end # def self.types
404
+
405
+ # This allows packages to define flags for the fpm command line
406
+ def option(flag, param, help, options={}, &block)
407
+ @options ||= []
408
+ if !flag.is_a?(Array)
409
+ flag = [flag]
410
+ end
411
+
412
+ if param == :flag
413
+ # Automatically make 'flag' (boolean) options tunable with '--[no-]...'
414
+ flag = flag.collect { |f| "--[no-]#{type}-#{f.gsub(/^--/, "")}" }
415
+ else
416
+ flag = flag.collect { |f| "--#{type}-#{f.gsub(/^--/, "")}" }
417
+ end
418
+
419
+ help = "(#{type} only) #{help}"
420
+ @options << [flag, param, help, options, block]
421
+ end # def options
422
+
423
+ # Apply the options for this package on the clamp command
424
+ #
425
+ # Package flags become attributes '{type}-flag'
426
+ #
427
+ # So if you have:
428
+ #
429
+ # class Foo < FPM::Package
430
+ # option "--bar-baz" ...
431
+ # end
432
+ #
433
+ # The attribute value for --foo-bar-baz will be :foo_bar_baz"
434
+ def apply_options(clampcommand)
435
+ @options ||= []
436
+ @options.each do |args|
437
+ flag, param, help, options, block = args
438
+ clampcommand.option(flag, param, help, options, &block)
439
+ end
440
+ end # def apply_options
441
+
442
+ def default_attributes(&block)
443
+ return if @options.nil?
444
+ @options.each do |flag, param, help, options, _block|
445
+ attr = flag.first.gsub(/^-+/, "").gsub(/-/, "_").gsub("[no_]", "")
446
+ attr += "?" if param == :flag
447
+ yield attr.to_sym, options[:default]
448
+ end
449
+ end # def default_attributes
450
+
451
+ # Get the type of this package class.
452
+ #
453
+ # For "Foo::Bar::BAZ" this will return "baz"
454
+ def type
455
+ self.name.split(':').last.downcase
456
+ end # def self.type
457
+ end # class << self
458
+
459
+ # Get the version of this package
460
+ def version
461
+ if instance_variable_defined?(:@version) && !@version.nil?
462
+ return @version
463
+ elsif attributes[:version_given?]
464
+ # 'version_given?' will be true in cases where the
465
+ # fpm command-line tool has been given '-v' or '--version' settings
466
+ # We do this check because the default version is "1.0"
467
+ # on the fpm command line.
468
+ return attributes.fetch(:version)
469
+ end
470
+
471
+ # No version yet, nil.
472
+ return nil
473
+ end # def version
474
+
475
+ # Does this package have the given script?
476
+ def script?(name)
477
+ return scripts.include?(name)
478
+ end # def script?
479
+
480
+ # Get the contents of the script by a given name.
481
+ #
482
+ # If template_scripts? is set in attributes (often by the --template-scripts
483
+ # flag), then apply it as an ERB template.
484
+ def script(script_name)
485
+ if attributes[:template_scripts?]
486
+ erb = ERB.new(scripts[script_name], nil, "-")
487
+ # TODO(sissel): find the original file name for the file.
488
+ erb.filename = "script(#{script_name})"
489
+ return erb.result(binding)
490
+ else
491
+ return scripts[script_name]
492
+ end
493
+ end # def script
494
+
495
+ def output_check(output_path)
496
+ if !File.directory?(File.dirname(output_path))
497
+ raise ParentDirectoryMissing.new(output_path)
498
+ end
499
+ if File.file?(output_path)
500
+ if attributes[:force?]
501
+ logger.warn("Force flag given. Overwriting package at #{output_path}")
502
+ File.delete(output_path)
503
+ else
504
+ raise FileAlreadyExists.new(output_path)
505
+ end
506
+ end
507
+ end # def output_path
508
+
509
+ def provides=(value)
510
+ if !value.is_a?(Array)
511
+ @provides = [value]
512
+ else
513
+ @provides = value
514
+ end
515
+ end
516
+
517
+ # General public API
518
+ public(:type, :initialize, :convert, :input, :output, :to_s, :cleanup, :files,
519
+ :version, :script, :provides=)
520
+
521
+ # Package internal public api
522
+ public(:cleanup_staging, :cleanup_build, :staging_path, :converted_from,
523
+ :edit_file, :build_path)
524
+ end # class FPM::Package