fpm-aeppert 1.6.2 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  require "fpm/package"
2
- require "backports"
2
+ require "backports/latest"
3
3
  require "fileutils"
4
4
  require "find"
5
5
  require "arr-pm/file" # gem 'arr-pm'
@@ -24,9 +24,10 @@ class FPM::Package::RPM < FPM::Package
24
24
 
25
25
  COMPRESSION_MAP = {
26
26
  "none" => "w0.gzdio",
27
- "xz" => "w9.xzdio",
28
- "gzip" => "w9.gzdio",
29
- "bzip2" => "w9.bzdio"
27
+ "xz" => ".xzdio",
28
+ "xzmt" => "T.xzdio",
29
+ "gzip" => ".gzdio",
30
+ "bzip2" => ".bzdio"
30
31
  } unless defined?(COMPRESSION_MAP)
31
32
 
32
33
  option "--use-file-permissions", :flag,
@@ -67,6 +68,15 @@ class FPM::Package::RPM < FPM::Package
67
68
  value.downcase
68
69
  end
69
70
 
71
+ option "--compression-level", "[0-9]", "Select a compression level. 0 is store-only. 9 is max compression.",
72
+ :default => "9" do |value|
73
+ valint = value.to_i
74
+ unless value =~ /^\d$/ && valint >= 0 && valint <= 9
75
+ raise "Invalid compression level '#{value}'. Valid values are integers between 0 and 9 inclusive."
76
+ end
77
+ valint
78
+ end
79
+
70
80
  option "--compression", COMPRESSION_MAP.keys.join("|"),
71
81
  "Select a compression method. gzip works on the most platforms.",
72
82
  :default => "gzip" do |value|
@@ -141,6 +151,10 @@ class FPM::Package::RPM < FPM::Package
141
151
  "names in rpm requires instead of the redhat style " \
142
152
  "rubygem(foo).", :default => false
143
153
 
154
+ option "--macro-expansion", :flag,
155
+ "install-time macro expansion in %pre %post %preun %postun scripts " \
156
+ "(see: https://rpm.org/user_doc/scriptlet_expansion.html)", :default => false
157
+
144
158
  option "--verifyscript", "FILE",
145
159
  "a script to be run on verification" do |val|
146
160
  File.expand_path(val) # Get the full path to the script
@@ -472,6 +486,22 @@ class FPM::Package::RPM < FPM::Package
472
486
  self.directories = alldirs
473
487
  end
474
488
 
489
+ # include external config files
490
+ (attributes[:config_files] or []).each do |conf|
491
+ dest_conf = File.join(staging_path, conf)
492
+
493
+ if File.exist?(dest_conf)
494
+ logger.debug("Using --config-file from staging area", :path => conf)
495
+ elsif File.exist?(conf)
496
+ logger.info("Copying --config-file from local path", :path => conf)
497
+ FileUtils.mkdir_p(File.dirname(dest_conf))
498
+ FileUtils.cp_r conf, dest_conf
499
+ else
500
+ logger.error("Failed to find given --config-file", :path => conf)
501
+ raise "Could not find config file '#{conf}' in staging area or on host. This can happen if you specify `--config-file '#{conf}'` but this file does not exist in the source package and also does not exist in filesystem."
502
+ end
503
+ end
504
+
475
505
  # scan all conf file paths for files and add them
476
506
  allconfigs = []
477
507
  self.config_files.each do |path|
@@ -499,10 +529,11 @@ class FPM::Package::RPM < FPM::Package
499
529
  end
500
530
 
501
531
  # copy all files from staging to BUILD dir
532
+ # [#1538] Be sure to preserve the original timestamps.
502
533
  Find.find(staging_path) do |path|
503
534
  src = path.gsub(/^#{staging_path}/, '')
504
535
  dst = File.join(build_path, build_sub_dir, src)
505
- copy_entry(path, dst)
536
+ copy_entry(path, dst, preserve=true)
506
537
  end
507
538
 
508
539
  rpmspec = template("rpm.erb").result(binding)
@@ -537,7 +568,7 @@ class FPM::Package::RPM < FPM::Package
537
568
 
538
569
  def summary
539
570
  if !attributes[:rpm_summary]
540
- return @description.split("\n").first || "_"
571
+ return @description.split("\n").find { |line| !line.strip.empty? } || "_"
541
572
  end
542
573
 
543
574
  return attributes[:rpm_summary]
@@ -580,7 +611,12 @@ class FPM::Package::RPM < FPM::Package
580
611
  end # def to_s
581
612
 
582
613
  def payload_compression
583
- return COMPRESSION_MAP[attributes[:rpm_compression]]
614
+ if attributes[:rpm_compression] == 'none'
615
+ # when 'none' ignore any compression level and return w0.gzdio
616
+ return COMPRESSION_MAP[attributes[:rpm_compression]]
617
+ else
618
+ return "w#{attributes[:rpm_compression_level]}" + COMPRESSION_MAP[attributes[:rpm_compression]]
619
+ end
584
620
  end # def payload_compression
585
621
 
586
622
  def digest_algorithm
@@ -3,7 +3,7 @@ require "fpm/namespace"
3
3
  require "fpm/package"
4
4
  require "fpm/errors"
5
5
  require "fpm/util"
6
- require "backports"
6
+ require "backports/latest"
7
7
  require "fileutils"
8
8
  require "digest"
9
9
 
@@ -0,0 +1,130 @@
1
+ require "yaml"
2
+
3
+ require "fpm/package"
4
+ require "fpm/util"
5
+ require "fileutils"
6
+ require "fpm/package/dir"
7
+
8
+ # Support for snaps (.snap files).
9
+ #
10
+ # This supports the input and output of snaps.
11
+ class FPM::Package::Snap < FPM::Package
12
+
13
+ option "--yaml", "FILEPATH",
14
+ "Custom version of the snap.yaml file." do | snap_yaml |
15
+ File.expand_path(snap_yaml)
16
+ end
17
+
18
+ option "--confinement", "CONFINEMENT",
19
+ "Type of confinement to use for this snap.",
20
+ default: "devmode" do | confinement |
21
+ if ['strict', 'devmode', 'classic'].include? confinement
22
+ confinement
23
+ else
24
+ raise "Unsupported confinement type '#{confinement}'"
25
+ end
26
+ end
27
+
28
+ option "--grade", "GRADE", "Grade of this snap.",
29
+ default: "devel" do | grade |
30
+ if ['stable', 'devel'].include? grade
31
+ grade
32
+ else
33
+ raise "Unsupported grade type '#{grade}'"
34
+ end
35
+ end
36
+
37
+ # Input a snap
38
+ def input(input_snap)
39
+ extract_snap_to_staging input_snap
40
+ extract_snap_metadata_from_staging
41
+ end # def input
42
+
43
+ # Output a snap.
44
+ def output(output_snap)
45
+ output_check(output_snap)
46
+
47
+ write_snap_yaml
48
+
49
+ # Create the snap from the staging path
50
+ safesystem("mksquashfs", staging_path, output_snap, "-noappend", "-comp",
51
+ "xz", "-no-xattrs", "-no-fragments", "-all-root")
52
+ end # def output
53
+
54
+ def to_s(format=nil)
55
+ # Default format if nil
56
+ # name_version_arch.snap
57
+ return super(format.nil? ? "NAME_FULLVERSION_ARCH.EXTENSION" : format)
58
+ end # def to_s
59
+
60
+ private
61
+
62
+ def extract_snap_to_staging(snap_path)
63
+ safesystem("unsquashfs", "-f", "-d", staging_path, snap_path)
64
+ end
65
+
66
+ def extract_snap_metadata_from_staging
67
+ metadata = YAML.safe_load(File.read(
68
+ staging_path(File.join("meta", "snap.yaml"))))
69
+
70
+ self.name = metadata["name"]
71
+ self.version = metadata["version"]
72
+ self.description = metadata["summary"] + "\n" + metadata["description"]
73
+ self.architecture = metadata["architectures"][0]
74
+ self.attributes[:snap_confinement] = metadata["confinement"]
75
+ self.attributes[:snap_grade] = metadata["grade"]
76
+
77
+ if metadata["apps"].nil?
78
+ attributes[:snap_apps] = []
79
+ else
80
+ attributes[:snap_apps] = metadata["apps"]
81
+ end
82
+
83
+ if metadata["hooks"].nil?
84
+ attributes[:snap_hooks] = []
85
+ else
86
+ attributes[:snap_hooks] = metadata["hooks"]
87
+ end
88
+ end
89
+
90
+ def write_snap_yaml
91
+ # Write the snap.yaml
92
+ if attributes[:snap_yaml]
93
+ logger.debug("Using '#{attributes[:snap_yaml]}' as the snap.yaml")
94
+ yaml_data = File.read(attributes[:snap_yaml])
95
+ else
96
+ summary, *remainder = (self.description or "no summary given").split("\n")
97
+ description = "no description given"
98
+ if remainder.any?
99
+ description = remainder.join("\n")
100
+ end
101
+
102
+ yaml_data = {
103
+ "name" => self.name,
104
+ "version" => self.version,
105
+ "summary" => summary,
106
+ "description" => description,
107
+ "architectures" => [self.architecture],
108
+ "confinement" => self.attributes[:snap_confinement],
109
+ "grade" => self.attributes[:snap_grade],
110
+ }
111
+
112
+ unless attributes[:snap_apps].nil? or attributes[:snap_apps].empty?
113
+ yaml_data["apps"] = attributes[:snap_apps]
114
+ end
115
+
116
+ unless attributes[:snap_hooks].nil? or attributes[:snap_hooks].empty?
117
+ yaml_data["hooks"] = attributes[:snap_hooks]
118
+ end
119
+
120
+ yaml_data = yaml_data.to_yaml
121
+ end
122
+
123
+ FileUtils.mkdir_p(staging_path("meta"))
124
+ snap_yaml_path = staging_path(File.join("meta", "snap.yaml"))
125
+ logger.debug("Writing snap.yaml", :path => snap_yaml_path)
126
+ File.write(snap_yaml_path, yaml_data)
127
+ File.chmod(0644, snap_yaml_path)
128
+ edit_file(snap_yaml_path) if attributes[:edit?]
129
+ end # def write_snap_yaml
130
+ end # class FPM::Package::Snap
@@ -1,4 +1,4 @@
1
- require "backports" # gem backports
1
+ require "backports/latest" # gem backports
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
4
  require "fileutils"
@@ -50,15 +50,7 @@ class FPM::Package::Tar < FPM::Package
50
50
  output_check(output_path)
51
51
 
52
52
  # Write the scripts, too.
53
- scripts_path = File.join(staging_path, ".scripts")
54
- ::Dir.mkdir(scripts_path)
55
- [:before_install, :after_install, :before_remove, :after_remove].each do |name|
56
- next unless script?(name)
57
- out = File.join(scripts_path, name.to_s)
58
- logger.debug("Writing script", :source => name, :target => out)
59
- File.write(out, script(name))
60
- File.chmod(0755, out)
61
- end
53
+ write_scripts
62
54
 
63
55
  # Unpack the tarball to the staging path
64
56
  args = ["-cf", output_path, "-C", staging_path]
@@ -15,8 +15,11 @@ class FPM::Package::Virtualenv < FPM::Package
15
15
  option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
16
16
  "name with.", :default => "virtualenv"
17
17
 
18
- option "--install-location", "DIRECTORY", "Location to which to " \
19
- "install the virtualenv by default.", :default => "/usr/share/python" do |path|
18
+ option "--install-location", "DIRECTORY", "DEPRECATED: Use --prefix instead." \
19
+ " Location to which to install the virtualenv by default.",
20
+ :default => "/usr/share/python" do |path|
21
+ logger.warn("Using deprecated flag: --install-location. Please use " \
22
+ "--prefix instead.")
20
23
  File.expand_path(path)
21
24
  end
22
25
 
@@ -30,6 +33,19 @@ class FPM::Package::Virtualenv < FPM::Package
30
33
  :multivalued => true, :attribute_name => :virtualenv_pypi_extra_index_urls,
31
34
  :default => nil
32
35
 
36
+ option "--setup-install", :flag, "After building virtualenv run setup.py install "\
37
+ "useful when building a virtualenv for packages and including their requirements from "
38
+ "requirements.txt"
39
+
40
+ option "--system-site-packages", :flag, "Give the virtual environment access to the "\
41
+ "global site-packages"
42
+
43
+ option "--find-links", "PIP_FIND_LINKS", "If a url or path to an html file, then parse for "\
44
+ "links to archives. If a local path or file:// url that's a directory, then look "\
45
+ "for archives in the directory listing.",
46
+ :multivalued => true, :attribute_name => :virtualenv_find_links_urls,
47
+ :default => nil
48
+
33
49
  private
34
50
 
35
51
  # Input a package.
@@ -69,23 +85,33 @@ class FPM::Package::Virtualenv < FPM::Package
69
85
  self.name].join("-")
70
86
  end
71
87
 
88
+ # prefix wins over previous virtual_install_location behaviour
72
89
  virtualenv_folder =
73
- File.join(installdir,
74
- virtualenv_name)
90
+ if self.attributes[:prefix]
91
+ self.attributes[:prefix]
92
+ else
93
+ File.join(installdir,
94
+ virtualenv_name)
95
+ end
75
96
 
76
97
  virtualenv_build_folder = build_path(virtualenv_folder)
77
98
 
78
99
  ::FileUtils.mkdir_p(virtualenv_build_folder)
79
100
 
80
- safesystem("virtualenv", virtualenv_build_folder)
101
+ if self.attributes[:virtualenv_system_site_packages?]
102
+ logger.info("Creating virtualenv with --system-site-packages")
103
+ safesystem("virtualenv", "--system-site-packages", virtualenv_build_folder)
104
+ else
105
+ safesystem("virtualenv", virtualenv_build_folder)
106
+ end
107
+
81
108
  pip_exe = File.join(virtualenv_build_folder, "bin", "pip")
82
109
  python_exe = File.join(virtualenv_build_folder, "bin", "python")
83
110
 
84
111
  # Why is this hack here? It looks important, so I'll keep it in.
85
- safesystem(pip_exe, "install", "-U", "-i",
112
+ safesystem(python_exe, pip_exe, "install", "-U", "-i",
86
113
  attributes[:virtualenv_pypi],
87
- "pip", "distribute")
88
- safesystem(pip_exe, "uninstall", "-y", "distribute")
114
+ "pip")
89
115
 
90
116
  extra_index_url_args = []
91
117
  if attributes[:virtualenv_pypi_extra_index_urls]
@@ -94,6 +120,13 @@ class FPM::Package::Virtualenv < FPM::Package
94
120
  end
95
121
  end
96
122
 
123
+ find_links_url_args = []
124
+ if attributes[:virtualenv_find_links_urls]
125
+ attributes[:virtualenv_find_links_urls].each do |links_url|
126
+ find_links_url_args << "--find-links" << links_url
127
+ end
128
+ end
129
+
97
130
  target_args = []
98
131
  if is_requirements_file
99
132
  target_args << "-r" << package
@@ -101,12 +134,19 @@ class FPM::Package::Virtualenv < FPM::Package
101
134
  target_args << package
102
135
  end
103
136
 
104
- pip_args = [pip_exe, "install", "-i", attributes[:virtualenv_pypi]] << extra_index_url_args << target_args
137
+ pip_args = [python_exe, pip_exe, "install", "-i", attributes[:virtualenv_pypi]] << extra_index_url_args << find_links_url_args << target_args
105
138
  safesystem(*pip_args.flatten)
106
139
 
140
+ if attributes[:virtualenv_setup_install?]
141
+ logger.info("Running PACKAGE setup.py")
142
+ setup_args = [python_exe, "setup.py", "install"]
143
+ safesystem(*setup_args.flatten)
144
+ end
145
+
107
146
  if ! is_requirements_file && package_version.nil?
108
- frozen = safesystemout(pip_exe, "freeze")
109
- package_version = frozen[/#{package}==[^=]+$/].split("==")[1].chomp!
147
+ frozen = safesystemout(python_exe, pip_exe, "freeze")
148
+ frozen_version = frozen[/#{package}==[^=]+$/]
149
+ package_version = frozen_version && frozen_version.split("==")[1].chomp!
110
150
  self.version ||= package_version
111
151
  end
112
152
 
@@ -135,7 +175,8 @@ class FPM::Package::Virtualenv < FPM::Package
135
175
  # use dir to set stuff up properly, mainly so I don't have to reimplement
136
176
  # the chdir/prefix stuff special for tar.
137
177
  dir = convert(FPM::Package::Dir)
138
-
178
+ # don't double prefix the files
179
+ dir.attributes[:prefix] = nil
139
180
  if attributes[:chdir]
140
181
  dir.attributes[:chdir] = File.join(build_path, attributes[:chdir])
141
182
  else
@@ -1,4 +1,4 @@
1
- require "backports" # gem backports
1
+ require "backports/latest" # gem backports
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
4
  require "fileutils"
@@ -36,28 +36,12 @@ class FPM::Package::Zip < FPM::Package
36
36
  dir.cleanup_build
37
37
  end # def input
38
38
 
39
- # Output a tarball.
40
- #
41
- # If the output path ends predictably (like in .tar.gz) it will try to obey
42
- # the compression type.
39
+ # Output a zipfile.
43
40
  def output(output_path)
44
41
  output_check(output_path)
45
-
46
- files = Find.find(staging_path).to_a
47
- safesystem("zip", output_path, *files)
48
- end # def output
49
-
50
- # Generate the proper tar flags based on the path name.
51
- def tar_compression_flag(path)
52
- case path
53
- when /\.tar\.bz2$/
54
- return "-j"
55
- when /\.tar\.gz$|\.tgz$/
56
- return "-z"
57
- when /\.tar\.xz$/
58
- return "-J"
59
- else
60
- return nil
42
+ realpath = Pathname.new(output_path).realdirpath.to_s
43
+ ::Dir.chdir(staging_path) do
44
+ safesystem("zip", "-9r", realpath, ".")
61
45
  end
62
- end # def tar_compression_flag
46
+ end # def output
63
47
  end # class FPM::Package::Tar
@@ -1,6 +1,7 @@
1
1
  require "fpm/namespace"
2
2
  require "childprocess"
3
3
  require "ffi"
4
+ require "fileutils"
4
5
 
5
6
  # Some utility functions
6
7
  module FPM::Util
@@ -190,9 +191,14 @@ module FPM::Util
190
191
  if args.size == 1
191
192
  args = [ default_shell, "-c", args[0] ]
192
193
  end
193
- program = args[0]
194
194
 
195
- exit_code = execmd(args)
195
+ if args[0].kind_of?(Hash)
196
+ env = args.shift()
197
+ exit_code = execmd(env, args)
198
+ else
199
+ exit_code = execmd(args)
200
+ end
201
+ program = args[0]
196
202
  success = (exit_code == 0)
197
203
 
198
204
  if !success
@@ -226,26 +232,90 @@ module FPM::Util
226
232
  return stdout_r_str
227
233
  end # def safesystemout
228
234
 
235
+ # Get an array containing the recommended 'ar' command for this platform
236
+ # and the recommended options to quickly create/append to an archive
237
+ # without timestamps or uids (if possible).
238
+ def ar_cmd
239
+ return @@ar_cmd if defined? @@ar_cmd
240
+
241
+ @@ar_cmd_deterministic = false
242
+
243
+ # FIXME: don't assume current directory writeable
244
+ FileUtils.touch(["fpm-dummy.tmp"])
245
+ ["ar", "gar"].each do |ar|
246
+ ["-qc", "-qcD"].each do |ar_create_opts|
247
+ FileUtils.rm_f(["fpm-dummy.ar.tmp"])
248
+ # Return this combination if it creates archives without uids or timestamps.
249
+ # Exitstatus will be nonzero if the archive can't be created,
250
+ # or its table of contents doesn't match the regular expression.
251
+ # Be extra-careful about locale and timezone when matching output.
252
+ system("#{ar} #{ar_create_opts} fpm-dummy.ar.tmp fpm-dummy.tmp 2>/dev/null && env TZ=UTC LANG=C LC_TIME=C #{ar} -tv fpm-dummy.ar.tmp | grep '0/0.*1970' > /dev/null 2>&1")
253
+ if $?.exitstatus == 0
254
+ @@ar_cmd = [ar, ar_create_opts]
255
+ @@ar_cmd_deterministic = true
256
+ return @@ar_cmd
257
+ end
258
+ end
259
+ end
260
+ # If no combination of ar and options omits timestamps, fall back to default.
261
+ @@ar_cmd = ["ar", "-qc"]
262
+ return @@ar_cmd
263
+ ensure
264
+ # Clean up
265
+ FileUtils.rm_f(["fpm-dummy.ar.tmp", "fpm-dummy.tmp"])
266
+ end # def ar_cmd
267
+
268
+ # Return whether the command returned by ar_cmd can create deterministic archives
269
+ def ar_cmd_deterministic?
270
+ ar_cmd if not defined? @@ar_cmd_deterministic
271
+ return @@ar_cmd_deterministic
272
+ end
273
+
229
274
  # Get the recommended 'tar' command for this platform.
230
275
  def tar_cmd
231
- # Rely on gnu tar for solaris and OSX.
232
- case %x{uname -s}.chomp
233
- when "SunOS"
234
- return "gtar"
235
- when "Darwin"
236
- # Try running gnutar, it was renamed(??) in homebrew to 'gtar' at some point, I guess? I don't know.
237
- ["gnutar", "gtar"].each do |tar|
238
- system("#{tar} > /dev/null 2> /dev/null")
239
- return tar unless $?.exitstatus == 127
276
+ return @@tar_cmd if defined? @@tar_cmd
277
+
278
+ # FIXME: don't assume current directory writeable
279
+ FileUtils.touch(["fpm-dummy.tmp"])
280
+
281
+ # Prefer tar that supports more of the features we want, stop if we find tar of our dreams
282
+ best="tar"
283
+ bestscore=0
284
+ @@tar_cmd_deterministic = false
285
+ # GNU Tar, if not the default, is usually on the path as gtar, but
286
+ # Mac OS X 10.8 and earlier shipped it as /usr/bin/gnutar
287
+ ["tar", "gtar", "gnutar"].each do |tar|
288
+ opts=[]
289
+ score=0
290
+ ["--sort=name", "--mtime=@0"].each do |opt|
291
+ system("#{tar} #{opt} -cf fpm-dummy.tar.tmp fpm-dummy.tmp > /dev/null 2>&1")
292
+ if $?.exitstatus == 0
293
+ opts << opt
294
+ score += 1
295
+ end
296
+ end
297
+ if score > bestscore
298
+ best=tar
299
+ bestscore=score
300
+ if score == 2
301
+ @@tar_cmd_deterministic = true
302
+ break
303
+ end
240
304
  end
241
- when "FreeBSD"
242
- # use gnutar instead
243
- return "gtar"
244
- else
245
- return "tar"
246
305
  end
306
+ @@tar_cmd = best
307
+ return @@tar_cmd
308
+ ensure
309
+ # Clean up
310
+ FileUtils.rm_f(["fpm-dummy.tar.tmp", "fpm-dummy.tmp"])
247
311
  end # def tar_cmd
248
312
 
313
+ # Return whether the command returned by tar_cmd can create deterministic archives
314
+ def tar_cmd_supports_sort_names_and_set_mtime?
315
+ tar_cmd if not defined? @@tar_cmd_deterministic
316
+ return @@tar_cmd_deterministic
317
+ end
318
+
249
319
  # wrapper around mknod ffi calls
250
320
  def mknod_w(path, mode, dev)
251
321
  rc = -1
@@ -298,8 +368,8 @@ module FPM::Util
298
368
  if known_entry
299
369
  FileUtils.ln(known_entry, dst)
300
370
  else
301
- FileUtils.copy_entry(src, dst, preserve=preserve,
302
- remove_destination=remove_destination)
371
+ FileUtils.copy_entry(src, dst, preserve, false,
372
+ remove_destination)
303
373
  copied_entries[[st.dev, st.ino]] = dst
304
374
  end
305
375
  end # else...