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
@@ -75,7 +75,7 @@ class FPM::Package::Dir < FPM::Package
75
75
  end
76
76
  end
77
77
  rescue Errno::ENOENT => e
78
- raise FPM::InvalidPackageConfiguration,
78
+ raise FPM::InvalidPackageConfiguration,
79
79
  "Cannot chdir to '#{chdir}'. Does it exist?"
80
80
  end
81
81
 
@@ -99,6 +99,20 @@ class FPM::Package::Dir < FPM::Package
99
99
  logger["method"] = "output"
100
100
  clone(".", output_path)
101
101
  end
102
+
103
+ # Write the scripts, too.
104
+ scripts_path = File.join(output_path, ".scripts")
105
+ ::Dir.mkdir(scripts_path)
106
+ [:before_install, :after_install, :before_remove, :after_remove].each do |name|
107
+ next unless script?(name)
108
+ out = File.join(scripts_path, name.to_s)
109
+ logger.debug("Writing script", :source => name, :target => out)
110
+ File.write(out, script(name))
111
+ require "pry"
112
+ binding.pry
113
+ File.chmod(0755, out)
114
+ end
115
+
102
116
  ensure
103
117
  logger.remove("method")
104
118
  end # def output
@@ -128,19 +142,19 @@ class FPM::Package::Dir < FPM::Package
128
142
  "to stage files during packaging, so this setting would have " \
129
143
  "caused fpm to loop creating staging directories and copying " \
130
144
  "them into your package! Oops! If you are confused, maybe you could " \
131
- "check your TMPDIR or TEMPDIR environment variables?"
145
+ "check your TMPDIR, TMP, or TEMP environment variables?"
132
146
  end
133
147
 
134
148
  # For single file copies, permit file destinations
135
149
  fileinfo = File.lstat(source)
136
- if fileinfo.file? && !File.directory?(destination)
150
+ if fileinfo.file? && !File.directory?(destination)
137
151
  if destination[-1,1] == "/"
138
152
  copy(source, File.join(destination, source))
139
153
  else
140
154
  copy(source, destination)
141
155
  end
142
156
  elsif fileinfo.symlink?
143
- copy(source, destination)
157
+ copy(source, File.join(destination, source))
144
158
  else
145
159
  # Copy all files from 'path' into staging_path
146
160
  Find.find(source) do |path|
@@ -157,8 +171,17 @@ class FPM::Package::Dir < FPM::Package
157
171
  def copy(source, destination)
158
172
  logger.debug("Copying path", :source => source, :destination => destination)
159
173
  directory = File.dirname(destination)
160
- if !File.directory?(directory)
161
- FileUtils.mkdir_p(directory)
174
+ # lstat to follow symlinks
175
+ dstat = File.stat(directory) rescue nil
176
+ if dstat.nil?
177
+ FileUtils.mkdir_p(directory, :mode => 0755)
178
+ elsif dstat.directory?
179
+ # do nothing, it's already a directory!
180
+ else
181
+ # It exists and is not a directory. This is probably a user error or a bug.
182
+ readable_path = directory.gsub(staging_path, "")
183
+ logger.error("You wanted to copy a file into a directory, but that's not a directory, it's a file!", :path => readable_path, :stat => dstat)
184
+ raise FPM::InvalidPackageConfiguration, "Tried to treat #{readable_path} like a directory, but it's a file!"
162
185
  end
163
186
 
164
187
  if File.directory?(source)
@@ -194,27 +217,5 @@ class FPM::Package::Dir < FPM::Package
194
217
  copy_metadata(source, destination)
195
218
  end # def copy
196
219
 
197
- def copy_metadata(source, destination)
198
- source_stat = File::lstat(source)
199
- dest_stat = File::lstat(destination)
200
-
201
- # If this is a hard-link, there's no metadata to copy.
202
- # If this is a symlink, what it points to hasn't been copied yet.
203
- return if source_stat.ino == dest_stat.ino || dest_stat.symlink?
204
-
205
- File.utime(source_stat.atime, source_stat.mtime, destination)
206
- mode = source_stat.mode
207
- begin
208
- File.lchown(source_stat.uid, source_stat.gid, destination)
209
- rescue Errno::EPERM
210
- # clear setuid/setgid
211
- mode &= 01777
212
- end
213
-
214
- unless source_stat.symlink?
215
- File.chmod(mode, destination)
216
- end
217
- end # def copy_metadata
218
-
219
220
  public(:input, :output)
220
221
  end # class FPM::Package::Dir
@@ -4,4 +4,10 @@ require "backports"
4
4
  # Empty Package type. For strict/meta/virtual package creation
5
5
 
6
6
  class FPM::Package::Empty < FPM::Package
7
+ def output(output_path)
8
+ logger.warn("Your package has gone into the void.")
9
+ end
10
+ def to_s(fmt)
11
+ return ""
12
+ end
7
13
  end
@@ -0,0 +1,144 @@
1
+ require "backports" # gem backports
2
+ require "fpm/package"
3
+ require "fpm/util"
4
+ require "digest"
5
+ require "fileutils"
6
+ require "xz"
7
+
8
+ class FPM::Package::FreeBSD < FPM::Package
9
+ SCRIPT_MAP = {
10
+ :before_install => "pre-install",
11
+ :after_install => "post-install",
12
+ :before_remove => "pre-deinstall",
13
+ :after_remove => "post-deinstall",
14
+ } unless defined?(SCRIPT_MAP)
15
+
16
+ def self.default_abi
17
+ abi_name = %x{uname -s}.chomp
18
+ abi_version = %x{uname -r}.chomp.split(".")[0]
19
+ abi_arch = %x{uname -m}.chomp
20
+
21
+ [abi_name, abi_version, abi_arch].join(":")
22
+ end
23
+
24
+ option "--abi", "ABI",
25
+ "Sets the FreeBSD abi pkg field to specify binary compatibility.",
26
+ :default => default_abi
27
+
28
+ option "--origin", "ABI",
29
+ "Sets the FreeBSD 'origin' pkg field",
30
+ :default => "fpm/<name>"
31
+
32
+ def output(output_path)
33
+ output_check(output_path)
34
+
35
+ # Build the packaging metadata files.
36
+ checksums = {}
37
+ self.files.each do |f|
38
+ path = staging_path(f)
39
+ if File.symlink?(path)
40
+ checksums[f] = "-"
41
+ elsif File.file?(path)
42
+ checksums[f] = Digest::SHA256.file(path).hexdigest
43
+ end
44
+ end
45
+
46
+ pkg_origin = attributes[:freebsd_origin]
47
+ if pkg_origin == "fpm/<name>" # fill in default
48
+ pkg_origin = "fpm/#{name}"
49
+ end
50
+
51
+ # Follow similar rules to these used in ``to_s_fullversion`` method.
52
+ # FIXME: maybe epoch should also be introduced somehow ("#{version},#{epoch})?
53
+ # should it go to pkgdata["version"] or to another place?
54
+ # https://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html
55
+ pkg_version = (iteration and (iteration.to_i > 0)) ? "#{version}-#{iteration}" : "#{version}"
56
+
57
+ pkgdata = {
58
+ "abi" => attributes[:freebsd_abi],
59
+ "name" => name,
60
+ "version" => pkg_version,
61
+ "comment" => description,
62
+ "desc" => description,
63
+ "origin" => pkg_origin,
64
+ "maintainer" => maintainer,
65
+ "www" => url,
66
+ # prefix is required, but it doesn't seem to matter
67
+ "prefix" => "/",
68
+ }
69
+
70
+ # Write +COMPACT_MANIFEST, without the "files" section.
71
+ File.open(staging_path("+COMPACT_MANIFEST"), "w+") do |file|
72
+ file.write(pkgdata.to_json + "\n")
73
+ end
74
+
75
+ # Populate files + checksums, then write +MANIFEST.
76
+ pkgdata["files"] = {}
77
+ checksums.each do |f, shasum|
78
+ # pkg expands % URL-style escapes, so make sure to escape % as %25
79
+ pkgdata["files"]["/" + f.gsub("%", "%25")] = shasum
80
+ end
81
+
82
+ # Populate scripts
83
+ pkgdata["scripts"] = {}
84
+ scripts.each do |name, data|
85
+ pkgdata["scripts"][SCRIPT_MAP[name]] = data
86
+ end
87
+
88
+ File.open(staging_path("+MANIFEST"), "w+") do |file|
89
+ file.write(pkgdata.to_json + "\n")
90
+ end
91
+
92
+ # Create the .txz package archive from the files in staging_path.
93
+ File.open(output_path, "wb") do |file|
94
+ XZ::StreamWriter.new(file) do |xz|
95
+ FPM::Util::TarWriter.new(xz) do |tar|
96
+ # The manifests must come first for pkg.
97
+ add_path(tar, "+COMPACT_MANIFEST",
98
+ File.join(staging_path, "+COMPACT_MANIFEST"))
99
+ add_path(tar, "+MANIFEST",
100
+ File.join(staging_path, "+MANIFEST"))
101
+
102
+ checksums.keys.each do |path|
103
+ add_path(tar, "/" + path, File.join(staging_path, path))
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end # def output
109
+
110
+ def add_path(tar, tar_path, path)
111
+ stat = File.lstat(path)
112
+ if stat.directory?
113
+ tar.mkdir(tar_path, stat.mode)
114
+ elsif stat.symlink?
115
+ tar.add_symlink(tar_path, File.readlink(path), stat.mode)
116
+ else
117
+ tar.add_file_simple(tar_path, stat.mode, stat.size) do |io|
118
+ File.open(path) do |fd|
119
+ chunk = nil
120
+ size = 0
121
+ while chunk = fd.read(16384) do
122
+ size += io.write(chunk)
123
+ end
124
+ if size != stat.size
125
+ raise "Failed to add #{path} to the archive; expected to " +
126
+ "write #{stat.size} bytes, only wrote #{size}"
127
+ end
128
+ end
129
+ end # tar.tar.add_file_simple
130
+ end
131
+ end # def add_path
132
+
133
+ def to_s_extension; "txz"; end
134
+
135
+ def to_s_fullversion()
136
+ # iteration (PORTREVISION on FreeBSD) shall be appended only(?) if non-zero.
137
+ # https://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html
138
+ (iteration and (iteration.to_i > 0)) ? "#{version}_#{iteration}" : "#{version}"
139
+ end
140
+
141
+ def to_s(format=nil)
142
+ return super(format.nil? ? "NAME-FULLVERSION.EXTENSION" : format)
143
+ end # def to_s
144
+ end # class FPM::Package::FreeBSD
@@ -58,7 +58,7 @@ class FPM::Package::Gem < FPM::Package
58
58
 
59
59
  def download_if_necessary(gem, gem_version)
60
60
  path = gem
61
- if !File.exists?(path)
61
+ if !File.exist?(path)
62
62
  path = download(gem, gem_version)
63
63
  end
64
64
 
@@ -114,7 +114,7 @@ class FPM::Package::Gem < FPM::Package
114
114
  # where missing 'build' number prevents correct dependency resolution by target
115
115
  # package manager. Ie. for dpkg 1.1 != 1.1.0
116
116
  m = spec.version.to_s.scan(/(\d+)\.?/)
117
- self.version = m.flatten.fill('0', m.length..2).join('.')
117
+ self.version = m.flatten.fill('0', m.length..2).join('.')
118
118
 
119
119
  self.vendor = spec.author
120
120
  self.url = spec.homepage
@@ -132,7 +132,7 @@ class FPM::Package::Gem < FPM::Package
132
132
  self.description = description_options.find { |d| !(d.nil? or d.strip.empty?) }
133
133
 
134
134
  # Upstream rpms seem to do this, might as well share.
135
- # TODO(sissel): Figure out how to hint this only to rpm?
135
+ # TODO(sissel): Figure out how to hint this only to rpm?
136
136
  # maybe something like attributes[:rpm_provides] for rpm specific stuff?
137
137
  # Or just ignore it all together.
138
138
  #self.provides << "rubygem(#{self.name})"
@@ -178,7 +178,7 @@ class FPM::Package::Gem < FPM::Package
178
178
  ::FileUtils.mkdir_p(installdir)
179
179
  # TODO(sissel): Allow setting gem tool path
180
180
  args = [attributes[:gem_gem], "install", "--quiet", "--no-ri", "--no-rdoc",
181
- "--install-dir", installdir, "--ignore-dependencies"]
181
+ "--no-user-install", "--install-dir", installdir, "--ignore-dependencies"]
182
182
  if attributes[:gem_env_shebang?]
183
183
  args += ["-E"]
184
184
  end
@@ -213,7 +213,7 @@ class FPM::Package::Gem < FPM::Package
213
213
  end
214
214
  end
215
215
  end # def install_to_staging
216
-
216
+
217
217
  # Sanitize package name.
218
218
  # This prefixes the package name with 'rubygem' (but depends on the attribute
219
219
  # :gem_package_name_prefix
@@ -72,7 +72,7 @@ class FPM::Package::NPM < FPM::Package
72
72
  end
73
73
 
74
74
  self.description = info["description"]
75
- # Supposedly you can upload a package for npm with no author/author email
75
+ # Supposedly you can upload a package for npm with no author/author email
76
76
  # so I'm being safer with this. Author can also be a hash or a string
77
77
  self.vendor = "Unknown <unknown@unknown.unknown>"
78
78
  if info.include?("author")
@@ -91,7 +91,7 @@ class FPM::Package::NPM < FPM::Package
91
91
  # 'npm install express' it installs dependencies (like 'connect')
92
92
  # to: node_modules/express/node_modules/connect/...
93
93
  #
94
- # To that end, I don't think we necessarily need to include
94
+ # To that end, I don't think we necessarily need to include
95
95
  # any automatic dependency information since every 'npm install'
96
96
  # is fully self-contained. That's why you don't see any bother, yet,
97
97
  # to include the package's dependencies in here.
@@ -21,7 +21,7 @@ class FPM::Package::OSXpkg < FPM::Package
21
21
  POSTINSTALL_ACTIONS = [ "logout", "restart", "shutdown" ]
22
22
  OWNERSHIP_OPTIONS = ["recommended", "preserve", "preserve-other"]
23
23
 
24
- option "--identifier-prefix", "IDENTIFIER_PREFIX",
24
+ option "--identifier-prefix", "IDENTIFIER_PREFIX",
25
25
  "Reverse domain prefix prepended to package identifier, " \
26
26
  "ie. 'org.great.my'. If this is omitted, the identifer " \
27
27
  "will be the package name."
@@ -81,18 +81,18 @@ class FPM::Package::OSXpkg < FPM::Package
81
81
  SCRIPT_MAP.each do |scriptname, filename|
82
82
  next unless script?(scriptname)
83
83
 
84
- with(scripts_path(filename)) do |pkgscript|
84
+ scripts_path(filename).tap do |pkgscript|
85
85
  logger.info("Writing pkg script", :source => filename, :target => pkgscript)
86
86
  File.write(pkgscript, script(scriptname))
87
87
  # scripts are required to be executable
88
88
  File.chmod(0755, pkgscript)
89
89
  end
90
- end
90
+ end
91
91
  end # def write_scripts
92
92
 
93
93
  # Returns path of a processed template PackageInfo given to 'pkgbuild --info'
94
94
  # note: '--info' is undocumented:
95
- # http://managingosx.wordpress.com/2012/07/05/stupid-tricks-with-pkgbuild
95
+ # http://managingosx.wordpress.com/2012/07/05/stupid-tricks-with-pkgbuild
96
96
  def pkginfo_template_path
97
97
  pkginfo_template = Tempfile.open("fpm-PackageInfo")
98
98
  pkginfo_data = template("osxpkg.erb").result(binding)
@@ -103,7 +103,7 @@ class FPM::Package::OSXpkg < FPM::Package
103
103
 
104
104
  # Extract name and version from PackageInfo XML
105
105
  def extract_info(package)
106
- with(build_path("expand")) do |path|
106
+ build_path("expand").tap do |path|
107
107
  doc = REXML::Document.new File.open(File.join(path, "PackageInfo"))
108
108
  pkginfo_elem = doc.elements["pkg-info"]
109
109
  identifier = pkginfo_elem.attribute("identifier").value
@@ -154,9 +154,10 @@ class FPM::Package::OSXpkg < FPM::Package
154
154
  FileUtils.remove_file(temp_info)
155
155
  end # def output
156
156
 
157
+ def to_s_extension; "pkg"; end
158
+
157
159
  def to_s(format=nil)
158
- return super("NAME-VERSION.pkg") if format.nil?
159
- return super(format)
160
+ return super(format.nil? ? "NAME-VERSION.EXTENSION" : format)
160
161
  end # def to_s
161
162
 
162
163
  public(:input, :output, :identifier, :to_s)
@@ -0,0 +1,124 @@
1
+ require "erb"
2
+ require "fpm/namespace"
3
+ require "fpm/package"
4
+ require "fpm/errors"
5
+ require "fpm/util"
6
+
7
+ class FPM::Package::P5P < FPM::Package
8
+
9
+ option "--user", "USER",
10
+ "Set the user to USER in the prototype files.",
11
+ :default => 'root'
12
+
13
+ option "--group", "GROUP",
14
+ "Set the group to GROUP in the prototype file.",
15
+ :default => 'root'
16
+
17
+ option "--zonetype", "ZONETYPE",
18
+ "Set the allowed zone types (global, nonglobal, both)",
19
+ :default => 'value=global value=nonglobal' do |value|
20
+ case @value
21
+ when "both"
22
+ value = "value=global value=nonglobal"
23
+ else
24
+ value = "value=#{value}"
25
+ end
26
+ end # value
27
+
28
+ option "--publisher", "PUBLISHER",
29
+ "Set the publisher name for the repository",
30
+ :default => 'FPM'
31
+
32
+ option "--lint" , :flag, "Check manifest with pkglint",
33
+ :default => true
34
+
35
+ option "--validate", :flag, "Validate with pkg install",
36
+ :default => true
37
+
38
+ def architecture
39
+ case @architecture
40
+ when nil, "native"
41
+ @architecture = %x{uname -p}.chomp
42
+ when "all"
43
+ @architecture = 'i386 value=sparc'
44
+ end
45
+
46
+ return @architecture
47
+ end # def architecture
48
+
49
+ def output(output_path)
50
+
51
+ # Fixup the category to an acceptable solaris category
52
+ case @category
53
+ when nil, "default"
54
+ @category = 'Applications/System Utilities'
55
+ end
56
+
57
+ # Manifest Filename
58
+ manifest_fn = build_path("#{name}.p5m")
59
+
60
+ # Generate a package manifest.
61
+ pkg_generate = safesystemout("pkgsend", "generate", "#{staging_path}")
62
+ File.write(build_path("#{name}.p5m.1"), pkg_generate)
63
+
64
+ # Add necessary metadata to the generated manifest.
65
+ metadata_template = template("p5p_metadata.erb").result(binding)
66
+ File.write(build_path("#{name}.mog"), metadata_template)
67
+
68
+ # Combine template and filelist; allow user to edit before proceeding
69
+ File.open(manifest_fn, "w") do |manifest|
70
+ manifest.write metadata_template
71
+ manifest.write pkg_generate
72
+ end
73
+ edit_file(manifest_fn) if attributes[:edit?]
74
+
75
+ # Execute the transmogrification on the manifest
76
+ pkg_mogrify = safesystemout("pkgmogrify", manifest_fn)
77
+ File.write(build_path("#{name}.p5m.2"), pkg_mogrify)
78
+ safesystem("cp", build_path("#{name}.p5m.2"), manifest_fn)
79
+
80
+ # Evaluate dependencies.
81
+ if !attributes[:no_auto_depends?]
82
+ pkgdepend_gen = safesystemout("pkgdepend", "generate", "-md", "#{staging_path}", manifest_fn)
83
+ File.write(build_path("#{name}.p5m.3"), pkgdepend_gen)
84
+
85
+ # Allow user to review added dependencies
86
+ edit_file(build_path("#{name}.p5m.3")) if attributes[:edit?]
87
+
88
+ safesystem("pkgdepend", "resolve", "-m", build_path("#{name}.p5m.3"))
89
+ safesystem("cp", build_path("#{name}.p5m.3.res"), manifest_fn)
90
+ end
91
+
92
+ # Final format of manifest
93
+ safesystem("pkgfmt", manifest_fn)
94
+
95
+ # Final edit for lint check and packaging
96
+ edit_file(manifest_fn) if attributes[:edit?]
97
+
98
+ # Add any facets or actuators that are needed.
99
+ # TODO(jcraig): add manpage actuator to enable install wo/ man pages
100
+
101
+ # Verify the package.
102
+ if attributes[:p5p_lint] then
103
+ safesystem("pkglint", manifest_fn)
104
+ end
105
+
106
+ # Publish the package.
107
+ repo_path = build_path("#{name}_repo")
108
+ safesystem("pkgrepo", "create", repo_path)
109
+ safesystem("pkgrepo", "set", "-s", repo_path, "publisher/prefix=#{attributes[:p5p_publisher]}")
110
+ safesystem("pkgsend", "-s", repo_path,
111
+ "publish", "-d", "#{staging_path}", "#{build_path}/#{name}.p5m")
112
+ safesystem("pkgrecv", "-s", repo_path, "-a",
113
+ "-d", build_path("#{name}.p5p"), name)
114
+
115
+ # Test the package
116
+ if attributes[:p5p_validate] then
117
+ safesystem("pkg", "install", "-nvg", build_path("#{name}.p5p"), name)
118
+ end
119
+
120
+ # Cleanup
121
+ safesystem("mv", build_path("#{name}.p5p"), output_path)
122
+
123
+ end # def output
124
+ end # class FPM::Package::IPS