lono 3.5.0 → 4.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 (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