jets 0.10.4 → 1.0.0

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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile.lock +12 -12
  4. data/README.md +4 -2
  5. data/exe/jets +2 -2
  6. data/lib/jets/application.rb +41 -5
  7. data/lib/jets/aws_info.rb +20 -0
  8. data/lib/jets/builders/code_builder.rb +201 -296
  9. data/lib/jets/builders/gem_replacer.rb +1 -1
  10. data/lib/jets/builders/handler_generator.rb +62 -43
  11. data/lib/jets/builders/md5.rb +55 -0
  12. data/lib/jets/builders/md5_zip.rb +60 -0
  13. data/lib/jets/builders/rack_packager.rb +29 -0
  14. data/lib/jets/builders/rackup_wrappers/rackup +23 -0
  15. data/lib/jets/builders/rackup_wrappers/rackup.rb +7 -0
  16. data/lib/jets/builders/reconfigure_rails/config/initializers/jets.rb +14 -0
  17. data/lib/jets/builders/reconfigure_rails.rb +99 -0
  18. data/lib/jets/builders/ruby_packager.rb +198 -0
  19. data/lib/jets/builders/{deducer.rb → shim_vars/app.rb} +14 -10
  20. data/lib/jets/builders/shim_vars/base.rb +24 -0
  21. data/lib/jets/builders/{shared_deducer.rb → shim_vars/shared.rb} +4 -3
  22. data/lib/jets/builders/shim_vars.rb +5 -0
  23. data/lib/jets/builders/templates/handler.js +9 -0
  24. data/lib/jets/builders/templates/shim.js +271 -0
  25. data/lib/jets/builders/tidy.rb +80 -0
  26. data/lib/jets/builders/util.rb +28 -0
  27. data/lib/jets/builders.rb +8 -2
  28. data/lib/jets/cfn/builders/function_builder.rb +0 -10
  29. data/lib/jets/cfn/builders/parent_builder.rb +7 -6
  30. data/lib/jets/cfn/ship.rb +9 -103
  31. data/lib/jets/cfn/upload.rb +139 -0
  32. data/lib/jets/cfn.rb +1 -0
  33. data/lib/jets/commands/build.rb +17 -19
  34. data/lib/jets/commands/deploy.rb +2 -0
  35. data/lib/jets/commands/help/deploy.md +2 -2
  36. data/lib/jets/commands/help/import/rack.md +13 -0
  37. data/lib/jets/commands/help/import/rails.md +11 -0
  38. data/lib/jets/commands/import/base.rb +39 -0
  39. data/lib/jets/commands/import/rack.rb +16 -0
  40. data/lib/jets/commands/import/rail.rb +68 -0
  41. data/lib/jets/commands/import/sequence.rb +68 -0
  42. data/lib/jets/commands/import.rb +14 -0
  43. data/lib/jets/commands/main.rb +2 -1
  44. data/lib/jets/commands/new.rb +1 -1
  45. data/lib/jets/commands/sequence.rb +26 -22
  46. data/lib/jets/commands/templates/skeleton/Gemfile.tt +5 -2
  47. data/lib/jets/commands/templates/skeleton/README.md +11 -2
  48. data/lib/jets/commands/templates/skeleton/app/jobs/application_job.rb +1 -1
  49. data/lib/jets/commands/templates/skeleton/app/views/layouts/application.html.erb.tt +1 -1
  50. data/lib/jets/commands/templates/skeleton/config/application.rb.tt +12 -5
  51. data/lib/jets/commands/templates/skeleton/config/database.yml.tt +5 -1
  52. data/lib/jets/commands/templates/skeleton/config/environments/development.rb +3 -0
  53. data/lib/jets/commands/templates/skeleton/config/environments/production.rb +5 -0
  54. data/lib/jets/commands/templates/skeleton/public/{images/favicon.ico → favicon.ico} +0 -0
  55. data/lib/jets/commands/templates/skeleton/spec/controllers/posts_controller_spec.rb +1 -3
  56. data/lib/jets/commands.rb +1 -0
  57. data/lib/jets/controller/base.rb +1 -1
  58. data/lib/jets/controller/layout.rb +3 -0
  59. data/lib/jets/controller/params.rb +3 -2
  60. data/lib/jets/controller/request.rb +4 -0
  61. data/lib/jets/core.rb +20 -18
  62. data/lib/jets/core_ext/kernel.rb +9 -5
  63. data/lib/jets/default/application.rb +1 -1
  64. data/lib/jets/inflections.rb +16 -8
  65. data/lib/jets/internal/app/controllers/jets/public_controller.rb +17 -22
  66. data/lib/jets/internal/app/controllers/jets/rack_controller.rb +15 -0
  67. data/lib/jets/naming.rb +0 -23
  68. data/lib/jets/rack/hash_converter.rb +25 -0
  69. data/lib/jets/rack/request.rb +71 -0
  70. data/lib/jets/rack/server.rb +47 -0
  71. data/lib/jets/rack.rb +7 -0
  72. data/lib/jets/rails_overrides/asset_tag_helper.rb +12 -11
  73. data/lib/jets/resource/function.rb +13 -5
  74. data/lib/jets/router.rb +1 -1
  75. data/lib/jets/ruby_server.rb +63 -18
  76. data/lib/jets/server/api_gateway.rb +1 -1
  77. data/lib/jets/stack/resource.rb +3 -1
  78. data/lib/jets/version.rb +1 -1
  79. data/lib/jets.rb +3 -5
  80. metadata +34 -9
  81. data/lib/jets/builders/node-hello.js +0 -73
  82. data/lib/jets/builders/node-shim.js +0 -182
  83. data/lib/jets/internal/app/controllers/jets/public_controller/python/show.py +0 -47
  84. data/lib/jets/internal/app/controllers/jets/public_controller/python/show.pyc +0 -0
@@ -4,7 +4,6 @@ require "colorize"
4
4
  require "socket"
5
5
  require "net/http"
6
6
  require "action_view"
7
- require "bundler" # for clean_old_submodules only
8
7
 
9
8
  # Some important folders to help understand how jets builds a project:
10
9
  #
@@ -18,7 +17,7 @@ require "bundler" # for clean_old_submodules only
18
17
  # Initially, macosx gems but then get replaced by linux gems where appropriate.
19
18
  # cache/downloads/rubies: ruby tarballs.
20
19
  # cache/downloads/gems: gem tarballs.
21
- # app_root: Where project gets copied into in order for us to configure it.
20
+ # code: Where project gets copied into in order for us to configure it.
22
21
  # app_root/bundled/gems: Where vendored gems finally end up at. The compiled
23
22
  # gems at this point are only linux gems.
24
23
  # artifacts/code/code-md5sha.zip: code artifact that gets uploaded to lambda.
@@ -40,20 +39,24 @@ require "bundler" # for clean_old_submodules only
40
39
  # * bundle install: cache/bundled/gems
41
40
  #
42
41
  ### setup bundled on app root from cache
43
- # * copy bundled to app_root: app_root/bundled
42
+ # * copy bundled to code: code/bundled
44
43
  # * extract linux ruby: cache/downloads/rubies:
45
44
  # cache/bundled/rbenv, cache/bundled/linuxbrew
46
45
  # * extract linux gems: cache/downloads/gems:
47
46
  # cache/bundled/gems, cache/bundled/linuxbrew
48
- # * setup bundled config: app_root/.bundle/config
47
+ # * setup bundled config: code/.bundle/config
49
48
  #
50
49
  ### zip
51
50
  # * create zip file
52
51
  class Jets::Builders
53
52
  class CodeBuilder
53
+ # https://docs.aws.amazon.com/lambda/latest/dg/limits.html
54
+ AWS_CODE_SIZE_LIMIT = 250 * 1024 * 1024 # 250MB
55
+
54
56
  include Jets::Timing
55
- include ActionView::Helpers::NumberHelper # number_to_human_size
56
57
  include Jets::AwsServices
58
+ include Util
59
+ extend Memoist
57
60
 
58
61
  attr_reader :full_project_path
59
62
  def initialize
@@ -63,54 +66,148 @@ class Jets::Builders
63
66
  end
64
67
 
65
68
  def build
66
- return create_zip_file(fake=true) if ENV['TEST_CODE'] # early return
67
-
68
69
  cache_check_message
69
70
  check_ruby_version
70
71
 
71
72
  clean_start
72
- compile_assets # easier to do before we copy the project
73
+ compile_assets # easier to do before we copy the project because node and yarn has been likely setup in the that dir
74
+ compile_rails_assets
73
75
  copy_project
74
- Dir.chdir(full(tmp_app_root)) do
76
+ Dir.chdir(full(tmp_code)) do
75
77
  # These commands run from project root
76
- start_app_root_setup
77
- bundle
78
- finish_app_root_setup
79
- create_zip_file
78
+ code_setup
79
+ package_ruby
80
+ code_finish
80
81
  end
81
82
  end
82
83
  time :build
83
84
 
84
- # Finds out of the app has polymorphic functions only and zero ruby functions.
85
- # In this case, we can skip a lot of the ruby related building and speed up the
86
- # deploy process.
87
- def poly_only?
88
- return true if ENV['POLY_ONLY'] # bypass to allow rapid development of handlers
89
- Jets::Commands::Build.poly_only?
85
+ # Resolves the chicken-and-egg problem with md5 checksums. The handlers need
86
+ # to reference files with the md5 checksum. The files are the:
87
+ #
88
+ # jets/code/rack-checksum.zip
89
+ # jets/code/bundled-checksum.zip
90
+ #
91
+ # We compute the checksums before we generate the node shim handlers.
92
+ def calculate_md5s
93
+ Md5.compute! # populates Md5.checksums hash
94
+ end
95
+
96
+ def generate_node_shims
97
+ headline "Generating shims in the handlers folder."
98
+ # Crucial that the Dir.pwd is in the tmp_code because for
99
+ # Jets::Builders::app_files because Jets.boot set ups
100
+ # autoload_paths and this is how project classes are loaded.
101
+ Jets::Builders::HandlerGenerator.build!
102
+ end
103
+
104
+ def create_zip_files
105
+ folders = Md5.stage_folders
106
+ folders.each do |folder|
107
+ zip = Md5Zip.new(folder)
108
+ if exist_on_s3?(zip.md5_name)
109
+ puts "Already exists: s3://#{s3_bucket}/jets/code/#{zip.md5_name}"
110
+ else
111
+ zip = Md5Zip.new(folder)
112
+ zip.create
113
+ end
114
+ end
115
+ end
116
+ time :create_zip_files
117
+
118
+ def exist_on_s3?(filename)
119
+ s3_key = "jets/code/#{filename}"
120
+ begin
121
+ s3.head_object(bucket: s3_bucket, key: s3_key)
122
+ true
123
+ rescue Aws::S3::Errors::NotFound
124
+ false
125
+ end
126
+ end
127
+
128
+ # Moves code/bundled and code/rack to build_root.
129
+ # These files will be packaged separated and lazy loaded as part of the
130
+ # node shim. This keeps the code zipfile smaller in size and helps
131
+ # with the 250MB extract limited. /tmp permits up to 512MB.
132
+ # AWS Lambda Limits: https://amzn.to/2A7y6v6
133
+ #
134
+ # > Each Lambda function receives an additional 512MB of non-persistent disk space in its own /tmp directory. The /tmp directory can be used for loading additional resources like dependency libraries or data sets during function initialization.
135
+ #
136
+ def setup_tmp
137
+ tmp_symlink("bundled") if Jets.lazy_load?
138
+ tmp_symlink("rack")
139
+ end
140
+
141
+ def stage_area
142
+ "#{Jets.build_root}/stage"
90
143
  end
91
144
 
92
- def start_app_root_setup
93
- tidy_project
145
+ # Moves folder to a stage folder and create a symlink its place
146
+ # that links from /var/task to /tmp. Example:
147
+ #
148
+ # /var/task/bundled => /tmp/bundled
149
+ #
150
+ def tmp_symlink(folder)
151
+ src = "#{full(tmp_code)}/#{folder}"
152
+ return unless File.exist?(src)
153
+
154
+ dest = "#{stage_area}/#{folder}"
155
+ dir = File.dirname(dest)
156
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
157
+ FileUtils.mv(src, dest)
158
+
159
+ # Create symlink
160
+ FileUtils.ln_sf("/tmp/#{folder}", "/#{full(tmp_code)}/#{folder}")
161
+ end
162
+
163
+ def code_setup
94
164
  reconfigure_development_webpacker
95
- reconfigure_ruby_version
165
+ end
166
+ time :code_setup
167
+
168
+ def code_finish
169
+ update_lazy_load_config # at the top, must be called before Jets.lazy_load? is used
170
+ store_s3_base_url
171
+ setup_tmp
172
+ calculate_md5s # must be called before generate_node_shims and create_zip_files
96
173
  generate_node_shims
174
+ create_zip_files
97
175
  end
98
- time :start_app_root_setup
176
+ time :code_finish
99
177
 
100
- def finish_app_root_setup
101
- return if poly_only?
178
+ def update_lazy_load_config
179
+ size_limit = AWS_CODE_SIZE_LIMIT
180
+ code_size = dir_size(full(tmp_code))
181
+ if code_size > size_limit
182
+ # override the setting because we dont have to a choice but to lazy load
183
+ Jets.config.ruby.lazy_load = true
184
+ end
185
+ end
102
186
 
103
- copy_bundled_to_app_root
104
- setup_bundle_config
105
- extract_ruby
106
- extract_gems
107
- store_s3_base_url
187
+ # Thanks https://stackoverflow.com/questions/9354595/recursively-getting-the-size-of-a-directory
188
+ # Seems to overestimate a little bit but close enough.
189
+ def dir_size(folder)
190
+ Dir.glob(File.join(folder, '**', '*'))
191
+ .select { |f| File.file?(f) }
192
+ .map{ |f| File.size(f) }
193
+ .inject(:+)
108
194
  end
109
- time :finish_app_root_setup
110
195
 
111
- # At this point the minimal stack exists.
196
+ # Store s3 base url is needed for asset serving from s3 later. Need to package this
197
+ # as part of the code so we have a reference to it.
198
+ # At this point the minimal stack exists, so we can grab it with the AWS API.
199
+ # We do not want to grab this as part of the live request because it is slow.
112
200
  def store_s3_base_url
113
- IO.write("#{full(tmp_app_root)}/config/s3_base_url.txt", s3_base_url)
201
+ return if poly_only?
202
+
203
+ write_s3_base_url("config/s3_base_url.txt")
204
+ write_s3_base_url("rack/config/s3_base_url.txt") if Jets.rack?
205
+ end
206
+
207
+ def write_s3_base_url(relative_path)
208
+ full_path = "#{full(tmp_code)}/#{relative_path}"
209
+ FileUtils.mkdir_p(File.dirname(full_path))
210
+ IO.write(full_path, s3_base_url)
114
211
  end
115
212
 
116
213
  def s3_base_url
@@ -122,50 +219,69 @@ class Jets::Builders
122
219
  #
123
220
  return Jets.config.assets.base_url if Jets.config.assets.base_url
124
221
 
125
- resp = cfn.describe_stacks(stack_name: Jets::Naming.parent_stack_name)
126
- stack = resp.stacks.first
127
- output = stack["outputs"].find { |o| o["output_key"] == "S3Bucket" }
128
- bucket_name = output["output_value"] # s3_bucket
129
222
  region = Jets.aws.region
130
223
 
131
224
  asset_base_url = "https://s3-#{region}.amazonaws.com"
132
- "#{asset_base_url}/#{bucket_name}/jets/public" # s3_base_url
133
- end
134
-
135
- def lambdagem_options
136
- {
137
- s3: "lambdagems",
138
- build_root: cache_area, # used in lambdagem
139
- project_root: full(tmp_app_root), # used in gem_replacer and lambdagem
140
- }
141
- end
142
-
143
- def extract_ruby
144
- headline "Setting up a vendored copy of ruby."
145
- Lambdagem.log_level = :info
146
- Lambdagem::Extract::Ruby.new(Jets::RUBY_VERSION, lambdagem_options).run
225
+ "#{asset_base_url}/#{s3_bucket}/jets" # s3_base_url
147
226
  end
148
227
 
149
- def extract_gems
150
- headline "Replacing compiled gems with AWS Lambda Linux compiled versions."
151
- GemReplacer.new(Jets::RUBY_VERSION, lambdagem_options).run
228
+ def s3_bucket
229
+ Jets.aws.s3_bucket
152
230
  end
153
231
 
154
- # This happens in the current app directory not the tmp app_root for simplicity
232
+ # This happens in the current app directory not the tmp code for simplicity.
233
+ # This is because the node and yarn has likely been set up correctly there.
155
234
  def compile_assets
235
+ if ENV['JETS_SKIP_ASSETS']
236
+ puts "Skip compiling assets".colorize(:yellow) # useful for debugging
237
+ return
238
+ end
239
+
156
240
  headline "Compling assets in current project directory"
157
241
  # Thanks: https://stackoverflow.com/questions/4195735/get-list-of-gems-being-used-by-a-bundler-project
158
242
  webpacker_loaded = Gem.loaded_specs.keys.include?("webpacker")
159
243
  return unless webpacker_loaded
160
244
 
161
245
  sh("yarn install")
162
- webpack_bin = File.exist?("#{Jets.root}bin/webpack") ?
246
+ webpack_command = File.exist?("#{Jets.root}bin/webpack") ?
163
247
  "bin/webpack" :
164
248
  `which webpack`.strip
165
- sh("JETS_ENV=#{Jets.env} #{webpack_bin}")
249
+ sh("JETS_ENV=#{Jets.env} #{webpack_command}")
166
250
  end
167
251
  time :compile_assets
168
252
 
253
+ # This happens in the current app directory not the tmp code for simplicity
254
+ # This is because the node likely been set up correctly there.
255
+ def compile_rails_assets
256
+ return unless rails?
257
+
258
+ if ENV['JETS_SKIP_ASSETS']
259
+ puts "Skip compiling rack assets".colorize(:yellow) # useful for debugging
260
+ return
261
+ end
262
+
263
+ return unless Jets.rack?
264
+
265
+ Bundler.with_clean_env do
266
+ rails_assets(:clobber)
267
+ rails_assets(:precompile)
268
+ end
269
+ end
270
+
271
+ def rails_assets(cmd)
272
+ # rake is available in both rails 4 and 5. rails command only in 5
273
+ command = "rake assets:#{cmd} --trace"
274
+ command = "RAILS_ENV=#{Jets.env} #{fulL_cmd}" unless Jets.env.development?
275
+ sh("cd rack && #{command}")
276
+ end
277
+
278
+ # Rudimentary rails detection
279
+ def rails?
280
+ config_ru = "#{Jets.root}rack/config.ru"
281
+ return false unless File.exist?(config_ru)
282
+ !IO.readlines(config_ru).grep(/Rails.application/).empty?
283
+ end
284
+
169
285
  # Cleans out non-cached files like code-*.zip in Jets.build_root
170
286
  # for a clean start. Also ensure that the /tmp/jets/project build root exists.
171
287
  #
@@ -181,11 +297,14 @@ class Jets::Builders
181
297
  # directory untouched and we can also remove a bunch of unnecessary files like
182
298
  # logs before zipping it up.
183
299
  def copy_project
184
- headline "Copying current project directory to temporary build area: #{full(tmp_app_root)}"
185
- FileUtils.rm_rf(full(tmp_app_root)) # remove current app_root folder
300
+ headline "Copying current project directory to temporary build area: #{full(tmp_code)}"
301
+ FileUtils.rm_rf(stage_area) # clear out from previous build
302
+ FileUtils.mkdir_p(stage_area)
303
+ FileUtils.rm_rf(full(tmp_code)) # remove current code folder
186
304
  move_node_modules(Jets.root, Jets.build_root)
187
305
  begin
188
- FileUtils.cp_r(@full_project_path, full(tmp_app_root))
306
+ # puts "cp -r #{@full_project_path} #{full(tmp_code)}".colorize(:yellow) # uncomment to debug
307
+ FileUtils.cp_r(@full_project_path, full(tmp_code))
189
308
  ensure
190
309
  move_node_modules(Jets.build_root, Jets.root) # move node_modules directory back
191
310
  end
@@ -205,35 +324,13 @@ class Jets::Builders
205
324
  end
206
325
  end
207
326
 
208
- # Because we're removing files (something dangerous) use full paths.
209
- def tidy_project
210
- headline "Tidying project: removing ignored files to reduce package size."
211
- excludes.each do |exclude|
212
- exclude = exclude.sub(%r{^/},'') # remove leading slash
213
- remove_path = "#{full(tmp_app_root)}/#{exclude}"
214
- FileUtils.rm_rf(remove_path)
215
- # puts " rm -rf #{remove_path}" # uncomment to debug
216
- end
217
- end
218
-
219
- def generate_node_shims
220
- headline "Generating node shims in the handlers folder."
221
- # Crucial that the Dir.pwd is in the tmp_app_root because for
222
- # Jets::Builders::app_files because Jets.boot set ups
223
- # autoload_paths and this is how project classes are loaded.
224
- Jets::Commands::Build.app_files.each do |path|
225
- handler = Jets::Builders::HandlerGenerator.new(path)
226
- handler.generate
227
- end
228
- end
229
-
230
327
  # Bit hacky but this saves the user from accidentally forgetting to change this
231
328
  # when they deploy a jets project in development mode
232
329
  def reconfigure_development_webpacker
233
330
  return unless Jets.env.development?
234
331
  headline "Reconfiguring webpacker development settings for AWS Lambda."
235
332
 
236
- webpacker_yml = "#{full(tmp_app_root)}/config/webpacker.yml"
333
+ webpacker_yml = "#{full(tmp_code)}/config/webpacker.yml"
237
334
  return unless File.exist?(webpacker_yml)
238
335
 
239
336
  config = YAML.load_file(webpacker_yml)
@@ -242,199 +339,28 @@ class Jets::Builders
242
339
  IO.write(webpacker_yml, new_yaml)
243
340
  end
244
341
 
245
- # This is in case the user has a 2.5.x variant.
246
- # Force usage of ruby version that jets supports
247
- # The lambda server only has ruby 2.5.0 installed.
248
- def reconfigure_ruby_version
249
- ruby_version = "#{full(tmp_app_root)}/.ruby-version"
250
- IO.write(ruby_version, Jets::RUBY_VERSION)
342
+ def ruby_packager
343
+ RubyPackager.new(tmp_code)
251
344
  end
345
+ memoize :ruby_packager
252
346
 
253
- def copy_bundled_to_app_root
254
- app_root_bundled = "#{full(tmp_app_root)}/bundled"
255
- if File.exist?(app_root_bundled)
256
- puts "Removing current bundled from project"
257
- FileUtils.rm_rf(app_root_bundled)
258
- end
259
- # Leave #{Jets.build_root}/bundled behind to act as cache
260
- FileUtils.cp_r("#{cache_area}/bundled", app_root_bundled)
261
- end
262
-
263
- def setup_bundle_config
264
- ensure_build_cache_bundle_config_exists!
265
-
266
- # Override project's .bundle/config and ensure that .bundle/config matches
267
- # at these 2 spots:
268
- # app_root/.bundle/config
269
- # bundled/gems/.bundle/config
270
- cache_bundle_config = "#{cache_area}/.bundle/config"
271
- app_bundle_config = "#{full(tmp_app_root)}/.bundle/config"
272
- FileUtils.mkdir_p(File.dirname(app_bundle_config))
273
- FileUtils.cp(cache_bundle_config, app_bundle_config)
274
- end
275
-
276
- # On circleci the "#{Jets.build_root}/.bundle/config" doesnt exist
277
- # this only happens with ssh debugging, not when the ci.sh script gets ran.
278
- # But on macosx it exists.
279
- # Dont know why this is the case.
280
- def ensure_build_cache_bundle_config_exists!
281
- text =<<-EOL
282
- ---
283
- BUNDLE_PATH: "bundled/gems"
284
- BUNDLE_WITHOUT: "development:test"
285
- EOL
286
- bundle_config = "#{cache_area}/.bundle/config"
287
- FileUtils.mkdir_p(File.dirname(bundle_config))
288
- IO.write(bundle_config, text)
289
- end
290
-
291
- def create_zip_file(fake=nil)
292
- headline "Creating zip file."
293
- temp_code_zipfile = "#{Jets.build_root}/code/code-temp.zip"
294
- FileUtils.mkdir_p(File.dirname(temp_code_zipfile))
295
-
296
- # Use fake if testing CloudFormation only
297
- if fake
298
- hello_world = "/tmp/hello.js"
299
- puts "Uploading tiny #{hello_world} file to S3 for quick testing.".colorize(:red)
300
- code = IO.read(File.expand_path("../node-hello.js", __FILE__))
301
- IO.write(hello_world, code)
302
- command = "zip --symlinks -rq #{temp_code_zipfile} #{hello_world}"
303
- else
304
- # https://serverfault.com/questions/265675/how-can-i-zip-compress-a-symlink
305
- command = "cd #{full(tmp_app_root)} && zip --symlinks -rq #{temp_code_zipfile} ."
306
- end
307
-
308
- sh(command)
309
-
310
- # we can get the md5 only after the file has been created
311
- md5 = Digest::MD5.file(temp_code_zipfile).to_s[0..7]
312
- md5_zip_dest = "#{Jets.build_root}/code/code-#{md5}.zip"
313
- FileUtils.mkdir_p(File.dirname(md5_zip_dest))
314
- FileUtils.mv(temp_code_zipfile, md5_zip_dest)
315
- # mv /tmp/jets/demo/code/code-temp.zip /tmp/jets/demo/code/code-a8a604aa.zip
316
-
317
- file_size = number_to_human_size(File.size(md5_zip_dest))
318
- puts "Zip file with code and bundled linux ruby created at: #{md5_zip_dest.colorize(:green)} (#{file_size})"
319
-
320
- # Save state
321
- IO.write("#{Jets.build_root}/code/current-md5-filename.txt", md5_zip_dest)
322
- # Much later: ship, base_child_builder need set an s3_key which requires
323
- # the md5_zip_dest.
324
- # It is a pain to pass this all the way up from the
325
- # CodeBuilder class.
326
- # Let's store the "/tmp/jets/demo/code/code-a8a604aa.zip" into a
327
- # file that can be read from any places where this is needed.
328
- # Can also just generate a "fake file" for specs
347
+ def rack_packager
348
+ RackPackager.new("#{tmp_code}/rack")
329
349
  end
330
- time :create_zip_file
350
+ memoize :rack_packager
331
351
 
332
- def bundle
333
- clean_old_submodules
334
- bundle_install
352
+ def package_ruby
353
+ ruby_packager.install
354
+ reconfigure_rails
355
+ rack_packager.install
356
+ ruby_packager.finish
357
+ rack_packager.finish
335
358
  end
336
- time :bundle
337
-
338
- # Installs gems on the current target system: both compiled and non-compiled.
339
- # If user is on a macosx machine, macosx gems will be installed.
340
- # If user is on a linux machine, linux gems will be installed.
341
- #
342
- # Copies Gemfile* to /tmp/jetss/demo/bundled folder and installs
343
- # gems with bundle install from there.
344
- #
345
- # We take the time to copy Gemfile and bundle into a separate directory
346
- # because it gets left around to act as a 'cache'. So, when the builds the
347
- # project gets built again not all the gems from get installed from the
348
- # beginning.
349
- def bundle_install
350
- return if poly_only?
351
-
352
- headline "Bundling: running bundle install in cache area: #{cache_area}."
359
+ time :package_ruby
353
360
 
354
- copy_gemfiles
355
-
356
- require "bundler" # dynamically require bundler so user can use any bundler
357
- Bundler.with_clean_env do
358
- # cd /tmp/jets/demo
359
- sh(
360
- "cd #{cache_area} && " \
361
- "env BUNDLE_IGNORE_CONFIG=1 bundle install --path bundled/gems --without development test"
362
- )
363
- end
364
-
365
- puts 'Bundle install success.'
366
- end
367
-
368
- # When using submodules, bundler leaves old submodules behind. Over time this inflates
369
- # the size of the the bundled gems. So we'll clean it up.
370
- def clean_old_submodules
371
- # https://stackoverflow.com/questions/38800129/parsing-a-gemfile-lock-with-bundler
372
- lockfile = "#{cache_area}/Gemfile.lock"
373
- return unless File.exist?(lockfile)
374
-
375
- parser = Bundler::LockfileParser.new(Bundler.read_file(lockfile))
376
- specs = parser.specs
377
-
378
- # specs = Bundler.load.specs
379
- # IE: spec.source.to_s: "https://github.com/tongueroo/webpacker.git (at jets@a8c4661)"
380
- submoduled_specs = specs.select do |spec|
381
- spec.source.to_s =~ /@\w+\)/
382
- end
383
-
384
- # find git shas to keep
385
- # IE: ["a8c4661", "abc4661"]
386
- git_shas = submoduled_specs.map do |spec|
387
- md = spec.source.to_s.match(/@(\w+)\)/)
388
- git_sha = md[1]
389
- end
390
-
391
- # IE: /tmp/jets/demo/cache/bundled/gems/ruby/2.5.0/bundler/gems/webpacker-a8c46614c675
392
- Dir.glob("#{cache_area}/bundled/gems/ruby/2.5.0/bundler/gems/*").each do |path|
393
- sha = path.split('-').last[0..6] # only first 7 chars of the git sha
394
- unless git_shas.include?(sha)
395
- puts "Removing old submoduled gem: #{path}"
396
- FileUtils.rm_rf(path) # REMOVE old submodule directory
397
- end
398
- end
399
- end
400
-
401
- def copy_gemfiles
402
- FileUtils.mkdir_p(cache_area)
403
- FileUtils.cp("#{@full_project_path}Gemfile", "#{cache_area}/Gemfile")
404
- FileUtils.cp("#{@full_project_path}Gemfile.lock", "#{cache_area}/Gemfile.lock")
405
- end
406
-
407
- def excludes
408
- excludes = %w[.git tmp log spec]
409
- excludes += get_excludes("#{full(tmp_app_root)}/.gitignore")
410
- excludes += get_excludes("#{full(tmp_app_root)}/.dockerignore")
411
- excludes = excludes.reject do |p|
412
- jetskeep.find do |keep|
413
- p.include?(keep)
414
- end
415
- end
416
- excludes
417
- end
418
-
419
- def get_excludes(file)
420
- path = file
421
- return [] unless File.exist?(path)
422
-
423
- exclude = File.read(path).split("\n")
424
- exclude.map {|i| i.strip}.reject {|i| i =~ /^#/ || i.empty?}
425
- # IE: ["/handlers", "/bundled*", "/vendor/jets]
426
- end
427
-
428
- # We clean out ignored files pretty aggressively. So provide
429
- # a way for users to keep files from being cleaned ou.
430
- def jetskeep
431
- defaults = %w[pack handlers]
432
- path = Jets.root + ".jetskeep"
433
- return defaults unless path.exist?
434
-
435
- keep = path.read.split("\n")
436
- keep = keep.map {|i| i.strip}.reject {|i| i =~ /^#/ || i.empty?}
437
- (defaults + keep).uniq
361
+ # TODO: Move logic into plugin instead
362
+ def reconfigure_rails
363
+ ReconfigureRails.new("#{full(tmp_code)}/rack").run
438
364
  end
439
365
 
440
366
  def cache_check_message
@@ -461,34 +387,13 @@ EOL
461
387
  ruby[:major] == jets[:major] && ruby[:minor] == jets[:minor]
462
388
  end
463
389
 
464
- def cache_area
465
- "#{Jets.build_root}/cache" # cleaner to use full path for this setting
466
- end
467
-
468
- # Provide pretty clear way to desinate full path.
469
- # full("bundled") => /tmp/jets/demo/bundled
470
- def full(relative_path)
471
- "#{Jets.build_root}/#{relative_path}"
472
- end
473
-
474
390
  # Group all the path settings together here
475
- def self.tmp_app_root
476
- Jets::Commands::Build.tmp_app_root
477
- end
478
-
479
- def tmp_app_root
480
- self.class.tmp_app_root
481
- end
482
-
483
- def sh(command)
484
- puts "=> #{command}".colorize(:green)
485
- success = system(command)
486
- abort("#{command} failed to run") unless success
487
- success
391
+ def self.tmp_code
392
+ Jets::Commands::Build.tmp_code
488
393
  end
489
394
 
490
- def headline(message)
491
- puts "=> #{message}".colorize(:cyan)
395
+ def tmp_code
396
+ self.class.tmp_code
492
397
  end
493
398
  end
494
399
  end
@@ -79,7 +79,7 @@ Jets is unable to build a deployment package that will work on AWS Lambda withou
79
79
  * Wait until it added to lambdagems.com. No need to report this to us, as we've already been notified.
80
80
  * Use another gem that does not require compilation.
81
81
 
82
- Compiled gems usually take some time to figure out how to build as they each depend on different libraries and packages. We make an effort add new gems as soon as we can. You can support us by going to: http://rubyonjets.com/support-jets/
82
+ Compiled gems usually take some time to figure out how to build as they each depend on different libraries and packages. We make an effort add new gems as soon as we can.
83
83
  EOL
84
84
  erb = ERB.new(template, nil, '-') # trim mode https://stackoverflow.com/questions/4632879/erb-template-removing-the-trailing-line
85
85
  erb.result(binding)