omnibus 1.0.3 → 1.0.4

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.
@@ -20,17 +20,19 @@ module Omnibus
20
20
  # Used to generate the manifest of all software components with versions
21
21
  class Library
22
22
 
23
- def initialize
24
- @projects = []
23
+ attr_reader :components
24
+
25
+ def initialize(project)
25
26
  @components = []
27
+ @project = project
26
28
  end
27
29
 
28
30
  def component_added(component)
29
31
  @components << component
30
32
  end
31
33
 
32
- def version_map(project)
33
- @components.select {|c| c.project == project}.inject({}) {|map, component|
34
+ def version_map
35
+ @components.inject({}) {|map, component|
34
36
  map[component.name] = if component.given_version
35
37
  {:version => component.version,
36
38
  :given_version => component.given_version,
@@ -41,9 +43,9 @@ module Omnibus
41
43
  ## pieces of the omnibus project
42
44
  ## itself, and so don't really fit
43
45
  ## with the concept of overrides
44
- v = {:version => project.build_version}
45
- if project.build_version.respond_to?(:git_sha)
46
- v[:version_guid] = "git:#{project.build_version.git_sha}"
46
+ v = {:version => @project.build_version}
47
+ if @project.build_version.respond_to?(:git_sha)
48
+ v[:version_guid] = "git:#{@project.build_version.git_sha}"
47
49
  end
48
50
  v
49
51
  end
@@ -54,16 +56,6 @@ module Omnibus
54
56
  def select(*args, &block)
55
57
  @components.select(*args, &block)
56
58
  end
57
-
58
-
59
- end
60
-
61
- def self.library
62
- @library ||= Library.new
63
- end
64
-
65
- def self.component_added(*args)
66
- library.component_added(*args)
67
59
  end
68
60
 
69
61
  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
@@ -14,7 +14,10 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
  #
17
+ require 'omnibus/artifact'
17
18
  require 'omnibus/exceptions'
19
+ require 'omnibus/library'
20
+ require 'omnibus/util'
18
21
 
19
22
  module Omnibus
20
23
 
@@ -28,10 +31,12 @@ module Omnibus
28
31
  # @todo: Generate the DSL methods via metaprogramming... they're all so similar
29
32
  class Project
30
33
  include Rake::DSL
34
+ include Util
31
35
 
32
- # @todo Why not just use `nil`?
33
36
  NULL_ARG = Object.new
34
37
 
38
+ attr_reader :library
39
+
35
40
  # Convenience method to initialize a Project from a DSL file.
36
41
  #
37
42
  # @param filename [String] the filename of the Project DSL file to load.
@@ -39,7 +44,6 @@ module Omnibus
39
44
  new(IO.read(filename), filename)
40
45
  end
41
46
 
42
-
43
47
  # Create a new Project from the contents of a DSL file. Prefer
44
48
  # calling {Omnibus::Project#load} instead of using this method
45
49
  # directly.
@@ -51,12 +55,22 @@ module Omnibus
51
55
  #
52
56
  # @todo Remove filename parameter, as it is unused.
53
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
+
54
66
  @exclusions = Array.new
55
67
  @conflicts = Array.new
56
68
  @dependencies = Array.new
57
69
  @runtime_dependencies = Array.new
58
70
  instance_eval(io)
59
71
  validate
72
+
73
+ @library = Omnibus::Library.new(self)
60
74
  render_tasks
61
75
  end
62
76
 
@@ -381,7 +395,7 @@ module Omnibus
381
395
  def dependency?(software)
382
396
  name = if software.respond_to?(:name)
383
397
  software.send(:name)
384
- elsif
398
+ else
385
399
  software
386
400
  end
387
401
  @dependencies.include?(name)
@@ -391,6 +405,66 @@ module Omnibus
391
405
 
392
406
  private
393
407
 
408
+ # An Array of platform data suitable for `Artifact.new`. This will go into
409
+ # metadata generated for the artifact, and be used for the file hierarchy
410
+ # of released packages if the default release scripts are used.
411
+ # @return [Array<String>] platform_shortname, platform_version_for_package,
412
+ # machine architecture.
413
+ def platform_tuple
414
+ [platform_shortname, platform_version_for_package, machine]
415
+ end
416
+
417
+ # Platform version to be used in package metadata. For rhel, the minor
418
+ # version is removed, e.g., "5.6" becomes "5". For all other platforms,
419
+ # this is just the platform_version.
420
+ # @return [String] the platform version
421
+ def platform_version_for_package
422
+ if platform == "rhel"
423
+ platform_version[/([\d]+)\..+/, 1]
424
+ else
425
+ platform_version
426
+ end
427
+ end
428
+
429
+ # Platform name to be used when creating metadata for the artifact.
430
+ # rhel/centos become "el", all others are just platform
431
+ # @return [String] the platform family short name
432
+ def platform_shortname
433
+ if platform_family == "rhel"
434
+ "el"
435
+ else
436
+ platform
437
+ end
438
+ end
439
+
440
+ def render_metadata(pkg_type)
441
+ basename = output_package(pkg_type)
442
+ pkg_path = "#{config.package_dir}/#{basename}"
443
+ artifact = Artifact.new(pkg_path, [ platform_tuple ], :version => build_version)
444
+ metadata = artifact.flat_metadata
445
+ File.open("#{pkg_path}.metadata.json", "w+") do |f|
446
+ f.print(JSON.pretty_generate(metadata))
447
+ end
448
+ end
449
+
450
+ # The basename of the resulting package file.
451
+ # @return [String] the basename of the package file
452
+ def output_package(pkg_type)
453
+ case pkg_type
454
+ when "makeself"
455
+ "#{package_name}-#{build_version}_#{iteration}.sh"
456
+ when "msi"
457
+ "#{package_name}-#{build_version}-#{iteration}.msi"
458
+ else # fpm
459
+ require "fpm/package/#{pkg_type}"
460
+ pkg = FPM::Package.types[pkg_type].new
461
+ pkg.version = build_version
462
+ pkg.name = package_name
463
+ pkg.iteration = iteration
464
+ pkg.to_s
465
+ end
466
+ end
467
+
394
468
  # The command to generate an MSI package on Windows platforms.
395
469
  #
396
470
  # Does not execute the command, only assembles it.
@@ -411,7 +485,7 @@ module Omnibus
411
485
  "-loc #{install_path}\\msi-tmp\\#{package_name}-en-us.wxl",
412
486
  "#{install_path}\\msi-tmp\\#{package_name}-Files.wixobj",
413
487
  "#{install_path}\\msi-tmp\\#{package_name}.wixobj",
414
- "-out #{config.package_dir}\\#{package_name}-#{build_version}-#{iteration}.msi"]
488
+ "-out #{config.package_dir}\\#{output_package("msi")}"]
415
489
 
416
490
  # Don't care about the 204 return code from light.exe since it's
417
491
  # about some expected warnings...
@@ -438,8 +512,8 @@ module Omnibus
438
512
  "-t #{pkg_type}",
439
513
  "-v #{build_version}",
440
514
  "-n #{package_name}",
515
+ "-p #{output_package(pkg_type)}",
441
516
  "--iteration #{iteration}",
442
- install_path,
443
517
  "-m '#{maintainer}'",
444
518
  "--description '#{description}'",
445
519
  "--url #{homepage}"]
@@ -468,6 +542,7 @@ module Omnibus
468
542
  end
469
543
 
470
544
  command_and_opts << " --replaces #{@replaces}" if @replaces
545
+ command_and_opts << install_path
471
546
  command_and_opts
472
547
  end
473
548
 
@@ -476,13 +551,63 @@ module Omnibus
476
551
  command_and_opts = [ File.expand_path(File.join(Omnibus.source_root, "bin", "makeself.sh")),
477
552
  "--gzip",
478
553
  install_path,
479
- "#{package_name}-#{build_version}_#{iteration}.sh",
554
+ output_package("makeself"),
480
555
  "'The full stack of #{@name}'"
481
556
  ]
482
557
  command_and_opts << "./makeselfinst" if File.exists?("#{package_scripts_path}/makeselfinst")
483
558
  command_and_opts
484
559
  end
485
560
 
561
+ # Runs the makeself commands to make a self extracting archive package.
562
+ # As a (necessary) side-effect, sets
563
+ # @return void
564
+ def run_makeself
565
+ package_commands = []
566
+ # copy the makeself installer into package
567
+ if File.exists?("#{package_scripts_path}/makeselfinst")
568
+ package_commands << "cp #{package_scripts_path}/makeselfinst #{install_path}/"
569
+ end
570
+
571
+ # run the makeself program
572
+ package_commands << makeself_command.join(" ")
573
+
574
+ # rm the makeself installer (for incremental builds)
575
+ package_commands << "rm -f #{install_path}/makeselfinst"
576
+ package_commands.each {|cmd| run_package_command(cmd) }
577
+ end
578
+
579
+ # Runs the necessary command to make an MSI. As a side-effect, sets `output_package`
580
+ # @return void
581
+ def run_msi
582
+ run_package_command(msi_command)
583
+ end
584
+
585
+ # Runs the necessary command to make a package with fpm. As a side-effect,
586
+ # sets `output_package`
587
+ # @return void
588
+ def run_fpm(pkg_type)
589
+ run_package_command(fpm_command(pkg_type).join(" "))
590
+ end
591
+
592
+ # Executes the given command via mixlib-shellout.
593
+ # @return [Mixlib::ShellOut] returns the underlying Mixlib::ShellOut
594
+ # object, so the caller can inspect the stdout and stderr.
595
+ def run_package_command(cmd)
596
+ cmd_options = {
597
+ :timeout => 3600,
598
+ :cwd => config.package_dir
599
+ }
600
+
601
+ if cmd.is_a?(Array)
602
+ command = cmd[0]
603
+ cmd_options.merge!(cmd[1])
604
+ else
605
+ command = cmd
606
+ end
607
+
608
+ shellout!(command, cmd_options)
609
+ end
610
+
486
611
  # Dynamically generate Rake tasks to build projects and all the software they depend on.
487
612
  #
488
613
  # @note Much Rake magic ahead!
@@ -493,81 +618,52 @@ module Omnibus
493
618
  directory "pkg"
494
619
 
495
620
  namespace :projects do
621
+ namespace @name do
496
622
 
497
- package_types.each do |pkg_type|
498
- namespace @name do
499
- desc "package #{@name} into a #{pkg_type}"
500
- task pkg_type => (@dependencies.map {|dep| "software:#{dep}"}) do
623
+ package_types.each do |pkg_type|
624
+ dep_tasks = @dependencies.map {|dep| "software:#{dep}"}
625
+ dep_tasks << config.package_dir
626
+ dep_tasks << "health_check"
501
627
 
502
- package_commands = []
628
+ desc "package #{@name} into a #{pkg_type}"
629
+ task pkg_type => dep_tasks do
503
630
  if pkg_type == "makeself"
504
- # copy the makeself installer into package
505
- if File.exists?("#{package_scripts_path}/makeselfinst")
506
- package_commands << "cp #{package_scripts_path}/makeselfinst #{install_path}/"
507
- end
508
-
509
- # run the makeself program
510
- package_commands << makeself_command.join(" ")
511
-
512
- # rm the makeself installer (for incremental builds)
513
- package_commands << "rm -f #{install_path}/makeselfinst"
631
+ run_makeself
514
632
  elsif pkg_type == "msi"
515
- package_commands << msi_command
633
+ run_msi
516
634
  else # pkg_type == "fpm"
517
- package_commands << fpm_command(pkg_type).join(" ")
635
+ run_fpm(pkg_type)
518
636
  end
519
637
 
520
- # run the commands
521
- package_commands.each do |cmd|
522
- cmd_options = {
523
- :live_stream => STDOUT,
524
- :timeout => 3600,
525
- :cwd => config.package_dir
526
- }
527
-
528
- if cmd.is_a?(Array)
529
- command = cmd[0]
530
- cmd_options.merge!(cmd[1])
531
- else
532
- command = cmd
533
- end
534
-
535
- shell = Mixlib::ShellOut.new(command, cmd_options)
536
- shell.run_command
537
- shell.error!
538
- end
539
- end
638
+ render_metadata(pkg_type)
540
639
 
541
- # TODO: why aren't these dependencies just added in at the
542
- # initial creation of the 'pkg_type' task?
543
- task pkg_type => config.package_dir
544
- task pkg_type => "#{@name}:health_check"
640
+ end
545
641
  end
546
- end
547
642
 
548
- task "#{@name}:copy" => (package_types.map {|pkg_type| "#{@name}:#{pkg_type}"}) do
549
- if OHAI.platform == "windows"
550
- cp_cmd = "xcopy #{config.package_dir}\\*.msi pkg\\ /Y"
551
- else
552
- cp_cmd = "cp #{config.package_dir}/* pkg/"
643
+ task "copy" => package_types do
644
+ if OHAI.platform == "windows"
645
+ cp_cmd = "xcopy #{config.package_dir}\\*.msi pkg\\ /Y"
646
+ else
647
+ cp_cmd = "cp #{config.package_dir}/* pkg/"
648
+ end
649
+ shell = Mixlib::ShellOut.new(cp_cmd)
650
+ shell.run_command
651
+ shell.error!
652
+ end
653
+ task "copy" => "pkg"
654
+
655
+ desc "run the health check on the #{@name} install path"
656
+ task "health_check" do
657
+ if OHAI.platform == "windows"
658
+ puts "Skipping health check on windows..."
659
+ else
660
+ Omnibus::HealthCheck.run(install_path)
661
+ end
553
662
  end
554
- shell = Mixlib::ShellOut.new(cp_cmd)
555
- shell.run_command
556
- shell.error!
557
663
  end
558
- task "#{@name}:copy" => "pkg"
559
664
 
560
665
  desc "package #{@name}"
561
666
  task @name => "#{@name}:copy"
562
-
563
- desc "run the health check on the #{@name} install path"
564
- task "#{@name}:health_check" do
565
- if OHAI.platform == "windows"
566
- puts "Skipping health check on windows..."
567
- else
568
- Omnibus::HealthCheck.run(install_path)
569
- end
570
- end
571
667
  end
572
668
  end
573
669
  end