fpm 0.3.11 → 0.4.0pre1
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.
- 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
|