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.
- checksums.yaml +15 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +9 -0
- data/LICENSE +201 -0
- data/NOTICE +9 -0
- data/README.md +195 -0
- data/Rakefile +7 -0
- data/bin/makeself-header.sh +401 -0
- data/bin/makeself.sh +407 -0
- data/bin/omnibus +11 -0
- data/lib/omnibus.rb +304 -0
- data/lib/omnibus/artifact.rb +151 -0
- data/lib/omnibus/build_version.rb +285 -0
- data/lib/omnibus/builder.rb +328 -0
- data/lib/omnibus/clean_tasks.rb +30 -0
- data/lib/omnibus/cli.rb +35 -0
- data/lib/omnibus/cli/application.rb +140 -0
- data/lib/omnibus/cli/base.rb +118 -0
- data/lib/omnibus/cli/build.rb +62 -0
- data/lib/omnibus/cli/cache.rb +60 -0
- data/lib/omnibus/cli/release.rb +49 -0
- data/lib/omnibus/config.rb +224 -0
- data/lib/omnibus/exceptions.rb +143 -0
- data/lib/omnibus/fetcher.rb +184 -0
- data/lib/omnibus/fetchers.rb +22 -0
- data/lib/omnibus/fetchers/git_fetcher.rb +212 -0
- data/lib/omnibus/fetchers/net_fetcher.rb +193 -0
- data/lib/omnibus/fetchers/path_fetcher.rb +65 -0
- data/lib/omnibus/fetchers/s3_cache_fetcher.rb +42 -0
- data/lib/omnibus/health_check.rb +356 -0
- data/lib/omnibus/library.rb +62 -0
- data/lib/omnibus/overrides.rb +69 -0
- data/lib/omnibus/package_release.rb +163 -0
- data/lib/omnibus/project.rb +715 -0
- data/lib/omnibus/reports.rb +99 -0
- data/lib/omnibus/s3_cacher.rb +138 -0
- data/lib/omnibus/software.rb +441 -0
- data/lib/omnibus/templates/Berksfile.erb +3 -0
- data/lib/omnibus/templates/Gemfile.erb +4 -0
- data/lib/omnibus/templates/README.md.erb +102 -0
- data/lib/omnibus/templates/Vagrantfile.erb +95 -0
- data/lib/omnibus/templates/gitignore.erb +8 -0
- data/lib/omnibus/templates/omnibus.rb.example.erb +5 -0
- data/lib/omnibus/templates/package_scripts/makeselfinst.erb +27 -0
- data/lib/omnibus/templates/package_scripts/postinst.erb +17 -0
- data/lib/omnibus/templates/package_scripts/postrm.erb +9 -0
- data/lib/omnibus/templates/project.rb.erb +21 -0
- data/lib/omnibus/templates/software/c-example.rb.erb +42 -0
- data/lib/omnibus/templates/software/erlang-example.rb.erb +38 -0
- data/lib/omnibus/templates/software/ruby-example.rb.erb +24 -0
- data/lib/omnibus/util.rb +61 -0
- data/lib/omnibus/version.rb +20 -0
- data/omnibus.gemspec +34 -0
- data/spec/artifact_spec.rb +106 -0
- data/spec/build_version_spec.rb +266 -0
- data/spec/data/overrides/bad_line.overrides +3 -0
- data/spec/data/overrides/good.overrides +5 -0
- data/spec/data/overrides/with_dupes.overrides +4 -0
- data/spec/data/software/erchef.rb +40 -0
- data/spec/fetchers/net_fetcher_spec.rb +16 -0
- data/spec/overrides_spec.rb +114 -0
- data/spec/package_release_spec.rb +197 -0
- data/spec/s3_cacher_spec.rb +47 -0
- data/spec/software_spec.rb +85 -0
- data/spec/spec_helper.rb +28 -0
- 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
|