sonic-screwdriver 2.1.0 → 2.2.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c57f41a4fe34d84ef5b27b1a02119d4f44ebabfc7cb4a4c5b16eee2674d4a17
4
- data.tar.gz: 95a874ce5035de37d982a7e305c1da4fe264c12fa6caf4b25e789cf311c8ddf5
3
+ metadata.gz: 2ac8a33e325657ad0ee4fab9c2d7a4a1d8c3fafbaf0017fed20446983869c39b
4
+ data.tar.gz: 4b8bcf43b60ae33936f07b4d800173b2c1df27d091f1fac3e8f2f082501a8823
5
5
  SHA512:
6
- metadata.gz: d5945bde37e07a2bca384f1ed60b35aa03933adf6ce67fae1c3149c25df8debe564fe0b0ee1c266faafba48d7e3f815d8720b0f38eb40329884c7a795b17b099
7
- data.tar.gz: 3cacc002fd1f7926ec97a32534c62ed4c7081aee660173b93db3df2711c2dbeb61d33ae140c94eea336cb7f007715e964d418c817a7eff2ffeaffcd36139a05a
6
+ metadata.gz: 97d9a1992af8d4e2dbede1ae0f8f965b773b94cde16bc06493fc99932cdbc3205c63ebef16c649e95e2f6f9d539775c45ed36db01897f7e33fcc80fe5509f9bb
7
+ data.tar.gz: dd12a4015571ef144a3a793f9cae38c09ab4f55f8353aea07fbe8c5d598057167b5111988d1a12c595a3d044213bf126deca1fc5c8901fa21b23b3742c28cc94
data/.circleci/config.yml CHANGED
@@ -7,7 +7,7 @@ jobs:
7
7
  build:
8
8
  docker:
9
9
  # specify the version you desire here
10
- - image: circleci/ruby:2.5.1-node-browsers
10
+ - image: circleci/ruby:2.6.5-node-browsers
11
11
 
12
12
  # Specify service dependencies here if necessary
13
13
  # CircleCI maintains a library of pre-built images
@@ -35,6 +35,8 @@ jobs:
35
35
  - run:
36
36
  name: install dependencies
37
37
  command: |
38
+ gem install bundler
39
+ gem update --system
38
40
  bundle install --jobs=4 --retry=3 --path vendor/bundle
39
41
 
40
42
  - save_cache:
data/.gitignore CHANGED
@@ -14,3 +14,4 @@ spec/reports
14
14
  test/tmp
15
15
  test/version_tmp
16
16
  tmp
17
+ Gemfile.lock
data/CHANGELOG.md CHANGED
@@ -1,7 +1,25 @@
1
1
  # Change Log
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
- This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
4
+ This project *loosely* adheres to [Semantic Versioning](http://semver.org/), even before v1.0.
5
+
6
+ ## [2.2.3] - 2021-04-12
7
+ - [#12](https://github.com/boltops-tools/sonic/pull/12) fix cli check
8
+
9
+ ## [2.2.2] - 2021-04-12
10
+ - [#11](https://github.com/boltops-tools/sonic/pull/11) cli check
11
+
12
+ ## [2.2.1] - 2021-04-12
13
+ - [#9](https://github.com/boltops-tools/sonic/pull/9) improve command array handling
14
+
15
+ ## [2.2.0]
16
+ - #7 sonic execute command: `--tags` and `--instance-id` options instead of polymorphic list. Breaking behavior.
17
+ - display output even if tags used
18
+ - configure default log group
19
+ - correct exit status
20
+
21
+ ## [2.1.1]
22
+ - use rainbow gem for terminal colors
5
23
 
6
24
  ## [2.1.0]
7
25
  - change the setting format to ssh.user instead of bastion.user
data/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  [![Gitter](https://badges.gitter.im/boltopslabs/sonic.svg)](https://gitter.im/boltopslabs/sonic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
5
5
  [![Support](https://img.shields.io/badge/get-support-blue.svg)](https://boltops.com?utm_source=badge&utm_medium=badge&utm_campaign=sonic)
6
6
 
7
+ [![BoltOps Badge](https://img.boltops.com/boltops/badges/boltops-badge.png)](https://www.boltops.com)
8
+
7
9
  Sonic is a multi-functional tool that helps you manage AWS resources. Sonic contains a group of commands that help debug EC2 instances and ECS containers quickly.
8
10
 
9
11
  See [sonic-screwdriver.cloud](http://sonic-screwdriver.cloud) for full documentation.
@@ -32,15 +34,7 @@ By the time I get into the container, I need to remind my brain of what the orig
32
34
 
33
35
  You can install sonic with RubyGems
34
36
 
35
- ```sh
36
- gem install sonic
37
- ```
38
-
39
- If you want to quickly install sonic without having to worry about sonic's dependencies you can also install the Bolts Toolbelt which has sonic included.
40
-
41
- ```sh
42
- brew cask install boltopslabs/software/bolts
43
- ```
37
+ gem install sonic
44
38
 
45
39
  Full installation instructions are at [Install Sonic Screwdriver](http://sonic-screwdriver.cloud/docs/install/). There are some server side dependencies for some of the sonic commands, so it is important to read through the full installation guide.
46
40
 
@@ -48,30 +42,33 @@ Full installation instructions are at [Install Sonic Screwdriver](http://sonic-s
48
42
 
49
43
  Here is a quick overview of sonic abilities:
50
44
 
51
- ```sh
52
- # ssh into an instance
53
- sonic ssh i-0f7f833131a51ce35
54
- sonic ssh hi-web # ec2 tag
55
- sonic ssh hi-web --cluster staging # ecs service name
56
- sonic ssh hi-web --cluster staging # ecs service name
57
- sonic ssh 7fbc8c75-4675-4d39-a5a4-0395ff8cd474 --cluster staging # ECS container id
58
- sonic ssh 1ed12abd-645c-4a05-9acf-739b9d790170 --cluster staging # ECS task id
45
+ # ssh into an instance
46
+ sonic ssh i-0f7f833131a51ce35
47
+ sonic ssh demo-web # ec2 tag
48
+ sonic ssh demo-web --cluster staging # ecs service name
49
+ sonic ssh demo-web --cluster staging # ecs service name
50
+ sonic ssh 7fbc8c75-4675-4d39-a5a4-0395ff8cd474 --cluster staging # ECS container id
51
+ sonic ssh 1ed12abd-645c-4a05-9acf-739b9d790170 --cluster staging # ECS task id
52
+
53
+ # docker exec to a running ECS docker container
54
+ sonic ecs exec demo-web
59
55
 
60
- # docker exec to a running ECS docker container
61
- sonic ecs exec hi-web
56
+ # docker run with the same environment as the ECS docker running containers
57
+ sonic ecs sh demo-web
62
58
 
63
- # docker run with the same environment as the ECS docker running containers
64
- sonic ecs sh hi-web
59
+ # run command with instance ids
60
+ sonic execute --instance-ids i-111 uptime
61
+ sonic execute --instance-ids i-111,i-222 uptime
65
62
 
66
- # run command on 1 instance
67
- sonic execute i-0f7f833131a51ce35 uptime
63
+ # run command on all instances tagged with Name demo-web and worker
64
+ sonic execute --tags Name=demo-web,demo-worker uptime
68
65
 
69
- # run command on all instances tagged with hi-web and worker
70
- sonic execute hi-web,hi-worker uptime
66
+ # run command on all instances with multiple tags
67
+ # Quotes are important due to semi-colon
68
+ sonic execute --tags "Name=demo-web,demo-worker;Owner=test" uptime
71
69
 
72
- # list ec2 instances
73
- sonic list hi-web
74
- ```
70
+ # list ec2 instances
71
+ sonic list demo-web
75
72
 
76
73
  ## Contributing
77
74
 
@@ -16,16 +16,6 @@ You can also add sonic to your Gemfile in your project if you are working with a
16
16
  gem "sonic-screwdriver"
17
17
  ```
18
18
 
19
- ### Install with Bolts Toolbelt
20
-
21
- If you want to install sonic without having to worry about sonic's ruby dependency you can simply install the Bolts Toolbelt which has sonic included.
22
-
23
- ```sh
24
- brew cask install boltopslabs/software/bolts
25
- ```
26
-
27
- For more information about the Bolts Toolbelt or to get an installer for another operating system visit: [https://boltops.com/toolbelt](https://boltops.com/toolbelt)
28
-
29
19
  ### Server Side Dependencies
30
20
 
31
21
  For a small set of the commands there are server side dependencies.
@@ -6,56 +6,50 @@ title: Sonic Execute
6
6
 
7
7
  Sonic provides a way to execute commands remotely and securely across a list of AWS servers. It does this by leveraging [Amazon EC2 Run Command](https://aws.amazon.com/ec2/execute/). Sonic a simple interface and some conveniences for you. The command is called `sonic execute`:
8
8
 
9
- ```sh
10
- sonic execute [FILTER] [COMMAND]
11
- ```
9
+ sonic execute [FILTER] [COMMAND]
12
10
 
13
11
  ## Examples Summary
14
12
 
15
- ```sh
16
- sonic execute hi-web uptime
17
- sonic execute hi-web-prod uptime
18
- sonic execute i-030033c20c54bf149,i-030033c20c54bf150 uname -a
19
- sonic execute i-030033c20c54bf149 file://hello.sh
20
- ```
13
+ sonic execute --tags Name=demo-web uptime
14
+ sonic execute --tags Name=demo-web,demo-worker uptime # multiple tag values
15
+ sonic execute --instance-ids i-030033c20c54bf149,i-030033c20c54bf150 uname -a
16
+ sonic execute --instance-ids i-030033c20c54bf149 file://hello.sh
21
17
 
22
18
  ## Example Detailed
23
19
 
24
20
  Here's a command example output in detailed:
25
21
 
26
- ```sh
27
- $ sonic execute i-0bf51a000ab4e73a8 uptime
28
- Sending command to SSM with options:
29
- ---
30
- instance_ids:
31
- - i-0bf51a000ab4e73a8
32
- document_name: AWS-RunShellScript
33
- comment: sonic execute i-0bf51a000ab4e73a8 uptime
34
- parameters:
35
- commands:
36
- - uptime
37
- output_s3_region: us-east-1
38
- output_s3_bucket_name: [reacted]
39
- output_s3_key_prefix: ssm/commands/sonic
40
-
41
- Command sent to AWS SSM. To check the details of the command:
42
- aws ssm list-commands --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
43
- aws ssm get-command-invocation --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2 --instance-id i-0bf51a000ab4e73a8
44
-
45
- Waiting for ssm command to finish.....
46
- Command finished.
47
-
48
- Displaying output for i-0bf51a000ab4e73a8.
49
- Command status: Success
50
- Command standard output:
51
- 01:08:10 up 8 days, 6:41, 0 users, load average: 0.00, 0.00, 0.00
52
-
53
- To see the more output details visit:
54
- https://us-west-2.console.aws.amazon.com/systems-manager/run-command/0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
55
-
56
- Pro tip: the console url is already in your copy/paste clipboard.
57
- $
58
- ```
22
+ $ sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
23
+ Sending command to SSM with options:
24
+ ---
25
+ instance_ids:
26
+ - i-0bf51a000ab4e73a8
27
+ document_name: AWS-RunShellScript
28
+ comment: sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
29
+ parameters:
30
+ commands:
31
+ - uptime
32
+ output_s3_region: us-east-1
33
+ output_s3_bucket_name: [reacted]
34
+ output_s3_key_prefix: ssm/commands/sonic
35
+
36
+ Command sent to AWS SSM. To check the details of the command:
37
+ aws ssm list-commands --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
38
+ aws ssm get-command-invocation --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2 --instance-id i-0bf51a000ab4e73a8
39
+
40
+ Waiting for ssm command to finish.....
41
+ Command finished.
42
+
43
+ Displaying output for i-0bf51a000ab4e73a8.
44
+ Command status: Success
45
+ Command standard output:
46
+ 01:08:10 up 8 days, 6:41, 0 users, load average: 0.00, 0.00, 0.00
47
+
48
+ To see the more output details visit:
49
+ https://us-west-2.console.aws.amazon.com/systems-manager/run-command/0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
50
+
51
+ Pro tip: the console url is already in your copy/paste clipboard.
52
+ $
59
53
 
60
54
  Notice the conveniences of `sonic execute`, it:
61
55
 
@@ -69,28 +63,30 @@ The AWS SSM console looks like this:
69
63
 
70
64
  <img src="/img/tutorials/ec2-console-run-command.png" class="doc-photo" />
71
65
 
72
- ### Polymorphic Filter
66
+ ### Filter Options
73
67
 
74
- The `sonic execute` command can understand a variety of different filters. The filters can be a list of instances ids or one EC2 tag value. Note, ECS service names are *not* supported for the filter.
68
+ The `sonic execute` command can understand a variety of different filters: `--tags` and `--instance-ids`. Note, ECS service names are *not* supported for the filter.
75
69
 
76
70
  Here is an example, where the uptime command will run on both `i-030033c20c54bf149` and `i-030033c20c54bf150` instances.
77
71
 
78
- ```sh
79
- sonic execute i-066b140d9479e9681,i-09482b1a6e330fbf7 uptime
80
- ```
72
+ sonic execute --instance-ids i-066b140d9479e9681,i-09482b1a6e330fbf7 uptime
73
+
74
+ Here is an example, where the uptime command will run on instances tagged with `Name=demo-web`
75
+
76
+ sonic execute --tags Name=demo-web uptime
81
77
 
82
78
  ## Windows Support
83
79
 
84
80
  Windows is also supported. When running a command sonic will first attempt to use the `AWS-RunShellScript` run command, and if it detects that the instance's platform does not support `AWS-RunShellScript`, it will run the command with the `AWS-RunPowerShellScript` run command. Here's an example:
85
81
 
86
82
  ```
87
- $ sonic execute i-0917ad61b10fa1059 pwd
83
+ $ sonic execute --instance-ids i-0917ad61b10fa1059 pwd
88
84
  Sending command to SSM with options:
89
85
  ---
90
86
  instance_ids:
91
87
  - i-0917ad61b10fa1059
92
88
  document_name: AWS-RunShellScript
93
- comment: sonic execute i-0917ad61b10fa1059 pwd
89
+ comment: sonic execute --instance-ids i-0917ad61b10fa1059 pwd
94
90
  parameters:
95
91
  commands:
96
92
  - pwd
@@ -129,16 +125,12 @@ $
129
125
 
130
126
  Sometimes you might want to run more than just a one-liner command. If you need to run a full script, you can provide the file path to the script by designating it with `file://`. For example, here's a file called `hi.sh`:
131
127
 
132
- ```bash
133
- #!/bin/bash
134
- echo "hello world"
135
- ```
128
+ #!/bin/bash
129
+ echo "hello world"
136
130
 
137
131
  Here's how you run that file:
138
132
 
139
- ```sh
140
- sonic execute hi-web file://hi.sh
141
- ```
133
+ sonic execute demo-web file://hi.sh
142
134
 
143
135
  The file gets read by `sonic execute` and sent to EC2 Run Command to be executed.
144
136
 
@@ -6,10 +6,12 @@
6
6
  <div class="footer-col col-md-4">
7
7
  <h3>More Tools</h3>
8
8
  <ul class="list-unstyled tools">
9
- <li><a href="http://jack-eb.com">Jack</a></li>
10
- <li><a href="http://lono.cloud">Lono</a></li>
11
- <li><a href="http://ufoships.com">Ufo</a></li>
12
- <li><a href="https://boltops.com/toolbelt">Toolbelt</a></li>
9
+ <li><a href="https://terraspace.cloud">Terraspace</a></li>
10
+ <li><a href="https://kubes.guru">Kubes</a></li>
11
+ <li><a href="https://rubyonjets.com">Jets</a></li>
12
+ <li><a href="https://ufoships.com">Ufo</a></li>
13
+ <li><a href="https://lono.cloud">Lono</a></li>
14
+ <li><a href="https://jack-eb.com">Jack</a></li>
13
15
  </ul>
14
16
  </div>
15
17
  <div class="footer-col col-md-4">
@@ -16,10 +16,10 @@ Runs command across fleet of servers via AWS Run Command.
16
16
 
17
17
  ## Examples Summary
18
18
 
19
- $ sonic execute hi-web-prod uptime
20
- $ sonic execute hi-web-prod,hi-worker-prod,hi-clock-prod uptime
21
- $ sonic execute i-030033c20c54bf149,i-030033c20c54bf150 uname -a
22
- $ sonic execute i-030033c20c54bf149 file://hello.sh
19
+ sonic execute --tags Name=demo-web uptime
20
+ sonic execute --tags Name=demo-web,demo-worker uptime # multiple values
21
+ sonic execute --instance-ids i-030033c20c54bf149,i-030033c20c54bf150 uname -a
22
+ sonic execute --instance-ids i-030033c20c54bf149 file://hello.sh # script from file
23
23
 
24
24
  You cannot mix instance ids and tag names in the filter.
25
25
 
@@ -27,13 +27,13 @@ You cannot mix instance ids and tag names in the filter.
27
27
 
28
28
  Here's a command example output in detailed:
29
29
 
30
- $ sonic execute i-0bf51a000ab4e73a8 uptime
30
+ $ sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
31
31
  Sending command to SSM with options:
32
32
  ---
33
33
  instance_ids:
34
34
  - i-0bf51a000ab4e73a8
35
35
  document_name: AWS-RunShellScript
36
- comment: sonic execute i-0bf51a000ab4e73a8 uptime
36
+ comment: sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
37
37
  parameters:
38
38
  commands:
39
39
  - uptime
@@ -77,6 +77,8 @@ The AWS SSM console looks like this:
77
77
  ```
78
78
  [--zero-warn], [--no-zero-warn] # Warns user when no instances found
79
79
  # Default: true
80
+ [--instance-ids=INSTANCE_IDS] # Instance ids to execute command on. Format: --instance-ids "i-111,i-222"
81
+ [--tags=TAGS] # Tags used to determine what instances to execute command on. Format: --tags "Key1=v1,v2;Key2=v3"
80
82
  [--verbose], [--no-verbose]
81
83
  [--noop], [--no-noop]
82
84
  ```
data/docs/bin/web CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/bin/bash -ex
2
2
 
3
3
  bundle exec jekyll clean
4
- exec bundle exec jekyll serve
4
+ exec bundle exec jekyll serve --host 0.0.0.0 "$@"
data/docs/quick-start.md CHANGED
@@ -6,35 +6,31 @@ In a hurry? No sweat! Here's a quick overview of how to use sonic.
6
6
 
7
7
  ### Install
8
8
 
9
- ```sh
10
- gem install sonic-screwdriver
11
- ```
9
+ gem install sonic-screwdriver
12
10
 
13
11
  ### Usage
14
12
 
15
- ```sh
16
- # ssh into an instance
17
- sonic ssh i-0f7f833131a51ce35
18
- sonic ssh hi-web # ec2 tag
19
- sonic ssh hi-web --cluster staging # ecs service name
20
- sonic ssh 7fbc8c75-4675-4d39-a5a4-0395ff8cd474 --cluster staging # ECS container id
21
- sonic ssh 1ed12abd-645c-4a05-9acf-739b9d790170 --cluster staging # ECS task id
13
+ # ssh into an instance
14
+ sonic ssh i-0f7f833131a51ce35
15
+ sonic ssh demo-web # ec2 tag
16
+ sonic ssh demo-web --cluster staging # ecs service name
17
+ sonic ssh 7fbc8c75-4675-4d39-a5a4-0395ff8cd474 --cluster staging # ECS container id
18
+ sonic ssh 1ed12abd-645c-4a05-9acf-739b9d790170 --cluster staging # ECS task id
22
19
 
23
- # docker exec to a running ECS docker container
24
- sonic ecs exec hi-web
20
+ # docker exec to a running ECS docker container
21
+ sonic ecs exec demo-web
25
22
 
26
- # docker run with same environment as the ECS docker running containers
27
- sonic ecs sh hi-web
23
+ # docker run with same environment as the ECS docker running containers
24
+ sonic ecs sh demo-web
28
25
 
29
- # run command on 1 instance
30
- sonic execute i-0f7f833131a51ce35 uptime
26
+ # run command on 1 instance
27
+ sonic execute --instance-ids i-0f7f833131a51ce35 uptime
31
28
 
32
- # run command on all instances tagged with hi-web and worker
33
- sonic execute hi-web,hi-worker uptime
29
+ # run command on all instances tagged with demo-web and worker
30
+ sonic execute --tags Name=demo-web,demo-worker uptime
34
31
 
35
- # list ec2 instances
36
- sonic list hi-web
37
- ```
32
+ # list ec2 instances
33
+ sonic list demo-web
38
34
 
39
35
  Congratulations! You now know the basic sonic screwdriver commands now.
40
36
 
data/lib/sonic.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  $:.unshift(File.expand_path("../", __FILE__))
2
+ require "rainbow/ext/string"
2
3
  require "sonic/version"
3
- require "colorize"
4
4
 
5
5
  module Sonic
6
6
  autoload :Core, 'sonic/core'
data/lib/sonic/checks.rb CHANGED
@@ -3,7 +3,7 @@ module Sonic
3
3
  def check_cluster_exists!
4
4
  cluster = ecs.describe_clusters(clusters: [@cluster]).clusters.first
5
5
  unless cluster
6
- UI.error "The #{@cluster.green} cluster does not exist. Are you sure you specified the right cluster?"
6
+ UI.error "The #{@cluster.color(:green)} cluster does not exist. Are you sure you specified the right cluster?"
7
7
  exit 1
8
8
  end
9
9
  end
@@ -20,7 +20,7 @@ module Sonic
20
20
 
21
21
  service = resp.services.first
22
22
  unless service
23
- UI.error "The #{@service.green} service does not exist in #{@cluster.green} cluster. Are you sure you specified the right service and cluster?"
23
+ UI.error "The #{@service.color(:green)} service does not exist in #{@cluster.color(:green)} cluster. Are you sure you specified the right service and cluster?"
24
24
  exit 1
25
25
  end
26
26
  end
data/lib/sonic/cli.rb CHANGED
@@ -27,8 +27,10 @@ module Sonic
27
27
  long_desc Help.text("execute")
28
28
  option :zero_warn, type: :boolean, default: true, desc: "Warns user when no instances found"
29
29
  # filter - Filter ec2 instances by tag name or instance_ids separated by commas
30
- def execute(filter, *command)
31
- Execute.new(command, options.merge(filter: filter)).execute
30
+ option :instance_ids, desc: %Q|Instance ids to execute command on. Format: --instance-ids "i-111,i-222"|
31
+ option :tags, desc: %Q|Tags used to determine what instances to execute command on. Format: --tags "Key1=v1,v2;Key2=v3"|
32
+ def execute(*command)
33
+ Execute.new(command, options).execute
32
34
  end
33
35
 
34
36
  desc "list [FILTER]", "Lists ec2 instances."
data/lib/sonic/docker.rb CHANGED
@@ -30,7 +30,7 @@ module Sonic
30
30
 
31
31
  # command is an Array
32
32
  def execute(*command)
33
- UI.say "=> #{command.join(' ')}".colorize(:green)
33
+ UI.say "=> #{command.join(' ')}".color(:green)
34
34
  success = system(*command)
35
35
  unless success
36
36
  UI.error(command.join(' '))
@@ -59,7 +59,7 @@ module Sonic
59
59
 
60
60
  ssh = ["ssh", ssh_options, "-At", host, "uptime", "2>&1"]
61
61
  command = ssh.join(' ')
62
- puts "=> #{command}".colorize(:green)
62
+ puts "=> #{command}".color(:green)
63
63
  output = `#{command}`
64
64
  if output.include?("Permission denied")
65
65
  puts output
data/lib/sonic/execute.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'colorize'
2
1
  require 'yaml'
3
2
  require 'active_support/core_ext/hash'
4
3
 
@@ -9,7 +8,8 @@ module Sonic
9
8
  def initialize(command, options)
10
9
  @command = command
11
10
  @options = options
12
- @filter = @options[:filter].split(',').map{|s| s.strip}
11
+ @tags = @options[:tags]
12
+ @instance_ids = @options[:instance_ids]
13
13
  end
14
14
 
15
15
  # aws ssm send-command \
@@ -19,6 +19,7 @@ module Sonic
19
19
  # --parameters '{"commands":["#!/usr/bin/python","print \"Hello world from python\""]}' \
20
20
  # --query "Command.CommandId"
21
21
  def execute
22
+ check_filter_options!
22
23
  ssm_options = build_ssm_options
23
24
  if @options[:noop]
24
25
  UI.noop = true
@@ -34,6 +35,7 @@ module Sonic
34
35
  puts
35
36
  begin
36
37
  resp = send_command(ssm_options)
38
+
37
39
  command_id = resp.command.command_id
38
40
  success = true
39
41
  rescue Aws::SSM::Errors::InvalidInstanceId => e
@@ -50,9 +52,31 @@ module Sonic
50
52
  display_ssm_commands(command_id, ssm_options)
51
53
  puts
52
54
  return if @options[:noop]
53
- wait(command_id)
54
- display_ssm_output(command_id, ssm_options)
55
+ status = wait(command_id)
56
+ display_ssm_output(command_id)
55
57
  display_console_url(command_id)
58
+
59
+ if status == "Success"
60
+ puts "Command successful: #{status}".color(:green)
61
+ exit_status(0)
62
+ else
63
+ puts "Command unsuccessful: #{status}".color(:red)
64
+ exit_status(1)
65
+ end
66
+ end
67
+
68
+ def exit_status(code)
69
+ exit(code) if cli?
70
+
71
+ if code == 0
72
+ true
73
+ else
74
+ raise "Error running command"
75
+ end
76
+ end
77
+
78
+ def cli?
79
+ $0.include?('sonic')
56
80
  end
57
81
 
58
82
  def wait(command_id)
@@ -69,15 +93,21 @@ module Sonic
69
93
  end
70
94
  puts "\nCommand finished."
71
95
  puts
96
+ status
72
97
  end
73
98
 
74
- def display_ssm_output(command_id, ssm_options)
75
- instance_ids = ssm_options[:instance_ids]
76
- return unless instance_ids && instance_ids.size > 0
99
+ def display_ssm_output(command_id)
100
+ resp = ssm.list_command_invocations(command_id: command_id)
101
+ command_invocations = resp.command_invocations
102
+ command_invocation = command_invocations.first
103
+ unless command_invocation
104
+ puts "WARN: No instances found that matches the --tags or --instance-ids option".color(:yellow)
105
+ return false # instances_found
106
+ end
107
+ instance_id = command_invocation.instance_id
77
108
 
78
- instance_id = instance_ids.first
79
- if ssm_options[:instance_ids].size > 1
80
- puts "Multiple instance targets. Only displaying output for #{instance_id}."
109
+ if command_invocations.size > 1
110
+ puts "Multiple instance targets. Total targets: #{command_invocations.size}. Only displaying output for #{instance_id}."
81
111
  else
82
112
  puts "Displaying output for #{instance_id}."
83
113
  end
@@ -89,6 +119,7 @@ module Sonic
89
119
  ssm_output(resp, "output")
90
120
  ssm_output(resp, "error")
91
121
  puts
122
+ true # instances_found
92
123
  end
93
124
 
94
125
  def display_console_url(command_id)
@@ -97,16 +128,15 @@ module Sonic
97
128
  puts "To see the more output details visit:"
98
129
  puts " #{console_url}"
99
130
  puts
100
- copy_paste_clipboard(console_url)
101
- UI.say "Pro tip: the console url is already in your copy/paste clipboard."
131
+ copy_paste_clipboard(console_url, "Pro tip: the console url is already in your copy/paste clipboard.")
102
132
  end
103
133
 
104
134
  def colorized_status(status)
105
135
  case status
106
136
  when "Success"
107
- status.colorize(:green)
137
+ status.color(:green)
108
138
  when "Failed"
109
- status.colorize(:red)
139
+ status.color(:red)
110
140
  else
111
141
  status
112
142
  end
@@ -121,7 +151,7 @@ module Sonic
121
151
  return if content.empty?
122
152
 
123
153
  puts "Command standard #{type}:"
124
- # "https://s3.amazonaws.com/lr-infrastructure-prod/ssm/commands/sonic/0a4f4bef-8f63-4235-8b30-ae296477261a/i-0b2e6e187a3f9ada9/awsrunPowerShellScript/0.awsrunPowerShellScript/stderr">
154
+ # "https://s3.amazonaws.com/infra-prod/ssm/commands/sonic/0a4f4bef-8f63-4235-8b30-ae296477261a/i-0b2e6e187a3f9ada9/awsrunPowerShellScript/0.awsrunPowerShellScript/stderr">
125
155
  if content.include?("--output truncated--") && !resp[s3_key].empty?
126
156
  s3_url = resp[s3_key]
127
157
  info = s3_url.sub('https://s3.amazonaws.com/', '').split('/')
@@ -147,7 +177,6 @@ module Sonic
147
177
 
148
178
  begin
149
179
  resp = ssm.send_command(options)
150
- # puts "NOOP FOR NOW"
151
180
  rescue Aws::SSM::Errors::UnsupportedPlatformType
152
181
  retries += 1
153
182
  # toggle AWS-RunShellScript / AWS-RunPowerShellScript
@@ -166,12 +195,18 @@ module Sonic
166
195
  end
167
196
 
168
197
  def build_ssm_options
169
- criteria = transform_filter(@filter)
198
+ criteria = transform_filter_option
170
199
  command = build_command(@command)
171
200
  options = criteria.merge(
172
201
  document_name: "AWS-RunShellScript", # default
173
202
  comment: "sonic #{ARGV.join(' ')}"[0..99], # comment has a max of 100 chars
174
- parameters: { "commands" => command }
203
+ parameters: { "commands" => command },
204
+ # Default CloudWatchLog settings. Can be overwritten with settings.yml send_command
205
+ # IMPORTANT: make sure the EC2 instance the command runs on has access to write to CloudWatch Logs.
206
+ cloud_watch_output_config: {
207
+ # cloud_watch_log_group_name: "ssm", # Defaults to /aws/ssm/AWS-RunShellScript (aws/ssm/SystemsManagerDocumentName https://amzn.to/38TKVse)
208
+ cloud_watch_output_enabled: true,
209
+ },
175
210
  )
176
211
  settings_options = settings["send_command"] || {}
177
212
  options.merge(settings_options.deep_symbolize_keys)
@@ -181,6 +216,12 @@ module Sonic
181
216
  @settings ||= Setting.new.data
182
217
  end
183
218
 
219
+ def check_filter_options!
220
+ return if @tags || @instance_ids
221
+ puts "ERROR: Please provide --tags or --instance-ids option".color(:red)
222
+ exit 1
223
+ end
224
+
184
225
  #
185
226
  # Public: Transform the filter to the ssm send_command equivalent options
186
227
  #
@@ -188,41 +229,31 @@ module Sonic
188
229
  #
189
230
  # Examples
190
231
  #
191
- # transform_filter(["hi-web-prod", "hi-worker-prod", "i-006a097bb10643e20"])
232
+ # transform_filter_option
192
233
  # # => {
193
234
  # instance_ids: ["i-006a097bb10643e20"],
194
235
  # targets: [{key: "Name", values: "hi-web-prod,hi-worker-prod"}]
195
236
  # }
196
237
  #
197
238
  # Returns the duplicated String.
198
- def transform_filter(filter)
199
- valid = validate_filter(filter)
200
- unless valid
201
- UI.error("The filter you provided '#{filter.join(',')}' is not valid.")
202
- UI.say("The filter must either be all instance ids or just a list of tag names.")
203
- exit 1
204
- end
205
-
206
- if filter.detect { |i| instance_id?(i) }
207
- instance_ids = filter
208
- {instance_ids: instance_ids}
209
- else
210
- tags = filter
211
- targets = [{
212
- key: "tag:#{tag_name}",
213
- values: tags
214
- }]
239
+ def transform_filter_option
240
+ if @tags
241
+ list = @tags.split(';')
242
+ targets = list.inject([]) do |final,item|
243
+ tag_name,value_list = item.split('=')
244
+ values = value_list.split(',').map(&:strip)
245
+ # structure expected by ssm send_command
246
+ option = {
247
+ key: "tag:#{tag_name}",
248
+ values: values
249
+ }
250
+ final << option
251
+ final
252
+ end
215
253
  {targets: targets}
216
- end
217
- end
218
-
219
- # Either all instance ids are no instance ids is a valid filter
220
- def validate_filter(filter)
221
- if filter.detect { |i| instance_id?(i) }
222
- instance_ids = filter.select { |i| instance_id?(i) }
223
- instance_ids.size == filter.size
224
- else
225
- true
254
+ else # @instance_ids
255
+ instance_ids = @instance_ids.split(',')
256
+ {instance_ids: instance_ids}
226
257
  end
227
258
  end
228
259
 
@@ -239,7 +270,7 @@ module Sonic
239
270
  # The script is being feed inline so just join the command together into one script.
240
271
  # Still keep in an array form because that's how ssn.send_command works with AWS-RunShellScript
241
272
  # usually reads the command.
242
- [command.join(" ")]
273
+ command.is_a?(Array) ? command : [command]
243
274
  end
244
275
  end
245
276
 
@@ -254,8 +285,7 @@ You can use the following command to check registered instances to SSM.
254
285
  #{ssm_describe_command}
255
286
  EOS
256
287
  UI.warn(message)
257
- copy_paste_clipboard(ssm_describe_command)
258
- UI.say "Pro tip: ssm describe-instance-information already in your copy/paste clipboard."
288
+ copy_paste_clipboard(ssm_describe_command, "Pro tip: ssm describe-instance-information already in your copy/paste clipboard.")
259
289
  end
260
290
 
261
291
  def file_path?(command)
@@ -312,9 +342,10 @@ EOL
312
342
  end
313
343
  end
314
344
 
315
- def copy_paste_clipboard(command)
345
+ def copy_paste_clipboard(command, text)
316
346
  return unless RUBY_PLATFORM =~ /darwin/
317
347
  system("echo '#{command}' | pbcopy")
348
+ UI.say text
318
349
  end
319
350
  end
320
351
  end
@@ -3,10 +3,10 @@
3
3
 
4
4
  ## Examples Summary
5
5
 
6
- $ sonic execute hi-web-prod uptime
7
- $ sonic execute hi-web-prod,hi-worker-prod,hi-clock-prod uptime
8
- $ sonic execute i-030033c20c54bf149,i-030033c20c54bf150 uname -a
9
- $ sonic execute i-030033c20c54bf149 file://hello.sh
6
+ sonic execute --tags Name=demo-web uptime
7
+ sonic execute --tags Name=demo-web,demo-worker uptime # multiple values
8
+ sonic execute --instance-ids i-030033c20c54bf149,i-030033c20c54bf150 uname -a
9
+ sonic execute --instance-ids i-030033c20c54bf149 file://hello.sh # script from file
10
10
 
11
11
  You cannot mix instance ids and tag names in the filter.
12
12
 
@@ -14,13 +14,13 @@ You cannot mix instance ids and tag names in the filter.
14
14
 
15
15
  Here's a command example output in detailed:
16
16
 
17
- $ sonic execute i-0bf51a000ab4e73a8 uptime
17
+ $ sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
18
18
  Sending command to SSM with options:
19
19
  ---
20
20
  instance_ids:
21
21
  - i-0bf51a000ab4e73a8
22
22
  document_name: AWS-RunShellScript
23
- comment: sonic execute i-0bf51a000ab4e73a8 uptime
23
+ comment: sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
24
24
  parameters:
25
25
  commands:
26
26
  - uptime
data/lib/sonic/list.rb CHANGED
@@ -35,7 +35,7 @@ module Sonic
35
35
 
36
36
  if @options[:header] && !zero_instances
37
37
  header = ["Instance Id", "Name", "Public IP", "Private IP", "Type"]
38
- UI.say header.join("\t").colorize(:green)
38
+ UI.say header.join("\t").color(:green)
39
39
  end
40
40
 
41
41
  instances.each do |i|
data/lib/sonic/ssh.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'colorize'
2
-
3
1
  module Sonic
4
2
  class Ssh
5
3
  autoload :IdentifierDetector, 'sonic/ssh/identifier_detector'
@@ -82,7 +80,7 @@ module Sonic
82
80
  def kernel_exec(*args)
83
81
  # append the optional command that can be provided to the ssh command
84
82
  full_command = args + @options[:command]
85
- puts "=> #{full_command.join(' ')}".colorize(:green)
83
+ puts "=> #{full_command.join(' ')}".color(:green)
86
84
  # https://ruby-doc.org/core-2.3.1/Kernel.html#method-i-exec
87
85
  # Using 2nd form
88
86
  Kernel.exec(*full_command) unless @options[:noop]
@@ -73,7 +73,7 @@ class Ssh
73
73
 
74
74
  task = response.tasks.first
75
75
  unless task
76
- puts "Unable to find a #{task_arn.green} container instance or task in the #{@cluster.green} cluster."
76
+ puts "Unable to find a #{task_arn.color(:green)} container instance or task in the #{@cluster.color(:green)} cluster."
77
77
  exit 1
78
78
  end
79
79
 
data/lib/sonic/ui.rb CHANGED
@@ -19,10 +19,10 @@ module Sonic
19
19
  puts msg unless mute
20
20
  end
21
21
  def error(msg='')
22
- say "ERROR: #{msg}".colorize(:red)
22
+ say "ERROR: #{msg}".color(:red)
23
23
  end
24
24
  def warn(msg='')
25
- say "WARN: #{msg}".colorize(:yellow)
25
+ say "WARN: #{msg}".color(:yellow)
26
26
  end
27
27
  end
28
28
  end
data/lib/sonic/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sonic
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.3"
3
3
  end
data/sonic.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = "http://sonic-screwdriver.cloud/"
13
13
  spec.license = "MIT"
14
14
 
15
- spec.files = `git ls-files`.split($/)
15
+ spec.files = File.directory?('.git') ? `git ls-files`.split($/) : Dir.glob("**/*")
16
16
  spec.bindir = "exe"
17
17
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
@@ -23,9 +23,9 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency "aws-sdk-ecs"
24
24
  spec.add_dependency "aws-sdk-s3"
25
25
  spec.add_dependency "aws-sdk-ssm"
26
- spec.add_dependency "colorize"
27
26
  spec.add_dependency "hashie"
28
27
  spec.add_dependency "memoist"
28
+ spec.add_dependency "rainbow"
29
29
  spec.add_dependency "thor"
30
30
  spec.add_dependency "tty-prompt"
31
31
 
data/spec/lib/cli_spec.rb CHANGED
@@ -11,8 +11,13 @@ describe Sonic::CLI do
11
11
  expect(out).to include("=> ssh")
12
12
  end
13
13
 
14
- it "execute should print that command has been sent" do
15
- out = execute("exe/sonic execute #{@args} 1,2,3 uptime")
14
+ it "execute should print that command has been sent --instance-ids" do
15
+ out = execute("exe/sonic execute #{@args} --instance-ids i-1,i-2,i-3 uptime")
16
+ expect(out).to include("Command sent")
17
+ end
18
+
19
+ it "execute should print that command has been sent --tags" do
20
+ out = execute("exe/sonic execute #{@args} --tags Name=value uptime")
16
21
  expect(out).to include("Command sent")
17
22
  end
18
23
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Sonic::Execute do
4
4
  before(:each) do
5
5
  @options = {
6
- filter: "i-066b140d9479e9681,i-09482b1a6e330fbf7"
6
+ instance_ids: "i-066b140d9479e9681,i-09482b1a6e330fbf7"
7
7
  }
8
8
  end
9
9
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sonic-screwdriver
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-01 00:00:00.000000000 Z
11
+ date: 2021-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: colorize
84
+ name: hashie
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: hashie
98
+ name: memoist
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: memoist
112
+ name: rainbow
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -260,11 +260,9 @@ files:
260
260
  - ".circleci/config.yml"
261
261
  - ".gitignore"
262
262
  - ".rspec"
263
- - ".ruby-version"
264
263
  - CHANGELOG.md
265
264
  - CONTRIBUTING.md
266
265
  - Gemfile
267
- - Gemfile.lock
268
266
  - Guardfile
269
267
  - LICENSE.txt
270
268
  - README.md
@@ -421,8 +419,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
421
419
  - !ruby/object:Gem::Version
422
420
  version: '0'
423
421
  requirements: []
424
- rubyforge_project:
425
- rubygems_version: 2.7.6
422
+ rubygems_version: 3.2.5
426
423
  signing_key:
427
424
  specification_version: 4
428
425
  summary: Multi-functional tool to manage AWS infrastructure
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.5.1
data/Gemfile.lock DELETED
@@ -1,163 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- sonic-screwdriver (2.0.0)
5
- activesupport
6
- aws-sdk-ec2
7
- aws-sdk-ecs
8
- aws-sdk-s3
9
- aws-sdk-ssm
10
- colorize
11
- hashie
12
- memoist
13
- thor
14
- tty-prompt
15
-
16
- GEM
17
- remote: https://rubygems.org/
18
- specs:
19
- activesupport (5.2.0)
20
- concurrent-ruby (~> 1.0, >= 1.0.2)
21
- i18n (>= 0.7, < 2)
22
- minitest (~> 5.1)
23
- tzinfo (~> 1.1)
24
- aws-eventstream (1.0.0)
25
- aws-partitions (1.89.1)
26
- aws-sdk-core (3.21.2)
27
- aws-eventstream (~> 1.0)
28
- aws-partitions (~> 1.0)
29
- aws-sigv4 (~> 1.0)
30
- jmespath (~> 1.0)
31
- aws-sdk-ec2 (1.34.0)
32
- aws-sdk-core (~> 3)
33
- aws-sigv4 (~> 1.0)
34
- aws-sdk-ecs (1.13.0)
35
- aws-sdk-core (~> 3)
36
- aws-sigv4 (~> 1.0)
37
- aws-sdk-kms (1.5.0)
38
- aws-sdk-core (~> 3)
39
- aws-sigv4 (~> 1.0)
40
- aws-sdk-s3 (1.13.0)
41
- aws-sdk-core (~> 3, >= 3.21.2)
42
- aws-sdk-kms (~> 1)
43
- aws-sigv4 (~> 1.0)
44
- aws-sdk-ssm (1.13.0)
45
- aws-sdk-core (~> 3)
46
- aws-sigv4 (~> 1.0)
47
- aws-sigv4 (1.0.2)
48
- byebug (10.0.2)
49
- cli_markdown (0.1.0)
50
- codeclimate-test-reporter (1.0.8)
51
- simplecov (<= 0.13)
52
- coderay (1.1.2)
53
- colorize (0.8.1)
54
- concurrent-ruby (1.0.5)
55
- diff-lcs (1.3)
56
- docile (1.1.5)
57
- equatable (0.5.0)
58
- ffi (1.9.23)
59
- formatador (0.2.5)
60
- guard (2.14.2)
61
- formatador (>= 0.2.4)
62
- listen (>= 2.7, < 4.0)
63
- lumberjack (>= 1.0.12, < 2.0)
64
- nenv (~> 0.1)
65
- notiffany (~> 0.0)
66
- pry (>= 0.9.12)
67
- shellany (~> 0.0)
68
- thor (>= 0.18.1)
69
- guard-bundler (2.1.0)
70
- bundler (~> 1.0)
71
- guard (~> 2.2)
72
- guard-compat (~> 1.1)
73
- guard-compat (1.2.1)
74
- guard-rspec (4.7.3)
75
- guard (~> 2.1)
76
- guard-compat (~> 1.1)
77
- rspec (>= 2.99.0, < 4.0)
78
- hashie (3.5.7)
79
- hitimes (1.2.6)
80
- i18n (1.0.1)
81
- concurrent-ruby (~> 1.0)
82
- jmespath (1.4.0)
83
- json (2.1.0)
84
- listen (3.1.5)
85
- rb-fsevent (~> 0.9, >= 0.9.4)
86
- rb-inotify (~> 0.9, >= 0.9.7)
87
- ruby_dep (~> 1.2)
88
- lumberjack (1.0.13)
89
- memoist (0.16.0)
90
- method_source (0.9.0)
91
- minitest (5.11.3)
92
- necromancer (0.4.0)
93
- nenv (0.3.0)
94
- notiffany (0.1.1)
95
- nenv (~> 0.1)
96
- shellany (~> 0.0)
97
- pastel (0.7.2)
98
- equatable (~> 0.5.0)
99
- tty-color (~> 0.4.0)
100
- pry (0.11.3)
101
- coderay (~> 1.1.0)
102
- method_source (~> 0.9.0)
103
- rake (12.3.1)
104
- rb-fsevent (0.10.3)
105
- rb-inotify (0.9.10)
106
- ffi (>= 0.5.0, < 2)
107
- rspec (3.7.0)
108
- rspec-core (~> 3.7.0)
109
- rspec-expectations (~> 3.7.0)
110
- rspec-mocks (~> 3.7.0)
111
- rspec-core (3.7.1)
112
- rspec-support (~> 3.7.0)
113
- rspec-expectations (3.7.0)
114
- diff-lcs (>= 1.2.0, < 2.0)
115
- rspec-support (~> 3.7.0)
116
- rspec-mocks (3.7.0)
117
- diff-lcs (>= 1.2.0, < 2.0)
118
- rspec-support (~> 3.7.0)
119
- rspec-support (3.7.1)
120
- ruby_dep (1.5.0)
121
- shellany (0.0.1)
122
- simplecov (0.13.0)
123
- docile (~> 1.1.0)
124
- json (>= 1.8, < 3)
125
- simplecov-html (~> 0.10.0)
126
- simplecov-html (0.10.2)
127
- thor (0.20.0)
128
- thread_safe (0.3.6)
129
- timers (4.1.2)
130
- hitimes
131
- tty-color (0.4.2)
132
- tty-cursor (0.5.0)
133
- tty-prompt (0.16.1)
134
- necromancer (~> 0.4.0)
135
- pastel (~> 0.7.0)
136
- timers (~> 4.0)
137
- tty-cursor (~> 0.5.0)
138
- tty-reader (~> 0.3.0)
139
- tty-reader (0.3.0)
140
- tty-cursor (~> 0.5.0)
141
- tty-screen (~> 0.6.4)
142
- wisper (~> 2.0.0)
143
- tty-screen (0.6.4)
144
- tzinfo (1.2.5)
145
- thread_safe (~> 0.1)
146
- wisper (2.0.0)
147
-
148
- PLATFORMS
149
- ruby
150
-
151
- DEPENDENCIES
152
- bundler
153
- byebug
154
- cli_markdown
155
- codeclimate-test-reporter
156
- guard
157
- guard-bundler
158
- guard-rspec
159
- rake
160
- sonic-screwdriver!
161
-
162
- BUNDLED WITH
163
- 1.16.1