fpm-aeppert 1.6.2

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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELIST +661 -0
  3. data/CONTRIBUTORS +26 -0
  4. data/LICENSE +21 -0
  5. data/bin/fpm +8 -0
  6. data/lib/fpm.rb +20 -0
  7. data/lib/fpm/command.rb +648 -0
  8. data/lib/fpm/errors.rb +4 -0
  9. data/lib/fpm/namespace.rb +4 -0
  10. data/lib/fpm/package.rb +539 -0
  11. data/lib/fpm/package/apk.rb +510 -0
  12. data/lib/fpm/package/cpan.rb +405 -0
  13. data/lib/fpm/package/deb.rb +935 -0
  14. data/lib/fpm/package/dir.rb +221 -0
  15. data/lib/fpm/package/empty.rb +13 -0
  16. data/lib/fpm/package/freebsd.rb +147 -0
  17. data/lib/fpm/package/gem.rb +243 -0
  18. data/lib/fpm/package/npm.rb +120 -0
  19. data/lib/fpm/package/osxpkg.rb +165 -0
  20. data/lib/fpm/package/p5p.rb +124 -0
  21. data/lib/fpm/package/pacman.rb +403 -0
  22. data/lib/fpm/package/pear.rb +117 -0
  23. data/lib/fpm/package/pkgin.rb +35 -0
  24. data/lib/fpm/package/pleaserun.rb +63 -0
  25. data/lib/fpm/package/puppet.rb +120 -0
  26. data/lib/fpm/package/pyfpm/__init__.py +1 -0
  27. data/lib/fpm/package/pyfpm/get_metadata.py +104 -0
  28. data/lib/fpm/package/python.rb +318 -0
  29. data/lib/fpm/package/rpm.rb +593 -0
  30. data/lib/fpm/package/sh.rb +69 -0
  31. data/lib/fpm/package/solaris.rb +95 -0
  32. data/lib/fpm/package/tar.rb +86 -0
  33. data/lib/fpm/package/virtualenv.rb +164 -0
  34. data/lib/fpm/package/zip.rb +63 -0
  35. data/lib/fpm/rake_task.rb +60 -0
  36. data/lib/fpm/util.rb +358 -0
  37. data/lib/fpm/util/tar_writer.rb +80 -0
  38. data/lib/fpm/version.rb +3 -0
  39. data/templates/deb.erb +52 -0
  40. data/templates/deb/changelog.erb +5 -0
  41. data/templates/deb/ldconfig.sh.erb +13 -0
  42. data/templates/deb/postinst_upgrade.sh.erb +62 -0
  43. data/templates/deb/postrm_upgrade.sh.erb +46 -0
  44. data/templates/deb/preinst_upgrade.sh.erb +41 -0
  45. data/templates/deb/prerm_upgrade.sh.erb +39 -0
  46. data/templates/osxpkg.erb +11 -0
  47. data/templates/p5p_metadata.erb +12 -0
  48. data/templates/pacman.erb +47 -0
  49. data/templates/pacman/INSTALL.erb +41 -0
  50. data/templates/pleaserun/generate-cleanup.sh +17 -0
  51. data/templates/pleaserun/install-path.sh +17 -0
  52. data/templates/pleaserun/install.sh +117 -0
  53. data/templates/pleaserun/scripts/after-install.sh +4 -0
  54. data/templates/pleaserun/scripts/before-remove.sh +12 -0
  55. data/templates/puppet/package.pp.erb +34 -0
  56. data/templates/puppet/package/remove.pp.erb +13 -0
  57. data/templates/rpm.erb +260 -0
  58. data/templates/rpm/filesystem_list +14514 -0
  59. data/templates/sh.erb +369 -0
  60. data/templates/solaris.erb +15 -0
  61. metadata +322 -0
@@ -0,0 +1,510 @@
1
+ require "erb"
2
+ require "fpm/namespace"
3
+ require "fpm/package"
4
+ require "fpm/errors"
5
+ require "fpm/util"
6
+ require "backports"
7
+ require "fileutils"
8
+ require "digest"
9
+ require 'digest/sha1'
10
+
11
+ # Support for debian packages (.deb files)
12
+ #
13
+ # This class supports both input and output of packages.
14
+ class FPM::Package::APK< FPM::Package
15
+
16
+ TAR_CHUNK_SIZE = 512
17
+ TAR_TYPEFLAG_OFFSET = 156
18
+ TAR_NAME_OFFSET_START = 0
19
+ TAR_NAME_OFFSET_END = 99
20
+ TAR_LENGTH_OFFSET_START = 124
21
+ TAR_LENGTH_OFFSET_END = 135
22
+ TAR_CHECKSUM_OFFSET_START = 148
23
+ TAR_CHECKSUM_OFFSET_END = 155
24
+ TAR_MAGIC_START = 257
25
+ TAR_MAGIC_END = 264
26
+ TAR_UID_START = 108
27
+ TAR_UID_END = 115
28
+ TAR_GID_START = 116
29
+ TAR_GID_END = 123
30
+ TAR_UNAME_START = 265
31
+ TAR_UNAME_END = 296
32
+ TAR_GNAME_START = 297
33
+ TAR_GNAME_END = 328
34
+ TAR_MAJOR_START = 329
35
+ TAR_MAJOR_END = 336
36
+ TAR_MINOR_START = 337
37
+ TAR_MINOR_END = 344
38
+
39
+ private
40
+
41
+ # Get the name of this package. See also FPM::Package#name
42
+ #
43
+ # This accessor actually modifies the name if it has some invalid or unwise
44
+ # characters.
45
+ def name
46
+ if @name =~ /[A-Z]/
47
+ logger.warn("apk packages should not have uppercase characters in their names")
48
+ @name = @name.downcase
49
+ end
50
+
51
+ if @name.include?("_")
52
+ logger.warn("apk packages should not include underscores")
53
+ @name = @name.gsub(/[_]/, "-")
54
+ end
55
+
56
+ if @name.include?(" ")
57
+ logger.warn("apk packages should not contain spaces")
58
+ @name = @name.gsub(/[ ]/, "-")
59
+ end
60
+
61
+ return @name
62
+ end
63
+
64
+ def prefix
65
+ return (attributes[:prefix] or "/")
66
+ end
67
+
68
+ def architecture
69
+
70
+ # "native" in apk should be "noarch"
71
+ if @architecture.nil? or @architecture == "native"
72
+ @architecture = "noarch"
73
+ end
74
+ return @architecture
75
+ end
76
+
77
+ def input(input_path)
78
+ logger.error("apk extraction is not yet implemented")
79
+ end
80
+
81
+ def output(output_path)
82
+
83
+ output_check(output_path)
84
+
85
+ control_path = build_path("control")
86
+ controltar_path = build_path("control.tar")
87
+ datatar_path = build_path("data.tar")
88
+
89
+ FileUtils.mkdir(control_path)
90
+
91
+ # data tar.
92
+ tar_path(staging_path(""), datatar_path)
93
+
94
+ # control tar.
95
+ begin
96
+ write_pkginfo(control_path)
97
+ write_control_scripts(control_path)
98
+ tar_path(control_path, controltar_path)
99
+ ensure
100
+ FileUtils.rm_r(control_path)
101
+ end
102
+
103
+ # concatenate the two into a real apk.
104
+ begin
105
+
106
+ # cut end-of-tar record from control tar
107
+ cut_tar_record(controltar_path)
108
+
109
+ # calculate/rewrite sha1 hashes for data tar
110
+ hash_datatar(datatar_path)
111
+
112
+ # concatenate the two into the final apk
113
+ concat_zip_tars(controltar_path, datatar_path, output_path)
114
+ end
115
+
116
+ logger.warn("apk output does not currently sign packages.")
117
+ logger.warn("It's recommended that your package be installed with '--allow-untrusted'")
118
+ end
119
+
120
+ def write_pkginfo(base_path)
121
+
122
+ pkginfo = ""
123
+
124
+ pkginfo << "# Generated by fpm\n"
125
+ pkginfo << "pkgname = #{@name}\n"
126
+ pkginfo << "pkgver = #{to_s("FULLVERSION")}\n"
127
+ pkginfo << "arch = #{architecture()}\n"
128
+ pkginfo << "pkgdesc = #{description()}\n"
129
+ pkginfo << "url = #{url()}\n"
130
+ pkginfo << "size = 102400\n" # totally magic, not sure what it's used for.
131
+
132
+ # write depends lines
133
+ for dependency in dependencies()
134
+ pkginfo << "depend = #{dependency}\n"
135
+ end
136
+
137
+ File.write("#{base_path}/.PKGINFO", pkginfo)
138
+ end
139
+
140
+ # Writes each control script from template into the build path,
141
+ # in the folder given by [base_path]
142
+ def write_control_scripts(base_path)
143
+
144
+ scripts = {}
145
+
146
+ scripts = register_script('post-install', :after_install, scripts)
147
+ scripts = register_script('post-install', :before_install, scripts)
148
+ scripts = register_script('post-install', :before_upgrade, scripts)
149
+ scripts = register_script('post-install', :after_upgrade, scripts)
150
+ scripts = register_script('pre-deinstall', :before_remove, scripts)
151
+ scripts = register_script('post-deinstall', :after_remove, scripts)
152
+
153
+ scripts.each do |key, content|
154
+
155
+ File.write("#{base_path}/.#{key}", content)
156
+ end
157
+ end
158
+
159
+ # Convenience method for 'write_control_scripts' to register control scripts
160
+ # if they exist.
161
+ def register_script(key, value, hash)
162
+
163
+ if(script?(value))
164
+ hash[key] = scripts[value]
165
+ end
166
+ return hash
167
+ end
168
+
169
+ # Removes the end-of-tar records from the given [target_path].
170
+ # End of tar records are two contiguous empty tar records at the end of the file
171
+ # Taken together, they comprise 1k of null data.
172
+ def cut_tar_record(target_path)
173
+
174
+ temporary_target_path = target_path + "~"
175
+
176
+ record_length = 0
177
+ empty_records = 0
178
+
179
+ open(temporary_target_path, "wb") do |target_file|
180
+
181
+ # Scan to find the location of the two contiguous null records
182
+ open(target_path, "rb") do |file|
183
+
184
+ until(empty_records == 2)
185
+
186
+ header = file.read(TAR_CHUNK_SIZE)
187
+
188
+ # clear off ownership info
189
+ header = replace_ownership_headers(header, true)
190
+
191
+ typeflag = header[TAR_TYPEFLAG_OFFSET]
192
+ ascii_length = header[TAR_LENGTH_OFFSET_START..TAR_LENGTH_OFFSET_END]
193
+
194
+ if(file.eof?())
195
+ raise StandardError.new("Invalid tar stream, eof before end-of-tar record")
196
+ end
197
+
198
+ if(typeflag == "\0")
199
+ empty_records += 1
200
+ next
201
+ end
202
+
203
+ record_length = ascii_length.to_i(8)
204
+ record_length = determine_record_length(record_length)
205
+
206
+ target_file.write(header)
207
+ target_file.write(file.read(record_length))
208
+ end
209
+ end
210
+ end
211
+
212
+ FileUtils::mv(temporary_target_path, target_path)
213
+ end
214
+
215
+ # Rewrites the tar file located at the given [target_tar_path]
216
+ # to have its record headers use a simple checksum,
217
+ # and the apk sha1 hash extension.
218
+ def hash_datatar(target_path)
219
+
220
+ header = extension_header = ""
221
+ data = extension_data = ""
222
+ record_length = extension_length = 0
223
+ empty_records = 0
224
+
225
+ temporary_file_name = target_path + "~"
226
+
227
+ target_file = open(temporary_file_name, "wb")
228
+ file = open(target_path, "rb")
229
+ begin
230
+
231
+ until(file.eof?() || empty_records == 2)
232
+
233
+ header = file.read(TAR_CHUNK_SIZE)
234
+ typeflag = header[TAR_TYPEFLAG_OFFSET]
235
+ record_length = header[TAR_LENGTH_OFFSET_START..TAR_LENGTH_OFFSET_END].to_i(8)
236
+
237
+ data = ""
238
+ record_length = determine_record_length(record_length)
239
+
240
+ until(data.length == record_length)
241
+ data += file.read(TAR_CHUNK_SIZE)
242
+ end
243
+
244
+ # Clear ownership fields
245
+ header = replace_ownership_headers(header, false)
246
+
247
+ # If it's not a null record, do extension hash.
248
+ if(typeflag != "\0")
249
+ extension_header = header.dup()
250
+
251
+ extension_header = replace_ownership_headers(extension_header, true)
252
+
253
+ # directories have a magic string inserted into their name
254
+ full_record_path = extension_header[TAR_NAME_OFFSET_START..TAR_NAME_OFFSET_END].delete("\0")
255
+ full_record_path = add_paxstring(full_record_path)
256
+
257
+ # hash data contents with sha1, if there is any content.
258
+ if(typeflag == '5')
259
+
260
+ extension_data = ""
261
+
262
+ # ensure it doesn't end with a slash
263
+ if(full_record_path[full_record_path.length-1] == '/')
264
+ full_record_path = full_record_path.chop()
265
+ end
266
+ else
267
+ extension_data = hash_record(data)
268
+ end
269
+
270
+ full_record_path = pad_string_to(full_record_path, 100)
271
+ extension_header[TAR_NAME_OFFSET_START..TAR_NAME_OFFSET_END] = full_record_path
272
+
273
+ extension_header[TAR_TYPEFLAG_OFFSET] = 'x'
274
+ extension_header[TAR_LENGTH_OFFSET_START..TAR_LENGTH_OFFSET_END] = extension_data.length.to_s(8).rjust(12, '0')
275
+ extension_header = checksum_header(extension_header)
276
+
277
+ # write extension record
278
+ target_file.write(extension_header)
279
+ target_file.write(extension_data)
280
+ else
281
+ empty_records += 1
282
+ end
283
+
284
+ # write header and data to target file.
285
+ target_file.write(header)
286
+ target_file.write(data)
287
+ end
288
+ FileUtils.mv(temporary_file_name, target_path)
289
+ ensure
290
+ file.close()
291
+ target_file.close()
292
+ end
293
+ end
294
+
295
+ # Concatenates each of the given [apath] and [bpath] into the given [target_path]
296
+ def concat_zip_tars(apath, bpath, target_path)
297
+
298
+ temp_apath = apath + "~"
299
+ temp_bpath = bpath + "~"
300
+
301
+ # zip each path separately
302
+ Zlib::GzipWriter.open(temp_apath) do |target_writer|
303
+ open(apath, "rb") do |file|
304
+ until(file.eof?())
305
+ target_writer.write(file.read(4096))
306
+ end
307
+ end
308
+ end
309
+
310
+ Zlib::GzipWriter.open(temp_bpath) do |target_writer|
311
+ open(bpath, "rb") do |file|
312
+ until(file.eof?())
313
+ target_writer.write(file.read(4096))
314
+ end
315
+ end
316
+ end
317
+
318
+ # concat both into one.
319
+ File.open(target_path, "wb") do |target_writer|
320
+ open(temp_apath, "rb") do |file|
321
+ until(file.eof?())
322
+ target_writer.write(file.read(4096))
323
+ end
324
+ end
325
+ open(temp_bpath, "rb") do |file|
326
+ until(file.eof?())
327
+ target_writer.write(file.read(4096))
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ # Rounds the given [record_length] to the nearest highest evenly-divisble number of 512.
334
+ def determine_record_length(record_length)
335
+
336
+ sans_size = TAR_CHUNK_SIZE-1
337
+
338
+ if(record_length % TAR_CHUNK_SIZE != 0)
339
+ record_length = (record_length + sans_size) & ~sans_size;
340
+ end
341
+ return record_length
342
+ end
343
+
344
+ # Checksums the entire contents of the given [header]
345
+ # Writes the resultant checksum into indices 148-155 of the same [header],
346
+ # and returns the modified header.
347
+ # 148-155 is the "size" range in a tar/ustar header.
348
+ def checksum_header(header)
349
+
350
+ # blank out header checksum
351
+ replace_string_range(header, TAR_CHECKSUM_OFFSET_START, TAR_CHECKSUM_OFFSET_END, ' ')
352
+
353
+ # calculate new checksum
354
+ checksum = 0
355
+
356
+ for i in 0..(TAR_CHUNK_SIZE-1)
357
+ checksum += header.getbyte(i)
358
+ end
359
+
360
+ checksum = checksum.to_s(8).rjust(6, '0')
361
+ header[TAR_CHECKSUM_OFFSET_START..TAR_CHECKSUM_OFFSET_END-2] = checksum
362
+ header[TAR_CHECKSUM_OFFSET_END-1] = "\0"
363
+ return header
364
+ end
365
+
366
+ # SHA-1 hashes the given data, then places it in the APK hash string format
367
+ # then returns.
368
+ def hash_record(data)
369
+
370
+ # %u %s=%s\n
371
+ # len name=hash
372
+
373
+ hash = Digest::SHA1.hexdigest(data)
374
+ name = "APK-TOOLS.checksum.SHA1"
375
+
376
+ ret = "#{name}=#{hash}\n"
377
+
378
+ # the length requirement needs to know its own length too, because the length
379
+ # is the entire length of the line, not just the contents.
380
+ length = ret.length
381
+ line_length = length.to_s
382
+ length += line_length.length
383
+ candidate_ret = "#{line_length} #{ret}"
384
+
385
+ if(candidate_ret.length != length)
386
+ length += 1
387
+ candidate_ret = "#{length.to_s} #{ret}"
388
+ end
389
+
390
+ ret = candidate_ret
391
+
392
+ # pad out the result
393
+ ret = pad_string_to(ret, TAR_CHUNK_SIZE)
394
+ return ret
395
+ end
396
+
397
+ # Tars the current contents of the given [path] to the given [target_path].
398
+ def tar_path(path, target_path)
399
+
400
+ # Change directory to the source path, and glob files
401
+ # This is done so that we end up with a "flat" archive, that doesn't
402
+ # have any path artifacts from the packager's absolute path.
403
+ ::Dir::chdir(path) do
404
+ entries = ::Dir::glob("**", File::FNM_DOTMATCH)
405
+
406
+ args =
407
+ [
408
+ tar_cmd,
409
+ "-f",
410
+ target_path,
411
+ "-c"
412
+ ]
413
+
414
+ # Move pkginfo to the front, if it exists.
415
+ for i in (0..entries.length)
416
+ if(entries[i] == ".PKGINFO")
417
+ entries[i] = entries[0]
418
+ entries[0] = ".PKGINFO"
419
+ break
420
+ end
421
+ end
422
+
423
+ # add entries to arguments.
424
+ entries.each do |entry|
425
+ unless(entry == '..' || entry == '.')
426
+ args = args << entry
427
+ end
428
+ end
429
+
430
+ safesystem(*args)
431
+ end
432
+ end
433
+
434
+ # APK adds a "PAX" magic string into most directory names.
435
+ # This takes an unchanged directory name and "paxifies" it.
436
+ def add_paxstring(ret)
437
+
438
+ pax_slash = ret.rindex('/')
439
+ if(pax_slash == nil)
440
+ pax_slash = 0
441
+ else
442
+ pax_slash = ret.rindex('/', pax_slash-1)
443
+ if(pax_slash == nil || pax_slash < 0)
444
+ pax_slash = 0
445
+ end
446
+ end
447
+
448
+ ret = ret.insert(pax_slash, "/PaxHeaders.14670/")
449
+ ret = ret.sub("//", "/")
450
+ return ret
451
+ end
452
+
453
+ # Appends null zeroes to the end of [ret] until it is divisible by [length].
454
+ # Returns the padded result.
455
+ def pad_string_to(ret, length)
456
+
457
+ until(ret.length % length == 0)
458
+ ret << "\0"
459
+ end
460
+ return ret
461
+ end
462
+
463
+ # Replaces every character between [start] and [finish] in the given [str]
464
+ # with [character].
465
+ def replace_string_range(str, start, finish, character)
466
+
467
+ for i in (start..finish)
468
+ str[i] = character
469
+ end
470
+
471
+ return str
472
+ end
473
+
474
+ # Nulls out the ownership bits of the given tar [header].
475
+ def replace_ownership_headers(header, nullify_names)
476
+
477
+ # magic
478
+ header[TAR_MAGIC_START..TAR_MAGIC_END] = "ustar\0" + "00"
479
+
480
+ # ids
481
+ header = replace_string_range(header, TAR_UID_START, TAR_UID_END, "0")
482
+ header = replace_string_range(header, TAR_GID_START, TAR_GID_END, "0")
483
+ header[TAR_GID_END] = "\0"
484
+ header[TAR_UID_END] = "\0"
485
+
486
+ # names
487
+ if(nullify_names)
488
+ header = replace_string_range(header, TAR_UNAME_START, TAR_UNAME_END, "\0")
489
+ header = replace_string_range(header, TAR_GNAME_START, TAR_GNAME_END, "\0")
490
+
491
+ # major/minor
492
+ header[TAR_MAJOR_START..TAR_MAJOR_END] = "0".rjust(8, '0')
493
+ header[TAR_MINOR_START..TAR_MINOR_END] = "0".rjust(8, '0')
494
+ header[TAR_MAJOR_END] = "\0"
495
+ header[TAR_MINOR_END] = "\0"
496
+ else
497
+ header[TAR_UNAME_START..TAR_UNAME_END] = pad_string_to("root", 32)
498
+ header[TAR_GNAME_START..TAR_GNAME_END] = pad_string_to("root", 32)
499
+ end
500
+
501
+ return header
502
+ end
503
+
504
+ def to_s(format=nil)
505
+ return super("NAME_FULLVERSION_ARCH.TYPE") if format.nil?
506
+ return super(format)
507
+ end
508
+
509
+ public(:input, :output, :architecture, :name, :prefix, :converted_from, :to_s)
510
+ end