lono 3.5.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -4
  3. data/.rspec +1 -0
  4. data/CHANGELOG.md +15 -1
  5. data/Gemfile +3 -3
  6. data/Guardfile +17 -8
  7. data/{LICENSE → LICENSE.txt} +1 -1
  8. data/README.md +20 -12
  9. data/Rakefile +1 -2
  10. data/{bin → exe}/lono +1 -0
  11. data/lib/lono.rb +12 -9
  12. data/lib/lono/cfn.rb +7 -9
  13. data/lib/lono/cfn/{aws_services.rb → aws_service.rb} +1 -1
  14. data/lib/lono/cfn/base.rb +41 -38
  15. data/lib/lono/cfn/create.rb +6 -2
  16. data/lib/lono/cfn/delete.rb +2 -2
  17. data/lib/lono/cfn/diff.rb +1 -1
  18. data/lib/lono/cfn/preview.rb +26 -15
  19. data/lib/lono/cfn/update.rb +11 -9
  20. data/lib/lono/cfn/util.rb +3 -3
  21. data/lib/lono/clean.rb +1 -1
  22. data/lib/lono/cli.rb +71 -39
  23. data/lib/lono/command.rb +42 -18
  24. data/lib/lono/completer.rb +162 -0
  25. data/lib/lono/completer/script.rb +6 -0
  26. data/lib/lono/completer/script.sh +10 -0
  27. data/lib/lono/completion.rb +15 -0
  28. data/lib/lono/core.rb +23 -9
  29. data/lib/lono/core/config.rb +20 -0
  30. data/lib/lono/default/settings.yml +33 -13
  31. data/lib/lono/help.rb +6 -79
  32. data/lib/lono/help/cfn.md +6 -0
  33. data/lib/lono/help/cfn/create.md +22 -0
  34. data/lib/lono/help/cfn/delete.md +5 -0
  35. data/lib/lono/help/cfn/diff.md +5 -0
  36. data/lib/lono/help/cfn/download.md +5 -0
  37. data/lib/lono/help/cfn/preview.md +11 -0
  38. data/lib/lono/help/cfn/update.md +21 -0
  39. data/lib/lono/help/completion.md +22 -0
  40. data/lib/lono/help/completion_script.md +3 -0
  41. data/lib/lono/help/generate.md +7 -0
  42. data/lib/lono/help/hello.md +5 -0
  43. data/lib/lono/help/import.md +7 -0
  44. data/lib/lono/help/inspect.md +4 -0
  45. data/lib/lono/help/inspect/depends.md +3 -0
  46. data/lib/lono/help/inspect/summary.md +3 -0
  47. data/lib/lono/help/new.md +8 -0
  48. data/lib/lono/help/param.md +3 -0
  49. data/lib/lono/{param/help.rb → help/param/generate.md} +1 -9
  50. data/lib/lono/help/script/build.md +5 -0
  51. data/lib/lono/help/script/upload.md +8 -0
  52. data/lib/lono/help/template.md +4 -0
  53. data/lib/lono/help/template/bashify.md +4 -0
  54. data/lib/lono/help/template/generate.md +7 -0
  55. data/lib/lono/help/user_data.md +3 -0
  56. data/lib/lono/importer.rb +43 -20
  57. data/lib/lono/inspector.rb +2 -19
  58. data/lib/lono/inspector/base.rb +2 -2
  59. data/lib/lono/inspector/{depends.rb → graph.rb} +3 -3
  60. data/lib/lono/inspector/summary.rb +1 -1
  61. data/lib/lono/new.rb +79 -26
  62. data/lib/lono/new/helper.rb +16 -0
  63. data/lib/lono/new/message.rb +35 -0
  64. data/lib/lono/param.rb +1 -2
  65. data/lib/lono/param/generator.rb +34 -86
  66. data/lib/lono/project_checker.rb +35 -40
  67. data/lib/lono/script.rb +19 -0
  68. data/lib/lono/script/base.rb +9 -0
  69. data/lib/lono/script/build.rb +73 -0
  70. data/lib/lono/script/upload.rb +81 -0
  71. data/lib/lono/sequence.rb +33 -0
  72. data/lib/lono/setting.rb +83 -0
  73. data/lib/lono/template.rb +8 -9
  74. data/lib/lono/template/{aws_services.rb → aws_service.rb} +1 -1
  75. data/lib/lono/template/context.rb +73 -0
  76. data/lib/lono/template/dsl.rb +63 -64
  77. data/lib/lono/template/helper.rb +201 -0
  78. data/lib/lono/template/template.rb +29 -221
  79. data/lib/lono/template/upload.rb +41 -33
  80. data/lib/lono/upgrade4.rb +175 -0
  81. data/lib/lono/user_data.rb +31 -0
  82. data/lib/lono/version.rb +1 -1
  83. data/lib/starter_projects/autoscaling/.gitignore +1 -0
  84. data/lib/starter_projects/{json_project → autoscaling}/Gemfile +0 -0
  85. data/lib/starter_projects/{yaml_project → autoscaling}/Guardfile +0 -0
  86. data/lib/starter_projects/autoscaling/README.md +118 -0
  87. data/lib/starter_projects/autoscaling/app/definitions/base.rb +2 -0
  88. data/lib/starter_projects/autoscaling/app/templates/autoscaling.yml +682 -0
  89. data/lib/starter_projects/autoscaling/config/params/base/autoscaling.txt +6 -0
  90. data/lib/starter_projects/autoscaling/config/settings.yml +33 -0
  91. data/lib/starter_projects/ec2/.gitignore +1 -0
  92. data/lib/starter_projects/{yaml_project → ec2}/Gemfile +0 -0
  93. data/lib/starter_projects/{json_project → ec2}/Guardfile +1 -1
  94. data/lib/starter_projects/ec2/README.md +86 -0
  95. data/lib/starter_projects/ec2/app/definitions/base.rb +2 -0
  96. data/lib/starter_projects/ec2/app/definitions/development.rb +1 -0
  97. data/lib/starter_projects/ec2/app/definitions/production.rb +1 -0
  98. data/lib/starter_projects/{yaml_project → ec2/app}/helpers/my_custom_helper.rb +0 -0
  99. data/lib/starter_projects/{json_project/templates/user_data/app.sh → ec2/app/partials/user_data/bootstrap.sh} +1 -2
  100. data/lib/starter_projects/{yaml_project → ec2/app}/templates/example.yml +0 -0
  101. data/lib/starter_projects/{json_project/params/base/api-web.txt → ec2/config/params/base/example.txt} +0 -0
  102. data/lib/starter_projects/ec2/config/params/development/example.txt +3 -0
  103. data/lib/starter_projects/ec2/config/params/production/example.txt +2 -0
  104. data/lib/starter_projects/ec2/config/settings.yml +33 -0
  105. data/lib/starter_projects/ec2/config/variables/base.rb +3 -0
  106. data/lib/starter_projects/ec2/config/variables/development.rb +2 -0
  107. data/lib/starter_projects/ec2/config/variables/production.rb +2 -0
  108. data/lib/starter_projects/ec2/welcome.txt +8 -0
  109. data/lib/starter_projects/skeleton/.gitignore +1 -0
  110. data/lib/starter_projects/skeleton/Gemfile +3 -0
  111. data/lib/starter_projects/skeleton/Guardfile +12 -0
  112. data/lib/starter_projects/skeleton/README.md +53 -0
  113. data/{spec/fixtures/my_project/templates/.gitkeep → lib/starter_projects/skeleton/app/definitions/base.rb} +0 -0
  114. data/lib/starter_projects/skeleton/config/settings.yml +33 -0
  115. data/lib/starter_projects/skeleton/welcome.txt +7 -0
  116. data/lono.gemspec +12 -10
  117. data/spec/fixtures/lono_project/.gitignore +1 -0
  118. data/spec/fixtures/lono_project/Gemfile +3 -0
  119. data/spec/fixtures/lono_project/Guardfile +12 -0
  120. data/spec/fixtures/lono_project/app/definitions/base.rb +10 -0
  121. data/spec/fixtures/lono_project/app/definitions/base/more.rb +7 -0
  122. data/spec/fixtures/lono_project/app/definitions/development.rb +1 -0
  123. data/spec/fixtures/lono_project/app/definitions/production.rb +1 -0
  124. data/spec/fixtures/lono_project/app/helpers/custom_helper.rb +5 -0
  125. data/spec/fixtures/lono_project/app/partials/security_group.yml +10 -0
  126. data/{lib/starter_projects/yaml_project/templates/partial → spec/fixtures/lono_project/app/partials}/user_data/bootstrap.sh +8 -2
  127. data/spec/fixtures/lono_project/app/templates/example.yml +50 -0
  128. data/{lib/starter_projects/yaml_project/params/base/api-web-prod.txt → spec/fixtures/lono_project/config/params/base/example.txt} +1 -0
  129. data/spec/fixtures/lono_project/config/params/development/example.txt +1 -0
  130. data/spec/fixtures/lono_project/config/params/production/example.txt +1 -0
  131. data/spec/fixtures/lono_project/config/settings.yml +31 -0
  132. data/spec/fixtures/lono_project/config/variables/base.rb +3 -0
  133. data/spec/fixtures/lono_project/config/variables/development.rb +1 -0
  134. data/spec/fixtures/lono_project/config/variables/production.rb +1 -0
  135. data/spec/fixtures/params/envonly/params/{prod → development}/network.txt +0 -0
  136. data/spec/fixtures/params/overlay/params/{prod → development}/network.txt +0 -0
  137. data/spec/fixtures/raw_templates/aws-waf-security-automations.template +2 -2
  138. data/spec/lib/lono/cfn_spec.rb +6 -9
  139. data/spec/lib/lono/cli_spec.rb +44 -0
  140. data/spec/lib/lono/completion_spec.rb +17 -0
  141. data/spec/lib/lono/inspect_spec.rb +6 -15
  142. data/spec/lib/lono/param/generator_spec.rb +45 -26
  143. data/spec/lib/lono/param_spec.rb +1 -3
  144. data/spec/lib/lono/setting_spec.rb +47 -0
  145. data/spec/lib/lono/template/dsl_spec.rb +33 -157
  146. data/spec/lib/lono/template_spec.rb +4 -16
  147. data/spec/spec_helper.rb +45 -14
  148. metadata +168 -82
  149. data/.coveralls.yml +0 -1
  150. data/lib/lono/cfn/help.rb +0 -103
  151. data/lib/lono/current_region.rb +0 -42
  152. data/lib/lono/inspector/help.rb +0 -21
  153. data/lib/lono/settings.rb +0 -45
  154. data/lib/lono/template/help.rb +0 -25
  155. data/lib/lono/template/helpers.rb +0 -136
  156. data/lib/starter_projects/json_project/.gitignore +0 -1
  157. data/lib/starter_projects/json_project/config/templates/base/blog.rb +0 -20
  158. data/lib/starter_projects/json_project/config/templates/base/stacks.rb +0 -58
  159. data/lib/starter_projects/json_project/templates/db.json +0 -212
  160. data/lib/starter_projects/json_project/templates/partial/host_record.json +0 -28
  161. data/lib/starter_projects/json_project/templates/partial/server.json +0 -45
  162. data/lib/starter_projects/json_project/templates/user_data/db.sh +0 -39
  163. data/lib/starter_projects/json_project/templates/user_data/db2.sh +0 -2
  164. data/lib/starter_projects/json_project/templates/user_data/ruby_script.rb +0 -5
  165. data/lib/starter_projects/json_project/templates/web.json +0 -386
  166. data/lib/starter_projects/yaml_project/.gitignore +0 -1
  167. data/lib/starter_projects/yaml_project/config/templates/base/blog.rb +0 -20
  168. data/lib/starter_projects/yaml_project/config/templates/base/stacks.rb +0 -56
  169. data/lib/starter_projects/yaml_project/config/templates/prod/stacks.rb +0 -1
  170. data/lib/starter_projects/yaml_project/config/templates/stag/stacks.rb +0 -1
  171. data/lib/starter_projects/yaml_project/config/variables/base/variables.rb +0 -4
  172. data/lib/starter_projects/yaml_project/config/variables/prod/variables.rb +0 -1
  173. data/lib/starter_projects/yaml_project/config/variables/stag/variables.rb +0 -1
  174. data/lib/starter_projects/yaml_project/params/base/example.txt +0 -2
  175. data/lib/starter_projects/yaml_project/params/prod/example.txt +0 -1
  176. data/lib/starter_projects/yaml_project/params/stag/example.txt +0 -1
  177. data/lib/starter_projects/yaml_project/templates/db.yml +0 -148
  178. data/lib/starter_projects/yaml_project/templates/partial/host_record.yml +0 -14
  179. data/lib/starter_projects/yaml_project/templates/partial/server.yml +0 -59
  180. data/lib/starter_projects/yaml_project/templates/web.yml +0 -206
  181. data/spec/fixtures/my_project/config/templates/base/stacks.rb +0 -3
  182. data/spec/fixtures/my_project/params/my-stack.txt +0 -3
  183. data/spec/fixtures/my_project/templates/my-stack.yml +0 -0
  184. data/spec/lib/lono/new_spec.rb +0 -59
  185. data/spec/lib/lono/template/template_spec.rb +0 -104
  186. data/spec/lib/lono_spec.rb +0 -27
@@ -1,3 +1,5 @@
1
+ require "yaml"
2
+
1
3
  class Lono::Cfn::Create < Lono::Cfn::Base
2
4
  # save_stack is the interface method
3
5
  def save_stack(params)
@@ -23,13 +25,15 @@ class Lono::Cfn::Create < Lono::Cfn::Base
23
25
  end
24
26
 
25
27
  template_body = IO.read(@template_path)
26
- cfn.create_stack(
28
+ params = {
27
29
  stack_name: @stack_name,
28
30
  template_body: template_body,
29
31
  parameters: params,
30
32
  capabilities: capabilities, # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
31
33
  disable_rollback: !@options[:rollback],
32
- )
34
+ }
35
+ show_parameters(params, "cfn.create_stack")
36
+ cfn.create_stack(params)
33
37
  puts message unless @options[:mute]
34
38
  end
35
39
 
@@ -1,5 +1,5 @@
1
1
  class Lono::Cfn::Delete
2
- include Lono::Cfn::AwsServices
2
+ include Lono::Cfn::AwsService
3
3
  include Lono::Cfn::Util
4
4
 
5
5
  def initialize(stack_name, options={})
@@ -12,7 +12,7 @@ class Lono::Cfn::Delete
12
12
  if @options[:noop]
13
13
  puts "NOOP #{message}"
14
14
  else
15
- are_you_sure?(:delete)
15
+ are_you_sure?(@stack_name, :delete)
16
16
 
17
17
  if stack_exists?(@stack_name)
18
18
  cfn.delete_stack(stack_name: @stack_name)
data/lib/lono/cfn/diff.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class Lono::Cfn::Diff < Lono::Cfn::Base
2
- include Lono::Cfn::AwsServices
2
+ include Lono::Cfn::AwsService
3
3
 
4
4
  def run
5
5
  unless stack_exists?(@stack_name)
@@ -20,29 +20,40 @@ class Lono::Cfn::Preview < Lono::Cfn::Base
20
20
  puts "WARN: Cannot create a change set for the stack because the #{@stack_name} does not exists.".colorize(:yellow)
21
21
  return false
22
22
  end
23
- exist_unless_updatable(stack_status(@stack_name))
23
+ exit_unless_updatable!(stack_status(@stack_name))
24
24
 
25
25
  template_body = IO.read(@template_path)
26
+ params = {
27
+ change_set_name: change_set_name,
28
+ stack_name: @stack_name,
29
+ template_body: template_body,
30
+ parameters: params,
31
+ capabilities: capabilities, # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
32
+ }
33
+ show_parameters(params, "cfn.create_change_set")
26
34
  begin
27
- cfn.create_change_set(
28
- change_set_name: change_set_name,
29
- stack_name: @stack_name,
30
- template_body: template_body,
31
- parameters: params,
32
- capabilities: capabilities, # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
33
- )
35
+ cfn.create_change_set(params)
34
36
  rescue Aws::CloudFormation::Errors::ValidationError => e
35
- if e.message =~ /^Parameters: /
36
- puts "Error creating CloudFormation preview because invalid CloudFormation parameters. Full error message:".colorize(:red)
37
- puts e.message
38
- quit(1)
39
- else
40
- raise
41
- end
37
+ handle_error(e)
42
38
  end
43
39
  true
44
40
  end
45
41
 
42
+ # Example errors:
43
+ # "Template error: variable names in Fn::Sub syntax must contain only alphanumeric characters, underscores, periods, and colons"
44
+ def handle_error(e)
45
+ raise if ENV['FULL_BACKTRACE']
46
+
47
+ if e.message =~ /^Parameters: / || e.message =~ /^Template error: /
48
+ puts "Error creating CloudFormation preview because invalid CloudFormation parameters. Full error message:".colorize(:red)
49
+ puts e.message
50
+ puts "For full backtrace run command again with FULL_BACKTRACE=1"
51
+ quit(1)
52
+ else
53
+ raise
54
+ end
55
+ end
56
+
46
57
  def display_change_set
47
58
  print "Generating CloudFormation Change Set for preview.."
48
59
  change_set = describe_change_set
@@ -16,12 +16,12 @@ class Lono::Cfn::Update < Lono::Cfn::Base
16
16
  puts "Cannot update a stack because the #{@stack_name} does not exists."
17
17
  return
18
18
  end
19
- exist_unless_updatable(stack_status(@stack_name))
19
+ exit_unless_updatable!(stack_status(@stack_name))
20
20
 
21
21
  error = nil
22
22
  diff.run if @options[:diff]
23
23
  preview.run if @options[:preview]
24
- are_you_sure?(:update)
24
+ are_you_sure?(@stack_name, :update)
25
25
 
26
26
  if @options[:change_set] # defaults to this
27
27
  message << " via change set: #{preview.change_set_name}"
@@ -34,14 +34,16 @@ class Lono::Cfn::Update < Lono::Cfn::Base
34
34
 
35
35
  def standard_update(params)
36
36
  template_body = IO.read(@template_path)
37
+ params = {
38
+ stack_name: @stack_name,
39
+ template_body: template_body,
40
+ parameters: params,
41
+ capabilities: capabilities, # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
42
+ disable_rollback: !@options[:rollback],
43
+ }
44
+ show_parameters(params, "cfn.update_stack")
37
45
  begin
38
- cfn.update_stack(
39
- stack_name: @stack_name,
40
- template_body: template_body,
41
- parameters: params,
42
- capabilities: capabilities, # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
43
- disable_rollback: !@options[:rollback],
44
- )
46
+ cfn.update_stack(params)
45
47
  rescue Aws::CloudFormation::Errors::ValidationError => e
46
48
  puts "ERROR: #{e.message}".red
47
49
  error = true
data/lib/lono/cfn/util.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  module Lono::Cfn::Util
2
- def are_you_sure?(action)
2
+ def are_you_sure?(stack_name, action)
3
3
  if @options[:sure]
4
4
  sure = 'y'
5
5
  else
6
6
  message = case action
7
7
  when :update
8
- "Are you sure you want to want to update the stack with the changes? (y/N)"
8
+ "Are you sure you want to want to update the '#{stack_name}' stack with the changes? (y/N)"
9
9
  when :delete
10
- "Are you sure you want to want to delete the stack? (y/N)"
10
+ "Are you sure you want to want to delete the '#{stack_name}' stack? (y/N)"
11
11
  end
12
12
  puts message
13
13
  sure = $stdin.gets
data/lib/lono/clean.rb CHANGED
@@ -9,6 +9,6 @@ class Lono::Clean
9
9
  def run
10
10
  puts "Cleaning up"
11
11
  puts "Removing output/ folder"
12
- FileUtils.rm_rf("#{Lono.root}/output")
12
+ FileUtils.rm_rf("#{Lono.config.output_path}")
13
13
  end
14
14
  end
data/lib/lono/cli.rb CHANGED
@@ -1,63 +1,95 @@
1
- require 'thor'
2
- require 'lono/command'
3
-
4
1
  module Lono
5
- autoload :Help, 'lono/help'
6
- class CLI < Lono::Command
7
-
8
- desc "new [NAME]", "Generates lono starter project"
9
- long_desc Help.new_long_desc
10
- option :force, type: :boolean, aliases: "-f", desc: "override existing starter files"
11
- option :quiet, type: :boolean, aliases: "-q", desc: "silence the output"
12
- option :format, type: :string, default: "yaml", desc: "starter project template format: json or yaml"
13
- def new(project_root)
14
- Lono::New.new(options.clone.merge(project_root: project_root)).run
15
- end
16
-
17
- desc "import [SOURCE]", "Imports raw CloudFormation template and lono-fies it"
18
- long_desc Help.import
19
- option :format, type: :string, default: "yaml", desc: "format for the final template"
20
- option :casing, default: "underscore", desc: "camelcase or underscore the template name"
21
- option :name, default: nil, desc: "final name of downloaded template without extension"
2
+ class CLI < Command
3
+
4
+ long_desc Help.text(:new)
5
+ New.cli_options.each do |args|
6
+ option *args
7
+ end
8
+ register(New, "new", "new NAME", "generates new CLI project")
9
+
10
+ desc "import SOURCE", "Imports raw CloudFormation template and lono-fies it"
11
+ long_desc Help.text(:import)
12
+ option :name, required: true, default: nil, desc: "final name of downloaded template without extension"
13
+ option :summary, default: true, type: :boolean, desc: "provide template summary after import"
22
14
  def import(source)
23
15
  Importer.new(source, options).run
24
16
  end
25
17
 
26
18
  desc "generate", "Generate both CloudFormation templates and parameters files"
27
- Help.generate
28
- option :clean, type: :boolean, aliases: "-c", desc: "remove all output files before generating"
29
- option :quiet, type: :boolean, aliases: "-q", desc: "silence the output"
30
- option :pretty, type: :boolean, default: true, desc: "json pretty the output. only applies with json format"
19
+ long_desc Help.text(:generate)
20
+ option :clean, type: :boolean, default: true, desc: "remove all output files before generating"
21
+ option :quiet, type: :boolean, desc: "silence the output"
31
22
  def generate
32
- puts "Generating both CloudFormation template and parameter files."
33
- Lono::Template::DSL.new(options.clone).run
34
- Lono::Param::Generator.generate_all(options.clone)
23
+ puts "Generating CloudFormation templates, parameters, and scripts"
24
+ Script::Build.new(options).run
25
+ Template::DSL.new(options).run
26
+ Param::Generator.generate_all(options)
27
+ end
28
+
29
+ desc "user_data NAME", "Generates user_data script for debugging"
30
+ long_desc Help.text(:user_data)
31
+ option :clean, type: :boolean, default: true, desc: "remove all output/user_data files before generating"
32
+ def user_data(name)
33
+ Script::Build.new(options).run
34
+ UserData.new(options.merge(name: name)).generate
35
+ end
36
+
37
+ desc "summary STACK", "Prints summary of CloudFormation template"
38
+ long_desc Help.text("summary")
39
+ def summary(name)
40
+ Lono::Inspector::Summary.new(name, options).run
41
+ end
42
+
43
+ desc "xgraph STACK", "Graphs dependencies tree of CloudFormation template resources"
44
+ long_desc Help.text("graph")
45
+ option :display, type: :string, desc: "graph or text", default: "graph"
46
+ option :noop, type: :boolean, desc: "noop mode"
47
+ def xgraph(name)
48
+ Lono::Inspector::Graph.new(name, options).run
35
49
  end
36
50
 
37
51
  desc "clean", "Clean up generated files"
38
52
  def clean
39
- Lono::Clean.new(options.clone).run
53
+ Clean.new(options).run
54
+ end
55
+
56
+ desc "completion *PARAMS", "prints words for auto-completion"
57
+ long_desc Help.text("completion")
58
+ def completion(*params)
59
+ Completer.new(CLI, *params).run
60
+ end
61
+
62
+ desc "completion_script", "generates script that can be eval to setup auto-completion", hide: true
63
+ long_desc Help.text("completion_script")
64
+ def completion_script
65
+ Completer::Script.generate
66
+ end
67
+
68
+ desc "upgrade4", "upgrade from version 3 to 4"
69
+ long_desc Help.text("upgrade3")
70
+ def upgrade4
71
+ Upgrade4.new(options).run
40
72
  end
41
73
 
42
74
  desc "version", "Prints version"
43
75
  def version
44
- puts Lono::VERSION
76
+ puts VERSION
45
77
  end
46
78
 
47
- desc "template ACTION", "template subcommand tasks"
48
- long_desc Help.template
79
+ desc "template SUBCOMMAND", "template subcommand tasks"
80
+ long_desc Help.text(:template)
49
81
  subcommand "template", Template
50
82
 
51
- desc "cfn ACTION", "cfn subcommand tasks"
52
- long_desc Help.cfn
83
+ desc "cfn SUBCOMMAND", "cfn subcommand tasks"
84
+ long_desc Help.text(:cfn)
53
85
  subcommand "cfn", Cfn
54
86
 
55
- desc "param ACTION", "param subcommand tasks"
56
- long_desc Help.param
57
- subcommand "param", Lono::Param
87
+ desc "param SUBCOMMAND", "param subcommand tasks"
88
+ long_desc Help.text(:param)
89
+ subcommand "param", Param
58
90
 
59
- desc "inspect ACTION", "inspect subcommand tasks"
60
- long_desc Help.inspector
61
- subcommand "inspect", Inspector
91
+ desc "script SUBCOMMAND", "script subcommand tasks"
92
+ long_desc Help.text(:script)
93
+ subcommand "script", Script
62
94
  end
63
95
  end
data/lib/lono/command.rb CHANGED
@@ -1,23 +1,47 @@
1
- require 'thor'
1
+ require "thor"
2
2
 
3
- class Lono::Command < Thor
4
- class << self
5
- def dispatch(m, args, options, config)
6
- # Allow calling for help via:
7
- # lono generate help
8
- # lono generate -h
9
- # lono generate --help
10
- # lono generate -D
11
- #
12
- # as well thor's nomral setting as
13
- #
14
- # lono help generate
15
- help_flags = Thor::HELP_MAPPINGS + ["help"]
16
- if args.length > 1 && !(args & help_flags).empty?
17
- args -= help_flags
18
- args.insert(-2, "help")
3
+ # Override thor's long_desc identation behavior
4
+ # https://github.com/erikhuda/thor/issues/398
5
+ class Thor
6
+ module Shell
7
+ class Basic
8
+ def print_wrapped(message, options = {})
9
+ message = "\n#{message}" unless message[0] == "\n"
10
+ stdout.puts message
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module Lono
17
+ class Command < Thor
18
+ class << self
19
+ def dispatch(m, args, options, config)
20
+ # Allow calling for help via:
21
+ # lono command help
22
+ # lono command -h
23
+ # lono command --help
24
+ # lono command -D
25
+ #
26
+ # as well thor's normal way:
27
+ #
28
+ # lono help command
29
+ help_flags = Thor::HELP_MAPPINGS + ["help"]
30
+ if args.length > 1 && !(args & help_flags).empty?
31
+ args -= help_flags
32
+ args.insert(-2, "help")
33
+ end
34
+
35
+ # lono version
36
+ # lono --version
37
+ # lono -v
38
+ version_flags = ["--version", "-v"]
39
+ if args.length == 1 && !(args & version_flags).empty?
40
+ args = ["version"]
41
+ end
42
+
43
+ super
19
44
  end
20
- super
21
45
  end
22
46
  end
23
47
  end
@@ -0,0 +1,162 @@
1
+ =begin
2
+ Code Explanation:
3
+
4
+ There are 3 types of things to auto-complete:
5
+
6
+ 1. command: the command itself
7
+ 2. parameters: command parameters.
8
+ 3. options: command options
9
+
10
+ Here's an example:
11
+
12
+ mycli hello name --from me
13
+
14
+ * command: hello
15
+ * parameters: name
16
+ * option: --from
17
+
18
+ When command parameters are done processing, the remaining completion words will be options. We can tell that the command params are completed based on the method arity.
19
+
20
+ ## Arity
21
+
22
+ For example, say you had a method for a CLI command with the following form:
23
+
24
+ ufo scale service count --cluster development
25
+
26
+ It's equivalent ruby method:
27
+
28
+ scale(service, count) = has an arity of 2
29
+
30
+ So typing:
31
+
32
+ ufo scale service count [TAB] # there are 3 parameters including the "scale" command according to Thor's CLI processing.
33
+
34
+ So the completion should only show options, something like this:
35
+
36
+ --noop --verbose --cluster
37
+
38
+ ## Splat Arguments
39
+
40
+ When the ruby method has a splat argument, it's arity is negative. Here are some example methods and their arities.
41
+
42
+ ship(service) = 1
43
+ scale(service, count) = 2
44
+ ships(*services) = -1
45
+ foo(example, *rest) = -2
46
+
47
+ Fortunately, negative and positive arity values are processed the same way. So we take simply take the absolute value of the arity and process it the same.
48
+
49
+ Here are some test cases, hit TAB after typing the command:
50
+
51
+ lono completion
52
+ lono completion hello
53
+ lono completion hello name
54
+ lono completion hello name --
55
+ lono completion hello name --noop
56
+
57
+ lono completion
58
+ lono completion sub:goodbye
59
+ lono completion sub:goodbye name
60
+
61
+ ## Subcommands and Thor::Group Registered Commands
62
+
63
+ Sometimes the commands are not simple thor commands but are subcommands or Thor::Group commands. A good specific example is the ufo tool.
64
+
65
+ * regular command: ufo ship
66
+ * subcommand: ufo docker
67
+ * Thor::Group command: ufo init
68
+
69
+ Auto-completion accounts for each of these type of commands.
70
+ =end
71
+ module Lono
72
+ class Completer
73
+ autoload :Script, 'lono/completer/script'
74
+
75
+ def initialize(command_class, *params)
76
+ @params = params
77
+ @current_command = @params[0]
78
+ @command_class = command_class # CLI initiall
79
+ end
80
+
81
+
82
+ def run
83
+ if subcommand?(@current_command)
84
+ subcommand_class = @command_class.subcommand_classes[@current_command]
85
+ @params.shift # destructive
86
+ Completer.new(subcommand_class, *@params).run # recursively use subcommand
87
+ return
88
+ end
89
+
90
+ # full command has been found!
91
+ unless found?(@current_command)
92
+ puts all_commands
93
+ return
94
+ end
95
+
96
+ # will only get to here if command aws found (above)
97
+ arity = @command_class.instance_method(@current_command).arity.abs
98
+ if @params.size > arity or thor_group_command?
99
+ puts options_completion
100
+ else
101
+ puts params_completion
102
+ end
103
+ end
104
+
105
+ def subcommand?(command)
106
+ @command_class.subcommands.include?(command)
107
+ end
108
+
109
+ # hacky way to detect that command is a registered Thor::Group command
110
+ def thor_group_command?
111
+ command_params(raw=true) == [[:rest, :args]]
112
+ end
113
+
114
+ def found?(command)
115
+ public_methods = @command_class.public_instance_methods(false)
116
+ command && public_methods.include?(command.to_sym)
117
+ end
118
+
119
+ # all top-level commands
120
+ def all_commands
121
+ commands = @command_class.all_commands.reject do |k,v|
122
+ v.is_a?(Thor::HiddenCommand)
123
+ end
124
+ commands.keys
125
+ end
126
+
127
+ def command_params(raw=false)
128
+ params = @command_class.instance_method(@current_command).parameters
129
+ # Example:
130
+ # >> Sub.instance_method(:goodbye).parameters
131
+ # => [[:req, :name]]
132
+ # >>
133
+ raw ? params : params.map!(&:last)
134
+ end
135
+
136
+ def params_completion
137
+ offset = @params.size - 1
138
+ command_params[offset..-1]
139
+ command_params[offset..-1].first
140
+ end
141
+
142
+ def options_completion
143
+ used = ARGV.select { |a| a.include?('--') } # so we can remove used options
144
+
145
+ method_options = @command_class.all_commands[@current_command].options.keys
146
+ class_options = @command_class.class_options.keys
147
+
148
+ all_options = method_options + class_options + ['help']
149
+
150
+ all_options.map! { |o| "--#{o.to_s.gsub('_','-')}" }
151
+ filtered_options = all_options - used
152
+ filtered_options.uniq
153
+ end
154
+
155
+ # Useful for debugging. Using puts messes up completion.
156
+ def log(msg)
157
+ File.open("/tmp/complete.log", "a") do |file|
158
+ file.puts(msg)
159
+ end
160
+ end
161
+ end
162
+ end