Class: Lono::AppFile::Build::LambdaLayer::RubyPackager

Inherits:
Object
  • Object
show all
Defined in:
lib/lono/app_file/build/lambda_layer/ruby_packager.rb

Overview

Based on jets

Constant Summary collapse

@@rsync_installed =
false

Instance Method Summary collapse

Constructor Details

#initialize(blueprint, registry_item) ⇒ RubyPackager

Returns a new instance of RubyPackager.



6
7
8
9
10
11
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 6

def initialize(blueprint, registry_item)
  @blueprint, @registry_item = blueprint, registry_item

  @registry_name = @registry_item.name.sub(/\/$/,'')
  @app_root = "#{Lono.blueprint_root}/app/files/#{@registry_name}"
end

Instance Method Details

#buildObject



13
14
15
16
17
18
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 13

def build
  return unless gemfile_exist?

  bundle_install
  package
end

#build_areaObject



144
145
146
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 144

def build_area
  "#{Lono.config.output_path}/#{@blueprint}/lambda_layers/#{@registry_name}"
end

#bundle_installObject

Installs gems on the current target system: both compiled and non-compiled. If user is on a macosx machine, macosx gems will be installed. If user is on a linux machine, linux gems will be installed.

Copies Gemfile* to /tmp/jets/demo/cache folder and installs gems with bundle install from there.

We take the time to copy Gemfile and bundle into a separate directory because it gets left around to act as a ‘cache’. So, when the builds the project gets built again not all the gems from get installed from the beginning.



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
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 70

def bundle_install
  puts "Bundling: running bundle install in cache area: #{cache_area}."

  rsync(output_area, cache_area)

  # Uncomment out to always remove the cache/vendor/gems to debug
  # FileUtils.rm_rf("#{cache_area}/vendor/gems")

  # Remove .bundle folder so .bundle/config doesnt affect how gems are packages
  # Not using BUNDLE_IGNORE_CONFIG=1 to allow home ~/.bundle/config to affect bundling though.
  # This is useful if you have private gems sources that require authentication. Example:
  #
  #    bundle config gems.myprivatesource.com user:pass
  #

  FileUtils.rm_rf("#{cache_area}/.bundle")
  require "bundler" # dynamically require bundler so user can use any bundler
  setup_bundle_config(cache_area)
  Bundler.with_unbundled_env do
    sh "cd #{cache_area} && env bundle install"
  end

  remove_bundled_with("#{cache_area}/Gemfile.lock")

  # Copy the Gemfile.lock back to the project in case it was updated.
  # For example we add the jets-rails to the Gemfile.
  copy_back_gemfile_lock

  puts 'Bundle install success.'
end

#cache_areaObject



148
149
150
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 148

def cache_area
  "#{build_area}/cache"
end

#check_rsync_installed!Object



183
184
185
186
187
188
189
190
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 183

def check_rsync_installed!
  return if @@rsync_installed # only check once
  if system "type rsync > /dev/null 2>&1"
    @@rsync_installed = true
  else
    raise "ERROR: Rsync is required. Rsync does not seem to be installed.".color(:red)
  end
end

#consolidate_gems_to_optObject

Also restructure the folder from:

vendor/gems/ruby/2.5.0

To:

ruby/gems/2.5.0

For Lambda Layer structure



33
34
35
36
37
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 33

def consolidate_gems_to_opt
  src = "#{cache_area}/vendor/gems/ruby/#{ruby_folder}"
  dest = "#{opt_area}/ruby/gems/#{ruby_folder}"
  rsync_and_link(src, dest)
end

#copy_back_gemfile_lockObject



123
124
125
126
127
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 123

def copy_back_gemfile_lock
  src = "#{cache_area}/Gemfile.lock"
  dest = "#{@app_root}/Gemfile.lock"
  FileUtils.cp(src, dest)
end

#gemfile_exist?Boolean

Returns:

  • (Boolean)


156
157
158
159
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 156

def gemfile_exist?
  gemfile_path = "#{@app_root}/Gemfile"
  File.exist?(gemfile_path)
end

#opt_areaObject



152
153
154
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 152

def opt_area
  "#{build_area}/opt"
end

#output_areaObject



140
141
142
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 140

def output_area
  "#{Lono.config.output_path}/#{@blueprint}/files/#{@registry_name}"
end

#packageObject

We consolidate all gems to opt



21
22
23
24
25
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 21

def package
  setup_bundle_config(output_area)
  # copy_cache_gems # TODO: might not need this cache
  consolidate_gems_to_opt
end

#remove_bundled_with(gemfile_lock) ⇒ Object

Remove the BUNDLED WITH line since we don’t control the bundler gem version on AWS Lambda And this can cause issues with require ‘bundler/setup’



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 103

def remove_bundled_with(gemfile_lock)
  lines = IO.readlines(gemfile_lock)

  # amount is the number of lines to remove
  new_lines, capture, count, amount = [], true, 0, 2
  lines.each do |l|
    capture = false if l.include?('BUNDLED WITH')
    if capture
      new_lines << l
    end
    if capture == false
      count += 1
      capture = count > amount # renable capture
    end
  end

  content = new_lines.join('')
  IO.write(gemfile_lock, content)
end

#rsync(src, dest) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 170

def rsync(src, dest)
  # Using FileUtils.cp_r doesnt work if there are special files like socket files in the src dir.
  # Instead of using this hack https://bugs.ruby-lang.org/issues/10104
  # Using rsync to perform the copy.
  src.chop! if src.ends_with?('/')
  dest.chop! if dest.ends_with?('/')
  check_rsync_installed!
  # Ensures required trailing slashes
  FileUtils.mkdir_p(File.dirname(dest))
  sh "rsync -a --links --no-specials --no-devices #{Shellwords.escape(src)}/ #{Shellwords.escape(dest)}/"
end


39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 39

def rsync_and_link(src, dest)
  FileUtils.mkdir_p(dest)
  rsync(src, dest)

  # create symlink in output path not the cache path
  symlink_dest = "#{output_area}/vendor/gems/ruby/#{ruby_folder}"
  puts "symlink_dest #{symlink_dest}"
  FileUtils.rm_rf(symlink_dest) # blow away original 2.5.0 folder

  # Create symlink that will point to the gems in the Lambda Layer:
  #   stage/opt/ruby/gems/2.5.0 -> /opt/ruby/gems/2.5.0
  FileUtils.mkdir_p(File.dirname(symlink_dest))
  FileUtils.ln_sf("/opt/ruby/gems/#{ruby_folder}", symlink_dest)
end

#ruby_folderObject



54
55
56
57
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 54

def ruby_folder
  major, minor, _ = RUBY_VERSION.split('.')
  [major, minor, '0'].join('.') # 2.5.1 => 2.5.0
end

#setup_bundle_config(dir) ⇒ Object



129
130
131
132
133
134
135
136
137
138
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 129

def setup_bundle_config(dir)
  text ="---\nBUNDLE_PATH: \"vendor/gems\"\nBUNDLE_WITHOUT: \"development:test\"\n"
  bundle_config = "#{dir}/.bundle/config"
  FileUtils.mkdir_p(File.dirname(bundle_config))
  IO.write(bundle_config, text)
end

#sh(command) ⇒ Object



161
162
163
164
165
166
167
168
# File 'lib/lono/app_file/build/lambda_layer/ruby_packager.rb', line 161

def sh(command)
  puts "=> #{command}"
  out = `#{command}`
  puts out if ENV['LONO_DEBUG_LAMBDA_LAYER']
  success = $?.success?
  raise("ERROR: running command #{command}").color(:red) unless success
  success
end