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.
- checksums.yaml +7 -0
- data/CHANGELIST +661 -0
- data/CONTRIBUTORS +26 -0
- data/LICENSE +21 -0
- data/bin/fpm +8 -0
- data/lib/fpm.rb +20 -0
- data/lib/fpm/command.rb +648 -0
- data/lib/fpm/errors.rb +4 -0
- data/lib/fpm/namespace.rb +4 -0
- data/lib/fpm/package.rb +539 -0
- data/lib/fpm/package/apk.rb +510 -0
- data/lib/fpm/package/cpan.rb +405 -0
- data/lib/fpm/package/deb.rb +935 -0
- data/lib/fpm/package/dir.rb +221 -0
- data/lib/fpm/package/empty.rb +13 -0
- data/lib/fpm/package/freebsd.rb +147 -0
- data/lib/fpm/package/gem.rb +243 -0
- data/lib/fpm/package/npm.rb +120 -0
- data/lib/fpm/package/osxpkg.rb +165 -0
- data/lib/fpm/package/p5p.rb +124 -0
- data/lib/fpm/package/pacman.rb +403 -0
- data/lib/fpm/package/pear.rb +117 -0
- data/lib/fpm/package/pkgin.rb +35 -0
- data/lib/fpm/package/pleaserun.rb +63 -0
- data/lib/fpm/package/puppet.rb +120 -0
- data/lib/fpm/package/pyfpm/__init__.py +1 -0
- data/lib/fpm/package/pyfpm/get_metadata.py +104 -0
- data/lib/fpm/package/python.rb +318 -0
- data/lib/fpm/package/rpm.rb +593 -0
- data/lib/fpm/package/sh.rb +69 -0
- data/lib/fpm/package/solaris.rb +95 -0
- data/lib/fpm/package/tar.rb +86 -0
- data/lib/fpm/package/virtualenv.rb +164 -0
- data/lib/fpm/package/zip.rb +63 -0
- data/lib/fpm/rake_task.rb +60 -0
- data/lib/fpm/util.rb +358 -0
- data/lib/fpm/util/tar_writer.rb +80 -0
- data/lib/fpm/version.rb +3 -0
- data/templates/deb.erb +52 -0
- data/templates/deb/changelog.erb +5 -0
- data/templates/deb/ldconfig.sh.erb +13 -0
- data/templates/deb/postinst_upgrade.sh.erb +62 -0
- data/templates/deb/postrm_upgrade.sh.erb +46 -0
- data/templates/deb/preinst_upgrade.sh.erb +41 -0
- data/templates/deb/prerm_upgrade.sh.erb +39 -0
- data/templates/osxpkg.erb +11 -0
- data/templates/p5p_metadata.erb +12 -0
- data/templates/pacman.erb +47 -0
- data/templates/pacman/INSTALL.erb +41 -0
- data/templates/pleaserun/generate-cleanup.sh +17 -0
- data/templates/pleaserun/install-path.sh +17 -0
- data/templates/pleaserun/install.sh +117 -0
- data/templates/pleaserun/scripts/after-install.sh +4 -0
- data/templates/pleaserun/scripts/before-remove.sh +12 -0
- data/templates/puppet/package.pp.erb +34 -0
- data/templates/puppet/package/remove.pp.erb +13 -0
- data/templates/rpm.erb +260 -0
- data/templates/rpm/filesystem_list +14514 -0
- data/templates/sh.erb +369 -0
- data/templates/solaris.erb +15 -0
- metadata +322 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
require "fpm/namespace"
|
2
|
+
require "fpm/package"
|
3
|
+
require "fpm/util"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
class FPM::Package::NPM < FPM::Package
|
7
|
+
class << self
|
8
|
+
include FPM::Util
|
9
|
+
end
|
10
|
+
# Flags '--foo' will be accessable as attributes[:npm_foo]
|
11
|
+
option "--bin", "NPM_EXECUTABLE",
|
12
|
+
"The path to the npm executable you wish to run.", :default => "npm"
|
13
|
+
|
14
|
+
option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
|
15
|
+
"name with.", :default => "node"
|
16
|
+
|
17
|
+
option "--registry", "NPM_REGISTRY",
|
18
|
+
"The npm registry to use instead of the default."
|
19
|
+
|
20
|
+
private
|
21
|
+
def input(package)
|
22
|
+
# Notes:
|
23
|
+
# * npm respects PREFIX
|
24
|
+
settings = {
|
25
|
+
"cache" => build_path("npm_cache"),
|
26
|
+
"loglevel" => "warn",
|
27
|
+
"global" => "true"
|
28
|
+
}
|
29
|
+
|
30
|
+
settings["registry"] = attributes[:npm_registry] if attributes[:npm_registry_given?]
|
31
|
+
set_default_prefix unless attributes[:prefix_given?]
|
32
|
+
|
33
|
+
settings["prefix"] = staging_path(attributes[:prefix])
|
34
|
+
FileUtils.mkdir_p(settings["prefix"])
|
35
|
+
|
36
|
+
npm_flags = []
|
37
|
+
settings.each do |key, value|
|
38
|
+
# npm lets you specify settings in a .npmrc but the same key=value settings
|
39
|
+
# are valid as '--key value' command arguments to npm. Woo!
|
40
|
+
logger.debug("Configuring npm", key => value)
|
41
|
+
npm_flags += [ "--#{key}", value ]
|
42
|
+
end
|
43
|
+
|
44
|
+
install_args = [
|
45
|
+
attributes[:npm_bin],
|
46
|
+
"install",
|
47
|
+
# use 'package' or 'package@version'
|
48
|
+
(version ? "#{package}@#{version}" : package)
|
49
|
+
]
|
50
|
+
|
51
|
+
# The only way to get npm to respect the 'prefix' setting appears to
|
52
|
+
# be to set the --global flag.
|
53
|
+
#install_args << "--global"
|
54
|
+
install_args += npm_flags
|
55
|
+
|
56
|
+
safesystem(*install_args)
|
57
|
+
|
58
|
+
# Query details about our now-installed package.
|
59
|
+
# We do this by using 'npm ls' with json + long enabled to query details
|
60
|
+
# about the installed package.
|
61
|
+
npm_ls_out = safesystemout(attributes[:npm_bin], "ls", "--json", "--long", *npm_flags)
|
62
|
+
npm_ls = JSON.parse(npm_ls_out)
|
63
|
+
name, info = npm_ls["dependencies"].first
|
64
|
+
|
65
|
+
self.name = [attributes[:npm_package_name_prefix], name].join("-")
|
66
|
+
self.version = info.fetch("version", "0.0.0")
|
67
|
+
|
68
|
+
if info.include?("repository")
|
69
|
+
self.url = info["repository"]["url"]
|
70
|
+
else
|
71
|
+
self.url = "https://npmjs.org/package/#{self.name}"
|
72
|
+
end
|
73
|
+
|
74
|
+
self.description = info["description"]
|
75
|
+
# Supposedly you can upload a package for npm with no author/author email
|
76
|
+
# so I'm being safer with this. Author can also be a hash or a string
|
77
|
+
self.vendor = "Unknown <unknown@unknown.unknown>"
|
78
|
+
if info.include?("author")
|
79
|
+
author_info = info["author"]
|
80
|
+
# If a hash, assemble into a string
|
81
|
+
if author_info.respond_to? :fetch
|
82
|
+
self.vendor = sprintf("%s <%s>", author_info.fetch("name", "unknown"),
|
83
|
+
author_info.fetch("email", "unknown@unknown.unknown"))
|
84
|
+
else
|
85
|
+
# Probably will need a better check for validity here
|
86
|
+
self.vendor = author_info unless author_info == ""
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# npm installs dependencies in the module itself, so if you do
|
91
|
+
# 'npm install express' it installs dependencies (like 'connect')
|
92
|
+
# to: node_modules/express/node_modules/connect/...
|
93
|
+
#
|
94
|
+
# To that end, I don't think we necessarily need to include
|
95
|
+
# any automatic dependency information since every 'npm install'
|
96
|
+
# is fully self-contained. That's why you don't see any bother, yet,
|
97
|
+
# to include the package's dependencies in here.
|
98
|
+
#
|
99
|
+
# It's possible someone will want to decouple that in the future,
|
100
|
+
# but I will wait for that feature request.
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_default_prefix
|
104
|
+
attributes[:prefix] = self.class.default_prefix
|
105
|
+
attributes[:prefix_given?] = true
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.default_prefix
|
109
|
+
npm_prefix = safesystemout("npm", "prefix", "-g").chomp
|
110
|
+
if npm_prefix.count("\n") > 0
|
111
|
+
raise FPM::InvalidPackageConfiguration, "`npm prefix -g` returned unexpected output."
|
112
|
+
elsif !File.directory?(npm_prefix)
|
113
|
+
raise FPM::InvalidPackageConfiguration, "`npm prefix -g` returned a non-existent directory"
|
114
|
+
end
|
115
|
+
logger.info("Setting default npm install prefix", :prefix => npm_prefix)
|
116
|
+
npm_prefix
|
117
|
+
end
|
118
|
+
|
119
|
+
public(:input)
|
120
|
+
end # class FPM::Package::NPM
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require "fpm/package"
|
2
|
+
require "fpm/util"
|
3
|
+
require "fileutils"
|
4
|
+
require "fpm/package/dir"
|
5
|
+
require 'tempfile' # stdlib
|
6
|
+
require 'pathname' # stdlib
|
7
|
+
require 'rexml/document' # stdlib
|
8
|
+
|
9
|
+
# Use an OS X pkg built with pkgbuild.
|
10
|
+
#
|
11
|
+
# Supports input and output. Requires pkgbuild and (for input) pkgutil, part of a
|
12
|
+
# standard OS X install in 10.7 and higher.
|
13
|
+
class FPM::Package::OSXpkg < FPM::Package
|
14
|
+
|
15
|
+
# Map of what scripts are named.
|
16
|
+
SCRIPT_MAP = {
|
17
|
+
:before_install => "preinstall",
|
18
|
+
:after_install => "postinstall",
|
19
|
+
} unless defined?(SCRIPT_MAP)
|
20
|
+
|
21
|
+
POSTINSTALL_ACTIONS = [ "logout", "restart", "shutdown" ]
|
22
|
+
OWNERSHIP_OPTIONS = ["recommended", "preserve", "preserve-other"]
|
23
|
+
|
24
|
+
option "--identifier-prefix", "IDENTIFIER_PREFIX",
|
25
|
+
"Reverse domain prefix prepended to package identifier, " \
|
26
|
+
"ie. 'org.great.my'. If this is omitted, the identifer " \
|
27
|
+
"will be the package name."
|
28
|
+
option "--payload-free", :flag, "Define no payload, assumes use of script options.",
|
29
|
+
:default => false
|
30
|
+
option "--ownership", "OWNERSHIP",
|
31
|
+
"--ownership option passed to pkgbuild. Defaults to 'recommended'. " \
|
32
|
+
"See pkgbuild(1).", :default => 'recommended' do |value|
|
33
|
+
if !OWNERSHIP_OPTIONS.include?(value)
|
34
|
+
raise ArgumentError, "osxpkg-ownership value of '#{value}' is invalid. " \
|
35
|
+
"Must be one of #{OWNERSHIP_OPTIONS.join(", ")}"
|
36
|
+
end
|
37
|
+
value
|
38
|
+
end
|
39
|
+
|
40
|
+
option "--postinstall-action", "POSTINSTALL_ACTION",
|
41
|
+
"Post-install action provided in package metadata. " \
|
42
|
+
"Optionally one of '#{POSTINSTALL_ACTIONS.join("', '")}'." do |value|
|
43
|
+
if !POSTINSTALL_ACTIONS.include?(value)
|
44
|
+
raise ArgumentError, "osxpkg-postinstall-action value of '#{value}' is invalid. " \
|
45
|
+
"Must be one of #{POSTINSTALL_ACTIONS.join(", ")}"
|
46
|
+
end
|
47
|
+
value
|
48
|
+
end
|
49
|
+
|
50
|
+
dont_obsolete_paths = []
|
51
|
+
option "--dont-obsolete", "DONT_OBSOLETE_PATH",
|
52
|
+
"A file path for which to 'dont-obsolete' in the built PackageInfo. " \
|
53
|
+
"Can be specified multiple times." do |path|
|
54
|
+
dont_obsolete_paths << path
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
# return the identifier by prepending the reverse-domain prefix
|
59
|
+
# to the package name, else return just the name
|
60
|
+
def identifier
|
61
|
+
identifier = name.dup
|
62
|
+
if self.attributes[:osxpkg_identifier_prefix]
|
63
|
+
identifier.insert(0, "#{self.attributes[:osxpkg_identifier_prefix]}.")
|
64
|
+
end
|
65
|
+
identifier
|
66
|
+
end # def identifier
|
67
|
+
|
68
|
+
# scripts_path and write_scripts cribbed from deb.rb
|
69
|
+
def scripts_path(path=nil)
|
70
|
+
@scripts_path ||= build_path("Scripts")
|
71
|
+
FileUtils.mkdir(@scripts_path) if !File.directory?(@scripts_path)
|
72
|
+
|
73
|
+
if path.nil?
|
74
|
+
return @scripts_path
|
75
|
+
else
|
76
|
+
return File.join(@scripts_path, path)
|
77
|
+
end
|
78
|
+
end # def scripts_path
|
79
|
+
|
80
|
+
def write_scripts
|
81
|
+
SCRIPT_MAP.each do |scriptname, filename|
|
82
|
+
next unless script?(scriptname)
|
83
|
+
|
84
|
+
scripts_path(filename).tap do |pkgscript|
|
85
|
+
logger.info("Writing pkg script", :source => filename, :target => pkgscript)
|
86
|
+
File.write(pkgscript, script(scriptname))
|
87
|
+
# scripts are required to be executable
|
88
|
+
File.chmod(0755, pkgscript)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end # def write_scripts
|
92
|
+
|
93
|
+
# Returns path of a processed template PackageInfo given to 'pkgbuild --info'
|
94
|
+
# note: '--info' is undocumented:
|
95
|
+
# http://managingosx.wordpress.com/2012/07/05/stupid-tricks-with-pkgbuild
|
96
|
+
def pkginfo_template_path
|
97
|
+
pkginfo_template = Tempfile.open("fpm-PackageInfo")
|
98
|
+
pkginfo_data = template("osxpkg.erb").result(binding)
|
99
|
+
pkginfo_template.write(pkginfo_data)
|
100
|
+
pkginfo_template.close
|
101
|
+
pkginfo_template.path
|
102
|
+
end # def write_pkginfo_template
|
103
|
+
|
104
|
+
# Extract name and version from PackageInfo XML
|
105
|
+
def extract_info(package)
|
106
|
+
build_path("expand").tap do |path|
|
107
|
+
doc = REXML::Document.new File.open(File.join(path, "PackageInfo"))
|
108
|
+
pkginfo_elem = doc.elements["pkg-info"]
|
109
|
+
identifier = pkginfo_elem.attribute("identifier").value
|
110
|
+
self.version = pkginfo_elem.attribute("version").value
|
111
|
+
# set name to the last dot element of the identifier
|
112
|
+
self.name = identifier.split(".").last
|
113
|
+
logger.info("inferring name #{self.name} from pkg-id #{identifier}")
|
114
|
+
end
|
115
|
+
end # def extract_info
|
116
|
+
|
117
|
+
# Take a flat package as input
|
118
|
+
def input(input_path)
|
119
|
+
# TODO: Fail if it's a Distribution pkg or old-fashioned
|
120
|
+
expand_dir = File.join(build_path, "expand")
|
121
|
+
# expand_dir must not already exist for pkgutil --expand
|
122
|
+
safesystem("pkgutil --expand #{input_path} #{expand_dir}")
|
123
|
+
|
124
|
+
extract_info(input_path)
|
125
|
+
|
126
|
+
# extract Payload
|
127
|
+
safesystem("tar -xz -f #{expand_dir}/Payload -C #{staging_path}")
|
128
|
+
end # def input
|
129
|
+
|
130
|
+
# Output a pkgbuild pkg.
|
131
|
+
def output(output_path)
|
132
|
+
output_check(output_path)
|
133
|
+
|
134
|
+
temp_info = pkginfo_template_path
|
135
|
+
|
136
|
+
args = ["--identifier", identifier,
|
137
|
+
"--info", temp_info,
|
138
|
+
"--version", version.to_s,
|
139
|
+
"--ownership", attributes[:osxpkg_ownership]]
|
140
|
+
|
141
|
+
if self.attributes[:osxpkg_payload_free?]
|
142
|
+
args << "--nopayload"
|
143
|
+
else
|
144
|
+
args += ["--root", staging_path]
|
145
|
+
end
|
146
|
+
|
147
|
+
if attributes[:before_install_given?] or attributes[:after_install_given?]
|
148
|
+
write_scripts
|
149
|
+
args += ["--scripts", scripts_path]
|
150
|
+
end
|
151
|
+
args << output_path
|
152
|
+
|
153
|
+
safesystem("pkgbuild", *args)
|
154
|
+
FileUtils.remove_file(temp_info)
|
155
|
+
end # def output
|
156
|
+
|
157
|
+
def to_s_extension; "pkg"; end
|
158
|
+
|
159
|
+
def to_s(format=nil)
|
160
|
+
return super(format.nil? ? "NAME-VERSION.EXTENSION" : format)
|
161
|
+
end # def to_s
|
162
|
+
|
163
|
+
public(:input, :output, :identifier, :to_s)
|
164
|
+
|
165
|
+
end # class FPM::Package::OSXpkg
|
@@ -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
|
@@ -0,0 +1,403 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "fpm/package"
|
3
|
+
require "fpm/util"
|
4
|
+
require "backports"
|
5
|
+
require "fileutils"
|
6
|
+
require "find"
|
7
|
+
|
8
|
+
class FPM::Package::Pacman < FPM::Package
|
9
|
+
|
10
|
+
option "--optional-depends", "PACKAGE",
|
11
|
+
"Add an optional dependency to the pacman package.", :multivalued => true
|
12
|
+
|
13
|
+
option "--use-file-permissions", :flag,
|
14
|
+
"Use existing file permissions when defining ownership and modes"
|
15
|
+
|
16
|
+
option "--user", "USER", "The owner of files in this package", :default => 'root'
|
17
|
+
|
18
|
+
option "--group", "GROUP", "The group owner of files in this package", :default => 'root'
|
19
|
+
|
20
|
+
# The list of supported compression types. Default is xz (LZMA2)
|
21
|
+
COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "none" ]
|
22
|
+
|
23
|
+
option "--compression", "COMPRESSION", "The compression type to use, must " \
|
24
|
+
"be one of #{COMPRESSION_TYPES.join(", ")}.", :default => "xz" do |value|
|
25
|
+
if !COMPRESSION_TYPES.include?(value)
|
26
|
+
raise ArgumentError, "Pacman compression value of '#{value}' is invalid. " \
|
27
|
+
"Must be one of #{COMPRESSION_TYPES.join(", ")}"
|
28
|
+
end
|
29
|
+
value
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(*args)
|
33
|
+
super(*args)
|
34
|
+
attributes[:pacman_optional_depends] = []
|
35
|
+
end # def initialize
|
36
|
+
|
37
|
+
def architecture
|
38
|
+
case @architecture
|
39
|
+
when nil
|
40
|
+
return %x{uname -m}.chomp # default to current arch
|
41
|
+
when "amd64" # debian and pacman disagree on architecture names
|
42
|
+
return "x86_64"
|
43
|
+
when "native"
|
44
|
+
return %x{uname -m}.chomp # 'native' is current arch
|
45
|
+
when "all", "any", "noarch"
|
46
|
+
return "any"
|
47
|
+
else
|
48
|
+
return @architecture
|
49
|
+
end
|
50
|
+
end # def architecture
|
51
|
+
|
52
|
+
def iteration
|
53
|
+
return @iteration || 1
|
54
|
+
end # def iteration
|
55
|
+
|
56
|
+
def config_files
|
57
|
+
return @config_files || []
|
58
|
+
end # def config_files
|
59
|
+
|
60
|
+
def dependencies
|
61
|
+
bogus_regex = /[^\sA-Za-z0-9><=+._@-]/
|
62
|
+
# Actually modifies depencies if they are not right
|
63
|
+
bogus_dependencies = @dependencies.grep bogus_regex
|
64
|
+
if bogus_dependencies.any?
|
65
|
+
@dependencies.reject! { |a| a =~ bogus_regex }
|
66
|
+
logger.warn("Some of the dependencies looked like they weren't package " \
|
67
|
+
"names. Such dependency entries only serve to confuse arch. " \
|
68
|
+
"I am removing them.",
|
69
|
+
:removed_dependencies => bogus_dependencies,
|
70
|
+
:fixed_dependencies => @dependencies)
|
71
|
+
end
|
72
|
+
return @dependencies
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# This method is invoked on a package when it has been converted to a new
|
77
|
+
# package format. The purpose of this method is to do any extra conversion
|
78
|
+
# steps, like translating dependency conditions, etc.
|
79
|
+
#def converted_from(origin)
|
80
|
+
# nothing to do by default. Subclasses may implement this.
|
81
|
+
# See the RPM package class for an example.
|
82
|
+
#end # def converted_from
|
83
|
+
|
84
|
+
# Add a new source to this package.
|
85
|
+
# The exact behavior depends on the kind of package being managed.
|
86
|
+
#
|
87
|
+
# For instance:
|
88
|
+
#
|
89
|
+
# * for FPM::Package::Dir, << expects a path to a directory or files.
|
90
|
+
# * for FPM::Package::RPM, << expects a path to an rpm.
|
91
|
+
#
|
92
|
+
# The idea is that you can keep pumping in new things to a package
|
93
|
+
# for later conversion or output.
|
94
|
+
#
|
95
|
+
# Implementations are expected to put files relevant to the 'input' in the
|
96
|
+
# staging_path
|
97
|
+
def input(pacman_pkg_path)
|
98
|
+
control = {}
|
99
|
+
# Unpack the control tarball
|
100
|
+
safesystem(tar_cmd, "-C", staging_path, "-xf", pacman_pkg_path)
|
101
|
+
pkginfo = staging_path(".PKGINFO")
|
102
|
+
mtree = staging_path(".MTREE")
|
103
|
+
install = staging_path(".INSTALL")
|
104
|
+
|
105
|
+
control_contents = File.read(pkginfo)
|
106
|
+
FileUtils.rm(pkginfo)
|
107
|
+
FileUtils.rm(mtree)
|
108
|
+
|
109
|
+
|
110
|
+
control_lines = control_contents.split("\n")
|
111
|
+
control_lines.each do |line|
|
112
|
+
key, val = line.split(/ += +/, 2)
|
113
|
+
if control.has_key? key
|
114
|
+
control[key].push(val)
|
115
|
+
else
|
116
|
+
control[key] = [val]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
self.name = control["pkgname"][0]
|
121
|
+
|
122
|
+
# Parse 'epoch:version-iteration' in the version string
|
123
|
+
version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
|
124
|
+
m = version_re.match(control["pkgver"][0])
|
125
|
+
if !m
|
126
|
+
raise "Unsupported version string '#{control["pkgver"][0]}'"
|
127
|
+
end
|
128
|
+
self.epoch, self.version, self.iteration = m.captures
|
129
|
+
|
130
|
+
self.maintainer = control["packager"][0]
|
131
|
+
|
132
|
+
# Arch has no notion of vendor, so...
|
133
|
+
#self.vendor =
|
134
|
+
|
135
|
+
self.url = control["url"][0]
|
136
|
+
# Groups could include more than one.
|
137
|
+
# Speaking of just taking the first entry of the field:
|
138
|
+
# A crude thing to do, but I suppose it's better than nothing.
|
139
|
+
# -- Daniel Haskin, 3/24/2015
|
140
|
+
self.category = control["group"] && control["group"][0] || self.category
|
141
|
+
|
142
|
+
# Licenses could include more than one.
|
143
|
+
# Speaking of just taking the first entry of the field:
|
144
|
+
# A crude thing to do, but I suppose it's better than nothing.
|
145
|
+
# -- Daniel Haskin, 3/24/2015
|
146
|
+
self.license = control["license"][0] || self.license
|
147
|
+
|
148
|
+
self.architecture = control["arch"][0]
|
149
|
+
|
150
|
+
self.dependencies = control["depend"] || self.dependencies
|
151
|
+
|
152
|
+
self.provides = control["provides"] || self.provides
|
153
|
+
|
154
|
+
self.conflicts = control["conflict"] || self.conflicts
|
155
|
+
|
156
|
+
self.replaces = control["replaces"] || self.replaces
|
157
|
+
|
158
|
+
self.description = control["pkgdesc"][0]
|
159
|
+
|
160
|
+
if control.include? "backup"
|
161
|
+
self.config_files = control["backup"].map{|file| "/" + file}
|
162
|
+
else
|
163
|
+
self.config_files = []
|
164
|
+
end
|
165
|
+
|
166
|
+
self.dependencies = control["depend"] || self.dependencies
|
167
|
+
|
168
|
+
if attributes[:no_auto_depends?]
|
169
|
+
self.dependencies = []
|
170
|
+
end
|
171
|
+
|
172
|
+
self.attributes[:pacman_optional_depends] = control["optdepend"] || []
|
173
|
+
# There are other available attributes, but I didn't include them because:
|
174
|
+
# - makedepend: deps needed to make the arch package. But it's already
|
175
|
+
# made. It just needs to be converted at this point
|
176
|
+
# - checkdepend: See above
|
177
|
+
# - makepkgopt: See above
|
178
|
+
# - size: can be dynamically generated
|
179
|
+
# - builddate: Should be changed to time of package conversion in the new
|
180
|
+
# package, so this value should be thrown away.
|
181
|
+
|
182
|
+
if File.exist?(install)
|
183
|
+
functions = parse_install_script(install)
|
184
|
+
if functions.include?("pre_install")
|
185
|
+
self.scripts[:before_install] = functions["pre_install"].join("\n")
|
186
|
+
end
|
187
|
+
if functions.include?("post_install")
|
188
|
+
self.scripts[:after_install] = functions["post_install"].join("\n")
|
189
|
+
end
|
190
|
+
if functions.include?("pre_upgrade")
|
191
|
+
self.scripts[:before_upgrade] = functions["pre_upgrade"].join("\n")
|
192
|
+
end
|
193
|
+
if functions.include?("post_upgrade")
|
194
|
+
self.scripts[:after_upgrade] = functions["post_upgrade"].join("\n")
|
195
|
+
end
|
196
|
+
if functions.include?("pre_remove")
|
197
|
+
self.scripts[:before_remove] = functions["pre_remove"].join("\n")
|
198
|
+
end
|
199
|
+
if functions.include?("post_remove")
|
200
|
+
self.scripts[:after_remove] = functions["post_remove"].join("\n")
|
201
|
+
end
|
202
|
+
FileUtils.rm(install)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Note: didn't use `self.directories`.
|
206
|
+
# Pacman doesn't really record that information, to my knowledge.
|
207
|
+
|
208
|
+
end # def input
|
209
|
+
|
210
|
+
def compression_option
|
211
|
+
case self.attributes[:pacman_compression]
|
212
|
+
when nil, "xz"
|
213
|
+
return "--xz"
|
214
|
+
when "none"
|
215
|
+
return ""
|
216
|
+
when "gz"
|
217
|
+
return "-z"
|
218
|
+
when "bzip2"
|
219
|
+
return "-j"
|
220
|
+
else
|
221
|
+
return "--xz"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def compression_ending
|
226
|
+
case self.attributes[:pacman_compression]
|
227
|
+
when nil, "xz"
|
228
|
+
return ".xz"
|
229
|
+
when "none"
|
230
|
+
return ""
|
231
|
+
when "gz"
|
232
|
+
return ".gz"
|
233
|
+
when "bzip2"
|
234
|
+
return ".bz2"
|
235
|
+
else
|
236
|
+
return ".xz"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Output this package to the given path.
|
241
|
+
def output(output_path)
|
242
|
+
output_check(output_path)
|
243
|
+
|
244
|
+
# Copy all files from staging to BUILD dir
|
245
|
+
Find.find(staging_path) do |path|
|
246
|
+
src = path.gsub(/^#{staging_path}/, '')
|
247
|
+
dst = build_path(src)
|
248
|
+
copy_entry(path, dst, preserve=true, remove_destination=true)
|
249
|
+
copy_metadata(path, dst)
|
250
|
+
end
|
251
|
+
|
252
|
+
# This value is used later in the template for PKGINFO
|
253
|
+
size = safesystemout("du", "-sk", build_path).split(/\s+/)[0].to_i * 1024
|
254
|
+
builddate = Time.new.to_i
|
255
|
+
|
256
|
+
pkginfo = template("pacman.erb").result(binding)
|
257
|
+
pkginfo_file = build_path(".PKGINFO")
|
258
|
+
File.write(pkginfo_file, pkginfo)
|
259
|
+
|
260
|
+
if script?(:before_install) or script?(:after_install) or \
|
261
|
+
script?(:before_upgrade) or script?(:after_upgrade) or \
|
262
|
+
script?(:before_remove) or script?(:after_remove)
|
263
|
+
install_script = template("pacman/INSTALL.erb").result(binding)
|
264
|
+
install_script_file = build_path(".INSTALL")
|
265
|
+
File.write(install_script_file, install_script)
|
266
|
+
end
|
267
|
+
|
268
|
+
generate_mtree
|
269
|
+
|
270
|
+
File.expand_path(output_path).tap do |path|
|
271
|
+
::Dir.chdir(build_path) do
|
272
|
+
safesystem(*([tar_cmd,
|
273
|
+
compression_option,
|
274
|
+
"-cf",
|
275
|
+
path] + data_tar_flags + \
|
276
|
+
::Dir.entries(".").reject{|entry| entry =~ /^\.{1,2}$/ }))
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end # def output
|
280
|
+
|
281
|
+
def data_tar_flags
|
282
|
+
data_tar_flags = []
|
283
|
+
if attributes[:pacman_use_file_permissions?].nil?
|
284
|
+
if !attributes[:pacman_user].nil?
|
285
|
+
if attributes[:pacman_user] == 'root'
|
286
|
+
data_tar_flags += [ "--numeric-owner", "--owner", "0" ]
|
287
|
+
else
|
288
|
+
data_tar_flags += [ "--owner", attributes[:deb_user] ]
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
if !attributes[:pacman_group].nil?
|
293
|
+
if attributes[:pacman_group] == 'root'
|
294
|
+
data_tar_flags += [ "--numeric-owner", "--group", "0" ]
|
295
|
+
else
|
296
|
+
data_tar_flags += [ "--group", attributes[:deb_group] ]
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
return data_tar_flags
|
301
|
+
end # def data_tar_flags
|
302
|
+
|
303
|
+
def default_output
|
304
|
+
v = version
|
305
|
+
v = "#{epoch}:#{v}" if epoch
|
306
|
+
if iteration
|
307
|
+
"#{name}_#{v}-#{iteration}_#{architecture}.#{type}"
|
308
|
+
else
|
309
|
+
"#{name}_#{v}_#{architecture}.#{type}"
|
310
|
+
end
|
311
|
+
end # def default_output
|
312
|
+
|
313
|
+
def to_s_extension; "pkg.tar#{compression_ending}"; end
|
314
|
+
|
315
|
+
def to_s(format=nil)
|
316
|
+
# Default format if nil
|
317
|
+
# git_1.7.9.3-1-amd64.pkg.tar.xz
|
318
|
+
return super(format.nil? ? "NAME-FULLVERSION-ARCH.EXTENSION" : format)
|
319
|
+
end # def to_s
|
320
|
+
|
321
|
+
private
|
322
|
+
|
323
|
+
def generate_mtree
|
324
|
+
::Dir.chdir(build_path) do
|
325
|
+
cmd = "LANG=C bsdtar "
|
326
|
+
cmd += "-czf .MTREE "
|
327
|
+
cmd += "--format=mtree "
|
328
|
+
cmd += "--options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' "
|
329
|
+
cmd += ::Dir.entries(".").reject{|entry| entry =~ /^\.{1,2}$/ }.join(" ")
|
330
|
+
safesystem(cmd)
|
331
|
+
end
|
332
|
+
end # def generate_mtree
|
333
|
+
|
334
|
+
# KNOWN ISSUE:
|
335
|
+
# If an un-matched bracket is used in valid bash, as in
|
336
|
+
# `echo "{"`, this function will choke.
|
337
|
+
# However, to cover this case basically
|
338
|
+
# requires writing almost half a bash parser,
|
339
|
+
# and it is a very small corner case.
|
340
|
+
# Otherwise, this approach is very robust.
|
341
|
+
def gobble_function(cons,prod)
|
342
|
+
level = 1
|
343
|
+
while level > 0
|
344
|
+
line = cons.next
|
345
|
+
# Not the best, but pretty good
|
346
|
+
# short of writing an *actual* sh
|
347
|
+
# parser
|
348
|
+
level += line.count "{"
|
349
|
+
level -= line.count "}"
|
350
|
+
if level > 0
|
351
|
+
prod.push(line.rstrip())
|
352
|
+
else
|
353
|
+
fine = line.sub(/\s*[}]\s*$/, "")
|
354
|
+
if !(fine =~ /^\s*$/)
|
355
|
+
prod.push(fine.rstrip())
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end # def gobble_function
|
360
|
+
|
361
|
+
FIND_SCRIPT_FUNCTION_LINE =
|
362
|
+
/^\s*(\w+)\s*\(\s*\)\s*\{\s*([^}]+?)?\s*(\})?\s*$/
|
363
|
+
|
364
|
+
def parse_install_script(path)
|
365
|
+
global_lines = []
|
366
|
+
look_for = Set.new(["pre_install", "post_install",
|
367
|
+
"pre_upgrade", "post_upgrade",
|
368
|
+
"pre_remove", "post_remove"])
|
369
|
+
functions = {}
|
370
|
+
look_for.each do |fname|
|
371
|
+
functions[fname] = []
|
372
|
+
end
|
373
|
+
|
374
|
+
open(path, "r") do |iscript|
|
375
|
+
lines = iscript.each
|
376
|
+
begin
|
377
|
+
while true
|
378
|
+
line = lines.next
|
379
|
+
# This regex picks up beginning names of posix shell
|
380
|
+
# functions
|
381
|
+
# Examples:
|
382
|
+
# fname() {
|
383
|
+
# fname() { echo hi }
|
384
|
+
m = FIND_SCRIPT_FUNCTION_LINE.match(line)
|
385
|
+
if not m.nil? and look_for.include? m[1]
|
386
|
+
if not m[2].nil?
|
387
|
+
functions[m[1]].push(m[2].rstrip())
|
388
|
+
end
|
389
|
+
gobble_function(lines, functions[m[1]])
|
390
|
+
else
|
391
|
+
global_lines.push(line.rstrip())
|
392
|
+
end
|
393
|
+
end
|
394
|
+
rescue StopIteration
|
395
|
+
end
|
396
|
+
end
|
397
|
+
look_for.each do |name|
|
398
|
+
# Add global lines to each function to preserve global variables, etc.
|
399
|
+
functions[name] = global_lines + functions[name]
|
400
|
+
end
|
401
|
+
return functions
|
402
|
+
end # def parse_install_script
|
403
|
+
end # class FPM::Package::Pacman
|