Class: Jets::Builders::CodeBuilder
- Inherits:
-
Object
- Object
- Jets::Builders::CodeBuilder
show all
- Extended by:
- Memoist
- Includes:
- AwsServices, Util
- Defined in:
- lib/jets/builders/code_builder.rb
Constant Summary
collapse
- SUPPORTED_RUBY_VERSIONS =
%w[2.5 2.7]
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#apigateway, #aws_lambda, #cfn, #dynamodb, #logs, #s3, #s3_resource, #sns, #sqs, #sts
#lookup, #stack_exists?, #stack_in_progress?
included
Constructor Details
Returns a new instance of CodeBuilder.
21
22
23
24
25
26
|
# File 'lib/jets/builders/code_builder.rb', line 21
def initialize
@full_project_path = File.expand_path(Jets.root) + "/"
@version_purger = Purger.new
end
|
Instance Attribute Details
#full_project_path ⇒ Object
Returns the value of attribute full_project_path.
20
21
22
|
# File 'lib/jets/builders/code_builder.rb', line 20
def full_project_path
@full_project_path
end
|
Class Method Details
.tmp_code ⇒ Object
Group all the path settings together here
Instance Method Details
#build ⇒ Object
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
# File 'lib/jets/builders/code_builder.rb', line 28
def build
check_ruby_version
@version_purger.purge
cache_check_message
clean_start
compile_assets compile_rails_assets
copy_project
copy_ruby_version_file
Dir.chdir("#{stage_area}/code") do
code_setup
package_ruby
code_finish
end
end
|
#build_lambda_layer ⇒ Object
359
360
361
362
363
|
# File 'lib/jets/builders/code_builder.rb', line 359
def build_lambda_layer
return if Jets.poly_only?
lambda_layer = LambdaLayer.new
lambda_layer.build
end
|
#cache_check_message ⇒ Object
370
371
372
373
374
|
# File 'lib/jets/builders/code_builder.rb', line 370
def cache_check_message
if File.exist?("#{Jets.build_root}/cache")
puts "The #{Jets.build_root}/cache folder exists. Incrementally re-building the jets using the cache. To clear the cache: rm -rf #{Jets.build_root}/cache"
end
end
|
#calculate_md5s ⇒ Object
Resolves the chicken-and-egg problem with md5 checksums. The handlers need to reference files with the md5 checksum. The files are the:
jets/code/rack-checksum.zip
jets/code/opt-checksum.zip
We compute the checksums before we generate the node shim handlers.
53
54
55
|
# File 'lib/jets/builders/code_builder.rb', line 53
def calculate_md5s
Md5.compute! end
|
#check_agree ⇒ Object
354
355
356
357
|
# File 'lib/jets/builders/code_builder.rb', line 354
def check_agree
agree = Jets::Gems::Agree.new
agree.prompt
end
|
#check_code_size! ⇒ Object
116
117
118
|
# File 'lib/jets/builders/code_builder.rb', line 116
def check_code_size!
CodeSize.check!
end
|
#check_ruby_version ⇒ Object
383
384
385
386
387
388
389
390
391
392
|
# File 'lib/jets/builders/code_builder.rb', line 383
def check_ruby_version
return unless ENV['JETS_RUBY_CHECK'] == '0' || Jets.config.ruby.check == false
return if ruby_version_supported?
puts <<~EOL.color(:red)
You are using Ruby version #{RUBY_VERSION} which is not supported by Jets.
Please use one of the Jets supported ruby versions: #{SUPPORTED_RUBY_VERSIONS.join(' ')}
If you would like to skip this check you can set: JETS_RUBY_CHECK=0
EOL
exit 1
end
|
#clean_start ⇒ Object
Cleans out non-cached files like code-*.zip in Jets.build_root for a clean start. Also ensure that the /tmp/jets/project build root exists.
Most files are kept around after the build process for inspection and debugging. So we have to clean out the files. But we only want to clean out some of the files.
281
282
283
284
|
# File 'lib/jets/builders/code_builder.rb', line 281
def clean_start
Dir.glob("#{Jets.build_root}/code/code-*.zip").each { |f| FileUtils.rm_f(f) }
FileUtils.mkdir_p(Jets.build_root) end
|
#code_finish ⇒ Object
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
# File 'lib/jets/builders/code_builder.rb', line 94
def code_finish
store_s3_base_url
disable_webpacker_middleware
copy_internal_jets_code
check_code_size!
calculate_md5s generate_shims create_zip_files
end
|
#code_setup ⇒ Object
90
91
92
|
# File 'lib/jets/builders/code_builder.rb', line 90
def code_setup
reconfigure_development_webpacker
end
|
#compile_assets ⇒ Object
This happens in the current app directory not the tmp code for simplicity. This is because the node and yarn has likely been set up correctly there.
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
# File 'lib/jets/builders/code_builder.rb', line 198
def compile_assets
if ENV['JETS_SKIP_ASSETS']
puts "Skip compiling assets".color(:yellow) return
end
headline "Compling assets in current project directory"
return unless webpacker_included?
sh("yarn install")
webpack_command = File.exist?("#{Jets.root}/bin/webpack") ?
"bin/webpack" :
`which webpack`.strip
sh "JETS_ENV=#{Jets.env} #{webpack_command}"
end
|
#compile_rails_assets ⇒ Object
This happens in the current app directory not the tmp code for simplicity This is because the node likely been set up correctly there.
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
# File 'lib/jets/builders/code_builder.rb', line 228
def compile_rails_assets
return unless Jets.rack? && rails? && !rails_api?
if ENV['JETS_SKIP_ASSETS']
puts "Skip compiling rack assets".color(:yellow) return
end
jets_root = Jets.root
Bundler.with_unbundled_env do
gemfile = ENV['BUNDLE_GEMFILE']
ENV['BUNDLE_GEMFILE'] = "#{jets_root}/rack/Gemfile"
sh "cd #{jets_root} && bundle install"
ENV['BUNDLE_GEMFILE'] = gemfile
rails_assets(:clobber, jets_root: jets_root)
rails_assets(:precompile, jets_root: jets_root)
end
end
|
#copy_internal_jets_code ⇒ Object
Materialized internal code into actually user Jets app as part of the deploy process. Examples of things that we might materialize:
Views
Simple Functions
For functions, We copy the files into the project because we cannot require simple functions directly since they are wrapped by an anonymous class.
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
# File 'lib/jets/builders/code_builder.rb', line 128
def copy_internal_jets_code
files = []
mailers_controller = Jets::Router.has_controller?("Jets::MailersController")
if mailers_controller
files << "app/controllers/jets/mailers_controller.rb"
files << "app/views/jets/mailers"
files << "app/helpers/jets/mailers_helper.rb"
end
files.each do |relative_path|
src = File.expand_path("../internal/#{relative_path}", File.dirname(__FILE__))
dest = "#{"#{stage_area}/code"}/#{relative_path}"
FileUtils.mkdir_p(File.dirname(dest))
FileUtils.cp_r(src, dest)
end
end
|
#copy_project ⇒ Object
Copy project into temporary directory. Do this so we can keep the project directory untouched and we can also remove a bunch of unnecessary files like logs before zipping it up.
289
290
291
292
293
294
295
296
297
298
299
300
301
|
# File 'lib/jets/builders/code_builder.rb', line 289
def copy_project
headline "Copying current project directory to temporary build area: #{"#{stage_area}/code"}"
FileUtils.rm_rf("#{build_area}/stage") FileUtils.mkdir_p("#{build_area}/stage")
FileUtils.rm_rf("#{stage_area}/code") move_node_modules(Jets.root, Jets.build_root)
begin
Jets::Util.cp_r(@full_project_path, "#{stage_area}/code")
ensure
move_node_modules(Jets.build_root, Jets.root) end
end
|
#copy_ruby_version_file ⇒ Object
376
377
378
379
380
|
# File 'lib/jets/builders/code_builder.rb', line 376
def copy_ruby_version_file
ruby_version_path = Jets.root.join(".ruby-version")
return unless File.exists?(ruby_version_path)
FileUtils.cp_r(ruby_version_path, build_area)
end
|
#create_zip_files ⇒ Object
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# File 'lib/jets/builders/code_builder.rb', line 65
def create_zip_files
folders = Md5.stage_folders
folders.each do |folder|
zip = Md5Zip.new(folder)
if exist_on_s3?(zip.md5_name)
puts "Already exists: s3://#{s3_bucket}/jets/code/#{zip.md5_name}"
else
zip = Md5Zip.new(folder)
zip.create
end
end
end
|
#dir_size(folder) ⇒ Object
148
149
150
151
152
153
|
# File 'lib/jets/builders/code_builder.rb', line 148
def dir_size(folder)
Dir.glob(File.join(folder, '**', '*'))
.select { |f| File.file?(f) }
.map{ |f| File.size(f) }
.inject(:+)
end
|
#disable_webpacker_middleware ⇒ Object
190
191
192
193
194
|
# File 'lib/jets/builders/code_builder.rb', line 190
def disable_webpacker_middleware
full_path = "#{"#{stage_area}/code"}/config/disable-webpacker-middleware.txt"
FileUtils.mkdir_p(File.dirname(full_path))
FileUtils.touch(full_path)
end
|
#exist_on_s3?(filename) ⇒ Boolean
79
80
81
82
83
84
85
86
87
88
|
# File 'lib/jets/builders/code_builder.rb', line 79
def exist_on_s3?(filename)
return false if ENV['JETS_BUILD_NO_INTERNET']
s3_key = "jets/code/#{filename}"
begin
s3.head_object(bucket: s3_bucket, key: s3_key)
true
rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::Forbidden
false
end
end
|
#generate_shims ⇒ Object
57
58
59
60
61
62
63
|
# File 'lib/jets/builders/code_builder.rb', line 57
def generate_shims
headline "Generating shims in the handlers folder."
Jets::Builders::HandlerGenerator.build!
end
|
#move_node_modules(source_folder, dest_folder) ⇒ Object
Move the node modules to the tmp build folder to speed up project copying. A little bit risky because a ctrl-c in the middle of the project copying results in a missing node_modules but user can easily rebuild that.
Tesing shows 6.623413 vs 0.027754 speed improvement.
308
309
310
311
312
313
314
|
# File 'lib/jets/builders/code_builder.rb', line 308
def move_node_modules(source_folder, dest_folder)
source = "#{source_folder}/node_modules"
dest = "#{dest_folder}/node_modules"
if File.exist?(source)
FileUtils.mv(source, dest)
end
end
|
#package_ruby ⇒ Object
341
342
343
344
345
346
347
348
349
350
351
352
|
# File 'lib/jets/builders/code_builder.rb', line 341
def package_ruby
return if Jets.poly_only?
check_agree
ruby_packager.install
reconfigure_rails rack_packager.install
ruby_packager.finish rack_packager.finish
build_lambda_layer
end
|
#rack_packager ⇒ Object
336
337
338
|
# File 'lib/jets/builders/code_builder.rb', line 336
def rack_packager
RackPackager.new("#{tmp_code}/rack")
end
|
#rails? ⇒ Boolean
Rudimentary rails detection Duplicated in builders/reconfigure_rails.rb
259
260
261
262
263
|
# File 'lib/jets/builders/code_builder.rb', line 259
def rails?
config_ru = "#{Jets.root}/rack/config.ru"
return false unless File.exist?(config_ru)
!IO.readlines(config_ru).grep(/Rails.application/).empty?
end
|
#rails_api? ⇒ Boolean
Rudimentary rails api detection Duplicated in builders/reconfigure_rails.rb Another way of checking is loading a rails console and checking Rails.application.config.api_only Using this way for simplicity.
269
270
271
272
273
|
# File 'lib/jets/builders/code_builder.rb', line 269
def rails_api?
config_app = "#{Jets.root}/rack/config/application.rb"
return false unless File.exist?(config_app)
!IO.readlines(config_app).grep(/config.api_only.*=.*true/).empty?
end
|
#rails_assets(cmd, jets_root:) ⇒ Object
250
251
252
253
254
255
|
# File 'lib/jets/builders/code_builder.rb', line 250
def rails_assets(cmd, jets_root:)
command = "bundle exec rake assets:#{cmd} --trace"
command = "RAILS_ENV=#{Jets.env} #{command}" unless Jets.env.development?
sh("cd #{jets_root}/rack && #{command}")
end
|
Bit hacky but this saves the user from accidentally forgetting to change this when they deploy a jets project in development mode
318
319
320
321
322
323
324
325
326
327
328
329
|
# File 'lib/jets/builders/code_builder.rb', line 318
def reconfigure_development_webpacker
return unless Jets.env.development?
headline "Reconfiguring webpacker development settings for AWS Lambda."
webpacker_yml = "#{"#{stage_area}/code"}/config/webpacker.yml"
return unless File.exist?(webpacker_yml)
config = YAML.load_file(webpacker_yml)
config["development"]["compile"] = false new_yaml = YAML.dump(config)
IO.write(webpacker_yml, new_yaml)
end
|
TODO: Move logic into plugin instead
366
367
368
|
# File 'lib/jets/builders/code_builder.rb', line 366
def reconfigure_rails
ReconfigureRails.new("#{"#{stage_area}/code"}/rack").run
end
|
#ruby_packager ⇒ Object
331
332
333
|
# File 'lib/jets/builders/code_builder.rb', line 331
def ruby_packager
RubyPackager.new(tmp_code)
end
|
#ruby_version_supported? ⇒ Boolean
394
395
396
397
398
399
|
# File 'lib/jets/builders/code_builder.rb', line 394
def ruby_version_supported?
md = RUBY_VERSION.match(/(\d+)\.(\d+)\.\d+/)
major, minor = md[1], md[2]
detected_ruby = [major, minor].join('.')
SUPPORTED_RUBY_VERSIONS.include?(detected_ruby)
end
|
#s3_base_url ⇒ Object
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
# File 'lib/jets/builders/code_builder.rb', line 169
def s3_base_url
return Jets.config.assets.base_url if Jets.config.assets.base_url
region = Jets.aws.region
asset_base_url = region == 'us-east-1' ?
"https://s3.amazonaws.com" :
"https://s3-#{region}.amazonaws.com"
"#{asset_base_url}/#{s3_bucket}/jets" end
|
#s3_bucket ⇒ Object
186
187
188
|
# File 'lib/jets/builders/code_builder.rb', line 186
def s3_bucket
Jets.aws.s3_bucket
end
|
#store_s3_base_url ⇒ Object
Store s3 base url is needed for asset serving from s3 later. Need to package this as part of the code so we have a reference to it. At this point the minimal stack exists, so we can grab it with the AWS API. We do not want to grab this as part of the live request because it is slow.
159
160
161
162
|
# File 'lib/jets/builders/code_builder.rb', line 159
def store_s3_base_url
write_s3_base_url("#{stage_area}/code/config/s3_base_url.txt")
write_s3_base_url("#{stage_area}/rack/config/s3_base_url.txt") if Jets.rack?
end
|
#tmp_code ⇒ Object
406
407
408
|
# File 'lib/jets/builders/code_builder.rb', line 406
def tmp_code
self.class.tmp_code
end
|
#webpacker_included? ⇒ Boolean
214
215
216
217
218
219
220
221
222
223
224
|
# File 'lib/jets/builders/code_builder.rb', line 214
def webpacker_included?
lines = IO.readlines("#{Jets.root}/Gemfile")
lines.detect { |l| l =~ /webpacker/ || l =~ /jetpacker/ }
end
|
#write_s3_base_url(full_path) ⇒ Object
164
165
166
167
|
# File 'lib/jets/builders/code_builder.rb', line 164
def write_s3_base_url(full_path)
FileUtils.mkdir_p(File.dirname(full_path))
IO.write(full_path, s3_base_url)
end
|