summaryrefslogtreecommitdiff
path: root/omnibus/files/chef-gem/build-chef-gem/gem-install-software-def.rb
blob: ee096b8ed934fb771d119ee2e8ea3c5f940622f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
require "bundler"
require "omnibus"
require_relative "../build-chef-gem"
require_relative "../../../../tasks/gemfile_util"

module BuildChefGem
  class GemInstallSoftwareDef
    def self.define(software, software_filename)
      new(software, software_filename).send(:define)
    end

    include BuildChefGem
    include Omnibus::Logging

    protected

    def initialize(software, software_filename)
      @software = software
      @software_filename = software_filename
    end

    attr_reader :software, :software_filename

    def define
      software.name "#{File.basename(software_filename)[0..-4]}"
      software.default_version gem_version

      # If the source directory for building stuff changes, tell omnibus to
      # de-cache us
      software.source path: File.expand_path("../..", __FILE__)

      # ruby and bundler and friends
      software.dependency "ruby"
      software.dependency "rubygems"

      gem_name = self.gem_name
      gem_version = self.gem_version
      gem_metadata = self.gem_metadata
      lockfile_path = self.lockfile_path

      software.build do
        extend BuildChefGem

        if gem_version == "<skip>"
          if gem_metadata
            block do
              log.info(log_key) { "#{gem_name} has source #{gem_metadata} in #{lockfile_path}. We only cache rubygems.org installs in omnibus to keep things simple. The chef step will build #{gem_name} ..." }
            end
          else
            block do
              log.info(log_key) { "#{gem_name} is not in the #{lockfile_path}. This can happen if your OS doesn't build it, or if chef no longer depends on it. Skipping ..." }
            end
          end
        else
          block do
            log.info(log_key) { "Found version #{gem_version} of #{gem_name} in #{lockfile_path}. Building early to take advantage of omnibus caching ..." }
          end
          gem "install #{gem_name} -v #{gem_version} --no-doc --no-ri --ignore-dependencies --verbose -- #{install_args_for(gem_name)}", env: env
        end
      end
    end

    # Path above omnibus (where Gemfile is)
    def root_path
      File.expand_path("../../../../..", __FILE__)
    end

    def gemfile_path
      # gemfile path could be relative to software filename (and often is)
      @gemfile_path ||= begin
        # Grab the version (and maybe source) from the lockfile so omnibus knows whether
        # to toss the cache or not
        gemfile_path = File.join(root_path, "Gemfile")
        platform_gemfile_path = "#{gemfile_path}.#{Omnibus::Ohai["platform"]}"
        if File.exist?(platform_gemfile_path)
          gemfile_path = platform_gemfile_path
        end
        gemfile_path
      end
    end

    def lockfile_path
      @lockfile_path ||= "#{gemfile_path}.lock"
    end

    def gem_name
      @gem_name ||= begin
        # File must be named chef-<gemname>.rb
        # Will look at chef/Gemfile.lock and install that version of the gem using "gem install"
        # (and only that version)
        if File.basename(software_filename) =~ /^chef-gem-(.+)\.rb$/
          $1
        else
          raise "#{software_filename} must be named chef-<gemname>.rb to build a gem automatically"
        end
      end
    end

    def gem_metadata
      @gem_metadata ||= begin
        selected = GemfileUtil.select_gems(gemfile_path, without_groups: without_groups)
        result = GemfileUtil.locked_gems(lockfile_path, selected)[gem_name]
        if result
          log.info(software.name) { "Using #{gem_name} version #{result[:version]} from #{gemfile_path}" }
          result
        elsif GemfileUtil.locked_gems(lockfile_path, GemfileUtil.select_gems(gemfile_path))[gem_name]
          log.info(software.name) { "#{gem_name} not loaded from #{gemfile_path} because it was only in groups #{without_groups.join(", ")}, skipping" }
          nil
        else
          raise "#{gem_name} not found in #{gemfile_path} or #{lockfile_path}"
        end
      end
    end

    def gem_version
      @gem_version ||= begin
        if gem_metadata && URI(gem_metadata[:source]) == URI("https://rubygems.org/")
          gem_metadata[:version]
        else
          "<skip>"
        end
      end
    end
  end
end