fpm-itchio 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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,59 @@
1
+ require "ostruct"
2
+ require "rake"
3
+ require "rake/tasklib"
4
+
5
+ class FPM::RakeTask < Rake::TaskLib
6
+ attr_reader :options
7
+
8
+ def initialize(package_name, opts = {}, &block)
9
+ @options = OpenStruct.new(:name => package_name.to_s)
10
+ @source, @target = opts.values_at(:source, :target).map(&:to_s)
11
+ @directory = File.expand_path(opts[:directory].to_s)
12
+
13
+ (@source.empty? || @target.empty? || options.name.empty?) &&
14
+ abort("Must specify package name, source and output")
15
+
16
+ desc "Package #{@name}" unless ::Rake.application.last_comment
17
+
18
+ task(options.name) do |_, task_args|
19
+ block.call(*[options, task_args].first(block.arity)) if block_given?
20
+ abort("Must specify args") unless options.respond_to?(:args)
21
+ @args = options.delete_field(:args)
22
+ run_cli
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def parsed_options
29
+ options.to_h.map do |option, value|
30
+ opt = option.to_s.tr("_", "-")
31
+
32
+ case
33
+ when value.is_a?(String), value.is_a?(Symbol)
34
+ %W(--#{opt} #{value})
35
+ when value.is_a?(Array)
36
+ value.map { |v| %W(--#{opt} #{v}) }
37
+ when value.is_a?(TrueClass)
38
+ "--#{opt}"
39
+ when value.is_a?(FalseClass)
40
+ "--no-#{opt}"
41
+ else
42
+ fail TypeError, "Unexpected type: #{value.class}"
43
+ end
44
+ end
45
+ end
46
+
47
+ def run_cli
48
+ require "fpm"
49
+ require "fpm/command"
50
+
51
+ args = %W(-t #{@target} -s #{@source} -C #{@directory})
52
+ args << parsed_options
53
+ args << @args
54
+
55
+ args.flatten!.compact!
56
+
57
+ exit(FPM::Command.new("fpm").run(args) || 0)
58
+ end
59
+ end
@@ -0,0 +1,253 @@
1
+ require "fpm/namespace"
2
+ require "childprocess"
3
+ require "ffi"
4
+
5
+ # Some utility functions
6
+ module FPM::Util
7
+ extend FFI::Library
8
+ ffi_lib FFI::Library::LIBC
9
+
10
+ # mknod is __xmknod in glibc a wrapper around mknod to handle
11
+ # various stat struct formats. See bits/stat.h in glibc source
12
+ begin
13
+ attach_function :mknod, :mknod, [:string, :uint, :ulong], :int
14
+ rescue FFI::NotFoundError
15
+ # glibc/io/xmknod.c int __xmknod (int vers, const char *path, mode_t mode, dev_t *dev)
16
+ attach_function :xmknod, :__xmknod, [:int, :string, :uint, :pointer], :int
17
+ end
18
+
19
+ # Raised if safesystem cannot find the program to run.
20
+ class ExecutableNotFound < StandardError; end
21
+
22
+ # Raised if a safesystem program exits nonzero
23
+ class ProcessFailed < StandardError; end
24
+
25
+ # Is the given program in the system's PATH?
26
+ def program_in_path?(program)
27
+ # return false if path is not set
28
+ return false unless ENV['PATH']
29
+ # Scan path to find the executable
30
+ # Do this to help the user get a better error message.
31
+ envpath = ENV["PATH"].split(":")
32
+ return envpath.select { |p| File.executable?(File.join(p, program)) }.any?
33
+ end # def program_in_path
34
+
35
+ def program_exists?(program)
36
+ # Scan path to find the executable
37
+ # Do this to help the user get a better error message.
38
+ return program_in_path?(program) if !program.include?("/")
39
+ return File.executable?(program)
40
+ end # def program_exists?
41
+
42
+ def default_shell
43
+ shell = ENV["SHELL"]
44
+ return "/bin/sh" if shell.nil? || shell.empty?
45
+ return shell
46
+ end
47
+
48
+ # Run a command safely in a way that gets reports useful errors.
49
+ def safesystem(*args)
50
+ # ChildProcess isn't smart enough to run a $SHELL if there's
51
+ # spaces in the first arg and there's only 1 arg.
52
+ if args.size == 1
53
+ args = [ default_shell, "-c", args[0] ]
54
+ end
55
+ program = args[0]
56
+
57
+ if !program_exists?(program)
58
+ raise ExecutableNotFound.new(program)
59
+ end
60
+
61
+ logger.debug("Running command", :args => args)
62
+
63
+ # Create a pair of pipes to connect the
64
+ # invoked process to the cabin logger
65
+ stdout_r, stdout_w = IO.pipe
66
+ stderr_r, stderr_w = IO.pipe
67
+
68
+ process = ChildProcess.build(*args)
69
+ process.io.stdout = stdout_w
70
+ process.io.stderr = stderr_w
71
+
72
+ process.start
73
+ stdout_w.close; stderr_w.close
74
+ logger.debug('Process is running', :pid => process.pid)
75
+ # Log both stdout and stderr as 'info' because nobody uses stderr for
76
+ # actually reporting errors and as a result 'stderr' is a misnomer.
77
+ logger.pipe(stdout_r => :info, stderr_r => :info)
78
+
79
+ process.wait
80
+ success = (process.exit_code == 0)
81
+
82
+ if !success
83
+ raise ProcessFailed.new("#{program} failed (exit code #{process.exit_code})" \
84
+ ". Full command was:#{args.inspect}")
85
+ end
86
+ return success
87
+ end # def safesystem
88
+
89
+ # Run a command safely in a way that captures output and status.
90
+ def safesystemout(*args)
91
+ if args.size == 1
92
+ args = [ ENV["SHELL"], "-c", args[0] ]
93
+ end
94
+ program = args[0]
95
+
96
+ if !program.include?("/") and !program_in_path?(program)
97
+ raise ExecutableNotFound.new(program)
98
+ end
99
+
100
+ logger.debug("Running command", :args => args)
101
+
102
+ stdout_r, stdout_w = IO.pipe
103
+ stderr_r, stderr_w = IO.pipe
104
+
105
+ process = ChildProcess.build(*args)
106
+ process.io.stdout = stdout_w
107
+ process.io.stderr = stderr_w
108
+
109
+ process.start
110
+ stdout_w.close; stderr_w.close
111
+ stdout_r_str = stdout_r.read
112
+ stdout_r.close; stderr_r.close
113
+ logger.debug("Process is running", :pid => process.pid)
114
+
115
+ process.wait
116
+ success = (process.exit_code == 0)
117
+
118
+ if !success
119
+ raise ProcessFailed.new("#{program} failed (exit code #{process.exit_code})" \
120
+ ". Full command was:#{args.inspect}")
121
+ end
122
+
123
+ return stdout_r_str
124
+ end # def safesystemout
125
+
126
+ # Get the recommended 'tar' command for this platform.
127
+ def tar_cmd
128
+ # Rely on gnu tar for solaris and OSX.
129
+ case %x{uname -s}.chomp
130
+ when "SunOS"
131
+ return "gtar"
132
+ when "Darwin"
133
+ # Try running gnutar, it was renamed(??) in homebrew to 'gtar' at some point, I guess? I don't know.
134
+ ["gnutar", "gtar"].each do |tar|
135
+ system("#{tar} > /dev/null 2> /dev/null")
136
+ return tar unless $?.exitstatus == 127
137
+ end
138
+ when "FreeBSD"
139
+ # use gnutar instead
140
+ return "gtar"
141
+ else
142
+ return "tar"
143
+ end
144
+ end # def tar_cmd
145
+
146
+ # wrapper around mknod ffi calls
147
+ def mknod_w(path, mode, dev)
148
+ rc = -1
149
+ case %x{uname -s}.chomp
150
+ when 'Linux'
151
+ # bits/stat.h #define _MKNOD_VER_LINUX 0
152
+ rc = xmknod(0, path, mode, FFI::MemoryPointer.new(dev))
153
+ else
154
+ rc = mknod(path, mode, dev)
155
+ end
156
+ rc
157
+ end
158
+
159
+ def copy_metadata(source, destination)
160
+ source_stat = File::lstat(source)
161
+ dest_stat = File::lstat(destination)
162
+
163
+ # If this is a hard-link, there's no metadata to copy.
164
+ # If this is a symlink, what it points to hasn't been copied yet.
165
+ return if source_stat.ino == dest_stat.ino || dest_stat.symlink?
166
+
167
+ File.utime(source_stat.atime, source_stat.mtime, destination)
168
+ mode = source_stat.mode
169
+ begin
170
+ File.lchown(source_stat.uid, source_stat.gid, destination)
171
+ rescue Errno::EPERM
172
+ # clear setuid/setgid
173
+ mode &= 01777
174
+ end
175
+
176
+ unless source_stat.symlink?
177
+ File.chmod(mode, destination)
178
+ end
179
+ end # def copy_metadata
180
+
181
+
182
+ def copy_entry(src, dst, preserve=false, remove_destination=false)
183
+ case File.ftype(src)
184
+ when 'fifo', 'characterSpecial', 'blockSpecial', 'socket'
185
+ st = File.stat(src)
186
+ rc = mknod_w(dst, st.mode, st.dev)
187
+ raise SystemCallError.new("mknod error", FFI.errno) if rc == -1
188
+ when 'directory'
189
+ FileUtils.mkdir(dst) unless File.exists? dst
190
+ else
191
+ # if the file with the same dev and inode has been copied already -
192
+ # hard link it's copy to `dst`, otherwise make an actual copy
193
+ st = File.lstat(src)
194
+ known_entry = copied_entries[[st.dev, st.ino]]
195
+ if known_entry
196
+ FileUtils.ln(known_entry, dst)
197
+ else
198
+ FileUtils.copy_entry(src, dst, preserve=preserve,
199
+ remove_destination=remove_destination)
200
+ copied_entries[[st.dev, st.ino]] = dst
201
+ end
202
+ end # else...
203
+ end # def copy_entry
204
+
205
+ def copied_entries
206
+ # TODO(sissel): I wonder that this entry-copy knowledge needs to be put
207
+ # into a separate class/module. As is, calling copy_entry the same way
208
+ # in slightly different contexts will result in weird or bad behavior.
209
+ # What I mean is if we do:
210
+ # pkg = FPM::Package::Dir...
211
+ # pkg.output()...
212
+ # pkg.output()...
213
+ # The 2nd output call will fail or behave weirdly because @copied_entries
214
+ # is already populated. even though this is anew round of copying.
215
+ return @copied_entries ||= {}
216
+ end # def copied_entries
217
+
218
+ def expand_pessimistic_constraints(constraint)
219
+ name, op, version = constraint.split(/\s+/)
220
+
221
+ if op == '~>'
222
+
223
+ new_lower_constraint = "#{name} >= #{version}"
224
+
225
+ version_components = version.split('.').collect { |v| v.to_i }
226
+
227
+ version_prefix = version_components[0..-3].join('.')
228
+ portion_to_work_with = version_components.last(2)
229
+
230
+ prefix = ''
231
+ unless version_prefix.empty?
232
+ prefix = version_prefix + '.'
233
+ end
234
+
235
+ one_to_increment = portion_to_work_with[0].to_i
236
+ incremented = one_to_increment + 1
237
+
238
+ new_version = ''+ incremented.to_s + '.0'
239
+
240
+ upper_version = prefix + new_version
241
+
242
+ new_upper_constraint = "#{name} < #{upper_version}"
243
+
244
+ return [new_lower_constraint,new_upper_constraint]
245
+ else
246
+ return [constraint]
247
+ end
248
+ end #def expand_pesimistic_constraints
249
+
250
+ def logger
251
+ @logger ||= Cabin::Channel.get
252
+ end # def logger
253
+ end # module FPM::Util
@@ -0,0 +1,3 @@
1
+ module FPM
2
+ VERSION = "1.4.0"
3
+ end
@@ -0,0 +1,52 @@
1
+ Package: <%= name %>
2
+ Version: <%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>
3
+ License: <%= license %>
4
+ <% if !vendor.nil? and !vendor.empty? -%>
5
+ Vendor: <%= vendor %>
6
+ <% end -%>
7
+ Architecture: <%= architecture %>
8
+ Maintainer: <%= maintainer %>
9
+ Installed-Size: <%= attributes[:deb_installed_size] %>
10
+ <% if !dependencies.empty? and !attributes[:no_depends?] -%>
11
+ Depends: <%= dependencies.collect { |d| fix_dependency(d) }.flatten.join(", ") %>
12
+ <% end -%>
13
+ <% if !conflicts.empty? -%>
14
+ Conflicts: <%= conflicts.collect { |d| fix_dependency(d) }.flatten.join(", ") %>
15
+ <% end -%>
16
+ <% if attributes[:deb_breaks] -%>
17
+ Breaks: <%= attributes[:deb_breaks].collect { |d| fix_dependency(d) }.flatten.join(", ") %>
18
+ <% end -%>
19
+ <% if attributes[:deb_pre_depends_given?] -%>
20
+ Pre-Depends: <%= attributes[:deb_pre_depends].collect { |d| fix_dependency(d) }.flatten.join(", ") %>
21
+ <% end -%>
22
+ <% if attributes[:deb_build_depends_given?] -%>
23
+ Build-Depends: <%= attributes[:deb_build_depends].collect { |d| fix_dependency(d) }.flatten.join(", ") %>
24
+ <% end -%>
25
+ <% if !provides.empty? -%>
26
+ <%# Turn each provides from 'foo = 123' to simply 'foo' because Debian :\ -%>
27
+ <%# http://www.debian.org/doc/debian-policy/ch-relationships.html -%>
28
+ Provides: <%= provides.map {|p| p.split(" ").first}.join ", " %>
29
+ <% end -%>
30
+ <% if !replaces.empty? -%>
31
+ Replaces: <%= replaces.join(", ") %>
32
+ <% end -%>
33
+ <% if attributes[:deb_recommends_given?] -%>
34
+ Recommends: <%= attributes[:deb_recommends].collect { |d| fix_dependency(d) }.flatten.join(", ") %>
35
+ <% end -%>
36
+ <% if attributes[:deb_suggests_given?] -%>
37
+ Suggests: <%= attributes[:deb_suggests].collect { |d| fix_dependency(d) }.flatten.join(", ") %>
38
+ <% end -%>
39
+ Section: <%= category %>
40
+ Priority: <%= attributes[:deb_priority] %>
41
+ Homepage: <%= url or "http://nourlgiven.example.com/" %>
42
+ <% lines = (description or "no description given").split("\n") -%>
43
+ <% firstline, *remainder = lines -%>
44
+ Description: <%= firstline %>
45
+ <% if remainder.any? -%>
46
+ <%= remainder.collect { |l| l =~ /^ *$/ ? " ." : " #{l}" }.join("\n") %>
47
+ <% end -%>
48
+ <% if attributes[:deb_field_given?] -%>
49
+ <% attributes[:deb_field].each do |field, value| -%>
50
+ <%= field %>: <%= value %>
51
+ <% end -%>
52
+ <% end -%>
@@ -0,0 +1,5 @@
1
+ <%= name %> (<%= "#{epoch}:" if epoch %><%= version %><%= "-" + iteration.to_s if iteration %>) whatever; urgency=medium
2
+
3
+ * Package created with FPM.
4
+
5
+ -- <%= maintainer %> <%= Time.now.strftime("%a, %d %b %Y %T %z") %>
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+ # This script is automatically added by fpm when you specify
3
+ # conditions that usually require running ldconfig upon
4
+ # package installation and removal.
5
+ #
6
+ # For example, if you set '--deb-shlibs' in creating your package,
7
+ # fpm will use this script if you don't provide your own --after-install or
8
+ # --after-remove
9
+ set -e
10
+
11
+ case $1 in
12
+ configure|remove) ldconfig ;;
13
+ esac
@@ -0,0 +1,62 @@
1
+ #!/bin/sh
2
+ after_upgrade() {
3
+ <%# Making sure that at least one command is in the function -%>
4
+ <%# avoids a lot of potential errors, including the case that -%>
5
+ <%# the script is non-empty, but just whitespace and/or comments -%>
6
+ :
7
+ <% if script?(:after_upgrade) -%>
8
+ <%= script(:after_upgrade) %>
9
+ <% end -%>
10
+
11
+ <% if attributes[:deb_systemd] -%>
12
+ systemctl --system daemon-reload >/dev/null || true
13
+ if ! systemctl is-enabled <%= attributes[:deb_systemd] %> >/dev/null
14
+ then
15
+ systemctl enable <%= attributes[:deb_systemd] %> >/dev/null || true
16
+ systemctl start <%= attributes[:deb_systemd] %> >/dev/null || true
17
+ <% if attributes[:deb_systemd_restart_after_upgrade?] -%>
18
+ else
19
+ systemctl restart <%= attributes[:deb_systemd] %> >/dev/null || true
20
+ <% end -%>
21
+ fi
22
+ <% end -%>
23
+ }
24
+
25
+ after_install() {
26
+ <%# Making sure that at least one command is in the function -%>
27
+ <%# avoids a lot of potential errors, including the case that -%>
28
+ <%# the script is non-empty, but just whitespace and/or comments -%>
29
+ :
30
+ <% if script?(:after_install) -%>
31
+ <%= script(:after_install) %>
32
+ <% end -%>
33
+
34
+ <% if attributes[:deb_systemd] -%>
35
+ systemctl --system daemon-reload >/dev/null || true
36
+ systemctl enable <%= attributes[:deb_systemd] %> >/dev/null || true
37
+ systemctl start <%= attributes[:deb_systemd] %> >/dev/null || true
38
+ <% end -%>
39
+ }
40
+
41
+ if [ "${1}" = "configure" -a -z "${2}" ] || \
42
+ [ "${1}" = "abort-remove" ]
43
+ then
44
+ # "after install" here
45
+ # "abort-remove" happens when the pre-removal script failed.
46
+ # In that case, this script, which should be idemptoent, is run
47
+ # to ensure a clean roll-back of the removal.
48
+ after_install
49
+ elif [ "${1}" = "configure" -a -n "${2}" ]
50
+ then
51
+ upgradeFromVersion="${2}"
52
+ # "after upgrade" here
53
+ # NOTE: This slot is also used when deb packages are removed,
54
+ # but their config files aren't, but a newer version of the
55
+ # package is installed later, called "Config-Files" state.
56
+ # basically, that still looks a _lot_ like an upgrade to me.
57
+ after_upgrade "${2}"
58
+ elif echo "${1}" | grep -E -q "(abort|fail)"
59
+ then
60
+ echo "Failed to install before the post-installation script was run." >&2
61
+ exit 1
62
+ fi