sonic-screwdriver 2.1.1 → 2.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +3 -1
- data/.gitignore +1 -0
- data/CHANGELOG.md +19 -1
- data/README.md +25 -28
- data/docs/_docs/install.md +0 -10
- data/docs/_docs/tutorial-execute.md +48 -56
- data/docs/_includes/footer.html +6 -5
- data/docs/_reference/sonic-execute.md +8 -6
- data/docs/bin/web +1 -1
- data/docs/quick-start.md +17 -21
- data/lib/sonic/checks.rb +2 -2
- data/lib/sonic/cli.rb +5 -2
- data/lib/sonic/execute.rb +82 -48
- data/lib/sonic/help/execute.md +6 -6
- data/lib/sonic/ssh/identifier_detector.rb +1 -1
- data/lib/sonic/version.rb +1 -1
- data/sonic.gemspec +1 -1
- data/spec/lib/cli_spec.rb +7 -2
- data/spec/lib/sonic/execute_spec.rb +1 -1
- metadata +3 -6
- data/.ruby-version +0 -1
- data/Gemfile.lock +0 -161
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7012e35108a2515bf2ad5abe18cbe39502a4dc58e64732199334414cfb7b0808
|
4
|
+
data.tar.gz: 9cb1113b9bb23ce2605b01594d54d477e3f10f6322f0d97c8ebbd8a6fae5aa0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99ccd14a805e3bb635bd41c65565d35a6afed559ecf0bd781b36ddc61bfd05e8c3dff926018b2605385647fb55c3eb0e1bc322bec609a56c0e274143c2bc5bb1
|
7
|
+
data.tar.gz: c27388fc985c3ac35e02189ccdd748b28fe3f3d1399aa36cc2f601b6fe5fa54cf9634f9984230d9067c3d8060c7c34a052782abf8e889a6695e1a9391b753e13
|
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
|
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
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 *
|
4
|
+
This project *loosely* adheres to [Semantic Versioning](http://semver.org/), even before v1.0.
|
5
|
+
|
6
|
+
## [2.2.4] - 2021-04-13
|
7
|
+
- [#13](https://github.com/boltops-tools/sonic/pull/13) execute --comment option
|
8
|
+
|
9
|
+
## [2.2.3] - 2021-04-12
|
10
|
+
- [#12](https://github.com/boltops-tools/sonic/pull/12) fix cli check
|
11
|
+
|
12
|
+
## [2.2.2] - 2021-04-12
|
13
|
+
- [#11](https://github.com/boltops-tools/sonic/pull/11) cli check
|
14
|
+
|
15
|
+
## [2.2.1] - 2021-04-12
|
16
|
+
- [#9](https://github.com/boltops-tools/sonic/pull/9) improve command array handling
|
17
|
+
|
18
|
+
## [2.2.0]
|
19
|
+
- #7 sonic execute command: `--tags` and `--instance-id` options instead of polymorphic list. Breaking behavior.
|
20
|
+
- display output even if tags used
|
21
|
+
- configure default log group
|
22
|
+
- correct exit status
|
5
23
|
|
6
24
|
## [2.1.1]
|
7
25
|
- use rainbow gem for terminal colors
|
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
|
-
|
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
|
-
|
52
|
-
|
53
|
-
sonic ssh
|
54
|
-
sonic ssh
|
55
|
-
sonic ssh
|
56
|
-
sonic ssh
|
57
|
-
sonic ssh
|
58
|
-
|
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
|
61
|
-
sonic ecs
|
56
|
+
# docker run with the same environment as the ECS docker running containers
|
57
|
+
sonic ecs sh demo-web
|
62
58
|
|
63
|
-
#
|
64
|
-
sonic
|
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
|
67
|
-
sonic execute
|
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
|
70
|
-
|
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
|
74
|
-
```
|
70
|
+
# list ec2 instances
|
71
|
+
sonic list demo-web
|
75
72
|
|
76
73
|
## Contributing
|
77
74
|
|
data/docs/_docs/install.md
CHANGED
@@ -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
|
-
|
10
|
-
sonic execute [FILTER] [COMMAND]
|
11
|
-
```
|
9
|
+
sonic execute [FILTER] [COMMAND]
|
12
10
|
|
13
11
|
## Examples Summary
|
14
12
|
|
15
|
-
|
16
|
-
sonic execute
|
17
|
-
sonic execute
|
18
|
-
sonic execute i-030033c20c54bf149
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
Command
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
###
|
66
|
+
### Filter Options
|
73
67
|
|
74
|
-
The `sonic execute` command can understand a variety of different filters
|
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
|
-
|
79
|
-
|
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
|
-
|
133
|
-
|
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
|
-
|
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
|
|
data/docs/_includes/footer.html
CHANGED
@@ -6,11 +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="
|
10
|
-
<li><a href="
|
11
|
-
<li><a href="
|
12
|
-
<li><a href="
|
13
|
-
<li><a href="https://
|
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>
|
14
15
|
</ul>
|
15
16
|
</div>
|
16
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
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
|
-
|
10
|
-
gem install sonic-screwdriver
|
11
|
-
```
|
9
|
+
gem install sonic-screwdriver
|
12
10
|
|
13
11
|
### Usage
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
sonic ssh
|
18
|
-
sonic ssh
|
19
|
-
sonic ssh
|
20
|
-
sonic ssh
|
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
|
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
|
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
|
33
|
-
sonic execute
|
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
|
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/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,11 @@ 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
|
-
|
31
|
-
|
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
|
+
option :comment, desc: "comment. defaults to the sonic command used"
|
33
|
+
def execute(*command)
|
34
|
+
Execute.new(command, options).execute
|
32
35
|
end
|
33
36
|
|
34
37
|
desc "list [FILTER]", "Lists ec2 instances."
|
data/lib/sonic/execute.rb
CHANGED
@@ -8,7 +8,8 @@ module Sonic
|
|
8
8
|
def initialize(command, options)
|
9
9
|
@command = command
|
10
10
|
@options = options
|
11
|
-
@
|
11
|
+
@tags = @options[:tags]
|
12
|
+
@instance_ids = @options[:instance_ids]
|
12
13
|
end
|
13
14
|
|
14
15
|
# aws ssm send-command \
|
@@ -18,6 +19,7 @@ module Sonic
|
|
18
19
|
# --parameters '{"commands":["#!/usr/bin/python","print \"Hello world from python\""]}' \
|
19
20
|
# --query "Command.CommandId"
|
20
21
|
def execute
|
22
|
+
check_filter_options!
|
21
23
|
ssm_options = build_ssm_options
|
22
24
|
if @options[:noop]
|
23
25
|
UI.noop = true
|
@@ -33,6 +35,7 @@ module Sonic
|
|
33
35
|
puts
|
34
36
|
begin
|
35
37
|
resp = send_command(ssm_options)
|
38
|
+
|
36
39
|
command_id = resp.command.command_id
|
37
40
|
success = true
|
38
41
|
rescue Aws::SSM::Errors::InvalidInstanceId => e
|
@@ -49,9 +52,31 @@ module Sonic
|
|
49
52
|
display_ssm_commands(command_id, ssm_options)
|
50
53
|
puts
|
51
54
|
return if @options[:noop]
|
52
|
-
wait(command_id)
|
53
|
-
display_ssm_output(command_id
|
55
|
+
status = wait(command_id)
|
56
|
+
display_ssm_output(command_id)
|
54
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')
|
55
80
|
end
|
56
81
|
|
57
82
|
def wait(command_id)
|
@@ -68,15 +93,21 @@ module Sonic
|
|
68
93
|
end
|
69
94
|
puts "\nCommand finished."
|
70
95
|
puts
|
96
|
+
status
|
71
97
|
end
|
72
98
|
|
73
|
-
def display_ssm_output(command_id
|
74
|
-
|
75
|
-
|
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
|
76
108
|
|
77
|
-
|
78
|
-
|
79
|
-
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}."
|
80
111
|
else
|
81
112
|
puts "Displaying output for #{instance_id}."
|
82
113
|
end
|
@@ -88,6 +119,7 @@ module Sonic
|
|
88
119
|
ssm_output(resp, "output")
|
89
120
|
ssm_output(resp, "error")
|
90
121
|
puts
|
122
|
+
true # instances_found
|
91
123
|
end
|
92
124
|
|
93
125
|
def display_console_url(command_id)
|
@@ -96,8 +128,7 @@ module Sonic
|
|
96
128
|
puts "To see the more output details visit:"
|
97
129
|
puts " #{console_url}"
|
98
130
|
puts
|
99
|
-
copy_paste_clipboard(console_url)
|
100
|
-
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.")
|
101
132
|
end
|
102
133
|
|
103
134
|
def colorized_status(status)
|
@@ -120,7 +151,7 @@ module Sonic
|
|
120
151
|
return if content.empty?
|
121
152
|
|
122
153
|
puts "Command standard #{type}:"
|
123
|
-
# "https://s3.amazonaws.com/
|
154
|
+
# "https://s3.amazonaws.com/infra-prod/ssm/commands/sonic/0a4f4bef-8f63-4235-8b30-ae296477261a/i-0b2e6e187a3f9ada9/awsrunPowerShellScript/0.awsrunPowerShellScript/stderr">
|
124
155
|
if content.include?("--output truncated--") && !resp[s3_key].empty?
|
125
156
|
s3_url = resp[s3_key]
|
126
157
|
info = s3_url.sub('https://s3.amazonaws.com/', '').split('/')
|
@@ -146,7 +177,6 @@ module Sonic
|
|
146
177
|
|
147
178
|
begin
|
148
179
|
resp = ssm.send_command(options)
|
149
|
-
# puts "NOOP FOR NOW"
|
150
180
|
rescue Aws::SSM::Errors::UnsupportedPlatformType
|
151
181
|
retries += 1
|
152
182
|
# toggle AWS-RunShellScript / AWS-RunPowerShellScript
|
@@ -165,12 +195,20 @@ module Sonic
|
|
165
195
|
end
|
166
196
|
|
167
197
|
def build_ssm_options
|
168
|
-
criteria =
|
198
|
+
criteria = transform_filter_option
|
169
199
|
command = build_command(@command)
|
200
|
+
comment = @options[:comment] || "sonic #{ARGV.join(' ')}"
|
201
|
+
comment = comment[0..99] # comment has a max of 100 chars
|
170
202
|
options = criteria.merge(
|
171
203
|
document_name: "AWS-RunShellScript", # default
|
172
|
-
comment:
|
173
|
-
parameters: { "commands" => command }
|
204
|
+
comment: comment,
|
205
|
+
parameters: { "commands" => command },
|
206
|
+
# Default CloudWatchLog settings. Can be overwritten with settings.yml send_command
|
207
|
+
# IMPORTANT: make sure the EC2 instance the command runs on has access to write to CloudWatch Logs.
|
208
|
+
cloud_watch_output_config: {
|
209
|
+
# cloud_watch_log_group_name: "ssm", # Defaults to /aws/ssm/AWS-RunShellScript (aws/ssm/SystemsManagerDocumentName https://amzn.to/38TKVse)
|
210
|
+
cloud_watch_output_enabled: true,
|
211
|
+
},
|
174
212
|
)
|
175
213
|
settings_options = settings["send_command"] || {}
|
176
214
|
options.merge(settings_options.deep_symbolize_keys)
|
@@ -180,6 +218,12 @@ module Sonic
|
|
180
218
|
@settings ||= Setting.new.data
|
181
219
|
end
|
182
220
|
|
221
|
+
def check_filter_options!
|
222
|
+
return if @tags || @instance_ids
|
223
|
+
puts "ERROR: Please provide --tags or --instance-ids option".color(:red)
|
224
|
+
exit 1
|
225
|
+
end
|
226
|
+
|
183
227
|
#
|
184
228
|
# Public: Transform the filter to the ssm send_command equivalent options
|
185
229
|
#
|
@@ -187,41 +231,31 @@ module Sonic
|
|
187
231
|
#
|
188
232
|
# Examples
|
189
233
|
#
|
190
|
-
#
|
234
|
+
# transform_filter_option
|
191
235
|
# # => {
|
192
236
|
# instance_ids: ["i-006a097bb10643e20"],
|
193
237
|
# targets: [{key: "Name", values: "hi-web-prod,hi-worker-prod"}]
|
194
238
|
# }
|
195
239
|
#
|
196
240
|
# Returns the duplicated String.
|
197
|
-
def
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
key: "tag:#{tag_name}",
|
212
|
-
values: tags
|
213
|
-
}]
|
241
|
+
def transform_filter_option
|
242
|
+
if @tags
|
243
|
+
list = @tags.split(';')
|
244
|
+
targets = list.inject([]) do |final,item|
|
245
|
+
tag_name,value_list = item.split('=')
|
246
|
+
values = value_list.split(',').map(&:strip)
|
247
|
+
# structure expected by ssm send_command
|
248
|
+
option = {
|
249
|
+
key: "tag:#{tag_name}",
|
250
|
+
values: values
|
251
|
+
}
|
252
|
+
final << option
|
253
|
+
final
|
254
|
+
end
|
214
255
|
{targets: targets}
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
# Either all instance ids are no instance ids is a valid filter
|
219
|
-
def validate_filter(filter)
|
220
|
-
if filter.detect { |i| instance_id?(i) }
|
221
|
-
instance_ids = filter.select { |i| instance_id?(i) }
|
222
|
-
instance_ids.size == filter.size
|
223
|
-
else
|
224
|
-
true
|
256
|
+
else # @instance_ids
|
257
|
+
instance_ids = @instance_ids.split(',')
|
258
|
+
{instance_ids: instance_ids}
|
225
259
|
end
|
226
260
|
end
|
227
261
|
|
@@ -238,7 +272,7 @@ module Sonic
|
|
238
272
|
# The script is being feed inline so just join the command together into one script.
|
239
273
|
# Still keep in an array form because that's how ssn.send_command works with AWS-RunShellScript
|
240
274
|
# usually reads the command.
|
241
|
-
|
275
|
+
command.is_a?(Array) ? command : [command]
|
242
276
|
end
|
243
277
|
end
|
244
278
|
|
@@ -253,8 +287,7 @@ You can use the following command to check registered instances to SSM.
|
|
253
287
|
#{ssm_describe_command}
|
254
288
|
EOS
|
255
289
|
UI.warn(message)
|
256
|
-
copy_paste_clipboard(ssm_describe_command)
|
257
|
-
UI.say "Pro tip: ssm describe-instance-information already in your copy/paste clipboard."
|
290
|
+
copy_paste_clipboard(ssm_describe_command, "Pro tip: ssm describe-instance-information already in your copy/paste clipboard.")
|
258
291
|
end
|
259
292
|
|
260
293
|
def file_path?(command)
|
@@ -311,9 +344,10 @@ EOL
|
|
311
344
|
end
|
312
345
|
end
|
313
346
|
|
314
|
-
def copy_paste_clipboard(command)
|
347
|
+
def copy_paste_clipboard(command, text)
|
315
348
|
return unless RUBY_PLATFORM =~ /darwin/
|
316
349
|
system("echo '#{command}' | pbcopy")
|
350
|
+
UI.say text
|
317
351
|
end
|
318
352
|
end
|
319
353
|
end
|
data/lib/sonic/help/execute.md
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
|
4
4
|
## Examples Summary
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
@@ -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/version.rb
CHANGED
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)/})
|
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
|
|
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.
|
4
|
+
version: 2.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tung Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -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
|
-
|
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,161 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
sonic-screwdriver (2.1.1)
|
5
|
-
activesupport
|
6
|
-
aws-sdk-ec2
|
7
|
-
aws-sdk-ecs
|
8
|
-
aws-sdk-s3
|
9
|
-
aws-sdk-ssm
|
10
|
-
hashie
|
11
|
-
memoist
|
12
|
-
rainbow
|
13
|
-
thor
|
14
|
-
tty-prompt
|
15
|
-
|
16
|
-
GEM
|
17
|
-
remote: https://rubygems.org/
|
18
|
-
specs:
|
19
|
-
activesupport (5.2.2)
|
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.1)
|
25
|
-
aws-partitions (1.135.0)
|
26
|
-
aws-sdk-core (3.46.0)
|
27
|
-
aws-eventstream (~> 1.0)
|
28
|
-
aws-partitions (~> 1.0)
|
29
|
-
aws-sigv4 (~> 1.0)
|
30
|
-
jmespath (~> 1.0)
|
31
|
-
aws-sdk-ec2 (1.67.0)
|
32
|
-
aws-sdk-core (~> 3, >= 3.39.0)
|
33
|
-
aws-sigv4 (~> 1.0)
|
34
|
-
aws-sdk-ecs (1.28.0)
|
35
|
-
aws-sdk-core (~> 3, >= 3.39.0)
|
36
|
-
aws-sigv4 (~> 1.0)
|
37
|
-
aws-sdk-kms (1.13.0)
|
38
|
-
aws-sdk-core (~> 3, >= 3.39.0)
|
39
|
-
aws-sigv4 (~> 1.0)
|
40
|
-
aws-sdk-s3 (1.30.1)
|
41
|
-
aws-sdk-core (~> 3, >= 3.39.0)
|
42
|
-
aws-sdk-kms (~> 1)
|
43
|
-
aws-sigv4 (~> 1.0)
|
44
|
-
aws-sdk-ssm (1.35.0)
|
45
|
-
aws-sdk-core (~> 3, >= 3.39.0)
|
46
|
-
aws-sigv4 (~> 1.0)
|
47
|
-
aws-sigv4 (1.0.3)
|
48
|
-
byebug (10.0.2)
|
49
|
-
cli_markdown (0.1.0)
|
50
|
-
codeclimate-test-reporter (1.0.9)
|
51
|
-
simplecov (<= 0.13)
|
52
|
-
coderay (1.1.2)
|
53
|
-
concurrent-ruby (1.1.4)
|
54
|
-
diff-lcs (1.3)
|
55
|
-
docile (1.1.5)
|
56
|
-
equatable (0.5.0)
|
57
|
-
ffi (1.10.0)
|
58
|
-
formatador (0.2.5)
|
59
|
-
guard (2.15.0)
|
60
|
-
formatador (>= 0.2.4)
|
61
|
-
listen (>= 2.7, < 4.0)
|
62
|
-
lumberjack (>= 1.0.12, < 2.0)
|
63
|
-
nenv (~> 0.1)
|
64
|
-
notiffany (~> 0.0)
|
65
|
-
pry (>= 0.9.12)
|
66
|
-
shellany (~> 0.0)
|
67
|
-
thor (>= 0.18.1)
|
68
|
-
guard-bundler (2.2.1)
|
69
|
-
bundler (>= 1.3.0, < 3)
|
70
|
-
guard (~> 2.2)
|
71
|
-
guard-compat (~> 1.1)
|
72
|
-
guard-compat (1.2.1)
|
73
|
-
guard-rspec (4.7.3)
|
74
|
-
guard (~> 2.1)
|
75
|
-
guard-compat (~> 1.1)
|
76
|
-
rspec (>= 2.99.0, < 4.0)
|
77
|
-
hashie (3.6.0)
|
78
|
-
i18n (1.5.3)
|
79
|
-
concurrent-ruby (~> 1.0)
|
80
|
-
jmespath (1.4.0)
|
81
|
-
json (2.1.0)
|
82
|
-
listen (3.1.5)
|
83
|
-
rb-fsevent (~> 0.9, >= 0.9.4)
|
84
|
-
rb-inotify (~> 0.9, >= 0.9.7)
|
85
|
-
ruby_dep (~> 1.2)
|
86
|
-
lumberjack (1.0.13)
|
87
|
-
memoist (0.16.0)
|
88
|
-
method_source (0.9.2)
|
89
|
-
minitest (5.11.3)
|
90
|
-
necromancer (0.4.0)
|
91
|
-
nenv (0.3.0)
|
92
|
-
notiffany (0.1.1)
|
93
|
-
nenv (~> 0.1)
|
94
|
-
shellany (~> 0.0)
|
95
|
-
pastel (0.7.2)
|
96
|
-
equatable (~> 0.5.0)
|
97
|
-
tty-color (~> 0.4.0)
|
98
|
-
pry (0.12.2)
|
99
|
-
coderay (~> 1.1.0)
|
100
|
-
method_source (~> 0.9.0)
|
101
|
-
rainbow (3.0.0)
|
102
|
-
rake (12.3.2)
|
103
|
-
rb-fsevent (0.10.3)
|
104
|
-
rb-inotify (0.10.0)
|
105
|
-
ffi (~> 1.0)
|
106
|
-
rspec (3.8.0)
|
107
|
-
rspec-core (~> 3.8.0)
|
108
|
-
rspec-expectations (~> 3.8.0)
|
109
|
-
rspec-mocks (~> 3.8.0)
|
110
|
-
rspec-core (3.8.0)
|
111
|
-
rspec-support (~> 3.8.0)
|
112
|
-
rspec-expectations (3.8.2)
|
113
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
114
|
-
rspec-support (~> 3.8.0)
|
115
|
-
rspec-mocks (3.8.0)
|
116
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
117
|
-
rspec-support (~> 3.8.0)
|
118
|
-
rspec-support (3.8.0)
|
119
|
-
ruby_dep (1.5.0)
|
120
|
-
shellany (0.0.1)
|
121
|
-
simplecov (0.13.0)
|
122
|
-
docile (~> 1.1.0)
|
123
|
-
json (>= 1.8, < 3)
|
124
|
-
simplecov-html (~> 0.10.0)
|
125
|
-
simplecov-html (0.10.2)
|
126
|
-
thor (0.20.3)
|
127
|
-
thread_safe (0.3.6)
|
128
|
-
timers (4.3.0)
|
129
|
-
tty-color (0.4.3)
|
130
|
-
tty-cursor (0.6.0)
|
131
|
-
tty-prompt (0.18.1)
|
132
|
-
necromancer (~> 0.4.0)
|
133
|
-
pastel (~> 0.7.0)
|
134
|
-
timers (~> 4.0)
|
135
|
-
tty-cursor (~> 0.6.0)
|
136
|
-
tty-reader (~> 0.5.0)
|
137
|
-
tty-reader (0.5.0)
|
138
|
-
tty-cursor (~> 0.6.0)
|
139
|
-
tty-screen (~> 0.6.4)
|
140
|
-
wisper (~> 2.0.0)
|
141
|
-
tty-screen (0.6.5)
|
142
|
-
tzinfo (1.2.5)
|
143
|
-
thread_safe (~> 0.1)
|
144
|
-
wisper (2.0.0)
|
145
|
-
|
146
|
-
PLATFORMS
|
147
|
-
ruby
|
148
|
-
|
149
|
-
DEPENDENCIES
|
150
|
-
bundler
|
151
|
-
byebug
|
152
|
-
cli_markdown
|
153
|
-
codeclimate-test-reporter
|
154
|
-
guard
|
155
|
-
guard-bundler
|
156
|
-
guard-rspec
|
157
|
-
rake
|
158
|
-
sonic-screwdriver!
|
159
|
-
|
160
|
-
BUNDLED WITH
|
161
|
-
1.17.1
|