omnibus-sonian 1.2.0.1

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.
Files changed (70) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +5 -0
  5. data/.yardopts +6 -0
  6. data/CHANGELOG.md +96 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE +201 -0
  9. data/NOTICE +9 -0
  10. data/README.md +195 -0
  11. data/Rakefile +7 -0
  12. data/bin/makeself-header.sh +401 -0
  13. data/bin/makeself.sh +407 -0
  14. data/bin/omnibus +11 -0
  15. data/lib/omnibus.rb +304 -0
  16. data/lib/omnibus/artifact.rb +151 -0
  17. data/lib/omnibus/build_version.rb +285 -0
  18. data/lib/omnibus/builder.rb +328 -0
  19. data/lib/omnibus/clean_tasks.rb +30 -0
  20. data/lib/omnibus/cli.rb +35 -0
  21. data/lib/omnibus/cli/application.rb +140 -0
  22. data/lib/omnibus/cli/base.rb +118 -0
  23. data/lib/omnibus/cli/build.rb +62 -0
  24. data/lib/omnibus/cli/cache.rb +60 -0
  25. data/lib/omnibus/cli/release.rb +49 -0
  26. data/lib/omnibus/config.rb +224 -0
  27. data/lib/omnibus/exceptions.rb +143 -0
  28. data/lib/omnibus/fetcher.rb +184 -0
  29. data/lib/omnibus/fetchers.rb +22 -0
  30. data/lib/omnibus/fetchers/git_fetcher.rb +212 -0
  31. data/lib/omnibus/fetchers/net_fetcher.rb +193 -0
  32. data/lib/omnibus/fetchers/path_fetcher.rb +65 -0
  33. data/lib/omnibus/fetchers/s3_cache_fetcher.rb +42 -0
  34. data/lib/omnibus/health_check.rb +356 -0
  35. data/lib/omnibus/library.rb +62 -0
  36. data/lib/omnibus/overrides.rb +69 -0
  37. data/lib/omnibus/package_release.rb +163 -0
  38. data/lib/omnibus/project.rb +715 -0
  39. data/lib/omnibus/reports.rb +99 -0
  40. data/lib/omnibus/s3_cacher.rb +138 -0
  41. data/lib/omnibus/software.rb +441 -0
  42. data/lib/omnibus/templates/Berksfile.erb +3 -0
  43. data/lib/omnibus/templates/Gemfile.erb +4 -0
  44. data/lib/omnibus/templates/README.md.erb +102 -0
  45. data/lib/omnibus/templates/Vagrantfile.erb +95 -0
  46. data/lib/omnibus/templates/gitignore.erb +8 -0
  47. data/lib/omnibus/templates/omnibus.rb.example.erb +5 -0
  48. data/lib/omnibus/templates/package_scripts/makeselfinst.erb +27 -0
  49. data/lib/omnibus/templates/package_scripts/postinst.erb +17 -0
  50. data/lib/omnibus/templates/package_scripts/postrm.erb +9 -0
  51. data/lib/omnibus/templates/project.rb.erb +21 -0
  52. data/lib/omnibus/templates/software/c-example.rb.erb +42 -0
  53. data/lib/omnibus/templates/software/erlang-example.rb.erb +38 -0
  54. data/lib/omnibus/templates/software/ruby-example.rb.erb +24 -0
  55. data/lib/omnibus/util.rb +61 -0
  56. data/lib/omnibus/version.rb +20 -0
  57. data/omnibus.gemspec +34 -0
  58. data/spec/artifact_spec.rb +106 -0
  59. data/spec/build_version_spec.rb +266 -0
  60. data/spec/data/overrides/bad_line.overrides +3 -0
  61. data/spec/data/overrides/good.overrides +5 -0
  62. data/spec/data/overrides/with_dupes.overrides +4 -0
  63. data/spec/data/software/erchef.rb +40 -0
  64. data/spec/fetchers/net_fetcher_spec.rb +16 -0
  65. data/spec/overrides_spec.rb +114 -0
  66. data/spec/package_release_spec.rb +197 -0
  67. data/spec/s3_cacher_spec.rb +47 -0
  68. data/spec/software_spec.rb +85 -0
  69. data/spec/spec_helper.rb +28 -0
  70. metadata +252 -0
@@ -0,0 +1,69 @@
1
+ require 'pp'
2
+
3
+ module Omnibus
4
+ module Overrides
5
+
6
+ DEFAULT_OVERRIDE_FILE_NAME = "omnibus.overrides"
7
+
8
+ # Parses a file of override information into a Hash.
9
+ #
10
+ # Each line of the file must be of the form
11
+ #
12
+ #
13
+ # <package_name> <version>
14
+ #
15
+ # where the two pieces of data are separated by whitespace.
16
+ #
17
+ # @param file [String] the path to an overrides file
18
+ # @return [Hash, nil]
19
+ def self.parse_file(file)
20
+ if file
21
+ File.readlines(file).inject({}) do |acc, line|
22
+ info = line.split
23
+
24
+ unless info.count == 2
25
+ raise ArgumentError, "Invalid overrides line: '#{line.chomp}'"
26
+ end
27
+
28
+ package, version = info
29
+
30
+ if acc[package]
31
+ raise ArgumentError, "Multiple overrides present for '#{package}' in overrides file #{file}!"
32
+ end
33
+
34
+ acc[package] = version
35
+ acc
36
+ end
37
+ else
38
+ nil
39
+ end
40
+ end
41
+
42
+ # Return the full path to an overrides file, or +nil+ if no such
43
+ # file exists.
44
+ def self.resolve_override_file
45
+ file = ENV['OMNIBUS_OVERRIDE_FILE'] || DEFAULT_OVERRIDE_FILE_NAME
46
+ path = File.expand_path(file)
47
+ File.exist?(path) ? path : nil
48
+ end
49
+
50
+ # Return a hash of override information. If no such information
51
+ # can be found, the hash will be empty
52
+ #
53
+ # @return [Hash]
54
+ def self.overrides
55
+ file = resolve_override_file
56
+ overrides = parse_file(file)
57
+
58
+ if overrides
59
+ puts "********************************************************************************"
60
+ puts "Using Overrides from #{Omnibus::Overrides.resolve_override_file}"
61
+ pp overrides
62
+ puts "********************************************************************************"
63
+ end
64
+
65
+ overrides || {}
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,163 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ # internal
19
+ require 'omnibus/exceptions'
20
+
21
+ # stdlib
22
+ require 'json'
23
+
24
+ # external
25
+ require 'uber-s3'
26
+
27
+ module Omnibus
28
+ class PackageRelease
29
+
30
+ attr_reader :package_path
31
+ attr_reader :access_policy
32
+
33
+ # @param package_path [String] file system path to the package artifact
34
+ # @option opts [:private, :public_read] :access specifies access control on
35
+ # uploaded files
36
+ # @yield callback triggered by successful upload. Allows users of this
37
+ # class to add UI feedback.
38
+ # @yieldparam s3_object_key [String] the S3 key of the uploaded object.
39
+ def initialize(package_path, opts={:access=>:private}, &block)
40
+ @package_path = package_path
41
+ @metadata = nil
42
+ @s3_client = nil
43
+
44
+ @after_upload = if block_given?
45
+ block
46
+ else
47
+ lambda { |item_key| nil }
48
+ end
49
+
50
+ # sets @access_policy
51
+ handle_opts(opts)
52
+ end
53
+
54
+ # Primary API for this class. Validates S3 configuration and package files,
55
+ # then runs the upload.
56
+ # @return [void]
57
+ # @raise [NoPackageFile, NoPackageMetadataFile] when the package or
58
+ # associated metadata file do not exist.
59
+ # @raise [InvalidS3ReleaseConfiguration] when the Omnibus configuration is
60
+ # missing required settings.
61
+ # @raise Also may raise errors from uber-s3 or net/http.
62
+ def release
63
+ validate_config!
64
+ validate_package!
65
+ s3_client.store(metadata_key, metadata_json, :access => access_policy)
66
+ uploaded(metadata_key)
67
+ s3_client.store(package_key, package_content, :access => access_policy, :content_md5 => md5)
68
+ uploaded(package_key)
69
+ end
70
+
71
+ def uploaded(key)
72
+ @after_upload.call(key)
73
+ end
74
+
75
+ def package_key
76
+ File.join(platform_path, File.basename(package_path))
77
+ end
78
+
79
+ def metadata_key
80
+ File.join(platform_path, File.basename(package_metadata_path))
81
+ end
82
+
83
+ def platform_path
84
+ File.join(metadata["platform"], metadata["platform_version"], metadata["arch"])
85
+ end
86
+
87
+ def md5
88
+ metadata["md5"]
89
+ end
90
+
91
+ def metadata
92
+ @metadata ||= JSON.parse(metadata_json)
93
+ end
94
+
95
+ def metadata_json
96
+ IO.read(package_metadata_path)
97
+ end
98
+
99
+ def package_content
100
+ IO.read(package_path)
101
+ end
102
+
103
+ def package_metadata_path
104
+ "#{package_path}.metadata.json"
105
+ end
106
+
107
+ def validate_package!
108
+ if !File.exist?(package_path)
109
+ raise NoPackageFile.new(package_path)
110
+ elsif !File.exist?(package_metadata_path)
111
+ raise NoPackageMetadataFile.new(package_metadata_path)
112
+ else
113
+ true
114
+ end
115
+ end
116
+
117
+ def validate_config!
118
+ if s3_access_key && s3_secret_key && s3_bucket
119
+ true
120
+ else
121
+ err = InvalidS3ReleaseConfiguration.new(s3_bucket, s3_access_key, s3_secret_key)
122
+ raise err
123
+ end
124
+ end
125
+
126
+ def s3_client
127
+ @s3_client ||= UberS3.new(
128
+ :access_key => s3_access_key,
129
+ :secret_access_key => s3_secret_key,
130
+ :bucket => s3_bucket,
131
+ :adaper => :net_http
132
+ )
133
+ end
134
+
135
+ def s3_access_key
136
+ config[:release_s3_access_key]
137
+ end
138
+
139
+ def s3_secret_key
140
+ config[:release_s3_secret_key]
141
+ end
142
+
143
+ def s3_bucket
144
+ config[:release_s3_bucket]
145
+ end
146
+
147
+ def config
148
+ Omnibus.config
149
+ end
150
+
151
+ def handle_opts(opts)
152
+ access_policy = opts[:access]
153
+ if access_policy.nil?
154
+ raise ArgumentError, "options to #{self.class} must specify `:access' (given: #{opts.inspect})"
155
+ elsif not [:private, :public_read].include?(access_policy)
156
+ raise ArgumentError, "option `:access' must be one of `[:private, :public_read]' (given: #{access_policy.inspect})"
157
+ else
158
+ @access_policy = access_policy
159
+ end
160
+ end
161
+
162
+ end
163
+ end
@@ -0,0 +1,715 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ require 'omnibus/artifact'
18
+ require 'omnibus/exceptions'
19
+ require 'omnibus/library'
20
+ require 'omnibus/util'
21
+
22
+ module Omnibus
23
+
24
+ # Omnibus project DSL reader
25
+ #
26
+ # @todo It seems like there's a bit of a conflation between a
27
+ # "project" and a "package" in this class... perhaps the
28
+ # package-building portions should be extracted to a separate
29
+ # class.
30
+ # @todo: Reorder DSL methods to fit in the same YARD group
31
+ # @todo: Generate the DSL methods via metaprogramming... they're all so similar
32
+ class Project
33
+ include Rake::DSL
34
+ include Util
35
+
36
+ NULL_ARG = Object.new
37
+
38
+ attr_reader :library
39
+
40
+ # Convenience method to initialize a Project from a DSL file.
41
+ #
42
+ # @param filename [String] the filename of the Project DSL file to load.
43
+ def self.load(filename)
44
+ new(IO.read(filename), filename)
45
+ end
46
+
47
+ # Create a new Project from the contents of a DSL file. Prefer
48
+ # calling {Omnibus::Project#load} instead of using this method
49
+ # directly.
50
+ #
51
+ # @param io [String] the contents of a Project DSL (_not_ the filename!)
52
+ # @param filename [String] unused!
53
+ #
54
+ # @see Omnibus::Project#load
55
+ #
56
+ # @todo Remove filename parameter, as it is unused.
57
+ def initialize(io, filename)
58
+ @output_package = nil
59
+ @name = nil
60
+ @package_name = nil
61
+ @install_path = nil
62
+ @homepage = nil
63
+ @description = nil
64
+ @replaces = nil
65
+
66
+ @exclusions = Array.new
67
+ @conflicts = Array.new
68
+ @dependencies = Array.new
69
+ @runtime_dependencies = Array.new
70
+ instance_eval(io)
71
+ validate
72
+
73
+ @library = Omnibus::Library.new(self)
74
+ render_tasks
75
+ end
76
+
77
+ # Ensures that certain project information has been set
78
+ #
79
+ # @raise [MissingProjectConfiguration] if a required parameter has
80
+ # not been set
81
+ # @return [void]
82
+ def validate
83
+ name && install_path && maintainer && homepage
84
+ end
85
+
86
+ # @!group DSL methods
87
+ # Here is some broad documentation for the DSL methods as a whole.
88
+
89
+ # Set or retrieve the name of the project
90
+ #
91
+ # @param val [String] the name to set
92
+ # @return [String]
93
+ #
94
+ # @raise [MissingProjectConfiguration] if a value was not set
95
+ # before being subsequently retrieved (i.e., a name
96
+ # must be set in order to build a project)
97
+ def name(val=NULL_ARG)
98
+ @name = val unless val.equal?(NULL_ARG)
99
+ @name || raise(MissingProjectConfiguration.new("name", "my_project"))
100
+ end
101
+
102
+ # Set or retrieve the package name of the project. Unless
103
+ # explicitly set, the package name defaults to the project name
104
+ #
105
+ # @param val [String] the package name to set
106
+ # @return [String]
107
+ def package_name(val=NULL_ARG)
108
+ @package_name = val unless val.equal?(NULL_ARG)
109
+ @package_name.nil? ? @name : @package_name
110
+ end
111
+
112
+ # Set or retrieve the path at which the project should be
113
+ # installed by the generated package.
114
+ #
115
+ # @param val [String]
116
+ # @return [String]
117
+ #
118
+ # @raise [MissingProjectConfiguration] if a value was not set
119
+ # before being subsequently retrieved (i.e., an install_path
120
+ # must be set in order to build a project)
121
+ def install_path(val=NULL_ARG)
122
+ @install_path = val unless val.equal?(NULL_ARG)
123
+ @install_path || raise(MissingProjectConfiguration.new("install_path", "/opt/opscode"))
124
+ end
125
+
126
+ # Set or retrieve the the package maintainer.
127
+ #
128
+ # @param val [String]
129
+ # @return [String]
130
+ #
131
+ # @raise [MissingProjectConfiguration] if a value was not set
132
+ # before being subsequently retrieved (i.e., a maintainer must
133
+ # be set in order to build a project)
134
+ def maintainer(val=NULL_ARG)
135
+ @maintainer = val unless val.equal?(NULL_ARG)
136
+ @maintainer || raise(MissingProjectConfiguration.new("maintainer", "Opscode, Inc."))
137
+ end
138
+
139
+ # Set or retrive the package homepage.
140
+ #
141
+ # @param val [String]
142
+ # @return [String]
143
+ #
144
+ # @raise [MissingProjectConfiguration] if a value was not set
145
+ # before being subsequently retrieved (i.e., a homepage must be
146
+ # set in order to build a project)
147
+ def homepage(val=NULL_ARG)
148
+ @homepage = val unless val.equal?(NULL_ARG)
149
+ @homepage || raise(MissingProjectConfiguration.new("homepage", "http://www.opscode.com"))
150
+ end
151
+
152
+ # Defines the iteration for the package to be generated. Adheres
153
+ # to the conventions of the platform for which the package is
154
+ # being built.
155
+ #
156
+ # All iteration strings begin with the value set in {#build_iteration}
157
+ #
158
+ # @return [String]
159
+ def iteration
160
+ case platform_family
161
+ when 'rhel'
162
+ platform_version =~ /^(\d+)/
163
+ maj = $1
164
+ "#{build_iteration}.el#{maj}"
165
+ when 'freebsd'
166
+ platform_version =~ /^(\d+)/
167
+ maj = $1
168
+ "#{build_iteration}.#{platform}.#{maj}.#{machine}"
169
+ when 'windows'
170
+ "#{build_iteration}.windows"
171
+ when 'aix'
172
+ "#{build_iteration}"
173
+ else
174
+ "#{build_iteration}.#{platform}.#{platform_version}"
175
+ end
176
+ end
177
+
178
+ # Set or retrieve the project description. Defaults to `"The full
179
+ # stack of #{name}"`
180
+ #
181
+ # Corresponds to the `--description` flag of
182
+ # {https://github.com/jordansissel/fpm fpm}.
183
+ #
184
+ # @param val [String] the project description
185
+ # @return [String]
186
+ #
187
+ # @see #name
188
+ def description(val=NULL_ARG)
189
+ @description = val unless val.equal?(NULL_ARG)
190
+ @description || "The full stack of #{name}"
191
+ end
192
+
193
+ # Set or retrieve the name of the package this package will replace.
194
+ #
195
+ # Ultimately used as the value for the `--replaces` flag in
196
+ # {https://github.com/jordansissel/fpm fpm}.
197
+ #
198
+ # @param val [String] the name of the package to replace
199
+ # @return [String]
200
+ #
201
+ # @todo Consider having this default to {#package_name}; many uses of this
202
+ # method effectively do this already.
203
+ def replaces(val=NULL_ARG)
204
+ @replaces = val unless val.equal?(NULL_ARG)
205
+ @replaces
206
+ end
207
+
208
+ # Add to the list of packages this one conflicts with.
209
+ #
210
+ # Specifying conflicts is optional. See the `--conflicts` flag in
211
+ # {https://github.com/jordansissel/fpm fpm}.
212
+ #
213
+ # @param val [String]
214
+ # @return [void]
215
+ def conflict(val)
216
+ @conflicts << val
217
+ end
218
+
219
+ # Set or retrieve the version of the project.
220
+ #
221
+ # @param val [String] the version to set
222
+ # @return [String]
223
+ #
224
+ # @see Omnibus::BuildVersion
225
+ def build_version(val=NULL_ARG)
226
+ @build_version = val unless val.equal?(NULL_ARG)
227
+ @build_version
228
+ end
229
+
230
+ # Set or retrieve the build iteration of the project. Defaults to
231
+ # `1` if not otherwise set.
232
+ #
233
+ # @param val [Fixnum]
234
+ # @return [Fixnum]
235
+ #
236
+ # @todo Is there a better name for this than "build_iteration"?
237
+ # Would be nice to cut down confusiton with {#iteration}.
238
+ def build_iteration(val=NULL_ARG)
239
+ @build_iteration = val unless val.equal?(NULL_ARG)
240
+ @build_iteration || 1
241
+ end
242
+
243
+ # Add an Omnibus software dependency.
244
+ #
245
+ # Note that this is a *build time* dependency. If you need to
246
+ # specify an external dependency that is required at runtime, see
247
+ # {#runtime_dependency} instead.
248
+ #
249
+ # @param val [String] the name of a Software dependency
250
+ # @return [void]
251
+ def dependency(val)
252
+ @dependencies << val
253
+ end
254
+
255
+ # Add a package that is a runtime dependency of this
256
+ # project.
257
+ #
258
+ # This is distinct from a build-time dependency, which should
259
+ # correspond to an Omnibus software definition.
260
+ #
261
+ # Corresponds to the `--depends` flag of
262
+ # {https://github.com/jordansissel/fpm fpm}.
263
+ #
264
+ # @param val [String] the name of the runtime dependency
265
+ # @return [void]
266
+ def runtime_dependency(val)
267
+ @runtime_dependencies << val
268
+ end
269
+
270
+ # Set or retrieve the list of software dependencies for this
271
+ # project. As this is a DSL method, only pass the names of
272
+ # software components, not {Omnibus::Software} objects.
273
+ #
274
+ # These is the software that comprises your project, and is
275
+ # distinct from runtime dependencies.
276
+ #
277
+ # @note This will reinitialize the internal depdencies Array
278
+ # and overwrite any dependencies that may have been set using
279
+ # {#dependency}.
280
+ #
281
+ # @param val [Array<String>] a list of names of Software components
282
+ # @return [Array<String>]
283
+ def dependencies(val=NULL_ARG)
284
+ @dependencies = val unless val.equal?(NULL_ARG)
285
+ @dependencies
286
+ end
287
+
288
+ # Add a new exclusion pattern.
289
+ #
290
+ # Corresponds to the `--exclude` flag of {https://github.com/jordansissel/fpm fpm}.
291
+ #
292
+ # @param pattern [String]
293
+ # @return void
294
+ def exclude(pattern)
295
+ @exclusions << pattern
296
+ end
297
+
298
+ # Returns the platform version of the machine on which Omnibus is
299
+ # running, as determined by Ohai.
300
+ #
301
+ # @return [String]
302
+ def platform_version
303
+ OHAI.platform_version
304
+ end
305
+
306
+ # Returns the platform of the machine on which Omnibus is running,
307
+ # as determined by Ohai.
308
+ #
309
+ # @return [String]
310
+ def platform
311
+ OHAI.platform
312
+ end
313
+
314
+ # Returns the platform family of the machine on which Omnibus is
315
+ # running, as determined by Ohai.
316
+ #
317
+ # @return [String]
318
+ def platform_family
319
+ OHAI.platform_family
320
+ end
321
+
322
+ def machine
323
+ OHAI['kernel']['machine']
324
+ end
325
+
326
+ # Convenience method for accessing the global Omnibus configuration object.
327
+ #
328
+ # @return Omnibus::Config
329
+ #
330
+ # @see Omnibus::Config
331
+ def config
332
+ Omnibus.config
333
+ end
334
+
335
+ # The path to the package scripts directory for this project.
336
+ # These are optional scripts that can be bundled into the
337
+ # resulting package for running at various points in the package
338
+ # management lifecycle.
339
+ #
340
+ # Currently supported scripts include:
341
+ #
342
+ # * postinst
343
+ #
344
+ # A post-install script
345
+ # * prerm
346
+ #
347
+ # A pre-uninstall script
348
+ # * postrm
349
+ #
350
+ # A post-uninstall script
351
+ #
352
+ # Any scripts with these names that are present in the package
353
+ # scripts directory will be incorporated into the package that is
354
+ # built. This only applies to fpm-built packages.
355
+ #
356
+ # Additionally, there may be a `makeselfinst` script.
357
+ #
358
+ # @return [String]
359
+ #
360
+ # @todo This documentation really should be up at a higher level,
361
+ # particularly since the user has no way to change the path.
362
+ def package_scripts_path
363
+ "#{Omnibus.project_root}/package-scripts/#{name}"
364
+ end
365
+
366
+ # Determine the package type(s) to be built, based on the platform
367
+ # family for which the package is being built.
368
+ #
369
+ # If specific types cannot be determined, default to `["makeself"]`.
370
+ #
371
+ # @return [Array<(String)>]
372
+ #
373
+ # @todo Why does this only ever return a single-element array,
374
+ # instead of just a string, or symbol?
375
+ def package_types
376
+ case platform_family
377
+ when 'debian'
378
+ [ "deb" ]
379
+ when 'fedora', 'rhel'
380
+ [ "rpm" ]
381
+ when 'aix'
382
+ [ "bff" ]
383
+ when 'solaris2'
384
+ [ "solaris" ]
385
+ when 'windows'
386
+ [ "msi" ]
387
+ else
388
+ [ "makeself" ]
389
+ end
390
+ end
391
+
392
+ # Indicates whether `software` is defined as a software component
393
+ # of this project.
394
+ #
395
+ # @param software [String, Omnibus::Software, #name]
396
+ # @return [Boolean]
397
+ #
398
+ # @see #dependencies
399
+ def dependency?(software)
400
+ name = if software.respond_to?(:name)
401
+ software.send(:name)
402
+ else
403
+ software
404
+ end
405
+ @dependencies.include?(name)
406
+ end
407
+
408
+ # @!endgroup
409
+
410
+ private
411
+
412
+ # An Array of platform data suitable for `Artifact.new`. This will go into
413
+ # metadata generated for the artifact, and be used for the file hierarchy
414
+ # of released packages if the default release scripts are used.
415
+ # @return [Array<String>] platform_shortname, platform_version_for_package,
416
+ # machine architecture.
417
+ def platform_tuple
418
+ [platform_shortname, platform_version_for_package, machine]
419
+ end
420
+
421
+ # Platform version to be used in package metadata. For rhel, the minor
422
+ # version is removed, e.g., "5.6" becomes "5". For all other platforms,
423
+ # this is just the platform_version.
424
+ # @return [String] the platform version
425
+ def platform_version_for_package
426
+ if platform == "rhel"
427
+ platform_version[/([\d]+)\..+/, 1]
428
+ else
429
+ platform_version
430
+ end
431
+ end
432
+
433
+ # Platform name to be used when creating metadata for the artifact.
434
+ # rhel/centos become "el", all others are just platform
435
+ # @return [String] the platform family short name
436
+ def platform_shortname
437
+ if platform_family == "rhel"
438
+ "el"
439
+ else
440
+ platform
441
+ end
442
+ end
443
+
444
+ def render_metadata(pkg_type)
445
+ basename = output_package(pkg_type)
446
+ pkg_path = "#{config.package_dir}/#{basename}"
447
+ artifact = Artifact.new(pkg_path, [ platform_tuple ], :version => build_version)
448
+ metadata = artifact.flat_metadata
449
+ File.open("#{pkg_path}.metadata.json", "w+") do |f|
450
+ f.print(JSON.pretty_generate(metadata))
451
+ end
452
+ end
453
+
454
+ # The basename of the resulting package file.
455
+ # @return [String] the basename of the package file
456
+ def output_package(pkg_type)
457
+ case pkg_type
458
+ when "makeself"
459
+ "#{package_name}-#{build_version}_#{iteration}.sh"
460
+ when "msi"
461
+ "#{package_name}-#{build_version}-#{iteration}.msi"
462
+ when "bff"
463
+ "#{package_name}.#{bff_version}.bff"
464
+ else # fpm
465
+ require "fpm/package/#{pkg_type}"
466
+ pkg = FPM::Package.types[pkg_type].new
467
+ pkg.version = build_version
468
+ pkg.name = package_name
469
+ pkg.iteration = iteration
470
+ if pkg_type == "solaris"
471
+ pkg.to_s("NAME.FULLVERSION.ARCH.TYPE")
472
+ else
473
+ pkg.to_s
474
+ end
475
+ end
476
+ end
477
+
478
+ # The command to generate an MSI package on Windows platforms.
479
+ #
480
+ # Does not execute the command, only assembles it.
481
+ #
482
+ # @return [Array<(String, Hash)>] The complete MSI command, plus a
483
+ # Hash of options to be passed on to Mixlib::ShellOut
484
+ #
485
+ # @see Mixlib::ShellOut
486
+ #
487
+ # @todo For this and all the *_command methods, just return a
488
+ # Mixlib::ShellOut object ready for execution. Using Arrays
489
+ # makes downstream processing needlessly complicated.
490
+ def msi_command
491
+ msi_command = ["light.exe",
492
+ "-nologo",
493
+ "-ext WixUIExtension",
494
+ "-cultures:en-us",
495
+ "-loc #{install_path}\\msi-tmp\\#{package_name}-en-us.wxl",
496
+ "#{install_path}\\msi-tmp\\#{package_name}-Files.wixobj",
497
+ "#{install_path}\\msi-tmp\\#{package_name}.wixobj",
498
+ "-out #{config.package_dir}\\#{output_package("msi")}"]
499
+
500
+ # Don't care about the 204 return code from light.exe since it's
501
+ # about some expected warnings...
502
+ [msi_command.join(" "), {:returns => [0, 204]}]
503
+ end
504
+
505
+ def bff_command
506
+ bff_command = ["mkinstallp -d / -T /tmp/bff/gen.template"]
507
+ [bff_command.join(" "), {:returns => [0]}]
508
+ end
509
+
510
+ # The {https://github.com/jordansissel/fpm fpm} command to
511
+ # generate a package for RedHat, Ubuntu, Solaris, etc. platforms.
512
+ #
513
+ # Does not execute the command, only assembles it.
514
+ #
515
+ # In contrast to {#msi_command}, command generated by
516
+ # {#fpm_command} does not require any Mixlib::Shellout options.
517
+ #
518
+ # @return [Array<String>] the components of the fpm command; need
519
+ # to be joined with " " first.
520
+ #
521
+ # @todo Just make this return a String instead of an Array
522
+ # @todo Use the long option names (i.e., the double-dash ones) in
523
+ # the fpm command for maximum clarity.
524
+ def fpm_command(pkg_type)
525
+ command_and_opts = ["fpm",
526
+ "-s dir",
527
+ "-t #{pkg_type}",
528
+ "-v #{build_version}",
529
+ "-n #{package_name}",
530
+ "-p #{output_package(pkg_type)}",
531
+ "--iteration #{iteration}",
532
+ "-m '#{maintainer}'",
533
+ "--description '#{description}'",
534
+ "--url #{homepage}"]
535
+ if File.exist?("#{package_scripts_path}/postinst")
536
+ command_and_opts << "--post-install '#{package_scripts_path}/postinst'"
537
+ end
538
+ # solaris packages don't support --pre-uninstall
539
+ if File.exist?("#{package_scripts_path}/prerm") && pkg_type != "solaris"
540
+ command_and_opts << "--pre-uninstall '#{package_scripts_path}/prerm'"
541
+ end
542
+ # solaris packages don't support --post-uninstall
543
+ if File.exist?("#{package_scripts_path}/postrm") && pkg_type != "solaris"
544
+ command_and_opts << "--post-uninstall '#{package_scripts_path}/postrm'"
545
+ end
546
+
547
+ @exclusions.each do |pattern|
548
+ command_and_opts << "--exclude '#{pattern}'"
549
+ end
550
+
551
+ @runtime_dependencies.each do |runtime_dep|
552
+ command_and_opts << "--depends '#{runtime_dep}'"
553
+ end
554
+
555
+ @conflicts.each do |conflict|
556
+ command_and_opts << "--conflicts '#{conflict}'"
557
+ end
558
+
559
+ command_and_opts << " --replaces #{@replaces}" if @replaces
560
+ command_and_opts << install_path
561
+ command_and_opts
562
+ end
563
+
564
+ # TODO: what's this do?
565
+ def makeself_command
566
+ command_and_opts = [ File.expand_path(File.join(Omnibus.source_root, "bin", "makeself.sh")),
567
+ "--gzip",
568
+ install_path,
569
+ output_package("makeself"),
570
+ "'The full stack of #{@name}'"
571
+ ]
572
+ command_and_opts << "./makeselfinst" if File.exists?("#{package_scripts_path}/makeselfinst")
573
+ command_and_opts
574
+ end
575
+
576
+ # Runs the makeself commands to make a self extracting archive package.
577
+ # As a (necessary) side-effect, sets
578
+ # @return void
579
+ def run_makeself
580
+ package_commands = []
581
+ # copy the makeself installer into package
582
+ if File.exists?("#{package_scripts_path}/makeselfinst")
583
+ package_commands << "cp #{package_scripts_path}/makeselfinst #{install_path}/"
584
+ end
585
+
586
+ # run the makeself program
587
+ package_commands << makeself_command.join(" ")
588
+
589
+ # rm the makeself installer (for incremental builds)
590
+ package_commands << "rm -f #{install_path}/makeselfinst"
591
+ package_commands.each {|cmd| run_package_command(cmd) }
592
+ end
593
+
594
+ # Runs the necessary command to make an MSI. As a side-effect, sets `output_package`
595
+ # @return void
596
+ def run_msi
597
+ run_package_command(msi_command)
598
+ end
599
+
600
+ def bff_version
601
+ build_version.split(/[^\d]/)[0..2].join(".") + ".#{iteration}"
602
+ end
603
+
604
+ def run_bff
605
+ FileUtils.rm_rf "/.info"
606
+ FileUtils.rm_rf "/tmp/bff"
607
+ FileUtils.mkdir "/tmp/bff"
608
+
609
+ system "find #{install_path} -print > /tmp/bff/file.list"
610
+
611
+ system "cat #{package_scripts_path}/aix/opscode.chef.client.template | sed -e 's/TBS/#{bff_version}/' > /tmp/bff/gen.preamble"
612
+
613
+ # @todo can we just use an erb template here?
614
+ system "cat /tmp/bff/gen.preamble /tmp/bff/file.list #{package_scripts_path}/aix/opscode.chef.client.template.last > /tmp/bff/gen.template"
615
+
616
+ FileUtils.cp "#{package_scripts_path}/aix/unpostinstall.sh", "#{install_path}/bin"
617
+ FileUtils.cp "#{package_scripts_path}/aix/postinstall.sh", "#{install_path}/bin"
618
+
619
+ run_package_command(bff_command)
620
+
621
+ FileUtils.cp "/tmp/chef.#{bff_version}.bff", "/var/cache/omnibus/pkg/chef.#{bff_version}.bff"
622
+ end
623
+
624
+ # Runs the necessary command to make a package with fpm. As a side-effect,
625
+ # sets `output_package`
626
+ # @return void
627
+ def run_fpm(pkg_type)
628
+ run_package_command(fpm_command(pkg_type).join(" "))
629
+ end
630
+
631
+ # Executes the given command via mixlib-shellout.
632
+ # @return [Mixlib::ShellOut] returns the underlying Mixlib::ShellOut
633
+ # object, so the caller can inspect the stdout and stderr.
634
+ def run_package_command(cmd)
635
+ cmd_options = {
636
+ :timeout => 3600,
637
+ :cwd => config.package_dir
638
+ }
639
+
640
+ if cmd.is_a?(Array)
641
+ command = cmd[0]
642
+ cmd_options.merge!(cmd[1])
643
+ else
644
+ command = cmd
645
+ end
646
+
647
+ shellout!(command, cmd_options)
648
+ end
649
+
650
+ # Dynamically generate Rake tasks to build projects and all the software they depend on.
651
+ #
652
+ # @note Much Rake magic ahead!
653
+ #
654
+ # @return void
655
+ def render_tasks
656
+ directory config.package_dir
657
+ directory "pkg"
658
+
659
+ namespace :projects do
660
+ namespace @name do
661
+
662
+ package_types.each do |pkg_type|
663
+ dep_tasks = @dependencies.map {|dep| "software:#{dep}"}
664
+ dep_tasks << config.package_dir
665
+ dep_tasks << "health_check"
666
+
667
+ desc "package #{@name} into a #{pkg_type}"
668
+ task pkg_type => dep_tasks do
669
+ if pkg_type == "makeself"
670
+ run_makeself
671
+ elsif pkg_type == "msi"
672
+ run_msi
673
+ elsif pkg_type == "bff"
674
+ run_bff
675
+ else # pkg_type == "fpm"
676
+ run_fpm(pkg_type)
677
+ end
678
+
679
+ render_metadata(pkg_type)
680
+
681
+ end
682
+ end
683
+
684
+ task "copy" => package_types do
685
+ if OHAI.platform == "windows"
686
+ cp_cmd = "xcopy #{config.package_dir}\\*.msi pkg\\ /Y"
687
+ elsif OHAI.platform == "aix"
688
+ cp_cmd = "cp #{config.package_dir}/*.bff pkg/"
689
+ else
690
+ cp_cmd = "cp #{config.package_dir}/* pkg/"
691
+ end
692
+ shell = Mixlib::ShellOut.new(cp_cmd)
693
+ shell.run_command
694
+ shell.error!
695
+ end
696
+ task "copy" => "pkg"
697
+
698
+ desc "run the health check on the #{@name} install path"
699
+ task "health_check" do
700
+ if OHAI.platform == "windows"
701
+ puts "Skipping health check on windows..."
702
+ else
703
+ # build a list of all whitelist files from all project dependencies
704
+ whitelist_files = library.components.map{|component| component.whitelist_files }.flatten
705
+ Omnibus::HealthCheck.run(install_path, whitelist_files)
706
+ end
707
+ end
708
+ end
709
+
710
+ desc "package #{@name}"
711
+ task @name => "#{@name}:copy"
712
+ end
713
+ end
714
+ end
715
+ end