fpm 0.3.11 → 0.4.0pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELIST +15 -0
- data/bin/fpm +2 -15
- data/lib/fpm.rb +5 -12
- data/lib/fpm/command.rb +323 -0
- data/lib/fpm/errors.rb +1 -0
- data/lib/fpm/namespace.rb +2 -11
- data/lib/fpm/package.rb +255 -100
- data/lib/fpm/package/deb.rb +367 -0
- data/lib/fpm/package/dir.rb +86 -0
- data/lib/fpm/package/gem.rb +162 -0
- data/lib/fpm/{source → package}/npm.rb +3 -3
- data/lib/fpm/package/pear.rb +41 -0
- data/lib/fpm/{target → package}/puppet.rb +1 -3
- data/lib/fpm/{source → package}/pyfpm/__init__.py +0 -0
- data/lib/fpm/package/pyfpm/__init__.pyc +0 -0
- data/lib/fpm/{source → package}/pyfpm/get_metadata.py +15 -3
- data/lib/fpm/package/pyfpm/get_metadata.pyc +0 -0
- data/lib/fpm/package/python.rb +125 -0
- data/lib/fpm/package/rpm.rb +132 -0
- data/lib/fpm/{target → package}/solaris.rb +3 -2
- data/lib/fpm/package/tar.rb +62 -0
- data/lib/fpm/util.rb +56 -7
- data/templates/deb.erb +12 -12
- data/templates/rpm.erb +32 -38
- data/templates/solaris.erb +1 -1
- metadata +119 -78
- data/lib/fpm/builder.rb +0 -220
- data/lib/fpm/flags.rb +0 -20
- data/lib/fpm/program.rb +0 -273
- data/lib/fpm/rubyfixes.rb +0 -11
- data/lib/fpm/source.rb +0 -155
- data/lib/fpm/source/dir.rb +0 -59
- data/lib/fpm/source/gem.rb +0 -162
- data/lib/fpm/source/python.rb +0 -137
- data/lib/fpm/source/rpm.rb +0 -28
- data/lib/fpm/source/tar.rb +0 -50
- data/lib/fpm/target/deb.rb +0 -184
- data/lib/fpm/target/rpm.rb +0 -68
- data/lib/rpm/header.rb +0 -89
- data/lib/rpm/lead.rb +0 -48
- data/lib/rpm/namespace.rb +0 -1
- data/lib/rpm/rpmfile.rb +0 -81
- data/lib/rpm/tag.rb +0 -304
data/CHANGELIST
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
0.4.0 (???)
|
2
|
+
- Complete rewrite of pretty much everything.
|
3
|
+
* Otherwise, the 'fpm' command functionality should be the same
|
4
|
+
* Please let me know if something broke!
|
5
|
+
- Now has an API (see examples/api directory)
|
6
|
+
- Also has a proper test suite
|
7
|
+
- Add --license and --vendor settings (via Pieter Loubser)
|
8
|
+
- python support: try to name python packages sanely. Some pypi packages
|
9
|
+
are literally called 'python-foo' so make sure we generate packages named
|
10
|
+
'python-foo' and not 'python-python-foo' (via Thomas Meson)
|
11
|
+
- rpm support: Add --rpm-rpmbuild-define for passing a --define flag to rpmbuild
|
12
|
+
(via Naresh V)
|
13
|
+
TODO(sissel): PEAR
|
14
|
+
- PHP pear source support (fpm -s pear ...) (via Andrew Gaffney)
|
15
|
+
|
1
16
|
0.3.10 (Oct 10, 2011)
|
2
17
|
- Allow taking a list of files/inputs on stdin with '-' or with the --inputs
|
3
18
|
flag. (Matt Patterson)
|
data/bin/fpm
CHANGED
@@ -1,21 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
#
|
3
2
|
|
4
3
|
require "rubygems"
|
5
4
|
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
6
5
|
require "fpm"
|
7
|
-
require "fpm/
|
6
|
+
require "fpm/command"
|
8
7
|
|
9
|
-
|
10
|
-
module Kernel
|
11
|
-
alias :orig_system :system
|
12
|
-
def system(*args)
|
13
|
-
p :system => args
|
14
|
-
orig_system(*args)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
program = FPM::Program.new
|
20
|
-
ret = program.run(ARGV)
|
21
|
-
exit(ret.nil? ? 0 : ret)
|
8
|
+
exit(FPM::Command.run || 0)
|
data/lib/fpm.rb
CHANGED
@@ -1,15 +1,8 @@
|
|
1
1
|
require "fpm/namespace"
|
2
|
-
require "fpm/builder"
|
3
2
|
|
4
3
|
require "fpm/package"
|
5
|
-
require "fpm/
|
6
|
-
require "fpm/
|
7
|
-
require "fpm/
|
8
|
-
require "fpm/
|
9
|
-
|
10
|
-
require "fpm/source"
|
11
|
-
require "fpm/source/dir"
|
12
|
-
require "fpm/source/gem"
|
13
|
-
require "fpm/source/python"
|
14
|
-
require "fpm/source/rpm"
|
15
|
-
require "fpm/source/tar"
|
4
|
+
require "fpm/package/dir"
|
5
|
+
require "fpm/package/gem"
|
6
|
+
require "fpm/package/deb"
|
7
|
+
require "fpm/package/rpm"
|
8
|
+
require "fpm/package/python"
|
data/lib/fpm/command.rb
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "fpm/namespace"
|
3
|
+
require "fpm/util"
|
4
|
+
require "clamp"
|
5
|
+
require "ostruct"
|
6
|
+
require "fpm"
|
7
|
+
|
8
|
+
if $DEBUG
|
9
|
+
Cabin::Channel.get(Kernel).subscribe($stdout)
|
10
|
+
Cabin::Channel.get(Kernel).level = :debug
|
11
|
+
end
|
12
|
+
|
13
|
+
Dir[File.join(File.dirname(__FILE__), "package", "*.rb")].each do |plugin|
|
14
|
+
Cabin::Channel.get(Kernel).info("Loading plugin", :path => plugin)
|
15
|
+
require plugin
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# The main fpm command entry point.
|
20
|
+
class FPM::Command < Clamp::Command
|
21
|
+
include FPM::Util
|
22
|
+
|
23
|
+
option "-t", "OUTPUT_TYPE",
|
24
|
+
"the type of package you want to create (deb, rpm, solaris, etc)",
|
25
|
+
:attribute_name => :output_type
|
26
|
+
option "-s", "INPUT_TYPE",
|
27
|
+
"the package type to use as input (gem, rpm, python, etc)",
|
28
|
+
:attribute_name => :input_type
|
29
|
+
option "-C", "CHDIR",
|
30
|
+
"Change directory to here before searching for files",
|
31
|
+
:attribute_name => :chdir
|
32
|
+
option "--prefix", "PREFIX",
|
33
|
+
"A path to prefix files with when building the target package. This may " \
|
34
|
+
"be necessary for all input packages. For example, the 'gem' type will" \
|
35
|
+
"prefix with your gem directory automatically."
|
36
|
+
option ["-p", "--package"], "OUTPUT",
|
37
|
+
"The package file path to output.", :default => "NAME-FULLVERSION.ARCH.TYPE"
|
38
|
+
option ["-n", "--name"], "NAME", "The name to give to the package"
|
39
|
+
option "--verbose", :flag, "Enable verbose output"
|
40
|
+
option "--debug", :flag, "Enable debug output"
|
41
|
+
option ["-v", "--version"], "VERSION", "The version to give to the package"
|
42
|
+
option "--iteration", "ITERATION",
|
43
|
+
"The iteration to give to the package. RPM calls this the 'release'. " \
|
44
|
+
"FreeBSD calls it 'PORTREVISION'. Debian calls this 'debian_revision'",
|
45
|
+
:default => "1"
|
46
|
+
option "--epoch", "EPOCH",
|
47
|
+
"The epoch value for this package. RPM and Debian calls this 'epoch'. " \
|
48
|
+
"FreeBSD calls this 'PORTEPOCH'", :default => "1"
|
49
|
+
option "--license", "LICENSE",
|
50
|
+
"(optional) license name for this package"
|
51
|
+
option "--vendor", "VENDOR",
|
52
|
+
"(optional) vendor name for this package"
|
53
|
+
option "--category", "CATEGORY",
|
54
|
+
"(optional) category this package belongs to", :default => "none"
|
55
|
+
option ["-d", "--depends"], "DEPENDENCY",
|
56
|
+
"A dependency. This flag can be specified multiple times. Value is " \
|
57
|
+
"usually in the form of: -d 'name' or -d 'name > version'",
|
58
|
+
:default => [], :attribute_name => :dependencies do |val|
|
59
|
+
# Clamp doesn't support multivalue flags (ie; specifying -d multiple times)
|
60
|
+
# so we can hack around it with this trickery.
|
61
|
+
@dependencies ||= []
|
62
|
+
@dependencies << val
|
63
|
+
end # -d / --depends
|
64
|
+
option "--provides", "PROVIDES",
|
65
|
+
"What this package provides (usually a name)" do |val|
|
66
|
+
@provides ||= []
|
67
|
+
@provides << val
|
68
|
+
end # --provides
|
69
|
+
option "--conflicts", "CONFLICTS",
|
70
|
+
"Other packages/versions this package conflicts with" do |val|
|
71
|
+
@conflicts ||= []
|
72
|
+
@conflicts << val
|
73
|
+
end # --conflicts
|
74
|
+
option "--replaces", "REPLACES",
|
75
|
+
"Other packages/versions this package replaces" do |val|
|
76
|
+
@replaces ||= []
|
77
|
+
@replaces << val
|
78
|
+
end # --replaces
|
79
|
+
option "--config-files", "CONFIG_FILES",
|
80
|
+
"Mark a file in the package as being a config file. This uses 'conffiles'" \
|
81
|
+
" in debs and %config in rpm." do |val|
|
82
|
+
@config_files ||= []
|
83
|
+
@config_files << val
|
84
|
+
end # --config-files
|
85
|
+
option ["-a", "--architecture"], "ARCHITECTURE",
|
86
|
+
"The architecture name. Usually matches 'uname -m'. For automatic values," \
|
87
|
+
" you can use '-a all' or '-a native'. These two strings will be " \
|
88
|
+
"translated into the correct value for your platform and target package type."
|
89
|
+
option ["-m", "--maintainer"], "MAINTAINER",
|
90
|
+
"The maintainer of this package.",
|
91
|
+
:default => "<#{ENV["USER"]}@#{Socket.gethostname}>"
|
92
|
+
option ["-S", "--package-name-suffix"], "PACKAGE_NAME_SUFFIX",
|
93
|
+
"a name suffix to append to package and dependencies."
|
94
|
+
option ["-e", "--edit"], :flag,
|
95
|
+
"Edit the package spec before building."
|
96
|
+
option ["-x", "--exclude"], "EXCLUDE_PATTERN",
|
97
|
+
"Exclude paths matching pattern (shell wildcard globs valid here)" do |val|
|
98
|
+
@exclude_pattern ||= []
|
99
|
+
@exclude_pattern << val
|
100
|
+
end # -x / --exclude
|
101
|
+
option "--post-install", "FILE",
|
102
|
+
"a script to be run after package installation",
|
103
|
+
:attribute_name => :after_install do |val|
|
104
|
+
File.expand_path(val) # Get the full path to the script
|
105
|
+
end # --post-install
|
106
|
+
option "--pre-install", "FILE",
|
107
|
+
"a script to be run before package installation",
|
108
|
+
:attribute_name => :before_install do |val|
|
109
|
+
File.expand_path(val) # Get the full path to the script
|
110
|
+
end # --pre-install
|
111
|
+
# TODO(sissel): Name the flag --post-remove for clarity
|
112
|
+
option "--post-uninstall", "FILE",
|
113
|
+
"a script to be run after package removal",
|
114
|
+
:attribute_name => :after_remove do |val|
|
115
|
+
File.expand_path(val) # Get the full path to the script
|
116
|
+
end # --post-uninstall
|
117
|
+
# TODO(sissel): Name the flag --pre-remove for clarity
|
118
|
+
option "--pre-uninstall", "FILE",
|
119
|
+
"a script to be run before package removal",
|
120
|
+
:attribute_name => :before_remove do |val|
|
121
|
+
File.expand_path(val) # Get the full path to the script
|
122
|
+
end # --pre-uninstall
|
123
|
+
option "--description", "DESCRIPTION", "Add a description for this package.",
|
124
|
+
:default => "no description"
|
125
|
+
option "--url", "URI", "Add a url for this package.",
|
126
|
+
:default => "http://example.com/no-uri-given"
|
127
|
+
option "--inputs", "INPUTS_PATH",
|
128
|
+
"The path to a file containing a newline-separated list of " \
|
129
|
+
"files and dirs to use as input."
|
130
|
+
parameter "[ARGS] ...",
|
131
|
+
"Inputs to the source package type. For the 'dir' type, this is the files" \
|
132
|
+
" and directories you want to include in the package. For others, like " \
|
133
|
+
"'gem', it specifies the packages to download and use as the gem input",
|
134
|
+
:attribute_name => :args
|
135
|
+
|
136
|
+
# package-level settings
|
137
|
+
def settings
|
138
|
+
@settings ||= {}
|
139
|
+
end
|
140
|
+
|
141
|
+
FPM::Package.types.each do |name, klass|
|
142
|
+
klass.apply_options(self)
|
143
|
+
end
|
144
|
+
|
145
|
+
# TODO(sissel): expose 'option' and 'parameter' junk to FPM::Package and subclasses.
|
146
|
+
# Apply those things to this command.
|
147
|
+
#
|
148
|
+
# Add extra flags from plugins
|
149
|
+
#FPM::Package::Gem.flags(FPM::Flags.new(opts, "gem", "gem only"), @settings)
|
150
|
+
#FPM::Package::Python.flags(FPM::Flags.new(opts, "python", "python only"),
|
151
|
+
#@settings)
|
152
|
+
#FPM::Package::Deb.flags(FPM::Flags.new(opts, "deb", "deb only"), @settings)
|
153
|
+
#FPM::Package::Rpm.flags(FPM::Flags.new(opts, "rpm", "rpm only"), @settings)
|
154
|
+
|
155
|
+
# A new FPM::Command
|
156
|
+
def initialize(*args)
|
157
|
+
super(*args)
|
158
|
+
@conflicts = []
|
159
|
+
@replaces = []
|
160
|
+
@provides = []
|
161
|
+
@dependencies = []
|
162
|
+
@config_files = []
|
163
|
+
end # def initialize
|
164
|
+
|
165
|
+
# Execute this command. See Clamp::Command#execute and Clamp's documentation
|
166
|
+
def execute
|
167
|
+
@logger = Cabin::Channel.get
|
168
|
+
@logger.subscribe(STDOUT)
|
169
|
+
@logger.level = :warn
|
170
|
+
validator = Validator.new(self)
|
171
|
+
if !validator.ok?
|
172
|
+
validator.messages.each do |message|
|
173
|
+
@logger.warn(message)
|
174
|
+
end
|
175
|
+
|
176
|
+
@logger.fatal("Fix the above problems, and you'll be rolling packages in no time!")
|
177
|
+
return 1
|
178
|
+
end
|
179
|
+
|
180
|
+
input_class = FPM::Package.types[input_type]
|
181
|
+
output_class = FPM::Package.types[output_type]
|
182
|
+
|
183
|
+
@logger.level = :info if verbose? # --verbose
|
184
|
+
@logger.level = :debug if debug? # --debug
|
185
|
+
|
186
|
+
input = input_class.new
|
187
|
+
|
188
|
+
# Merge in package settings.
|
189
|
+
# The 'settings' stuff comes in from #apply_options, which goes through
|
190
|
+
# all the options defined in known packages and puts them into our command.
|
191
|
+
# Flags in packages defined as "--foo-bar" become named "--<packagetype>-foo-bar"
|
192
|
+
# They are stored in 'settings' as :gem_foo_bar.
|
193
|
+
input.attributes ||= {}
|
194
|
+
|
195
|
+
# Iterate over all the options
|
196
|
+
self.class.declared_options.each do |option|
|
197
|
+
with(option.attribute_name) do |attr|
|
198
|
+
# clamp makes option attributes available as accessor methods
|
199
|
+
# do --foo-bar is available as 'foo_bar'
|
200
|
+
# make these available as package attributes.
|
201
|
+
input.attributes[attr.to_sym] = send(attr) if respond_to?(attr)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
args.each do |arg|
|
206
|
+
input.input(arg)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Override package settings if they are not the default flag values
|
210
|
+
# the below proc essentially does:
|
211
|
+
#
|
212
|
+
# if someflag != default_someflag
|
213
|
+
# input.someflag = someflag
|
214
|
+
# end
|
215
|
+
set = proc do |object, attribute|
|
216
|
+
# if the package's attribute is currently nil *or* the flag setting for this
|
217
|
+
# attribute is non-default, use the value.
|
218
|
+
if object.send(attribute).nil? || send(attribute) != send("default_#{attribute}")
|
219
|
+
@logger.info("Setting from flags: #{attribute}=#{send(attribute)}")
|
220
|
+
object.send("#{attribute}=", send(attribute))
|
221
|
+
end
|
222
|
+
end
|
223
|
+
set.call(input, :architecture)
|
224
|
+
set.call(input, :category)
|
225
|
+
set.call(input, :description)
|
226
|
+
set.call(input, :epoch)
|
227
|
+
set.call(input, :iteration)
|
228
|
+
set.call(input, :license)
|
229
|
+
set.call(input, :maintainer)
|
230
|
+
set.call(input, :name)
|
231
|
+
set.call(input, :url)
|
232
|
+
set.call(input, :vendor)
|
233
|
+
set.call(input, :version)
|
234
|
+
set.call(input, :architecture)
|
235
|
+
|
236
|
+
input.conflicts += conflicts
|
237
|
+
input.dependencies += dependencies
|
238
|
+
input.provides += provides
|
239
|
+
input.replaces += replaces
|
240
|
+
|
241
|
+
input.scripts[:before_install] = before_install # --pre-install
|
242
|
+
input.scripts[:after_install] = after_install # --post-install
|
243
|
+
input.scripts[:before_remove] = before_remove # --pre-uninstall
|
244
|
+
input.scripts[:after_remove] = after_remove # --post-uninstall
|
245
|
+
|
246
|
+
# Convert to the output type
|
247
|
+
output = input.convert(output_class)
|
248
|
+
|
249
|
+
# Write the output somewhere
|
250
|
+
output.output(output.to_s(package))
|
251
|
+
return 0
|
252
|
+
ensure
|
253
|
+
input.cleanup unless input.nil?
|
254
|
+
output.cleanup unless output.nil?
|
255
|
+
end # def execute
|
256
|
+
|
257
|
+
# A simple flag validator
|
258
|
+
#
|
259
|
+
# The goal of this class is to ensure the flags and arguments given
|
260
|
+
# are a valid configuration.
|
261
|
+
class Validator
|
262
|
+
include FPM::Util
|
263
|
+
private
|
264
|
+
|
265
|
+
def initialize(command)
|
266
|
+
@command = command
|
267
|
+
@valid = true
|
268
|
+
@messages = []
|
269
|
+
|
270
|
+
validate
|
271
|
+
end # def initialize
|
272
|
+
|
273
|
+
def ok?
|
274
|
+
return @valid
|
275
|
+
end # def ok?
|
276
|
+
|
277
|
+
def validate
|
278
|
+
# Make sure the user has passed '-s' and '-t' flags
|
279
|
+
mandatory(@command.input_type,
|
280
|
+
"Missing required -s flag. What package source did you want?")
|
281
|
+
mandatory(@command.output_type,
|
282
|
+
"Missing required -t flag. What package output did you want?")
|
283
|
+
|
284
|
+
# Verify the types requested are valid
|
285
|
+
types = FPM::Package.types.keys.sort
|
286
|
+
with(@command.input_type) do |val|
|
287
|
+
next if val.nil?
|
288
|
+
mandatory(FPM::Package.types.include?(val),
|
289
|
+
"Invalid input package -s flag) type #{val.inspect}. " \
|
290
|
+
"Expected one of: #{types.join(", ")}")
|
291
|
+
end
|
292
|
+
|
293
|
+
with(@command.output_type) do |val|
|
294
|
+
next if val.nil?
|
295
|
+
mandatory(FPM::Package.types.include?(val),
|
296
|
+
"Invalid output package (-t flag) type #{val.inspect}. " \
|
297
|
+
"Expected one of: #{types.join(", ")}")
|
298
|
+
end
|
299
|
+
|
300
|
+
mandatory(@command.args.any?,
|
301
|
+
"No parameters given. You need to pass additional command " \
|
302
|
+
"arguments so that I know what you want to build packages " \
|
303
|
+
"from. For example, for '-s dir' you would pass a list of " \
|
304
|
+
"files and directories. For '-s gem' you would pass a one" \
|
305
|
+
" or more gems to package from. As a full example, this " \
|
306
|
+
"will make an rpm of the 'json' rubygem: " \
|
307
|
+
"`fpm -s gem -t rpm json`")
|
308
|
+
end # def validate
|
309
|
+
|
310
|
+
def mandatory(value, message)
|
311
|
+
if value.nil? or !value
|
312
|
+
@messages << message
|
313
|
+
@valid = false
|
314
|
+
end
|
315
|
+
end # def mandatory
|
316
|
+
|
317
|
+
def messages
|
318
|
+
return @messages
|
319
|
+
end # def messages
|
320
|
+
|
321
|
+
public(:initialize, :ok?, :messages)
|
322
|
+
end # class Validator
|
323
|
+
end # class FPM::Program
|
data/lib/fpm/errors.rb
CHANGED
data/lib/fpm/namespace.rb
CHANGED
@@ -1,13 +1,4 @@
|
|
1
|
+
# The FPM namespace
|
1
2
|
module FPM
|
2
|
-
|
3
|
-
DIRS = {
|
4
|
-
:templates => File.expand_path(
|
5
|
-
File.join(
|
6
|
-
File.dirname(__FILE__),
|
7
|
-
'..',
|
8
|
-
'..',
|
9
|
-
'templates'
|
10
|
-
)
|
11
|
-
)
|
12
|
-
}
|
3
|
+
class Package; end
|
13
4
|
end
|
data/lib/fpm/package.rb
CHANGED
@@ -1,9 +1,20 @@
|
|
1
1
|
require "fpm/namespace"
|
2
|
+
require "fpm/util"
|
2
3
|
require "socket" # for Socket.gethostname
|
3
|
-
require "
|
4
|
-
require "
|
4
|
+
require "cabin"
|
5
|
+
require "tmpdir"
|
5
6
|
|
7
|
+
# This class is the parent of all packages.
|
8
|
+
# If you want to implement an FPM package type, you'll inherit from this.
|
9
|
+
#
|
10
|
+
# There are
|
6
11
|
class FPM::Package
|
12
|
+
include FPM::Util
|
13
|
+
include Cabin::Inspectable
|
14
|
+
|
15
|
+
# This class is raised if there's something wrong with a setting in the package.
|
16
|
+
class InvalidArgument < StandardError; end
|
17
|
+
|
7
18
|
# The name of this package
|
8
19
|
attr_accessor :name
|
9
20
|
|
@@ -19,13 +30,19 @@ class FPM::Package
|
|
19
30
|
# Debian calls this 'release' and is the last '-NUMBER' in the version
|
20
31
|
# RedHat has this as 'Release' in the .spec file
|
21
32
|
# FreeBSD calls this 'PORTREVISION'
|
22
|
-
#
|
33
|
+
#
|
34
|
+
# Iteration can be nil. If nil, the fpm package implementation is expected
|
35
|
+
# to handle any default value that should be instead.
|
23
36
|
attr_accessor :iteration
|
24
37
|
|
25
38
|
# Who maintains this package? This could be the upstream author
|
26
39
|
# or the package maintainer. You pick.
|
27
40
|
attr_accessor :maintainer
|
28
41
|
|
42
|
+
# A identifier representing the vendor. Any string is fine.
|
43
|
+
# This is usually who produced the software.
|
44
|
+
attr_accessor :vendor
|
45
|
+
|
29
46
|
# URL for this package.
|
30
47
|
# Could be the homepage. Could be the download url. You pick.
|
31
48
|
attr_accessor :url
|
@@ -66,116 +83,254 @@ class FPM::Package
|
|
66
83
|
# Array of configuration files
|
67
84
|
attr_accessor :config_files
|
68
85
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
#
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
if @maintainer.nil? or @maintainer.empty?
|
95
|
-
# Reference
|
96
|
-
# http://www.debian.org/doc/manuals/maint-guide/first.en.html
|
97
|
-
# http://wiki.debian.org/DeveloperConfiguration
|
98
|
-
# https://github.com/jordansissel/fpm/issues/37
|
99
|
-
if ENV.include?("DEBEMAIL") and ENV.include?("DEBFULLNAME")
|
100
|
-
# Use DEBEMAIL and DEBFULLNAME as the default maintainer if available.
|
101
|
-
@maintainer = "#{ENV["DEBFULLNAME"]} <#{ENV["DEBEMAIL"]}>"
|
102
|
-
else
|
103
|
-
# TODO(sissel): Maybe support using 'git config' for a default as well?
|
104
|
-
# git config --get user.name, etc can be useful.
|
105
|
-
#
|
106
|
-
# Otherwise default to user@currenthost
|
107
|
-
@maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
|
108
|
-
end
|
86
|
+
# Any other attributes specific to this package.
|
87
|
+
# This is where you'd put rpm, deb, or other specific attributes.
|
88
|
+
attr_accessor :attributes
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def initialize
|
93
|
+
@logger = Cabin::Channel.get
|
94
|
+
|
95
|
+
# Attributes for this specific package
|
96
|
+
@attributes = {}
|
97
|
+
|
98
|
+
# Reference
|
99
|
+
# http://www.debian.org/doc/manuals/maint-guide/first.en.html
|
100
|
+
# http://wiki.debian.org/DeveloperConfiguration
|
101
|
+
# https://github.com/jordansissel/fpm/issues/37
|
102
|
+
if ENV.include?("DEBEMAIL") and ENV.include?("DEBFULLNAME")
|
103
|
+
# Use DEBEMAIL and DEBFULLNAME as the default maintainer if available.
|
104
|
+
@maintainer = "#{ENV["DEBFULLNAME"]} <#{ENV["DEBEMAIL"]}>"
|
105
|
+
else
|
106
|
+
# TODO(sissel): Maybe support using 'git config' for a default as well?
|
107
|
+
# git config --get user.name, etc can be useful.
|
108
|
+
#
|
109
|
+
# Otherwise default to user@currenthost
|
110
|
+
@maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
|
109
111
|
end
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
@
|
115
|
-
@
|
116
|
-
@
|
117
|
-
@
|
118
|
-
@
|
119
|
-
@
|
120
|
-
@
|
121
|
-
|
122
|
-
# Target-specific settings, mirrors :settings metadata in FPM::Source
|
123
|
-
@settings = params[:settings] || {}
|
124
|
-
end # def initialize
|
113
|
+
@name = nil
|
114
|
+
@architecture = "all"
|
115
|
+
@description = "no description given"
|
116
|
+
@version = nil
|
117
|
+
@epoch = nil
|
118
|
+
@iteration = nil
|
119
|
+
@url = nil
|
120
|
+
@category = "default"
|
121
|
+
@license = "unknown"
|
122
|
+
@vendor = "none"
|
125
123
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
124
|
+
@provides = []
|
125
|
+
@conflicts = []
|
126
|
+
@replaces = []
|
127
|
+
@dependencies = []
|
128
|
+
@scripts = {}
|
129
|
+
@config_files = []
|
130
130
|
|
131
|
-
|
131
|
+
staging_path
|
132
|
+
build_path
|
133
|
+
end # def initialize
|
134
|
+
|
135
|
+
# Get the 'type' for this instance.
|
136
|
+
#
|
137
|
+
# For FPM::Package::ABC, this returns 'abc'
|
132
138
|
def type
|
133
|
-
self.class.
|
139
|
+
self.class.type
|
134
140
|
end # def type
|
135
141
|
|
136
|
-
|
137
|
-
|
138
|
-
@logger.info("
|
139
|
-
|
140
|
-
|
141
|
-
end # def template
|
142
|
+
# Convert this package to a new package type
|
143
|
+
def convert(klass)
|
144
|
+
@logger.info("Converting #{self.type} to #{klass.type}")
|
145
|
+
pkg = klass.new
|
146
|
+
pkg.cleanup_staging # purge any directories that may have been created by klass.new
|
142
147
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
+
# copy other bits
|
149
|
+
ivars = [
|
150
|
+
:@architecture, :@attributes, :@category, :@config_files, :@conflicts,
|
151
|
+
:@dependencies, :@description, :@epoch, :@iteration, :@license, :@maintainer,
|
152
|
+
:@name, :@provides, :@replaces, :@scripts, :@url, :@vendor, :@version,
|
153
|
+
:@config_files, :@staging_path
|
154
|
+
]
|
155
|
+
ivars.each do |ivar|
|
156
|
+
#@logger.debug("Copying ivar", :ivar => ivar, :value => instance_variable_get(ivar),
|
157
|
+
#:from => self.type, :to => pkg.type)
|
158
|
+
pkg.instance_variable_set(ivar, instance_variable_get(ivar))
|
148
159
|
end
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
#
|
155
|
-
|
156
|
-
|
157
|
-
|
160
|
+
|
161
|
+
pkg.converted_from(self.class)
|
162
|
+
return pkg
|
163
|
+
end # def convert
|
164
|
+
|
165
|
+
# This method is invoked on a package when it has been covered to a new
|
166
|
+
# package format. The purpose of this method is to do any extra conversion
|
167
|
+
# steps, like translating dependency conditions, etc.
|
168
|
+
def converted_from(origin)
|
169
|
+
# nothing to do by default. Subclasses may implement this.
|
170
|
+
# See the RPM package class for an example.
|
171
|
+
end # def converted
|
172
|
+
|
173
|
+
# Add a new source to this package.
|
174
|
+
# The exact behavior depends on the kind of package being managed.
|
175
|
+
#
|
176
|
+
# For instance:
|
177
|
+
#
|
178
|
+
# * for FPM::Package::Dir, << expects a path to a directory or files.
|
179
|
+
# * for FPM::Package::RPM, << expects a path to an rpm.
|
180
|
+
#
|
181
|
+
# The idea is that you can keep pumping in new things to a package
|
182
|
+
# for later conversion or output.
|
183
|
+
#
|
184
|
+
# Implementations are expected to put files relevant to the 'input' in the
|
185
|
+
# staging_path
|
186
|
+
def input(thing_to_input)
|
187
|
+
raise NotImplementedError.new("#{self.class.name} does not yet support " \
|
188
|
+
"reading #{self.type} packages")
|
189
|
+
end # def input
|
190
|
+
|
191
|
+
# Output this package to the given path.
|
192
|
+
def output(path)
|
193
|
+
raise NotImplementedError.new("#{self.class.name} does not yet support " \
|
194
|
+
"creating #{self.type} packages")
|
195
|
+
end # def output
|
196
|
+
|
197
|
+
def staging_path(path=nil)
|
198
|
+
@staging_path ||= ::Dir.mktmpdir(File.join(::Dir.pwd, "package-#{type}-staging"))
|
199
|
+
|
200
|
+
if path.nil?
|
201
|
+
return @staging_path
|
202
|
+
else
|
203
|
+
return File.join(@staging_path, path)
|
158
204
|
end
|
159
|
-
end # def
|
205
|
+
end # def staging_path
|
206
|
+
|
207
|
+
def build_path(path=nil)
|
208
|
+
@build_path ||= ::Dir.mktmpdir(File.join(::Dir.pwd, "package-#{type}-build"))
|
160
209
|
|
161
|
-
|
162
|
-
|
163
|
-
"#{name}-#{version}-#{iteration}.#{architecture}.#{type}"
|
210
|
+
if path.nil?
|
211
|
+
return @build_path
|
164
212
|
else
|
165
|
-
|
213
|
+
return File.join(@build_path, path)
|
214
|
+
end
|
215
|
+
end # def build_path
|
216
|
+
|
217
|
+
# Clean up any temporary storage used by this class.
|
218
|
+
def cleanup
|
219
|
+
cleanup_staging
|
220
|
+
cleanup_build
|
221
|
+
end # def cleanup
|
222
|
+
|
223
|
+
def cleanup_staging
|
224
|
+
if File.directory?(staging_path)
|
225
|
+
@logger.debug("Cleaning up staging path", :path => staging_path)
|
226
|
+
FileUtils.rm_r(staging_path)
|
166
227
|
end
|
167
|
-
end # def
|
228
|
+
end # def cleanup_staging
|
168
229
|
|
169
|
-
def
|
170
|
-
if
|
171
|
-
|
230
|
+
def cleanup_build
|
231
|
+
if File.directory?(build_path)
|
232
|
+
@logger.debug("Cleaning up build path", :path => build_path)
|
233
|
+
FileUtils.rm_r(build_path)
|
172
234
|
end
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
235
|
+
end # def cleanup_build
|
236
|
+
|
237
|
+
# List all files in the staging_path
|
238
|
+
#
|
239
|
+
# The paths will all be relative to staging_path and will not include that
|
240
|
+
# path.
|
241
|
+
def files
|
242
|
+
# Find will print the path you're searching first, so skip it and return
|
243
|
+
# the rest. Also trim the leading path such that '#{staging_path}/' is removed
|
244
|
+
# from the path before returning.
|
245
|
+
return Find.find(staging_path) \
|
246
|
+
.select { |path| path != staging_path } \
|
247
|
+
.collect { |path| path[staging_path.length + 1.. -1] }
|
248
|
+
end # def files
|
249
|
+
|
250
|
+
def template(path)
|
251
|
+
require "erb"
|
252
|
+
template_dir = File.join(File.dirname(__FILE__), "..", "..", "templates")
|
253
|
+
template_path = File.join(template_dir, path)
|
254
|
+
template_code = File.read(template_path)
|
255
|
+
@logger.info("Reading template", :path => template_path)
|
256
|
+
erb = ERB.new(template_code, nil, "-")
|
257
|
+
erb.filename = template_path
|
258
|
+
return erb
|
259
|
+
end # def template
|
260
|
+
|
261
|
+
def to_s(fmt="NAME.TYPE")
|
262
|
+
fullversion = version.to_s
|
263
|
+
fullversion += "-#{iteration}" if iteration
|
264
|
+
return fmt.gsub("ARCH", architecture.to_s) \
|
265
|
+
.gsub("NAME", name.to_s) \
|
266
|
+
.gsub("FULLVERSION", fullversion) \
|
267
|
+
.gsub("VERSION", version.to_s) \
|
268
|
+
.gsub("EPOCH", epoch.to_s)
|
269
|
+
.gsub("TYPE", type.to_s)
|
270
|
+
end # def to_s
|
271
|
+
|
272
|
+
class << self
|
273
|
+
# This method is invoked when subclass occurs.
|
274
|
+
#
|
275
|
+
# Lets us track all known FPM::Package subclasses
|
276
|
+
def inherited(klass)
|
277
|
+
@subclasses ||= {}
|
278
|
+
@subclasses[klass.name.gsub(/.*:/, "").downcase] = klass
|
279
|
+
end # def self.inherited
|
280
|
+
|
281
|
+
# Get a list of all known package subclasses
|
282
|
+
def types
|
283
|
+
return @subclasses
|
284
|
+
end # def self.types
|
285
|
+
|
286
|
+
# This allows packages to define flags for the fpm command line
|
287
|
+
def option(flag, param, help, options={}, &block)
|
288
|
+
@options ||= []
|
289
|
+
if !flag.is_a?(Array)
|
290
|
+
flag = [flag]
|
291
|
+
end
|
292
|
+
|
293
|
+
flag = flag.collect { |f| "--#{type}-#{f.gsub(/^--/, "")}" }
|
294
|
+
help = "(#{type} only) #{help}"
|
295
|
+
@options << [flag, param, help, options, block]
|
296
|
+
end # def options
|
297
|
+
|
298
|
+
# Apply the options for this package on the clamp command
|
299
|
+
#
|
300
|
+
# Package flags become attributes '{type}-flag'
|
301
|
+
#
|
302
|
+
# So if you have:
|
303
|
+
#
|
304
|
+
# class Foo < FPM::Package
|
305
|
+
# option "--bar-baz" ...
|
306
|
+
# end
|
307
|
+
#
|
308
|
+
# The attribute value for --foo-bar-baz will be :foo_bar_baz"
|
309
|
+
def apply_options(clampcommand)
|
310
|
+
@options ||= []
|
311
|
+
@options.each do |args|
|
312
|
+
flag, param, help, options, block = args
|
313
|
+
clampcommand.option(flag, param, help, options) do |value|
|
314
|
+
# This is run in the scope of FPM::Command
|
315
|
+
value = block.call(value) unless block.nil?
|
316
|
+
# flag is an array, use the first flag as the attribute name
|
317
|
+
attr = flag.first[2..-1].gsub(/-+/, "_").to_sym
|
318
|
+
settings[attr] = value
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end # def apply_options
|
322
|
+
|
323
|
+
# Get the type of this package class.
|
324
|
+
#
|
325
|
+
# For "Foo::Bar::BAZ" this will return "baz"
|
326
|
+
def type
|
327
|
+
self.name.split(':').last.downcase
|
328
|
+
end # def self.type
|
329
|
+
end # class << self
|
330
|
+
|
331
|
+
# General public API
|
332
|
+
public(:type, :initialize, :convert, :input, :output, :to_s, :cleanup, :files)
|
333
|
+
|
334
|
+
# Package internal public api
|
335
|
+
public(:cleanup_staging, :cleanup_build, :staging_path, :converted_from)
|
181
336
|
end # class FPM::Package
|