jets 6.0.0.beta1 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 557cd5532ad372ba0ea9be975103f5dfb77a63b3472d0582f98570cd75a114e7
4
- data.tar.gz: 773a6bf556945018ae87c73f3c37ee3294be02f463209db3e2c8712ae9757e4f
3
+ metadata.gz: f47ac05e6c483fddfa95f18d2899386df9e228893b7ddc425ce9a5e6ef4df0c4
4
+ data.tar.gz: d18b78a75e123a66b954f35339e541df75d6e85a4dd16f1ac56ca090d00aeb6d
5
5
  SHA512:
6
- metadata.gz: eb08a46c81660e3d0c503427d2fbd4426b4d37a3a7dc27e855a34930bc3f5be6e812ddd9507dca82383e95b6d2aea360b3f92c8f10b03d95b9cd22b8cffd57cf
7
- data.tar.gz: 043127d02de6be061b8a3c1ad158bb206edf63ac4e4253685884987bbfac1421d1ef53974afa0ee8ba8c5912061b80df3b222538fe1f3dd0fd1aa17d07b18e40
6
+ metadata.gz: 1771244d6fed908320775d6cbeffd08d7b26defe3a57bc0c8a3e1db6e2ee92eaf49c0e277e138d9186215cdfa3b40b34508b0159aaa7d0b350ae40d464987100
7
+ data.tar.gz: '099a72152faeaad8bbbacfde6859e320025840a756115d55ee908a838da4d538d6ee5b06d3f498ac07864d4477ac4fcd455056261db5951d28253906d52be225'
@@ -27,7 +27,7 @@ Thanks!
27
27
  Make sure that you've done all of these. To mark a checkbox done, replace [ ] with [x]. Or after you create the issue you can click the checkbox.
28
28
  -->
29
29
 
30
- - [ ] Upgrade Jets: Are you using the latest version of Jets? This allows Jets to fix issues fast. There's a `jets upgrade` command that makes this a simple task. There's also an Upgrading Guide: http://rubyonjets.com/docs/upgrading/
30
+ - [ ] Upgrade Jets: Are you using the latest version of Jets? This allows Jets to fix issues fast.
31
31
  - [ ] Reproducibility: Are you reporting a bug others will be able to reproduce and not asking a question. If you're unsure or want to ask a question, do so on https://community.boltops.com
32
32
  - [ ] Code sample: Have you put together a code sample to reproduce the issue and make it available? Code samples help speed up fixes dramatically. If it's an easily reproducible issue, then code samples are not needed. If you're unsure, please include a code sample.
33
33
 
data/CHANGELOG.md CHANGED
@@ -3,7 +3,15 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/).
5
5
 
6
- ## [6.0.0] - Unrelease
6
+ ## [6.0.1] - 2024-05-27
7
+ - [#727](https://github.com/rubyonjets/jets/pull/727) small fixes
8
+ - associated outputs concept
9
+ - jets concurrency show lambda function name
10
+ - jets git:push command
11
+ - pretty time helper
12
+ - update github issue template bug report
13
+
14
+ ## [6.0.0] - 2024-05-23
7
15
 
8
16
  * Rails Support
9
17
  * Hooks Support
data/Gemfile CHANGED
@@ -5,10 +5,6 @@ gemspec
5
5
 
6
6
  # required here for specs
7
7
  group :development, :test do
8
- gem "mysql2", "~> 0.5.2"
9
- gem "dynomite", "~> 2.0.0"
10
- end
11
-
12
- group :test do
13
- gem "actionpack", "~> 7.1.3" # jets shim specs
8
+ gem "dynomite", ">= 2.0.0"
9
+ gem "mysql2", ">= 0.5.2"
14
10
  end
data/Rakefile CHANGED
@@ -9,12 +9,6 @@ RSpec::Core::RakeTask.new
9
9
 
10
10
  require_relative "lib/jets"
11
11
 
12
- desc "Generates cli reference docs as markdown"
13
- task :docs do
14
- require "cli_markdown_jets"
15
- CliMarkdown::Creator.new.create_all
16
- end
17
-
18
12
  # Thanks: https://docs.ruby-lang.org/en/2.1.0/RDoc/Task.html
19
13
  require "rdoc/task"
20
14
  require "jets/rdoc"
@@ -14,16 +14,13 @@ module Jets
14
14
  memoize :loader
15
15
 
16
16
  def configure(root = Jets.root)
17
- all_loaders = Zeitwerk::Registry.loaders
18
- already_configured_dirs = all_loaders.map(&:dirs).flatten
19
-
20
17
  full_path = proc { |path| "#{root}/#{path}" }
21
18
  autoload_paths = config.autoload_paths.map(&full_path)
22
19
  extension_paths = config.extension_paths.map(&full_path)
23
20
  load_paths = autoload_paths + extension_paths
24
21
 
25
22
  load_paths.each do |path|
26
- next if already_configured_dirs.include?(path)
23
+ next if already_configured_dir?(path)
27
24
  next unless File.directory?(path)
28
25
  loader.push_dir(path)
29
26
  end
@@ -34,6 +31,23 @@ module Jets
34
31
  end
35
32
  end
36
33
 
34
+ def already_configured_dir?(path)
35
+ all_loaders = Zeitwerk::Registry.loaders
36
+ already_configured_dirs = all_loaders.map(&:dirs).flatten
37
+
38
+ # Normalize the path to avoid trailing slashes issues
39
+ normalized_path = File.expand_path(path)
40
+
41
+ # Check the path and its parent directories
42
+ while normalized_path != "/"
43
+ return true if already_configured_dirs.include?(normalized_path)
44
+ normalized_path = File.dirname(normalized_path)
45
+ end
46
+
47
+ # Check the root directory explicitly
48
+ already_configured_dirs.include?("/")
49
+ end
50
+
37
51
  # Call at end
38
52
  def setup
39
53
  loader.on_load do |cpath, value, abspath|
@@ -0,0 +1,21 @@
1
+ class Jets::Cfn::Resource
2
+ class AssociatedOutputs
3
+ extend Memoist
4
+
5
+ def initialize(outputs = {}, replacements = {})
6
+ @outputs = outputs
7
+ @replacements = replacements
8
+ end
9
+
10
+ def replacer
11
+ Replacer.new(@replacements)
12
+ end
13
+ memoize :replacer
14
+
15
+ def outputs
16
+ outputs = replacer.replace_placeholders(@outputs)
17
+ outputs.transform_values! { |value| value.camelize }
18
+ outputs.transform_keys! { |key| replacer.replace_value(key) }
19
+ end
20
+ end
21
+ end
@@ -1,11 +1,14 @@
1
1
  class Jets::CLI::Ci
2
2
  class Status < Base
3
+ include Jets::Util::FormatTime
4
+
3
5
  def run
4
6
  check_build_id!
5
7
  run_with_exception_handling do
6
8
  puts "Build id: #{build_id}"
7
9
  resp = codebuild.batch_get_builds(ids: [build_id])
8
10
  build = resp.builds.first
11
+ puts "Build end time: #{pretty_time(build.end_time)}"
9
12
  puts "Build status: #{colored(build.build_status)}"
10
13
  end
11
14
  end
@@ -9,7 +9,7 @@ class Jets::CLI::Concurrency
9
9
  def run
10
10
  puts <<~EOL
11
11
  Settings for Function: #{@lambda_function.name}
12
- Reserved concurreny: #{reserved_concurrency}
12
+ Reserved concurrency: #{reserved_concurrency}
13
13
  Provisioned concurrency: #{provisioned_concurrency}
14
14
  EOL
15
15
  end
@@ -1,8 +1,8 @@
1
1
  class Jets::CLI::Concurrency
2
2
  class Set < Get
3
3
  def run
4
- sure? "Will update the concurrency settings for #{Jets.project.namespace}"
5
- puts "Updating concurrency settings for #{Jets.project.namespace}"
4
+ sure? "Will update the concurrency settings for #{@lambda_function.name}"
5
+ puts "Updating concurrency settings for #{@lambda_function.name}"
6
6
 
7
7
  if @options[:reserved]
8
8
  @lambda_function.reserved_concurrency = @options[:reserved]
@@ -1,8 +1,8 @@
1
1
  class Jets::CLI::Concurrency
2
2
  class Unset < Set
3
3
  def run
4
- sure? "Will unset the concurrency settings for #{Jets.project.namespace}"
5
- puts "Unsetting concurrency settings for #{Jets.project.namespace}"
4
+ sure? "Will unset the concurrency settings for #{@lambda_function.name}"
5
+ puts "Unsetting concurrency settings for #{@lambda_function.name}"
6
6
 
7
7
  if @options[:reserved]
8
8
  @lambda_function.reserved_concurrency = nil
@@ -8,7 +8,11 @@ class Jets::CLI
8
8
 
9
9
  def are_you_sure?
10
10
  stack_name = Jets.project.namespace
11
- message = "Will delete #{stack_name.color(:green)}"
11
+ message = <<~EOL
12
+ Will delete #{stack_name.color(:green)}
13
+
14
+ Uses remote runner to delete the stack and resources.
15
+ EOL
12
16
  unless stack_exists?(stack_name)
13
17
  message << <<~EOL
14
18
 
@@ -14,7 +14,7 @@ class Jets::CLI
14
14
  resources = cfn.describe_stack_resources(stack_name: stack_name).stack_resources
15
15
  resources.each do |r|
16
16
  if r.resource_type == "AWS::Lambda::Function"
17
- functions << r.physical_resource_id
17
+ functions << r.physical_resource_id if r.physical_resource_id # race condition. can be nil for a brief moment while provisioning
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,65 @@
1
+ require "open3"
2
+
3
+ class Jets::CLI::Git
4
+ class Push < Jets::CLI::Base
5
+ def initialize(options = {})
6
+ super
7
+ @args = options[:args] || []
8
+ end
9
+
10
+ def run
11
+ args = ["push"] + @args
12
+ puts "=> git #{args.join(" ")}"
13
+
14
+ IO.popen(["git", *args]) do |io|
15
+ io.each do |line|
16
+ puts line
17
+ end
18
+ end
19
+
20
+ return unless $?.success?
21
+
22
+ set_env_vars!
23
+ sleep 2 # wait ci to start
24
+ Jets::CLI::Ci::Logs.new(options).run
25
+ end
26
+
27
+ def set_env_vars!
28
+ env_vars = Jets.project.config.git.push.branch[push_branch] || {}
29
+ # IE: branch_name = {JETS_ENV: "xxx", AWS_PROFILE: "xxx"}
30
+ env_vars.each do |k, v|
31
+ ENV[k.to_s] = v
32
+ end
33
+ end
34
+
35
+ # man git-push
36
+ # git push
37
+ # git push origin
38
+ # git push origin :
39
+ # git push origin master
40
+ # git push origin HEAD
41
+ # git push mothership master:satellite/master dev:satellite/dev
42
+ # git push origin HEAD:master
43
+ # git push origin master:refs/heads/experimental
44
+ # git push origin :experimental
45
+ # git push origin +dev:master
46
+ def push_branch
47
+ args = @args.reject { |arg| arg.start_with?("-") } # remove options
48
+ case args.size
49
+ when 0
50
+ local.git_default_branch
51
+ when 1
52
+ local.git_current_branch
53
+ when 2
54
+ args.last
55
+ else
56
+ raise "ERROR: Too many arguments. Usage: jets git:push [REMOTE] [BRANCH]"
57
+ end
58
+ end
59
+
60
+ def local
61
+ Jets::Git::Local.new
62
+ end
63
+ memoize :local
64
+ end
65
+ end
@@ -0,0 +1,8 @@
1
+ class Jets::CLI
2
+ class Git < Jets::Thor::Base
3
+ desc "push", "Runs git push and jets ci:logs"
4
+ def push(*args)
5
+ Push.new(options.merge(args: args)).run
6
+ end
7
+ end
8
+ end
@@ -11,13 +11,13 @@ module Jets::CLI::Group
11
11
  end
12
12
 
13
13
  def init_project_name
14
- Jets.project.name # inferred from the folder name
14
+ # inferred from the folder name
15
+ Dir.pwd.split("/").last.gsub(/[^a-zA-Z0-9_]/, "-").squeeze("-")
15
16
  end
16
17
 
17
18
  def framework
18
- Jets::CLI::Init::Detect.new.framework
19
+ Jets::Framework.name
19
20
  end
20
- memoize :framework
21
21
 
22
22
  def package_type
23
23
  (framework == "rails") ? "image" : "zip"
@@ -0,0 +1,14 @@
1
+ ## Example
2
+
3
+ ❯ jets concurrency:info
4
+ Concurrency for demo-dev
5
+ +---------------------------+----------+
6
+ | Function | Reserved |
7
+ +---------------------------+----------+
8
+ | controller | 25 |
9
+ | jets-prewarm_event-handle | 2 |
10
+ | total | 27 |
11
+ +---------------------------+----------+
12
+ Account Limits
13
+ Concurrent Executions: 1000
14
+ Unreserved Concurrent Executions: 730
@@ -1,7 +1,36 @@
1
1
  Jets.deploy.configure do
2
+ # CloudFront Lambda URL https://docs.rubyonjets.com/docs/routing/lambda/cloudfront/distribution/
3
+ # config.lambda.url.cloudfront.enable = true
4
+ # config.lambda.url.cloudfront.cert.arn = acm_cert_arn(domain: "domain.com", region: "us-east-1")
5
+ # config.lambda.url.cloudfront.route53.enable = true
6
+
7
+ # CloudFront Assets
8
+ # config.assets.cloudfront.enable = true
9
+ # config.assets.cloudfront.cert.arn = acm_cert_arn(domain: "domain.com", region: "us-east-1")
10
+ # config.assets.cloudfront.route53.enable = true
11
+
12
+ # Release phase https://docs.rubyonjets.com/docs/hooks/remote/release/
13
+ # config.release.phase.command = "bundle exec rails db:migrate"
14
+
15
+ # Scaling https://docs.rubyonjets.com/docs/config/concurrency/
16
+ # config.lambda.controller.provisioned_concurrency = 1 # costs money, no cold start
17
+ # config.lambda.controller.reserved_concurrency = 25 # free and limits scaling
18
+
19
+ # IAM https://docs.rubyonjets.com/docs/iam/app/iam-policies/
20
+ # config.lambda.iam.policy = ["sns"]
21
+ # config.lambda.iam.managed_policy = ["AmazonS3FullAccess"]
22
+
23
+ # Docker https://docs.rubyonjets.com/docs/docker/dockerfile/managed/
24
+ # config.dockerfile.packages.apt.build_stage = ["default-libmysqlclient-dev"]
25
+ # config.dockerfile.packages.apt.deployment_stage = ["default-mysql-client"]
26
+
2
27
  <% unless framework == "rails" -%>
3
28
  # https://docs.rubyonjets.com/docs/config/package-type/
29
+ <% if %w[rack sinatra].include?(framework) -%>
30
+ # config.package_type = "zip" # default: image IE: image or zip
31
+ <% else -%>
4
32
  # config.package_type = "image" # default: image IE: image or zip
33
+ <% end -%>
5
34
 
6
35
  <% end -%>
7
36
  <% if framework == "rails" -%>
@@ -9,35 +38,4 @@ Jets.deploy.configure do
9
38
  # config.job.enable = true
10
39
 
11
40
  <% end -%>
12
- # Docker https://docs.rubyonjets.com/docs/docker/dockerfile/managed/
13
- # config.dockerfile.packages.apt.build_stage = ["default-libmysqlclient-dev"]
14
- # config.dockerfile.packages.apt.deployment_stage = ["default-mysql-client"]
15
-
16
- # CloudFront Lambda URL https://docs.rubyonjets.com/docs/routing/lambda/cloudfront/distribution/
17
- # config.lambda.url.cloudfront.enable = true
18
- # config.lambda.url.cloudfront.cert.arn = acm_cert_arn("domain.com", region: "us-east-1")
19
- # config.lambda.url.cloudfront.route53.enable = true
20
-
21
- # WAF https://docs.rubyonjets.com/docs/routing/lambda/cloudfront/waf/
22
- # config.lambda.url.cloudfront.web_acl_id = web_acl_arn(ssm_env, scope: "CLOUDFRONT")
23
- # config.waf.rules = ["AWSManagedRulesSQLiRuleSet"]
24
-
25
- # Scaling https://docs.rubyonjets.com/docs/config/provisioned-concurrency/
26
- # config.lambda.controller.provisioned_concurrency = 1 # costs money, no cold start
27
- # config.lambda.controller.reserved_concurrency = 10 # free and limits scaling
28
-
29
- # Release phase https://docs.rubyonjets.com/docs/hooks/remote/release/
30
- # config.release.phase.command = "bundle exec rails db:migrate"
31
-
32
- # CI https://docs.rubyonjets.com/docs/ci/getting-started/
33
- # use jets ci:init to add these settings automatically
34
- # config.ci.source = {
35
- # Type: "GITHUB",
36
- # Location: "https://github.com/ORG/REPO"
37
- # }
38
- # config.ci.source_version = "main"
39
- # config.ci.env.vars = {
40
- # BUNDLE_GITHUB__COM: "SSM:/#{ssm_env}/BUNDLE_GITHUB__COM",
41
- # JETS_API_KEY: "SSM:/#{ssm_env}/JETS_API_KEY"
42
- # }
43
41
  end
@@ -5,7 +5,7 @@
5
5
  <% if framework == "rails" -%>
6
6
  RAILS_ENV=production
7
7
  RAILS_LOG_TO_STDOUT=1
8
- # SECRET_KEY_BASE=SSM # SSM:/<%= init_project_name %>/dev/SECRET_KEY_BASE remember to set a secret key base
8
+ # SECRET_KEY_BASE=SSM # => SSM:/<%= init_project_name %>/dev/SECRET_KEY_BASE remember to set a secret key base
9
9
  <% end -%>
10
10
 
11
11
  # Examples:
@@ -14,5 +14,7 @@ RAILS_LOG_TO_STDOUT=1
14
14
  # KEY2=SSM:/abs/path/to/KEY2
15
15
  # Conventions https://docs.rubyonjets/docs/env/ssm/conventions/
16
16
  # SSM path under /<%= init_project_name %>/dev/ are autoloaded.
17
- # You can also control this behavior and then manually declare the SSM parameter.
18
- # DATABASE_URL=SSM # SSM:/<%= init_project_name %>/dev/DATABASE_URL
17
+ # For example, DATABASE_URL does not need to be declared here.
18
+ # It can be conventionally autoloaded.
19
+ # Below is an example of how to manually declare but it's not needed.
20
+ # DATABASE_URL=SSM # => SSM:/<%= init_project_name %>/dev/DATABASE_URL
data/lib/jets/cli/init.rb CHANGED
@@ -16,7 +16,7 @@ class Jets::CLI
16
16
  private
17
17
 
18
18
  def sure_message
19
- detected_message = "Detected #{framework} framework.\n" if framework
19
+ detected_message = "Detected #{framework.titleize} framework.\n" if framework
20
20
  <<~EOL
21
21
  #{detected_message}This will initialize the project for Jets.
22
22
 
@@ -26,6 +26,39 @@ class Jets::CLI
26
26
  EOL
27
27
  end
28
28
 
29
+ def configure_rails
30
+ # config/environments/production.rb adjustments
31
+ # Comment out public_file_server.enabled on new Rails 7.0 apps
32
+ # config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
33
+ comment_out_line("config.public_file_server.enabled = ENV", env: "production")
34
+ config_environment("config.public_file_server.enabled = false\n\n", env: "production")
35
+ # Comment out config.asset_host = "http://assets.example.com" on new Rails 7.0 apps
36
+ # config.asset_host = "http://assets.example.com"
37
+ comment_out_line('config.asset_host = "', env: "production")
38
+ asset_host = <<~EOL
39
+
40
+ # Assets https://docs.rubyonjets.com/docs/assets/cloudfront/
41
+ config.asset_host = ENV["JETS_ASSET_HOST"] unless ENV["JETS_ASSET_HOST"].blank?
42
+ EOL
43
+ config_environment(asset_host, env: "production")
44
+
45
+ jets_job = <<~EOL
46
+ # Jobs https://docs.rubyonjets.com/docs/jobs/enable/
47
+ # config.active_job.queue_adapter = :jets_job
48
+ EOL
49
+ config_environment(jets_job, env: "production")
50
+ end
51
+
52
+ def configure_hanami
53
+ after = " class App < Hanami::App\n"
54
+ data = <<~EOL
55
+ # https://guides.hanamirb.org/v2.1/assets/using-a-cdn/
56
+ # https://docs.rubyonjets.com/docs/assets/cloudfront/
57
+ config.assets.base_url = ENV["JETS_ASSET_HOST"] if ENV["JETS_ASSET_HOST"]
58
+ EOL
59
+ inject_into_file "config/app.rb", optimize_indentation(data, 4), after: after, verbose: true
60
+ end
61
+
29
62
  public
30
63
 
31
64
  def are_you_sure?
@@ -46,21 +79,12 @@ class Jets::CLI
46
79
  end
47
80
 
48
81
  def configure_environment
49
- return unless framework == "rails"
50
- # config/environments/production.rb adjustments
51
- # Comment out public_file_server.enabled on new Rails 7.0 apps
52
- # config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
53
- comment_out_line("config.public_file_server.enabled = ENV", env: "production")
54
- config_environment("config.public_file_server.enabled = false", env: "production")
55
- # Comment out config.asset_host = "http://assets.example.com" on new Rails 7.0 apps
56
- # config.asset_host = "http://assets.example.com"
57
- comment_out_line('config.asset_host = "', env: "production")
58
- config_environment('config.asset_host = ENV["JETS_ASSET_HOST"] unless ENV["JETS_ASSET_HOST"].blank?', env: "production")
59
- jets_job = <<~EOL
60
- # Docs: https://docs.rubyonjets.com/docs/jobs/enable/
61
- # config.active_job.queue_adapter = :jets_job
62
- EOL
63
- config_environment(jets_job, env: "production")
82
+ case framework
83
+ when "rails"
84
+ configure_rails
85
+ when "hanami"
86
+ configure_hanami
87
+ end
64
88
  end
65
89
  end
66
90
  end
@@ -3,6 +3,7 @@ require "tzinfo"
3
3
 
4
4
  class Jets::CLI::Release
5
5
  class History < Base
6
+ include Jets::Util::FormatTime
6
7
  rescue_api_error
7
8
 
8
9
  def run
@@ -29,11 +30,11 @@ class Jets::CLI::Release
29
30
  items.each do |item|
30
31
  version = item[:version]
31
32
  status = item[:stack_status]
32
- released_at = item[:pretty_created_at] || item[:created_at]
33
+ released_at = item[:created_at]
33
34
  message = item[:message] || "Deployed"
34
35
  message = message[0..50]
35
36
 
36
- row = [version, status, format_time(released_at), message]
37
+ row = [version, status, pretty_time(released_at), message]
37
38
  if @options[:sha]
38
39
  sha = item[:git_sha].to_s[0..7] if item[:git_sha]
39
40
  row << sha
@@ -42,27 +43,5 @@ class Jets::CLI::Release
42
43
  end
43
44
  presenter.show
44
45
  end
45
-
46
- def format_time(string)
47
- if string.include?("ago") # IE: 5 minutes ago
48
- string
49
- else
50
- utc = DateTime.parse(string)
51
-
52
- tz_override = ENV["JETS_TZ"] # IE: America/Los_Angeles
53
- local = if tz_override
54
- tz = TZInfo::Timezone.get(tz_override)
55
- tz.utc_to_local(utc)
56
- else
57
- utc.new_offset(DateTime.now.offset) # local time
58
- end
59
-
60
- if tz_override
61
- local.strftime("%b %-d, %Y %-l:%M:%S%P")
62
- else
63
- local.strftime("%b %-d, %Y %H:%M:%S")
64
- end
65
- end
66
- end
67
46
  end
68
47
  end
@@ -27,8 +27,7 @@ class Jets::CLI::Release
27
27
  data = release_fields.map do |field|
28
28
  # special cases for values
29
29
  value = if field == :created_at
30
- time_string = release[:pretty_created_at] || release[:created_at]
31
- format_time(time_string)
30
+ pretty_time(release[:created_at])
32
31
  else
33
32
  release[field]
34
33
  end
data/lib/jets/cli/stop.rb CHANGED
@@ -8,8 +8,8 @@ class Jets::CLI
8
8
  if stopped
9
9
  log.info <<~EOL
10
10
  Deploy has been stopped: #{build_id}
11
- Note: If the deploy has already started the CloudFormation update,
12
- it will continue. Please check the logs.
11
+ If the deploy has already started the CloudFormation update it will continue.
12
+ Please check the logs.
13
13
 
14
14
  EOL
15
15
  show_console_log_url(build_id)
@@ -9,18 +9,18 @@ Jets.deploy.configure do
9
9
  # config.waf.rules = []
10
10
 
11
11
  # Jets WAF Managed Rules
12
- # config.waf.managed_rules.blanket_rate_limiter.enable = true # default: true
13
- # config.waf.managed_rules.blanket_rate_limiter.limit = 1000
14
- # config.waf.managed_rules.blanket_rate_limiter.evaluation_window_sec = 300
15
- # config.waf.managed_rules.blanket_rate_limiter.aggregate_key_type = "IP"
12
+ # config.waf.custom_rules.blanket_rate_limiter.enable = true # default: true
13
+ # config.waf.custom_rules.blanket_rate_limiter.limit = 1000
14
+ # config.waf.custom_rules.blanket_rate_limiter.evaluation_window_sec = 300
15
+ # config.waf.custom_rules.blanket_rate_limiter.aggregate_key_type = "IP"
16
16
 
17
- # config.waf.managed_rules.uri_rate_limiter.enable = true # default: false
18
- # config.waf.managed_rules.uri_rate_limiter.limit = 100
19
- # config.waf.managed_rules.uri_rate_limiter.logical_statement = "Or"
20
- # config.waf.managed_rules.uri_rate_limiter.paths = ["/login", "/signup"] # default: ["/"]
21
- # config.waf.managed_rules.uri_rate_limiter.evaluation_window_sec = 300
22
- # config.waf.managed_rules.uri_rate_limiter.aggregate_key_type = "IP"
23
- # config.waf.managed_rules.uri_rate_limiter.string_match_condition = "STARTS_WITH"
17
+ # config.waf.custom_rules.uri_rate_limiter.enable = true # default: false
18
+ # config.waf.custom_rules.uri_rate_limiter.limit = 100
19
+ # config.waf.custom_rules.uri_rate_limiter.logical_statement = "Or"
20
+ # config.waf.custom_rules.uri_rate_limiter.paths = ["/login", "/signup"] # default: ["/"]
21
+ # config.waf.custom_rules.uri_rate_limiter.evaluation_window_sec = 300
22
+ # config.waf.custom_rules.uri_rate_limiter.aggregate_key_type = "IP"
23
+ # config.waf.custom_rules.uri_rate_limiter.string_match_condition = "STARTS_WITH"
24
24
 
25
25
  # Docs: https://docs.rubyonjets.com/docs/routing/lambda/cloudfront/waf/
26
26
  end
data/lib/jets/cli.rb CHANGED
@@ -15,6 +15,9 @@ module Jets
15
15
  desc "generate SUBCOMMAND", "generate subcommands"
16
16
  subcommand "generate", Generate
17
17
 
18
+ desc "git SUBCOMMAND", "git subcommands"
19
+ subcommand "git", Git
20
+
18
21
  desc "maintenance SUBCOMMAND", "maintenance subcommands"
19
22
  subcommand "maintenance", Maintenance
20
23
 
@@ -7,6 +7,7 @@ module Jets::Core::Config
7
7
  :ignore_paths,
8
8
  :base64_encode,
9
9
  :dotenv,
10
+ :git,
10
11
  :tips
11
12
  )
12
13
  def initialize(*)
@@ -40,6 +41,10 @@ module Jets::Core::Config
40
41
 
41
42
  @base64_encode = true
42
43
 
44
+ @git = ActiveSupport::OrderedOptions.new
45
+ @git.push = ActiveSupport::OrderedOptions.new
46
+ @git.push.branch = ActiveSupport::OrderedOptions.new
47
+
43
48
  @tips = ActiveSupport::OrderedOptions.new
44
49
  @tips.enable = true
45
50
  @tips.concurrency_change = true
@@ -62,12 +67,23 @@ module Jets::Core::Config
62
67
  Jets::Core::Booter.require_config(:project)
63
68
  return @name if @name
64
69
 
65
- if jets_info_project_name
66
- jets_info_project_name
67
- else
68
- # when not set, conventionally infer app name from current directory
69
- @name_inferred = true
70
- Dir.pwd.split("/").last.gsub(/[^a-zA-Z0-9_]/, "-").squeeze("-")
70
+ project_name = jets_info_project_name
71
+ unless project_name
72
+ puts "ERROR: Jets project name not set".color(:red)
73
+ abort <<~EOL
74
+ Please set config.name in config/jets/project.rb or the JETS_PROJECT environment variable
75
+ Example:
76
+
77
+ config/jets/project.rb
78
+
79
+ Jets.project.configure do
80
+ config.name = "demo"
81
+ end
82
+
83
+ Or set the JETS_PROJECT environment variable:
84
+
85
+ export JETS_PROJECT=demo
86
+ EOL
71
87
  end
72
88
  end
73
89
  memoize :name
@@ -0,0 +1,51 @@
1
+ module Jets
2
+ class Framework
3
+ class << self
4
+ extend Memoist
5
+ def env_var
6
+ "#{name.upcase}_ENV" if name # can be nil
7
+ end
8
+
9
+ def env
10
+ return unless env_var
11
+ ENV[env_var] || "production" # for Dockerfile default to production
12
+ end
13
+
14
+ def name
15
+ gems.each do |gem|
16
+ frameworks.each do |framework|
17
+ if gem == framework
18
+ # Special case for puma. If puma is detected, it means it is a rack app.
19
+ if framework == "puma"
20
+ return "rack"
21
+ else
22
+ return framework
23
+ end
24
+ end
25
+ end
26
+ end
27
+ nil
28
+ end
29
+ memoize :name
30
+
31
+ def frameworks
32
+ %w[
33
+ rails
34
+ sinatra
35
+ hanami
36
+ rack
37
+ puma
38
+ ]
39
+ end
40
+
41
+ def gems
42
+ return [] unless File.exist?("Gemfile")
43
+ Bundler.with_unbundled_env do
44
+ gemfile_content = File.read("Gemfile")
45
+ dsl = Bundler::Dsl.evaluate(Bundler.default_gemfile, gemfile_content, {})
46
+ dsl.dependencies.map(&:name)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -23,7 +23,7 @@ module Jets::Git
23
23
  end
24
24
 
25
25
  def git_url
26
- "#{host}/#{full_repo}"
26
+ "#{host}/#{full_repo}" if host && full_repo
27
27
  end
28
28
 
29
29
  def host
@@ -65,5 +65,9 @@ module Jets::Git
65
65
  end
66
66
  default
67
67
  end
68
+
69
+ def git_current_branch
70
+ `rev-parse --abbrev-ref HEAD`.strip
71
+ end
68
72
  end
69
73
  end
@@ -5,7 +5,7 @@ module Jets::Lambda
5
5
  attr_accessor :class_name, :type
6
6
  attr_reader(
7
7
  :meth, :properties, :provisioned_concurrency, :iam_policy, :managed_iam_policy,
8
- :lang, :associated_resources
8
+ :lang, :associated_resources, :associated_outputs
9
9
  )
10
10
  def initialize(class_name, meth, options = {})
11
11
  @class_name = class_name.to_s
@@ -18,6 +18,7 @@ module Jets::Lambda
18
18
  @managed_iam_policy = options[:managed_iam_policy]
19
19
  @lang = options[:lang] || :ruby
20
20
  @associated_resources = options[:associated_resources] || {}
21
+ @associated_outputs = options[:associated_outputs] || {}
21
22
  @replacements = options[:replacements] || {} # added to baseline replacements
22
23
  end
23
24
 
@@ -169,6 +169,11 @@ module Jets::Lambda::Dsl
169
169
  # User-friendly short resource method. Users will use this.
170
170
  alias_method :resource, :associated_resources
171
171
 
172
+ def associated_outputs(outputs = {})
173
+ @associated_outputs ||= []
174
+ @associated_outputs << outputs
175
+ end
176
+
172
177
  # Using this odd way of setting these properties so we can keep the
173
178
  # resource(*definitions) signature simple. Using keyword arguments at the end
174
179
  # interfere with being able to pass in any keys for the properties hash at the end.
@@ -282,6 +287,7 @@ module Jets::Lambda::Dsl
282
287
  iam_policy: @iam_policy,
283
288
  managed_iam_policy: @managed_iam_policy,
284
289
  associated_resources: @associated_resources,
290
+ associated_outputs: @associated_outputs,
285
291
  lang: lang,
286
292
  replacements: replacements(meth))
287
293
 
@@ -11,18 +11,22 @@ module Jets::Shim::Adapter
11
11
 
12
12
  class << self
13
13
  def handler(event, context = nil, target = nil)
14
- puts "The default fallback handler is being called because a handler for it could not be found"
15
- puts "For debugging, here is the"
14
+ puts <<~EOL
15
+ WARN: handler for it could not be found. The default fallback handler is being used.
16
+ The default fallback handler simply prints out the event payload for debugging, it will be printed below.
17
+ Usually the default handler is not what you want.
18
+ You might be using a test event payload for testing which does not represent a real event.
19
+ Please double check the event payload.
20
+ EOL
16
21
  if ENV["_HANDLER"] # on AWS Lambda
17
22
  puts "event: #{JSON.dump(event)}"
18
23
  else
19
24
  puts "event:"
20
25
  puts JSON.pretty_generate(event)
21
26
  end
22
- puts "Please double check the event payload.\n\n"
23
27
 
24
28
  if target
25
- puts "ERROR: event handler target not found: #{target}".color(:red)
29
+ puts "WARN: Event handler target not found: #{target}".color(:red)
26
30
  target_class, target_method = target.split(".")
27
31
  target_class = target_class.camelize
28
32
  target_method ||= "perform"
@@ -40,7 +44,8 @@ module Jets::Shim::Adapter
40
44
 
41
45
  else
42
46
  puts <<~EOL
43
- You can also configure a custom fallback handler in the config/jets/shim.rb file.
47
+ You can also configure a custom fallback handler with a config/jets/shim.rb file.
48
+ Though custom handlers rarely needed and you should double check the event payload.
44
49
  Example:
45
50
 
46
51
  config/jets/shim.rb
@@ -55,7 +60,7 @@ module Jets::Shim::Adapter
55
60
  end
56
61
  # Custom Jets error message
57
62
  {
58
- errorMessage: "event handler not found. Look at logs for details",
63
+ errorMessage: "Event handler not found. Look at logs for details",
59
64
  errorType: "JetsEventHandlerNotFound"
60
65
  }
61
66
  end
@@ -21,51 +21,46 @@ module Jets::Shim
21
21
  elsif @rack_app
22
22
  @rack_app
23
23
  else
24
- boot_path = @boot_path || infer_boot_path
25
- # Possible that boot_path is nil.
26
- # Case: Jets events app only.
27
- if boot_path
28
- require_boot_path(boot_path) if boot_path # in case boot_path is not set
29
- infer_rack_app(boot_path) # IE: Rails.application or App
30
- end
24
+ framework_app
31
25
  end
32
26
  end
33
27
  alias_method :app, :rack_app
34
28
  alias_method :app=, :rack_app=
35
29
 
36
- def infer_boot_path
37
- if rails?
38
- "config/environment"
39
- elsif File.exist?("app.rb")
40
- "app"
30
+ def framework_app
31
+ # Explicitly set boot_path takes precedence
32
+ # IE: my_app.rb => MyApp
33
+ if @boot_path && File.exist?(@boot_path)
34
+ app_class = @boot_path.sub(/\.rb$/, "").camelize.constantize
35
+ return app_class
41
36
  end
42
- end
43
37
 
44
- def require_boot_path(path)
45
- path = path.starts_with?(".") ? path : "./#{path}"
46
- path = path.ends_with?(".rb") ? path : "#{path}.rb"
47
- require path # IE: config/environment.rb (Rails) or app.rb (generic rack app)
48
- end
38
+ # Infer app.rb boot_path
39
+ # IE: app.rb => App
40
+ if File.exist?("app.rb")
41
+ require_boot_path("app")
42
+ return App
43
+ end
49
44
 
50
- def infer_rack_app(boot_path)
51
- if rails?
45
+ # Infer rack app from config.ru
46
+ case framework
47
+ when "rails"
48
+ require_boot_path "config/environment"
52
49
  Rails.application
53
- else
54
- boot_path.sub!(/\.rb$/, "") # remove .rb extension
55
- boot_path.camelize.constantize
50
+ when "hanami"
51
+ require "hanami/boot"
52
+ Hanami.app
56
53
  end
57
54
  end
58
55
 
59
- def rails?
60
- framework?(:rails)
56
+ def require_boot_path(path)
57
+ path = path.starts_with?(".") ? path : "./#{path}"
58
+ path = path.ends_with?(".rb") ? path : "#{path}.rb"
59
+ require path # IE: config/environment.rb (Rails) or app.rb (generic rack app)
61
60
  end
62
61
 
63
- def framework?(name)
64
- if File.exist?("config.ru")
65
- # IE: Jets.application or Rails.application
66
- IO.readlines("config.ru").any? { |l| l.include?("#{name.to_s.camelize}.application") }
67
- end
62
+ def framework
63
+ Jets::Framework.name
68
64
  end
69
- memoize :framework?
70
65
  end
71
66
  end
@@ -0,0 +1,52 @@
1
+ module Jets::Util
2
+ module FormatTime
3
+ def pretty_time(time)
4
+ datetime = case time
5
+ when Time
6
+ time.to_datetime
7
+ when String
8
+ DateTime.parse(time)
9
+ else
10
+ time
11
+ end
12
+
13
+ if datetime > 1.day.ago.utc
14
+ time_ago_in_words(datetime) + " ago"
15
+ else
16
+ tz_override = ENV["JETS_TZ"] # IE: America/Los_Angeles
17
+ local = if tz_override
18
+ tz = TZInfo::Timezone.get(tz_override)
19
+ tz.time_to_local(datetime)
20
+ else
21
+ datetime.new_offset(DateTime.now.offset) # local time
22
+ end
23
+
24
+ if tz_override
25
+ local.strftime("%b %-d, %Y %-l:%M:%S%P")
26
+ else
27
+ local.strftime("%b %-d, %Y %H:%M:%S")
28
+ end
29
+ end
30
+ end
31
+
32
+ # Simple implementation of time_ago_in_words so we dont have to include ActionView::Helpers::DateHelper
33
+ def time_ago_in_words(from_time, to_time = Time.now)
34
+ distance_in_seconds = (to_time - from_time).to_i
35
+ case distance_in_seconds
36
+ when 0..59
37
+ "#{distance_in_seconds} #{"second".pluralize(distance_in_seconds)}"
38
+ when 60..3599
39
+ minutes = distance_in_seconds / 60
40
+ "#{minutes} #{"minute".pluralize(minutes)}"
41
+ when 3600..86_399
42
+ hours = distance_in_seconds / 3600
43
+ "#{hours} #{"hour".pluralize(hours)}"
44
+ when 86_400..604_799
45
+ days = distance_in_seconds / 86_400
46
+ "#{days} #{"day".pluralize(days)}"
47
+ else
48
+ from_time.strftime("%B %d, %Y")
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/jets/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jets
2
- VERSION = "6.0.0.beta1"
2
+ VERSION = "6.0.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jets
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.beta1
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-20 00:00:00.000000000 Z
11
+ date: 2024-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-logs
@@ -647,6 +647,7 @@ files:
647
647
  - lib/jets/cfn/iam/policy.rb
648
648
  - lib/jets/cfn/resource.rb
649
649
  - lib/jets/cfn/resource/associated.rb
650
+ - lib/jets/cfn/resource/associated_outputs.rb
650
651
  - lib/jets/cfn/resource/codebuild/fleet.rb
651
652
  - lib/jets/cfn/resource/codebuild/iam_role.rb
652
653
  - lib/jets/cfn/resource/codebuild/project/base.rb
@@ -732,12 +733,15 @@ files:
732
733
  - lib/jets/cli/generate/templates/event_types/scheduled.rb.tt
733
734
  - lib/jets/cli/generate/templates/event_types/sns.rb.tt
734
735
  - lib/jets/cli/generate/templates/event_types/sqs.rb.tt
736
+ - lib/jets/cli/git.rb
737
+ - lib/jets/cli/git/push.rb
735
738
  - lib/jets/cli/group/actions.rb
736
739
  - lib/jets/cli/group/base.rb
737
740
  - lib/jets/cli/group/helpers.rb
738
741
  - lib/jets/cli/help.rb
739
742
  - lib/jets/cli/help/build.md
740
743
  - lib/jets/cli/help/call.md
744
+ - lib/jets/cli/help/concurrency/info.md
741
745
  - lib/jets/cli/help/curl.md
742
746
  - lib/jets/cli/help/delete.md
743
747
  - lib/jets/cli/help/deploy.md
@@ -756,7 +760,6 @@ files:
756
760
  - lib/jets/cli/help/status.md
757
761
  - lib/jets/cli/help/url.md
758
762
  - lib/jets/cli/init.rb
759
- - lib/jets/cli/init/detect.rb
760
763
  - lib/jets/cli/init/templates/config/jets/bootstrap.rb.tt
761
764
  - lib/jets/cli/init/templates/config/jets/deploy.rb.tt
762
765
  - lib/jets/cli/init/templates/config/jets/project.rb.tt
@@ -850,6 +853,7 @@ files:
850
853
  - lib/jets/event/helpers/sqs_event.rb
851
854
  - lib/jets/event/s3.rb
852
855
  - lib/jets/exception_reporting.rb
856
+ - lib/jets/framework.rb
853
857
  - lib/jets/git.rb
854
858
  - lib/jets/git/azure.rb
855
859
  - lib/jets/git/base.rb
@@ -918,6 +922,7 @@ files:
918
922
  - lib/jets/thor/version_check.rb
919
923
  - lib/jets/util/call_line.rb
920
924
  - lib/jets/util/camelize.rb
925
+ - lib/jets/util/format_time.rb
921
926
  - lib/jets/util/git.rb
922
927
  - lib/jets/util/logging.rb
923
928
  - lib/jets/util/pretty.rb
@@ -981,11 +986,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
981
986
  version: 2.7.0
982
987
  required_rubygems_version: !ruby/object:Gem::Requirement
983
988
  requirements:
984
- - - ">"
989
+ - - ">="
985
990
  - !ruby/object:Gem::Version
986
- version: 1.3.1
991
+ version: '0'
987
992
  requirements: []
988
- rubygems_version: 3.4.19
993
+ rubygems_version: 3.5.10
989
994
  signing_key:
990
995
  specification_version: 4
991
996
  summary: Serverless Deployment Service
@@ -1,30 +0,0 @@
1
- class Jets::CLI::Init
2
- class Detect
3
- def framework
4
- gems.each do |gem|
5
- frameworks.each do |framework|
6
- return framework if gem == framework
7
- end
8
- end
9
- nil
10
- end
11
-
12
- def frameworks
13
- %w[
14
- rails
15
- sinatra
16
- hanami
17
- grape
18
- ]
19
- end
20
-
21
- def gems
22
- return [] unless File.exist?("Gemfile")
23
- Bundler.with_unbundled_env do
24
- gemfile_content = File.read("Gemfile")
25
- dsl = Bundler::Dsl.evaluate(Bundler.default_gemfile, gemfile_content, {})
26
- dsl.dependencies.map(&:name)
27
- end
28
- end
29
- end
30
- end