omnibus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +7 -0
  5. data/CHANGELOG.md +3 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +201 -0
  8. data/NOTICE +9 -0
  9. data/README.md +186 -0
  10. data/Rakefile +7 -0
  11. data/bin/makeself-header.sh +401 -0
  12. data/bin/makeself.sh +407 -0
  13. data/bin/omnibus +11 -0
  14. data/lib/omnibus.rb +280 -0
  15. data/lib/omnibus/build_version.rb +281 -0
  16. data/lib/omnibus/builder.rb +323 -0
  17. data/lib/omnibus/clean_tasks.rb +30 -0
  18. data/lib/omnibus/cli.rb +35 -0
  19. data/lib/omnibus/cli/application.rb +136 -0
  20. data/lib/omnibus/cli/base.rb +112 -0
  21. data/lib/omnibus/cli/build.rb +66 -0
  22. data/lib/omnibus/cli/cache.rb +60 -0
  23. data/lib/omnibus/config.rb +186 -0
  24. data/lib/omnibus/exceptions.rb +54 -0
  25. data/lib/omnibus/fetcher.rb +184 -0
  26. data/lib/omnibus/fetchers.rb +22 -0
  27. data/lib/omnibus/fetchers/git_fetcher.rb +212 -0
  28. data/lib/omnibus/fetchers/net_fetcher.rb +191 -0
  29. data/lib/omnibus/fetchers/path_fetcher.rb +65 -0
  30. data/lib/omnibus/fetchers/s3_cache_fetcher.rb +42 -0
  31. data/lib/omnibus/health_check.rb +260 -0
  32. data/lib/omnibus/library.rb +70 -0
  33. data/lib/omnibus/overrides.rb +69 -0
  34. data/lib/omnibus/project.rb +566 -0
  35. data/lib/omnibus/reports.rb +99 -0
  36. data/lib/omnibus/s3_cacher.rb +136 -0
  37. data/lib/omnibus/software.rb +430 -0
  38. data/lib/omnibus/templates/Berksfile.erb +3 -0
  39. data/lib/omnibus/templates/Gemfile.erb +4 -0
  40. data/lib/omnibus/templates/README.md.erb +102 -0
  41. data/lib/omnibus/templates/Vagrantfile.erb +95 -0
  42. data/lib/omnibus/templates/gitignore.erb +8 -0
  43. data/lib/omnibus/templates/omnibus.rb.example.erb +5 -0
  44. data/lib/omnibus/templates/package_scripts/makeselfinst.erb +27 -0
  45. data/lib/omnibus/templates/package_scripts/postinst.erb +17 -0
  46. data/lib/omnibus/templates/package_scripts/postrm.erb +9 -0
  47. data/lib/omnibus/templates/project.rb.erb +21 -0
  48. data/lib/omnibus/templates/software/c-example.rb.erb +42 -0
  49. data/lib/omnibus/templates/software/erlang-example.rb.erb +38 -0
  50. data/lib/omnibus/templates/software/ruby-example.rb.erb +24 -0
  51. data/lib/omnibus/util.rb +61 -0
  52. data/lib/omnibus/version.rb +20 -0
  53. data/omnibus.gemspec +34 -0
  54. data/spec/build_version_spec.rb +228 -0
  55. data/spec/data/overrides/bad_line.overrides +3 -0
  56. data/spec/data/overrides/good.overrides +5 -0
  57. data/spec/data/overrides/with_dupes.overrides +4 -0
  58. data/spec/data/software/erchef.rb +40 -0
  59. data/spec/overrides_spec.rb +114 -0
  60. data/spec/software_spec.rb +71 -0
  61. data/spec/spec_helper.rb +28 -0
  62. metadata +239 -0
@@ -0,0 +1,323 @@
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
+ require 'forwardable'
19
+
20
+ module Omnibus
21
+ class Builder
22
+
23
+ # Proxies method calls to either a Builder object or the Software that the
24
+ # builder belongs to. Provides compatibility with our DSL where we never
25
+ # yield objects to blocks and hopefully hides some of the confusion that
26
+ # can arise from instance_eval.
27
+ class DSLProxy
28
+ extend Forwardable
29
+
30
+ # @todo def_delegators :@builder, :patch, :command, :ruby, ...
31
+
32
+ def_delegator :@builder, :patch
33
+ def_delegator :@builder, :command
34
+ def_delegator :@builder, :ruby
35
+ def_delegator :@builder, :gem
36
+ def_delegator :@builder, :bundle
37
+ def_delegator :@builder, :rake
38
+ def_delegator :@builder, :block
39
+ def_delegator :@builder, :name
40
+
41
+ def initialize(builder, software)
42
+ @builder, @software = builder, software
43
+ end
44
+
45
+ def eval_block(&block)
46
+ instance_eval(&block)
47
+ end
48
+
49
+ def respond_to?(method)
50
+ super || @software.respond_to?(method)
51
+ end
52
+
53
+ def methods
54
+ super | @software.methods
55
+ end
56
+
57
+ def method_missing(method_name, *args, &block)
58
+ if @software.respond_to?(method_name)
59
+ @software.send(method_name, *args, &block)
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+
68
+ # @todo code duplication with {Fetcher::ErrorReporter}
69
+ class ErrorReporter
70
+
71
+ # @todo fetcher isn't even used
72
+ def initialize(error, fetcher)
73
+ @error, @fetcher = error, fetcher
74
+ end
75
+
76
+ # @todo this isn't necessary
77
+ def e
78
+ @error
79
+ end
80
+
81
+ def explain(why)
82
+ $stderr.puts "* " * 40
83
+ $stderr.puts why
84
+ $stderr.puts "Exception:"
85
+ $stderr.puts indent("#{e.class}: #{e.message.strip}", 2)
86
+ Array(e.backtrace).each {|l| $stderr.puts indent(l, 4) }
87
+ $stderr.puts "* " * 40
88
+ end
89
+
90
+ private
91
+
92
+ def indent(string, n)
93
+ string.split("\n").map {|l| " ".rjust(n) << l }.join("\n")
94
+ end
95
+
96
+ end
97
+
98
+ # @todo Look at using Bundler.with_clean_env{ ... } instead
99
+ BUNDLER_BUSTER = { "RUBYOPT" => nil,
100
+ "BUNDLE_BIN_PATH" => nil,
101
+ "BUNDLE_GEMFILE" => nil,
102
+ "GEM_PATH" => nil,
103
+ "GEM_HOME" => nil }
104
+
105
+ attr_reader :build_commands
106
+
107
+ def initialize(software, &block)
108
+ @software = software
109
+ @build_commands = []
110
+ @dsl_proxy = DSLProxy.new(self, software)
111
+ @dsl_proxy.eval_block(&block) if block_given?
112
+ end
113
+
114
+ def name
115
+ @software.name
116
+ end
117
+
118
+ def command(*args)
119
+ @build_commands << args
120
+ end
121
+
122
+ def patch(*args)
123
+ args = args.dup.pop
124
+
125
+ # we'll search for a patch file in the project root AND
126
+ # the omnibus-software gem
127
+ candidate_roots = [Omnibus.project_root]
128
+ candidate_roots << Omnibus.omnibus_software_root if Omnibus.omnibus_software_root
129
+
130
+ candidate_paths = candidate_roots.map do |root|
131
+ File.expand_path("#{root}/config/patches/#{name}/#{args[:source]}")
132
+ end
133
+
134
+ source = candidate_paths.find{|path| File.exists?(path) }
135
+
136
+ plevel = args[:plevel] || 1
137
+ if args[:target]
138
+ target = File.expand_path("#{project_dir}/#{args[:target]}")
139
+ @build_commands <<
140
+ "cat #{source} | patch -p#{plevel} #{target}"
141
+ else
142
+ @build_commands <<
143
+ "patch -d #{project_dir} -p#{plevel} -i #{source}"
144
+ end
145
+ end
146
+
147
+ # @todo all these ruby commands (ruby, gem, bundle, rake) could
148
+ # all be collapsed into a single underlying implementation, since
149
+ # they all just differ on the executable being called
150
+ def ruby(*args)
151
+ @build_commands << bundle_bust(*prepend_cmd("#{install_dir}/embedded/bin/ruby", *args))
152
+ end
153
+
154
+ def gem(*args)
155
+ @build_commands << bundle_bust(*prepend_cmd("#{install_dir}/embedded/bin/gem", *args))
156
+ end
157
+
158
+ def bundle(*args)
159
+ @build_commands << bundle_bust(*prepend_cmd("#{install_dir}/embedded/bin/bundle", *args))
160
+ end
161
+
162
+ def rake(*args)
163
+ @build_commands << bundle_bust(*prepend_cmd("#{install_dir}/embedded/bin/rake", *args))
164
+ end
165
+
166
+ def block(&rb_block)
167
+ @build_commands << rb_block
168
+ end
169
+
170
+ def project_dir
171
+ @software.project_dir
172
+ end
173
+
174
+ def install_dir
175
+ @software.install_dir
176
+ end
177
+
178
+ def log(message)
179
+ puts "[builder:#{name}] #{message}"
180
+ end
181
+
182
+ def build
183
+ log "building #{name}"
184
+ time_it("#{name} build") do
185
+ @build_commands.each do |cmd|
186
+ execute(cmd)
187
+ end
188
+ end
189
+ end
190
+
191
+ def execute(cmd)
192
+ case cmd
193
+ when Proc
194
+ execute_proc(cmd)
195
+ else
196
+ execute_sh(cmd)
197
+ end
198
+ end
199
+
200
+ private
201
+
202
+ def execute_proc(cmd)
203
+ cmd.call
204
+ rescue Exception => e
205
+ # In Ruby 1.9, Procs have a #source_location method with file/line info.
206
+ # Too bad we can't use it :(
207
+ ErrorReporter.new(e, self).explain("Failed to build #{name} while running ruby block build step")
208
+ raise
209
+ end
210
+
211
+ def execute_sh(cmd)
212
+ retries ||= 0
213
+ shell = nil
214
+ cmd_args = Array(cmd)
215
+ options = {
216
+ :cwd => project_dir,
217
+ :timeout => 5400
218
+ }
219
+ options[:live_stream] = STDOUT if ENV['DEBUG']
220
+ if cmd_args.last.is_a? Hash
221
+ cmd_options = cmd_args.last
222
+ cmd_args[cmd_args.size - 1] = options.merge(cmd_options)
223
+ else
224
+ cmd_args << options
225
+ end
226
+
227
+ cmd_string = cmd_args[0..-2].join(' ')
228
+ cmd_opts_for_display = to_kv_str(cmd_args.last)
229
+
230
+ log "Executing: `#{cmd_string}` with #{cmd_opts_for_display}"
231
+
232
+ shell = Mixlib::ShellOut.new(*cmd)
233
+ shell.environment["HOME"] = "/tmp" unless ENV["HOME"]
234
+
235
+ cmd_name = cmd_string.split(/\s+/).first
236
+ time_it("#{cmd_name} command") do
237
+ shell.run_command
238
+ shell.error!
239
+ end
240
+ rescue Exception => e
241
+ # Getting lots of errors from github, particularly with erlang/rebar
242
+ # projects fetching tons of deps via git all the time. This isn't a
243
+ # particularly elegant way to solve that problem. But it should work.
244
+ if retries >= 3
245
+ ErrorReporter.new(e, self).explain("Failed to build #{name} while running `#{cmd_string}` with #{cmd_opts_for_display}")
246
+ raise
247
+ else
248
+ time_to_sleep = 5 * (2 ** retries)
249
+ retries +=1
250
+ log "Failed to execute cmd #{cmd} #{retries} time(s). Retrying in #{time_to_sleep}s."
251
+ sleep(time_to_sleep)
252
+ retry
253
+ end
254
+ end
255
+
256
+ def prepend_cmd(str, *cmd_args)
257
+ if cmd_args.size == 1
258
+ # command as a string, no opts
259
+ "#{str} #{cmd_args.first}"
260
+ elsif cmd_args.size == 2 && cmd_args.last.is_a?(Hash)
261
+ # command as a string w/ opts
262
+ ["#{str} #{cmd_args.first}", cmd_args.last]
263
+ elsif cmd_args.size == 0
264
+ raise ArgumentError, "I don't even"
265
+ else
266
+ # cmd given as argv array
267
+ cmd_args.dup.unshift(str)
268
+ end
269
+ end
270
+
271
+ def bundle_bust(*cmd_args)
272
+ if cmd_args.last.is_a?(Hash)
273
+ cmd_args = cmd_args.dup
274
+ cmd_opts = cmd_args.pop.dup
275
+ cmd_opts[:env] = cmd_opts[:env] ? BUNDLER_BUSTER.merge(cmd_opts[:env]) : BUNDLER_BUSTER
276
+ cmd_args << cmd_opts
277
+ else
278
+ cmd_args << {:env => BUNDLER_BUSTER}
279
+ end
280
+ end
281
+
282
+
283
+ def time_it(what)
284
+ start = Time.now
285
+ yield
286
+ rescue Exception
287
+ elapsed = Time.now - start
288
+ log "#{what} failed, #{elapsed.to_f}s"
289
+ raise
290
+ else
291
+ elapsed = Time.now - start
292
+ log "#{what} succeeded, #{elapsed.to_f}s"
293
+ end
294
+
295
+ # Convert a hash to a string in the form `key=value`. It should work with
296
+ # whatever input is given but is designed to make the options to ShellOut
297
+ # look nice.
298
+ def to_kv_str(hash, join_str=",")
299
+ hash.inject([]) do |kv_pair_strs, (k,v)|
300
+ val_str = case v
301
+ when Hash
302
+ %Q["#{to_kv_str(v, " ")}"]
303
+ else
304
+ v.to_s
305
+ end
306
+ kv_pair_strs << "#{k}=#{val_str}"
307
+ end.join(join_str)
308
+ end
309
+
310
+ end
311
+
312
+ # @todo What's the point of this class? Can we not just detect that
313
+ # there are no commands in {Omnibus::Builder#build} and output the
314
+ # appropriate message? Seems like a lot of extra ceremony.
315
+ class NullBuilder < Builder
316
+
317
+ def build
318
+ log "Nothing to build for #{name}"
319
+ end
320
+
321
+ end
322
+
323
+ end
@@ -0,0 +1,30 @@
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
+ # Adds source and build artifacts to Rake's CLEAN list, and install
19
+ # and cache artifacts to its CLOBBER list, ensuring that the `clean`
20
+ # and `clobber` Rake tasks do the right thing for Omnibus projects.
21
+ #
22
+ # @see https://github.com/jimweirich/rake/blob/master/lib/rake/clean.rb
23
+
24
+ require 'rake/clean'
25
+
26
+ ::CLEAN.include("#{Omnibus.config.source_dir}/**/*",
27
+ "#{Omnibus.config.build_dir}/**/*")
28
+
29
+ ::CLOBBER.include("#{Omnibus.config.cache_dir}/**/*",
30
+ "#{Omnibus.config.package_dir}/**/*")
@@ -0,0 +1,35 @@
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
+ require 'omnibus/cli/application'
19
+ require 'omnibus/cli/base'
20
+ require 'omnibus/cli/build'
21
+
22
+ module Omnibus
23
+ module CLI
24
+
25
+ class Error < StandardError
26
+ attr_reader :original
27
+
28
+ def initialize(msg, original=nil)
29
+ super(msg)
30
+ @original = original
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,136 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2013 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
+ require 'omnibus'
19
+ require 'omnibus/cli/base'
20
+ require 'omnibus/cli/build'
21
+ require 'omnibus/cli/cache'
22
+
23
+ module Omnibus
24
+ module CLI
25
+ class Application < Base
26
+
27
+ method_option :purge,
28
+ :type => :boolean,
29
+ :default => false,
30
+ :desc => "Remove ALL files generated during the build (including packages)."
31
+ method_option :path,
32
+ :aliases => [:p],
33
+ :type => :string,
34
+ :default => Dir.pwd,
35
+ :desc => "Path to the Omnibus project root."
36
+ desc "clean PROJECT", "Remove temporary files generated during the build process."
37
+ def clean(project_name)
38
+ project = load_project!(project_name)
39
+
40
+ deletion_list = []
41
+ deletion_list << Dir.glob("#{Omnibus.config.source_dir}/**/*")
42
+ deletion_list << Dir.glob("#{Omnibus.config.build_dir}/**/*")
43
+
44
+ if options[:purge]
45
+ deletion_list << Dir.glob("#{Omnibus.config.package_dir}/**/*")
46
+ deletion_list << Dir.glob("#{Omnibus.config.cache_dir}/**/*")
47
+ deletion_list << project.install_path if File.exist?(project.install_path)
48
+ end
49
+
50
+ deletion_list.flatten!
51
+ deletion_list.each{|f| remove_file(f) }
52
+ end
53
+
54
+ desc "project PROJECT", "Creates a skeletal Omnibus project"
55
+ def project(name)
56
+ name = name.chomp("/") # remove trailing slash if present
57
+ target = File.join(Dir.pwd, "omnibus-#{name}")
58
+ install_path = File.join("/opt", name)
59
+ opts = {
60
+ :name => name,
61
+ :install_path => install_path
62
+ }
63
+
64
+ # core project files
65
+ template(File.join("Gemfile.erb"), File.join(target, "Gemfile"), opts)
66
+ template(File.join("gitignore.erb"), File.join(target, ".gitignore"), opts)
67
+ template(File.join("README.md.erb"), File.join(target, "README.md"), opts)
68
+ template(File.join("omnibus.rb.example.erb"), File.join(target, "omnibus.rb.example"), opts)
69
+
70
+ # project definition
71
+ template(File.join("project.rb.erb"), File.join(target, "config", "projects", "#{name}.rb"), opts)
72
+
73
+ # example software definitions
74
+ config_software = File.join(target, "config", "software")
75
+ template(File.join("software", "c-example.rb.erb"),
76
+ File.join(config_software, "c-example.rb"), opts)
77
+ template(File.join("software", "erlang-example.rb.erb"),
78
+ File.join(config_software, "erlang-example.rb"), opts)
79
+ template(File.join("software", "ruby-example.rb.erb"),
80
+ File.join(config_software, "ruby-example.rb"), opts)
81
+
82
+ # Vagrant build lab
83
+ template(File.join("Berksfile.erb"), File.join(target, "Berksfile"), opts)
84
+ template(File.join("Vagrantfile.erb"), File.join(target, "Vagrantfile"), opts)
85
+
86
+ # render out stub packge scripts
87
+ %w{ makeselfinst postinst postrm }.each do |package_script|
88
+ script_path = File.join(target, "package-scripts", name, package_script)
89
+ template_path = File.join("package_scripts", "#{package_script}.erb")
90
+ # render the package script
91
+ template(template_path, script_path, opts)
92
+ # ensure the package script is executable
93
+ FileUtils.chmod(0755, script_path)
94
+ end
95
+ end
96
+
97
+ desc "version", "Display version information"
98
+ def version
99
+ say("Omnibus: #{Omnibus::VERSION}", :yellow)
100
+ end
101
+
102
+ ###########################################################################
103
+ # Subcommands
104
+ ###########################################################################
105
+
106
+ desc "build [COMMAND]", "Perform build-related tasks"
107
+ subcommand "build", Omnibus::CLI::Build
108
+
109
+ desc "cache [COMMAND]", "Perform cache management tasks"
110
+ subcommand "cache", Omnibus::CLI::Cache
111
+
112
+ ###########################################################################
113
+ # Class Methods
114
+ ###########################################################################
115
+
116
+ # Override start so we can catch and process any exceptions bubbling up
117
+ def self.start(*args)
118
+ begin
119
+ super
120
+ rescue => e
121
+ error_msg = "Something went wrong...the Omnibus just ran off the road!"
122
+ error_msg << "\n\nError raised was:\n\n\t#{$!}"
123
+ error_msg << "\n\nBacktrace:\n\n\t#{e.backtrace.join("\n\t")}"
124
+ if e.respond_to?(:original) && e.original
125
+ error_msg << "\n\nOriginal Error:\n\n\t#{e.original}"
126
+ error_msg << "\n\nOriginal Backtrace:\n\n\t#{e.original.backtrace.join("\n\t")}"
127
+ end
128
+ # TODO - we need a proper UI class
129
+ Thor::Base.shell.new.say(error_msg, :red)
130
+ exit 1
131
+ end
132
+ end
133
+
134
+ end
135
+ end
136
+ end