Class: Jets::Commands::Build

Inherits:
Object
  • Object
show all
Includes:
StackInfo
Defined in:
lib/jets/commands/build.rb

Constant Summary collapse

APP_FOLDERS =
%w[authorizers controllers functions jobs rules]

Class Method Summary collapse

Instance Method Summary collapse

Methods included from StackInfo

#first_run?, #parent_stack_name, #s3_bucket, #stack_type

Methods included from AwsServices

#apigateway, #aws_lambda, #aws_options, #cfn, #dynamodb, #logs, #s3, #s3_resource, #sns, #sqs, #sts

Methods included from AwsServices::StackStatus

#lookup, #stack_exists?, #stack_in_progress?

Methods included from AwsServices::GlobalMemoist

included

Constructor Details

#initialize(options) ⇒ Build

Returns a new instance of Build.



7
8
9
# File 'lib/jets/commands/build.rb', line 7

def initialize(options)
  @options = options.dup
end

Class Method Details

.app_file?(path) ⇒ Boolean

Returns:

  • (Boolean)


150
151
152
153
154
155
156
157
# File 'lib/jets/commands/build.rb', line 150

def app_file?(path)
  return false unless File.extname(path) == ".rb"
  return false unless File.file?(path) unless Jets.env.test?
  return false if application_abstract_classes.detect { |p| path.include?(p) }
  return false if concerns?(path)
  return true if APP_FOLDERS.detect { |p| path.include?("app/#{p}") }
  false
end

.app_filesObject

Crucial that the Dir.pwd is in the tmp_code because for because Jets.boot set ups autoload_paths and this is how project classes are loaded. TODO: rework code so that Dir.pwd does not have to be in tmp_code for build to work.

app_files used to determine what CloudFormation templates to build. app_files also used to determine what handlers to build.



137
138
139
140
141
142
143
144
145
146
147
# File 'lib/jets/commands/build.rb', line 137

def app_files
  paths = []
  expression = "#{Jets.root}/app/**/**/*.rb"
  Dir.glob(expression).each do |path|
    next unless app_file?(path)
    relative_path = path.sub("#{Jets.root}/", '') # rid of the Jets.root at beginning
    paths << relative_path
  end
  paths += internal_app_files
  paths
end

.app_has_ruby?Boolean

Returns:

  • (Boolean)


200
201
202
203
204
205
206
207
# File 'lib/jets/commands/build.rb', line 200

def app_has_ruby?
  has_ruby = app_files.detect do |path|
    app_class = Jets::Klass.from_path(path)  # IE: PostsController, Jets::PublicController
    langs = app_class.tasks.map(&:lang)
    langs.include?(:ruby) && app_class != Jets::PreheatJob
  end
  !!has_ruby
end

.application_abstract_classesObject

Do not define lamda functions for abstract application parent classes. Examples:

application_controller.rb
application_job.rb
application_authorizer.rb


164
165
166
# File 'lib/jets/commands/build.rb', line 164

def application_abstract_classes
  APP_FOLDERS.map { |a| "application_#{a.singularize}.rb" }
end

.authorizer_filesObject



172
173
174
# File 'lib/jets/commands/build.rb', line 172

def authorizer_files
  app_files.select { |p| p.include?("app/authorizers") }
end

.concerns?(path) ⇒ Boolean

Returns:

  • (Boolean)


168
169
170
# File 'lib/jets/commands/build.rb', line 168

def concerns?(path)
  path =~ %r{app/\w+/concerns/}
end

.find_app_paths(app_path) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/jets/commands/build.rb', line 180

def find_app_paths(app_path)
  paths = []
  expression = "#{Jets.root}/app/#{app_path}/**/*.rb"
  Dir.glob(expression).each do |path|
    return false unless File.file?(path)

    relative_path = path.sub("#{Jets.root}/", '')
    # Rids of the Jets.root at beginning
    paths << relative_path
  end
  paths
end

.internal_app_filesObject

Add internal Jets controllers if they are being used TODO: Interesting, this eventually just used to generate handlers and controllers only. Maybe rename to make that clear. The copying of other internal files like views is done in builders/code_builder.rb copy_internal_jets_code



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/jets/commands/build.rb', line 226

def internal_app_files
  paths = []
  internal = File.expand_path("../internal", __dir__)

  controllers = "#{internal}/app/controllers/jets"
  paths << "#{controllers}/public_controller.rb" if router_has?("Jets::PublicController")
  paths << "#{controllers}/rack_controller.rb" if router_has?("Jets::RackController")
  paths << "#{controllers}/mailers_controller.rb" if router_has?("Jets::MailersController")
  paths << "#{controllers}/mount_controller.rb" if router_has?("Jets::MountController")

  if Jets.config.prewarm.enable
    paths << "#{internal}/app/jobs/jets/preheat_job.rb"
  end

  paths
end

.poly_only?Boolean

Finds out of the app has polymorphic functions only and zero ruby functions. In this case, we can skip a lot of the ruby related building and speed up the deploy process.

Returns:

  • (Boolean)


196
197
198
# File 'lib/jets/commands/build.rb', line 196

def poly_only?
  !app_has_ruby? && !shared_has_ruby?
end

.router_has?(controller) ⇒ Boolean

Returns:

  • (Boolean)


243
244
245
# File 'lib/jets/commands/build.rb', line 243

def router_has?(controller)
  Jets::Router.has_controller?(controller)
end

.shared_filesObject



176
177
178
# File 'lib/jets/commands/build.rb', line 176

def shared_files
  find_app_paths("shared/resources")
end

.shared_has_ruby?Boolean

Returns:

  • (Boolean)


209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/jets/commands/build.rb', line 209

def shared_has_ruby?
  has_ruby = false
  Jets::Stack.subclasses.each do |klass|
    klass.functions.each do |fun|
      if fun.lang == :ruby
        has_ruby = true
        break
      end
    end
  end
  has_ruby
end

.tmp_code(full_build_path = false) ⇒ Object



247
248
249
# File 'lib/jets/commands/build.rb', line 247

def tmp_code(full_build_path=false)
  full_build_path ? "#{Jets.build_root}/stage/code" : "stage/code"
end

Instance Method Details

#app_filesObject



118
119
120
# File 'lib/jets/commands/build.rb', line 118

def app_files
  self.class.app_files
end

#authorizer?(path) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/jets/commands/build.rb', line 106

def authorizer?(path)
  path.include?("app/authorizers")
end

#authorizer_filesObject



126
127
128
# File 'lib/jets/commands/build.rb', line 126

def authorizer_files
  self.class.authorizer_files
end

#buildObject



21
22
23
24
# File 'lib/jets/commands/build.rb', line 21

def build
  build_code unless @options[:templates]
  build_templates
end

#build_all_templatesObject



42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/jets/commands/build.rb', line 42

def build_all_templates
  # CloudFormation templates
  # 1. Shared and authorizer templates - child templates needs them
  build_api_gateway_templates
  build_authorizer_templates # controllers can use these
  # 2. Child templates - parent template needs them
  build_app_child_templates
  # 2. Child templates - parent template needs them
  build_shared_resources_templates
  # 4. Finally parent template
  build_parent_template # must be called at the end
end

#build_api_gateway_templatesObject



59
60
61
62
# File 'lib/jets/commands/build.rb', line 59

def build_api_gateway_templates
  Jets::Cfn::Builders::ApiGatewayBuilder.new(@options).build
  Jets::Cfn::Builders::ApiDeploymentBuilder.new(@options).build
end

#build_app_child_templatesObject



70
71
72
73
74
# File 'lib/jets/commands/build.rb', line 70

def build_app_child_templates
  app_files.each do |path|
    build_child_template(path)
  end
end

#build_authorizer_templatesObject



64
65
66
67
68
# File 'lib/jets/commands/build.rb', line 64

def build_authorizer_templates
  authorizer_files.each do |path|
    Jets::Cfn::Builders::AuthorizerBuilder.new(path).build
  end
end

#build_child_template(path) ⇒ Object

path: app/controllers/comments_controller.rb path: app/jobs/easy_job.rb



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/jets/commands/build.rb', line 84

def build_child_template(path)
  return if authorizer?(path) # AuthorizerBuilder is built earlier

  md = path.match(%r{app/(.*?)/}) # extract: controller, job or function
  process_class = md[1].classify
  builder_class = "Jets::Cfn::Builders::#{process_class}Builder".constantize

  # Examples:
  #   Jets::Cfn::Builders::ControllerBuilder.new(PostsController)
  #   Jets::Cfn::Builders::JobBuilder.new(EasyJob)
  #   Jets::Cfn::Builders::RuleBuilder.new(CheckRule)
  #   Jets::Cfn::Builders::FunctionBuilder.new(Hello)
  #   Jets::Cfn::Builders::FunctionBuilder.new(HelloFunction)
  app_class = Jets::Klass.from_path(path)
  if Jets.poly_only? && app_class == Jets::PreheatJob
    return # No prewarm when there's only poly functions
  end

  builder = builder_class.new(app_class)
  builder.build
end

#build_codeObject



26
27
28
# File 'lib/jets/commands/build.rb', line 26

def build_code
  Jets::Builders::CodeBuilder.new.build unless @options[:noop]
end

#build_minimal_templateObject



55
56
57
# File 'lib/jets/commands/build.rb', line 55

def build_minimal_template
  Jets::Cfn::Builders::ParentBuilder.new(@options).build(parent=true)
end

#build_parent_templateObject



110
111
112
# File 'lib/jets/commands/build.rb', line 110

def build_parent_template
  Jets::Cfn::Builders::ParentBuilder.new(@options).build(parent=true)
end

#build_shared_resources_templatesObject



76
77
78
79
80
# File 'lib/jets/commands/build.rb', line 76

def build_shared_resources_templates
  Jets::Stack.subclasses.each do |subclass|
    Jets::Cfn::Builders::SharedBuilder.new(subclass).build
  end
end

#build_templatesObject



30
31
32
33
34
35
36
# File 'lib/jets/commands/build.rb', line 30

def build_templates
  puts "Building CloudFormation templates."
  clean_templates
  build_minimal_template
  build_all_templates if full?
  puts "Generated CloudFormation templates at #{Jets.build_root}/templates"
end

#clean_templatesObject



114
115
116
# File 'lib/jets/commands/build.rb', line 114

def clean_templates
  FileUtils.rm_rf("#{Jets.build_root}/templates")
end

#full?Boolean

Returns:

  • (Boolean)


38
39
40
# File 'lib/jets/commands/build.rb', line 38

def full?
  @options[:templates] || @options[:stack_type] == :full
end

#runObject



11
12
13
14
15
16
17
18
19
# File 'lib/jets/commands/build.rb', line 11

def run
  puts "Building project for Lambda..."

  return if @options[:noop]
  # run gets called from the CLI and does not have all the stack_options yet.
  # We compute it and change the options early here.
  @options.merge!(stack_type: stack_type, s3_bucket: s3_bucket)
  build
end

#shared_filesObject



122
123
124
# File 'lib/jets/commands/build.rb', line 122

def shared_files
  self.class.shared_files
end