cody 0.1.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.cody/buildspec.yml +8 -0
  3. data/.cody/project.rb +4 -0
  4. data/.cody/settings.yml +13 -0
  5. data/.gitignore +17 -10
  6. data/.gitmodules +9 -0
  7. data/.rspec +1 -1
  8. data/.ruby-version +1 -0
  9. data/CHANGELOG.md +59 -0
  10. data/Gemfile +3 -1
  11. data/Guardfile +19 -0
  12. data/LICENSE.txt +18 -17
  13. data/README.md +145 -18
  14. data/Rakefile +9 -2
  15. data/cody.gemspec +26 -12
  16. data/exe/cody +14 -0
  17. data/img/github-admin-settings-tab.png +0 -0
  18. data/lib/cody.rb +17 -1
  19. data/lib/cody/autoloader.rb +21 -0
  20. data/lib/cody/aws_services.rb +16 -0
  21. data/lib/cody/aws_services/helpers.rb +72 -0
  22. data/lib/cody/cli.rb +61 -0
  23. data/lib/cody/command.rb +82 -0
  24. data/lib/cody/completer.rb +159 -0
  25. data/lib/cody/completer/script.rb +6 -0
  26. data/lib/cody/completer/script.sh +10 -0
  27. data/lib/cody/core.rb +63 -0
  28. data/lib/cody/create.rb +12 -0
  29. data/lib/cody/default/settings.yml +3 -0
  30. data/lib/cody/delete.rb +27 -0
  31. data/lib/cody/deploy.rb +40 -0
  32. data/lib/cody/dsl/project.rb +119 -0
  33. data/lib/cody/dsl/project/ssm.rb +22 -0
  34. data/lib/cody/dsl/role.rb +50 -0
  35. data/lib/cody/dsl/schedule.rb +30 -0
  36. data/lib/cody/evaluate.rb +47 -0
  37. data/lib/cody/help.rb +9 -0
  38. data/lib/cody/help/completion.md +22 -0
  39. data/lib/cody/help/completion_script.md +3 -0
  40. data/lib/cody/help/deploy.md +32 -0
  41. data/lib/cody/help/init.md +71 -0
  42. data/lib/cody/help/start.md +12 -0
  43. data/lib/cody/init.rb +102 -0
  44. data/lib/cody/project.rb +72 -0
  45. data/lib/cody/role.rb +87 -0
  46. data/lib/cody/schedule.rb +102 -0
  47. data/lib/cody/sequence.rb +66 -0
  48. data/lib/cody/setting.rb +82 -0
  49. data/lib/cody/stack.rb +93 -0
  50. data/lib/cody/start.rb +69 -0
  51. data/lib/cody/update.rb +12 -0
  52. data/lib/cody/variables.rb +17 -0
  53. data/lib/cody/version.rb +2 -2
  54. data/lib/template/project/buildspec.yml +28 -0
  55. data/lib/template/project/project.rb.tt +29 -0
  56. data/lib/template/project/role.rb +2 -0
  57. data/lib/template/project/schedule.rb +3 -0
  58. data/lib/template/top/README.md +32 -0
  59. data/lib/template/top/settings.yml +9 -0
  60. data/lib/template/top/variables/base.rb +1 -0
  61. data/lib/template/top/variables/development.rb +1 -0
  62. data/lib/template/top/variables/production.rb +1 -0
  63. data/vendor/aws_data/CHANGELOG.md +7 -0
  64. data/vendor/aws_data/Gemfile +4 -0
  65. data/vendor/aws_data/LICENSE.txt +21 -0
  66. data/vendor/aws_data/README.md +42 -0
  67. data/vendor/aws_data/Rakefile +6 -0
  68. data/vendor/aws_data/aws_data.gemspec +30 -0
  69. data/{bin → vendor/aws_data/bin}/console +1 -1
  70. data/{bin → vendor/aws_data/bin}/setup +0 -0
  71. data/vendor/aws_data/lib/aws_data.rb +91 -0
  72. data/vendor/aws_data/lib/aws_data/version.rb +3 -0
  73. data/vendor/aws_data/spec/aws_data_spec.rb +5 -0
  74. data/vendor/aws_data/spec/spec_helper.rb +14 -0
  75. data/vendor/cfn-status/Gemfile +4 -0
  76. data/vendor/cfn-status/LICENSE.txt +21 -0
  77. data/vendor/cfn-status/README.md +56 -0
  78. data/vendor/cfn-status/Rakefile +6 -0
  79. data/vendor/cfn-status/bin/console +14 -0
  80. data/vendor/cfn-status/bin/setup +8 -0
  81. data/vendor/cfn-status/cfn-status.gemspec +30 -0
  82. data/vendor/cfn-status/lib/cfn/aws_service.rb +56 -0
  83. data/vendor/cfn-status/lib/cfn/status.rb +220 -0
  84. data/vendor/cfn-status/lib/cfn/status/version.rb +5 -0
  85. data/vendor/cfn-status/spec/cfn/status_spec.rb +81 -0
  86. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-complete.json +1080 -0
  87. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-in-progress.json +1080 -0
  88. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-update-rollback-complete.json +1086 -0
  89. data/vendor/cfn-status/spec/spec_helper.rb +14 -0
  90. data/vendor/cfn_camelizer/CHANGELOG.md +10 -0
  91. data/vendor/cfn_camelizer/Gemfile +4 -0
  92. data/vendor/cfn_camelizer/LICENSE.txt +21 -0
  93. data/vendor/cfn_camelizer/README.md +40 -0
  94. data/vendor/cfn_camelizer/Rakefile +6 -0
  95. data/vendor/cfn_camelizer/bin/console +14 -0
  96. data/vendor/cfn_camelizer/bin/setup +8 -0
  97. data/vendor/cfn_camelizer/cfn_camelizer.gemspec +32 -0
  98. data/vendor/cfn_camelizer/lib/camelizer.yml +33 -0
  99. data/vendor/cfn_camelizer/lib/cfn_camelizer.rb +92 -0
  100. data/vendor/cfn_camelizer/lib/cfn_camelizer/version.rb +3 -0
  101. data/vendor/cfn_camelizer/spec/cfn_camelizer_spec.rb +79 -0
  102. data/vendor/cfn_camelizer/spec/spec_helper.rb +14 -0
  103. metadata +268 -21
  104. data/.travis.yml +0 -7
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Trap ^C
4
+ Signal.trap("INT") {
5
+ puts "\nCtrl-C detected. Exiting..."
6
+ sleep 0.1
7
+ exit
8
+ }
9
+
10
+ $:.unshift(File.expand_path("../../lib", __FILE__))
11
+ require "cody"
12
+ require "cody/cli"
13
+
14
+ Cody::CLI.start(ARGV)
@@ -1,6 +1,22 @@
1
+ $:.unshift(File.expand_path("../", __FILE__))
1
2
  require "cody/version"
3
+ require "rainbow/ext/string"
4
+ require "yaml"
5
+
6
+ require "cody/autoloader"
7
+ Cody::Autoloader.setup
8
+
9
+ gem_root = File.dirname(__dir__)
10
+ $:.unshift("#{gem_root}/vendor/aws_data/lib")
11
+ require "aws_data"
12
+ $:.unshift("#{gem_root}/vendor/cfn_camelizer/lib")
13
+ require "cfn_camelizer"
14
+ $:.unshift("#{gem_root}/vendor/cfn-status/lib")
15
+ require "cfn/status"
2
16
 
3
17
  module Cody
4
18
  class Error < StandardError; end
5
- # Your code goes here...
19
+ extend Core
6
20
  end
21
+
22
+ Cody.set_aws_profile!
@@ -0,0 +1,21 @@
1
+ require "zeitwerk"
2
+
3
+ module Cody
4
+ class Autoloader
5
+ class Inflector < Zeitwerk::Inflector
6
+ def camelize(basename, _abspath)
7
+ map = { cli: "CLI", version: "VERSION" }
8
+ map[basename.to_sym] || super
9
+ end
10
+ end
11
+
12
+ class << self
13
+ def setup
14
+ loader = Zeitwerk::Loader.new
15
+ loader.inflector = Inflector.new
16
+ loader.push_dir(File.dirname(__dir__)) # lib
17
+ loader.setup
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require "aws-sdk-codebuild"
2
+ require "aws-sdk-cloudformation"
3
+
4
+ module Cody
5
+ module AwsServices
6
+ include Helpers
7
+
8
+ def codebuild
9
+ @codebuild ||= Aws::CodeBuild::Client.new
10
+ end
11
+
12
+ def cfn
13
+ @cfn ||= Aws::CloudFormation::Client.new
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,72 @@
1
+ module Cody::AwsServices
2
+ module Helpers
3
+ def stack_exists?(stack_name)
4
+ return false if ENV['TEST']
5
+
6
+ exist = nil
7
+ begin
8
+ # When the stack does not exist an exception is raised. Example:
9
+ # Aws::CloudFormation::Errors::ValidationError: Stack with id blah does not exist
10
+ cfn.describe_stacks(stack_name: stack_name)
11
+ exist = true
12
+ rescue Aws::CloudFormation::Errors::ValidationError => e
13
+ if e.message =~ /does not exist/
14
+ exist = false
15
+ elsif e.message.include?("'stackName' failed to satisfy constraint")
16
+ # Example of e.message when describe_stack with invalid stack name
17
+ # "1 validation error detected: Value 'instance_and_route53' at 'stackName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z][-a-zA-Z0-9]*|arn:[-a-zA-Z0-9:/._+]*"
18
+ puts "Invalid stack name: #{stack_name}"
19
+ puts "Full error message: #{e.message}"
20
+ exit 1
21
+ else
22
+ raise # re-raise exception because unsure what other errors can happen
23
+ end
24
+ end
25
+ exist
26
+ end
27
+
28
+ def project_name_convention(name_base)
29
+ items = [@project_name, @options[:type], Cody.env_extra]
30
+ items.insert(2, Cody.env) if Cody.settings.dig(:stack_naming, :append_env)
31
+ items.reject(&:blank?).compact.join("-")
32
+ end
33
+
34
+ def inferred_project_name
35
+ # Essentially the project's parent folder
36
+ File.basename(Dir.pwd).gsub('_','-').gsub(/\.+/,'-').gsub(/[^0-9a-zA-Z,-]/, '')
37
+ end
38
+
39
+ # Examples:
40
+ #
41
+ # myapp-ci-deploy # with Settings stack_naming append_env set to false.
42
+ # myapp-ci-deploy-development
43
+ # myapp-ci-deploy-development-2
44
+ #
45
+ def inferred_stack_name(project_name)
46
+ append_stack_name = Cody.settings.dig(:stack_naming, :append_stack_name) || "cody"
47
+ items = [project_name, @options[:type], Cody.env_extra, append_stack_name]
48
+ items.insert(3, Cody.env) if Cody.settings.dig(:stack_naming, :append_env)
49
+ items.reject(&:blank?).reject {|i| i == false}.compact.join("-")
50
+ end
51
+
52
+ def are_you_sure?(stack_name, action)
53
+ if @options[:sure]
54
+ sure = 'y'
55
+ else
56
+ message = case action
57
+ when :update
58
+ "Are you sure you want to want to update the #{stack_name.color(:green)} stack with the changes? (y/N)"
59
+ when :delete
60
+ "Are you sure you want to want to delete the #{stack_name.color(:green)} stack? (y/N)"
61
+ end
62
+ puts message
63
+ sure = $stdin.gets
64
+ end
65
+
66
+ unless sure =~ /^y/
67
+ puts "Whew! Exiting without running #{action}."
68
+ exit 0
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,61 @@
1
+ module Cody
2
+ class CLI < Command
3
+ class_option :verbose, type: :boolean
4
+ class_option :noop, type: :boolean
5
+
6
+ desc "init", "Initialize project with .cody files"
7
+ long_desc Help.text(:init)
8
+ Init.cli_options.each do |args|
9
+ option(*args)
10
+ end
11
+ register(Init, "init", "init", "Set up initial .cody files.")
12
+
13
+ common_options = Proc.new do
14
+ option :type, desc: "folder to use within .cody folder for different build types"
15
+ option :stack_name, desc: "Override the generated stack name. If you use this you must always specify it"
16
+ option :wait, type: :boolean, default: true, desc: "Wait for operation to complete"
17
+ end
18
+
19
+ desc "deploy", "Deploy codebuild project."
20
+ long_desc Help.text(:deploy)
21
+ common_options.call
22
+ def deploy(project_name=nil)
23
+ Deploy.new(options.merge(project_name: project_name)).run
24
+ end
25
+
26
+ desc "delete", "Delete codebuild project."
27
+ long_desc Help.text(:delete)
28
+ option :sure, desc: "Bypass are you sure prompt"
29
+ common_options.call
30
+ def delete(project_name=nil)
31
+ Delete.new(options.merge(project_name: project_name)).run
32
+ end
33
+
34
+ desc "start", "start codebuild project."
35
+ long_desc Help.text(:start)
36
+ option :source_version, default: "master", desc: "git branch"
37
+ option :branch, aliases: "b", default: "master", desc: "git branch"
38
+ option :env_vars, type: :array, desc: "env var overrides. IE: KEY1=VALUE1 KEY2=VALUE2"
39
+ common_options.call
40
+ def start(project_name=nil)
41
+ Start.new(options.merge(project_name: project_name)).run
42
+ end
43
+
44
+ desc "completion *PARAMS", "Prints words for auto-completion."
45
+ long_desc Help.text(:completion)
46
+ def completion(*params)
47
+ Completer.new(CLI, *params).run
48
+ end
49
+
50
+ desc "completion_script", "Generates a script that can be eval to setup auto-completion."
51
+ long_desc Help.text(:completion_script)
52
+ def completion_script
53
+ Completer::Script.generate
54
+ end
55
+
56
+ desc "version", "prints version"
57
+ def version
58
+ puts VERSION
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,82 @@
1
+ require "thor"
2
+
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 Cody
17
+ class Command < Thor
18
+ class << self
19
+ def dispatch(m, args, options, config)
20
+ # Allow calling for help via:
21
+ # codebuild command help
22
+ # codebuild command -h
23
+ # codebuild command --help
24
+ # codebuild command -D
25
+ #
26
+ # as well thor's normal way:
27
+ #
28
+ # codebuild 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
+ # codebuild version
36
+ # codebuild --version
37
+ # codebuild -v
38
+ version_flags = ["--version", "-v"]
39
+ if args.length == 1 && !(args & version_flags).empty?
40
+ args = ["version"]
41
+ end
42
+
43
+ super
44
+ end
45
+
46
+ # Override command_help to include the description at the top of the
47
+ # long_description.
48
+ def command_help(shell, command_name)
49
+ meth = normalize_command_name(command_name)
50
+ command = all_commands[meth]
51
+ alter_command_description(command)
52
+ super
53
+ end
54
+
55
+ def alter_command_description(command)
56
+ return unless command
57
+
58
+ # Add description to beginning of long_description
59
+ long_desc = if command.long_description
60
+ "#{command.description}\n\n#{command.long_description}"
61
+ else
62
+ command.description
63
+ end
64
+
65
+ # add reference url to end of the long_description
66
+ unless website.empty?
67
+ full_command = [command.ancestor_name, command.name].compact.join('-')
68
+ url = "#{website}/reference/codebuild-#{full_command}"
69
+ long_desc += "\n\nHelp also available at: #{url}"
70
+ end
71
+
72
+ command.long_description = long_desc
73
+ end
74
+ private :alter_command_description
75
+
76
+ # meant to be overriden
77
+ def website
78
+ ""
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,159 @@
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
+ codebuild completion
52
+ codebuild completion hello
53
+ codebuild completion hello name
54
+ codebuild completion hello name --
55
+ codebuild completion hello name --noop
56
+
57
+ codebuild completion
58
+ codebuild completion sub:goodbye
59
+ codebuild 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 Cody
72
+ class Completer
73
+ def initialize(command_class, *params)
74
+ @params = params
75
+ @current_command = @params[0]
76
+ @command_class = command_class # CLI initiall
77
+ end
78
+
79
+ def run
80
+ if subcommand?(@current_command)
81
+ subcommand_class = @command_class.subcommand_classes[@current_command]
82
+ @params.shift # destructive
83
+ Completer.new(subcommand_class, *@params).run # recursively use subcommand
84
+ return
85
+ end
86
+
87
+ # full command has been found!
88
+ unless found?(@current_command)
89
+ puts all_commands
90
+ return
91
+ end
92
+
93
+ # will only get to here if command aws found (above)
94
+ arity = @command_class.instance_method(@current_command).arity.abs
95
+ if @params.size > arity or thor_group_command?
96
+ puts options_completion
97
+ else
98
+ puts params_completion
99
+ end
100
+ end
101
+
102
+ def subcommand?(command)
103
+ @command_class.subcommands.include?(command)
104
+ end
105
+
106
+ # hacky way to detect that command is a registered Thor::Group command
107
+ def thor_group_command?
108
+ command_params(raw=true) == [[:rest, :args]]
109
+ end
110
+
111
+ def found?(command)
112
+ public_methods = @command_class.public_instance_methods(false)
113
+ command && public_methods.include?(command.to_sym)
114
+ end
115
+
116
+ # all top-level commands
117
+ def all_commands
118
+ commands = @command_class.all_commands.reject do |k,v|
119
+ v.is_a?(Thor::HiddenCommand)
120
+ end
121
+ commands.keys
122
+ end
123
+
124
+ def command_params(raw=false)
125
+ params = @command_class.instance_method(@current_command).parameters
126
+ # Example:
127
+ # >> Sub.instance_method(:goodbye).parameters
128
+ # => [[:req, :name]]
129
+ # >>
130
+ raw ? params : params.map!(&:last)
131
+ end
132
+
133
+ def params_completion
134
+ offset = @params.size - 1
135
+ offset_params = command_params[offset..-1]
136
+ command_params[offset..-1].first
137
+ end
138
+
139
+ def options_completion
140
+ used = ARGV.select { |a| a.include?('--') } # so we can remove used options
141
+
142
+ method_options = @command_class.all_commands[@current_command].options.keys
143
+ class_options = @command_class.class_options.keys
144
+
145
+ all_options = method_options + class_options + ['help']
146
+
147
+ all_options.map! { |o| "--#{o.to_s.gsub('_','-')}" }
148
+ filtered_options = all_options - used
149
+ filtered_options.uniq
150
+ end
151
+
152
+ # Useful for debugging. Using puts messes up completion.
153
+ def log(msg)
154
+ File.open("/tmp/complete.log", "a") do |file|
155
+ file.puts(msg)
156
+ end
157
+ end
158
+ end
159
+ end