ecs-solo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +89 -0
- data/Guardfile +19 -0
- data/README.md +42 -0
- data/Rakefile +14 -0
- data/ecs-solo.gemspec +45 -0
- data/exe/ecs-solo +14 -0
- data/lib/ecs-solo.rb +1 -0
- data/lib/ecs_solo.rb +11 -0
- data/lib/ecs_solo/abstract_base.rb +10 -0
- data/lib/ecs_solo/autoloader.rb +22 -0
- data/lib/ecs_solo/aws_services.rb +18 -0
- data/lib/ecs_solo/cli.rb +32 -0
- data/lib/ecs_solo/command.rb +89 -0
- data/lib/ecs_solo/completer.rb +159 -0
- data/lib/ecs_solo/completer/script.rb +6 -0
- data/lib/ecs_solo/completer/script.sh +10 -0
- data/lib/ecs_solo/deploy.rb +73 -0
- data/lib/ecs_solo/docker.rb +74 -0
- data/lib/ecs_solo/help.rb +9 -0
- data/lib/ecs_solo/help/completion.md +20 -0
- data/lib/ecs_solo/help/completion_script.md +3 -0
- data/lib/ecs_solo/help/deploy.md +12 -0
- data/lib/ecs_solo/version.rb +3 -0
- data/pkg/ecs-solo-0.1.0.gem +0 -0
- data/pkg/solo-0.1.0.gem +0 -0
- data/spec/cli_spec.rb +26 -0
- data/spec/spec_helper.rb +29 -0
- metadata +246 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c078f359d1c062293eca0caeb456d4f1ceb68c632fcc2cda74aab06cf967da3b
|
4
|
+
data.tar.gz: 4fc23a43f739db124d9aa470bc19bebe713979894e3eed467c5336e68a0b5d5c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1d84bb9d91991e3427b7b1d97d798545bbfa30bada05bbd3205fc819c3d948c2188e535c0b89bd10b9a8de89279a1f4dee916e065d43881b401b2bf9f6e834b8
|
7
|
+
data.tar.gz: 74bf6a982800c9db88fb0f6225a56569ff11ebd35bced9b0e4a9e187353daf1ef6fbe6fd5fc4dfba6b17a3c19afa23a2638c343102ac1cadb5ac6baa20b54844
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ecs-solo (0.1.0)
|
5
|
+
activesupport
|
6
|
+
aws-sdk-cloudformation
|
7
|
+
aws-sdk-ecs
|
8
|
+
memoist
|
9
|
+
rainbow
|
10
|
+
thor
|
11
|
+
zeitwerk
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: https://rubygems.org/
|
15
|
+
specs:
|
16
|
+
activesupport (6.0.2.1)
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
|
+
i18n (>= 0.7, < 2)
|
19
|
+
minitest (~> 5.1)
|
20
|
+
tzinfo (~> 1.1)
|
21
|
+
zeitwerk (~> 2.2)
|
22
|
+
aws-eventstream (1.0.3)
|
23
|
+
aws-partitions (1.278.0)
|
24
|
+
aws-sdk-cloudformation (1.30.0)
|
25
|
+
aws-sdk-core (~> 3, >= 3.71.0)
|
26
|
+
aws-sigv4 (~> 1.1)
|
27
|
+
aws-sdk-core (3.90.1)
|
28
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
29
|
+
aws-partitions (~> 1, >= 1.239.0)
|
30
|
+
aws-sigv4 (~> 1.1)
|
31
|
+
jmespath (~> 1.0)
|
32
|
+
aws-sdk-ecs (1.57.0)
|
33
|
+
aws-sdk-core (~> 3, >= 3.71.0)
|
34
|
+
aws-sigv4 (~> 1.1)
|
35
|
+
aws-sigv4 (1.1.1)
|
36
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
37
|
+
byebug (11.1.1)
|
38
|
+
cli_markdown (0.1.0)
|
39
|
+
codeclimate-test-reporter (1.0.9)
|
40
|
+
simplecov (<= 0.13)
|
41
|
+
concurrent-ruby (1.1.6)
|
42
|
+
diff-lcs (1.3)
|
43
|
+
docile (1.1.5)
|
44
|
+
i18n (1.8.2)
|
45
|
+
concurrent-ruby (~> 1.0)
|
46
|
+
jmespath (1.4.0)
|
47
|
+
json (2.3.0)
|
48
|
+
memoist (0.16.2)
|
49
|
+
minitest (5.14.0)
|
50
|
+
rainbow (3.0.0)
|
51
|
+
rake (13.0.1)
|
52
|
+
rspec (3.9.0)
|
53
|
+
rspec-core (~> 3.9.0)
|
54
|
+
rspec-expectations (~> 3.9.0)
|
55
|
+
rspec-mocks (~> 3.9.0)
|
56
|
+
rspec-core (3.9.1)
|
57
|
+
rspec-support (~> 3.9.1)
|
58
|
+
rspec-expectations (3.9.0)
|
59
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
+
rspec-support (~> 3.9.0)
|
61
|
+
rspec-mocks (3.9.1)
|
62
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
63
|
+
rspec-support (~> 3.9.0)
|
64
|
+
rspec-support (3.9.2)
|
65
|
+
simplecov (0.13.0)
|
66
|
+
docile (~> 1.1.0)
|
67
|
+
json (>= 1.8, < 3)
|
68
|
+
simplecov-html (~> 0.10.0)
|
69
|
+
simplecov-html (0.10.2)
|
70
|
+
thor (1.0.1)
|
71
|
+
thread_safe (0.3.6)
|
72
|
+
tzinfo (1.2.6)
|
73
|
+
thread_safe (~> 0.1)
|
74
|
+
zeitwerk (2.2.2)
|
75
|
+
|
76
|
+
PLATFORMS
|
77
|
+
ruby
|
78
|
+
|
79
|
+
DEPENDENCIES
|
80
|
+
bundler
|
81
|
+
byebug
|
82
|
+
cli_markdown
|
83
|
+
codeclimate-test-reporter
|
84
|
+
ecs-solo!
|
85
|
+
rake
|
86
|
+
rspec
|
87
|
+
|
88
|
+
BUNDLED WITH
|
89
|
+
2.1.4
|
data/Guardfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
guard "bundler", cmd: "bundle" do
|
2
|
+
watch("Gemfile")
|
3
|
+
watch(/^.+\.gemspec/)
|
4
|
+
end
|
5
|
+
|
6
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
7
|
+
require "guard/rspec/dsl"
|
8
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
9
|
+
|
10
|
+
# RSpec files
|
11
|
+
rspec = dsl.rspec
|
12
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
13
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
14
|
+
watch(rspec.spec_files)
|
15
|
+
|
16
|
+
# Ruby files
|
17
|
+
ruby = dsl.ruby
|
18
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
19
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# ecs-solo
|
2
|
+
|
3
|
+
Deploy Docker image from ECS service to the current instance. This is useful if you want to deploy the current running ECS docker image onto an EC2 instance outside of ECS purview. It can also be useful to run the Docker container locally and exam it.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
You may have to login to pull from the Docker regsitry first. Here's an example ECR login command:
|
8
|
+
|
9
|
+
eval $(aws ecr get-login --no-include-email --region us-west-2)
|
10
|
+
|
11
|
+
There are 2 forms that the `ecs-solo deploy` understands:
|
12
|
+
|
13
|
+
ecs-solo deploy STACK_NAME # CloudFormation template AWS::ECS::Service resource
|
14
|
+
ecs-solo deploy ECS_SERVICE --cluster development
|
15
|
+
|
16
|
+
* When passed the CloudFormation stack name, the ECS Cluster and Cluster is automatically infer. The cluster option is not needed and not used at all.
|
17
|
+
* When passed the ECS Service, the --cluster optional will probably be needed since the ECS service may not be running in the default development cluster.
|
18
|
+
|
19
|
+
## Override command
|
20
|
+
|
21
|
+
You can override the default command in the Dockerfile with the `-c` option.
|
22
|
+
|
23
|
+
ecs-solo deploy demo-web-development -c bin/loop.sh
|
24
|
+
|
25
|
+
## Example
|
26
|
+
|
27
|
+
Here's an example with output:
|
28
|
+
|
29
|
+
$ ecs-solo deploy demo-web-development
|
30
|
+
Finding Docker image associated with service
|
31
|
+
Found task definition with Docker image
|
32
|
+
=> docker ps -a -f name=ecs-demo-web-web --format '{{.Names}}' | grep ecs-demo-web-web
|
33
|
+
=> docker run --name ecs-demo-web-web -d 112233445566.dkr.ecr.us-west-2.amazonaws.com/demo/sinatra:ufo-2020-02-28T20-11-55-f6ef0e0
|
34
|
+
d39bc08598bb133c36690b1bfe2c321c1fc219d3e8526174cb9b8e281f161f24
|
35
|
+
$ docker ps
|
36
|
+
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
37
|
+
d39bc08598bb 112233445566.dkr.ecr.us-west-2.amazonaws.com/demo/sinatra:ufo-2020-02-28T20-11-55-f6ef0e0 "bin/web" 3 seconds ago Up 2 seconds 8080/tcp ecs-demo-web-web
|
38
|
+
$
|
39
|
+
|
40
|
+
## Installation
|
41
|
+
|
42
|
+
gem install ecs-solo
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
task default: :spec
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
require_relative "lib/ecs_solo"
|
9
|
+
require "cli_markdown"
|
10
|
+
desc "Generates cli reference docs as markdown"
|
11
|
+
task :docs do
|
12
|
+
mkdir_p "docs/_includes"
|
13
|
+
CliMarkdown::Creator.create_all(cli_class: EcsSolo::CLI, cli_name: "ecs_solo")
|
14
|
+
end
|
data/ecs-solo.gemspec
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ecs_solo/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ecs-solo"
|
8
|
+
spec.version = EcsSolo::VERSION
|
9
|
+
spec.authors = ["Tung Nguyen"]
|
10
|
+
spec.email = ["tongueroo@gmail.com"]
|
11
|
+
spec.summary = "Deploy Docker image from ECS service to current instance"
|
12
|
+
spec.homepage = "https://github.com/tongueroo/ecs-solo"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = Dir.glob("**/*")
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport"
|
22
|
+
spec.add_dependency "aws-sdk-cloudformation"
|
23
|
+
spec.add_dependency "aws-sdk-ecs"
|
24
|
+
spec.add_dependency "memoist"
|
25
|
+
spec.add_dependency "rainbow"
|
26
|
+
spec.add_dependency "thor"
|
27
|
+
spec.add_dependency "zeitwerk"
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler"
|
30
|
+
spec.add_development_dependency "byebug"
|
31
|
+
spec.add_development_dependency "cli_markdown"
|
32
|
+
spec.add_development_dependency "rake"
|
33
|
+
spec.add_development_dependency "rspec"
|
34
|
+
|
35
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
36
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
37
|
+
if spec.respond_to?(:metadata)
|
38
|
+
# spec.metadata["allowed_push_host"] = ""
|
39
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
40
|
+
spec.metadata["source_code_uri"] = "https://github.com/tongueroo/ecs-deploy"
|
41
|
+
spec.metadata["changelog_uri"] = "https://github.com/tongueroo/ecs-deploy/blob/master/CHANGELOG.md"
|
42
|
+
else
|
43
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
44
|
+
end
|
45
|
+
end
|
data/exe/ecs-solo
ADDED
@@ -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 "ecs_solo"
|
12
|
+
require "ecs_solo/cli"
|
13
|
+
|
14
|
+
EcsSolo::CLI.start(ARGV)
|
data/lib/ecs-solo.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "ecs_solo"
|
data/lib/ecs_solo.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "zeitwerk"
|
2
|
+
|
3
|
+
module EcsSolo
|
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.ignore("#{File.dirname(__dir__)}/ecs-solo.rb")
|
18
|
+
loader.setup
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "aws-sdk-cloudformation"
|
2
|
+
require "aws-sdk-ecs"
|
3
|
+
|
4
|
+
module EcsSolo
|
5
|
+
module AwsServices
|
6
|
+
extend Memoist
|
7
|
+
|
8
|
+
def cloudformation
|
9
|
+
Aws::CloudFormation::Client.new
|
10
|
+
end
|
11
|
+
memoize :cloudformation
|
12
|
+
|
13
|
+
def ecs
|
14
|
+
Aws::ECS::Client.new
|
15
|
+
end
|
16
|
+
memoize :ecs
|
17
|
+
end
|
18
|
+
end
|
data/lib/ecs_solo/cli.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module EcsSolo
|
2
|
+
class CLI < Command
|
3
|
+
class_option :verbose, type: :boolean
|
4
|
+
class_option :noop, type: :boolean
|
5
|
+
|
6
|
+
desc "deploy IDENTIFIER", "Deploys Docker image associated with ECS service"
|
7
|
+
long_desc Help.text(:deploy)
|
8
|
+
option :cluster, default: "development", desc: "ECS Cluster"
|
9
|
+
option :force_new, type: :boolean, desc: "Force new container if container name is already in use"
|
10
|
+
option :command, aliases: :c, desc: "Command to use as part of docker run. Overrides default in the Dockerfile"
|
11
|
+
def deploy(identifier)
|
12
|
+
Deploy.new(options.merge(identifier: identifier)).run
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "completion *PARAMS", "Prints words for auto-completion."
|
16
|
+
long_desc Help.text(:completion)
|
17
|
+
def completion(*params)
|
18
|
+
Completer.new(CLI, *params).run
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "completion_script", "Generates a script that can be eval to setup auto-completion."
|
22
|
+
long_desc Help.text(:completion_script)
|
23
|
+
def completion_script
|
24
|
+
Completer::Script.generate
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "version", "prints version"
|
28
|
+
def version
|
29
|
+
puts VERSION
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,89 @@
|
|
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 EcsSolo
|
17
|
+
class Command < Thor
|
18
|
+
class << self
|
19
|
+
def dispatch(m, args, options, config)
|
20
|
+
# Allow calling for help via:
|
21
|
+
# ecs_solo command help
|
22
|
+
# ecs_solo command -h
|
23
|
+
# ecs_solo command --help
|
24
|
+
# ecs_solo command -D
|
25
|
+
#
|
26
|
+
# as well thor's normal way:
|
27
|
+
#
|
28
|
+
# ecs_solo 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
|
+
# ecs_solo version
|
36
|
+
# ecs_solo --version
|
37
|
+
# ecs_solo -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/ecs_solo-#{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
|
+
|
81
|
+
# https://github.com/erikhuda/thor/issues/244
|
82
|
+
# Deprecation warning: Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `Lono::CLI`
|
83
|
+
# You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.
|
84
|
+
def exit_on_failure?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
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
|
+
ecs_solo completion
|
52
|
+
ecs_solo completion hello
|
53
|
+
ecs_solo completion hello name
|
54
|
+
ecs_solo completion hello name --
|
55
|
+
ecs_solo completion hello name --noop
|
56
|
+
|
57
|
+
ecs_solo completion
|
58
|
+
ecs_solo completion sub:goodbye
|
59
|
+
ecs_solo 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 EcsSolo
|
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
|
@@ -0,0 +1,10 @@
|
|
1
|
+
_ecs_solo() {
|
2
|
+
COMPREPLY=()
|
3
|
+
local word="${COMP_WORDS[COMP_CWORD]}"
|
4
|
+
local words=("${COMP_WORDS[@]}")
|
5
|
+
unset words[0]
|
6
|
+
local completion=$(ecs_solo completion ${words[@]})
|
7
|
+
COMPREPLY=( $(compgen -W "$completion" -- "$word") )
|
8
|
+
}
|
9
|
+
|
10
|
+
complete -F _ecs_solo ecs_solo
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module EcsSolo
|
2
|
+
class Deploy < AbstractBase
|
3
|
+
def initialize(options={})
|
4
|
+
super
|
5
|
+
@identifier = options[:identifier] # ECS Service or CloudFormation Stack
|
6
|
+
@cluster = options[:cluster]
|
7
|
+
@command = options[:command]
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
task_definition = find_task_definition
|
12
|
+
unless task_definition
|
13
|
+
puts "Unable to task definition associated with service #{@service.color(:green)} in the cluster #{@cluster.color(:green)}"
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
@docker = Docker.new(@options.merge(task_definition: task_definition))
|
18
|
+
@docker.execute
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_task_definition
|
22
|
+
puts "Finding Docker image associated with service #{@service}"
|
23
|
+
service = find_service
|
24
|
+
return unless service
|
25
|
+
|
26
|
+
task_definition = service.task_definition
|
27
|
+
resp = ecs.describe_task_definition(task_definition: task_definition)
|
28
|
+
puts "Found task definition with Docker image"
|
29
|
+
resp.task_definition
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_service
|
33
|
+
ecs_service_arn = cloudformation_ecs_service_arn(@identifier)
|
34
|
+
if ecs_service_arn
|
35
|
+
# IE: arn:aws:ecs:us-west-2:112233445566:service/development/demo-web-development-Ecs-179L598PRC44
|
36
|
+
@cluster = ecs_service_arn.split('/')[1] # override @cluster
|
37
|
+
ecs_service(ecs_service_arn)
|
38
|
+
else
|
39
|
+
ecs_service(@identifier)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def cloudformation_ecs_service_arn(stack_name)
|
44
|
+
resp = cloudformation.describe_stack_resources(stack_name: stack_name)
|
45
|
+
resource = resp.stack_resources.find { |r| r.resource_type == "AWS::ECS::Service" }
|
46
|
+
resource.physical_resource_id # IE: arn:aws:ecs:us-west-2:112233445566:service/development/demo-web-development-Ecs-179L598PRC44
|
47
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
48
|
+
if e.message.include?("does not exist")
|
49
|
+
return
|
50
|
+
else
|
51
|
+
raise(e)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def ecs_service(service)
|
56
|
+
begin
|
57
|
+
resp = ecs.describe_services(services: [service], cluster: @cluster)
|
58
|
+
rescue Aws::ECS::Errors::ClusterNotFoundException => e
|
59
|
+
puts "#{e.class}: #{e.message}"
|
60
|
+
puts "WARN: #{@cluster.color(:green)} not found."
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
resp.services.first
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def docker
|
69
|
+
Docker.new
|
70
|
+
end
|
71
|
+
memoize :docker
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module EcsSolo
|
2
|
+
class Docker
|
3
|
+
def initialize(options={})
|
4
|
+
@options = options
|
5
|
+
@task_definition = options[:task_definition] # describe_task_definition resp.task_definition
|
6
|
+
@command = options[:command] # will be array
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
unless in_use?
|
11
|
+
run
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
if @options[:force_new]
|
16
|
+
puts "INFO: Forcing new container"
|
17
|
+
stop
|
18
|
+
rm
|
19
|
+
run
|
20
|
+
else
|
21
|
+
puts "WARN: container name is already in use".color(:yellow)
|
22
|
+
puts "If you want to force a new container, use the --force-new option."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
sh "docker run --name #{name} -d #{image} #{@command}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def in_use?
|
31
|
+
sh "docker ps -a -f name=#{name} --format '{{.Names}}' | grep #{name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop
|
35
|
+
sh "docker stop #{name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def rm
|
39
|
+
sh "docker rm #{name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def pull
|
43
|
+
sh "docker pull #{image}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Almost resembles the name ecs-agent generates.
|
47
|
+
# Unsure how about the random looking id at the end though. Example:
|
48
|
+
#
|
49
|
+
# ecs-demo-web-217-web-86a0bcbac2b7d1b92d00
|
50
|
+
#
|
51
|
+
# So not including that.
|
52
|
+
#
|
53
|
+
# Also not including revision to make this script simpler. So final result:
|
54
|
+
#
|
55
|
+
# ecs-demo-web-web
|
56
|
+
#
|
57
|
+
def name
|
58
|
+
"ecs-#{@task_definition.family}-#{container_definition.name}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def image
|
62
|
+
container_definition.image
|
63
|
+
end
|
64
|
+
|
65
|
+
def container_definition
|
66
|
+
@task_definition.container_definitions.first
|
67
|
+
end
|
68
|
+
|
69
|
+
def sh(command)
|
70
|
+
puts "=> #{command}"
|
71
|
+
system(command)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
## Examples
|
2
|
+
|
3
|
+
ecs_solo completion
|
4
|
+
|
5
|
+
Prints words for TAB auto-completion.
|
6
|
+
|
7
|
+
ecs_solo completion
|
8
|
+
ecs_solo completion hello
|
9
|
+
ecs_solo completion hello name
|
10
|
+
|
11
|
+
To enable, TAB auto-completion add the following to your profile:
|
12
|
+
|
13
|
+
eval $(ecs_solo completion_script)
|
14
|
+
|
15
|
+
Auto-completion example usage:
|
16
|
+
|
17
|
+
ecs_solo [TAB]
|
18
|
+
ecs_solo hello [TAB]
|
19
|
+
ecs_solo hello name [TAB]
|
20
|
+
ecs_solo hello name --[TAB]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
## Examples
|
2
|
+
|
3
|
+
ecs-solo deploy IDENTIFIER
|
4
|
+
ecs-solo deploy IDENTIFIER --cluster development
|
5
|
+
|
6
|
+
The `IDENTIFIER` can be either a:
|
7
|
+
|
8
|
+
1. CloudFormation stack name that has an `AWS::ECS::Service` resource. The ECS Service and its Cluster is inferred from the stack.
|
9
|
+
2. ECS Service, the `--cluster` optional will probably be needed since the ECS service may not be running in the default development cluster.
|
10
|
+
|
11
|
+
ecs-solo deploy CFN_STACK
|
12
|
+
ecs-solo deploy ECS_SERVICE --cluster development
|
Binary file
|
data/pkg/solo-0.1.0.gem
ADDED
Binary file
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
describe EcsSolo::CLI do
|
2
|
+
before(:all) do
|
3
|
+
@args = "--from Tung"
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "ecs_solo" do
|
7
|
+
it "hello" do
|
8
|
+
out = execute("exe/ecs_solo hello world #{@args}")
|
9
|
+
expect(out).to include("from: Tung\nHello world")
|
10
|
+
end
|
11
|
+
|
12
|
+
commands = {
|
13
|
+
"hell" => "hello",
|
14
|
+
"hello" => "name",
|
15
|
+
"hello -" => "--from",
|
16
|
+
"hello name" => "--from",
|
17
|
+
"hello name --" => "--from",
|
18
|
+
}
|
19
|
+
commands.each do |command, expected_word|
|
20
|
+
it "completion #{command}" do
|
21
|
+
out = execute("exe/ecs_solo completion #{command}")
|
22
|
+
expect(out).to include(expected_word) # only checking for one word for simplicity
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
ENV["BACKEND_ADMIN_TEST"] = "1"
|
2
|
+
|
3
|
+
# CodeClimate test coverage: https://docs.codeclimate.com/docs/configuring-test-coverage
|
4
|
+
# require 'simplecov'
|
5
|
+
# SimpleCov.start
|
6
|
+
|
7
|
+
require "pp"
|
8
|
+
require "byebug"
|
9
|
+
root = File.expand_path("../", File.dirname(__FILE__))
|
10
|
+
require "#{root}/lib/ecs_solo"
|
11
|
+
|
12
|
+
module Helper
|
13
|
+
def execute(cmd)
|
14
|
+
puts "Running: #{cmd}" if show_command?
|
15
|
+
out = `#{cmd}`
|
16
|
+
puts out if show_command?
|
17
|
+
out
|
18
|
+
end
|
19
|
+
|
20
|
+
# Added SHOW_COMMAND because DEBUG is also used by other libraries like
|
21
|
+
# bundler and it shows its internal debugging logging also.
|
22
|
+
def show_command?
|
23
|
+
ENV['DEBUG'] || ENV['SHOW_COMMAND']
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
RSpec.configure do |c|
|
28
|
+
c.include Helper
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ecs-solo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tung Nguyen
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk-cloudformation
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: aws-sdk-ecs
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: memoist
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rainbow
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: thor
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: zeitwerk
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: bundler
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: byebug
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: cli_markdown
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rake
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rspec
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description:
|
182
|
+
email:
|
183
|
+
- tongueroo@gmail.com
|
184
|
+
executables:
|
185
|
+
- ecs-solo
|
186
|
+
extensions: []
|
187
|
+
extra_rdoc_files: []
|
188
|
+
files:
|
189
|
+
- CHANGELOG.md
|
190
|
+
- Gemfile
|
191
|
+
- Gemfile.lock
|
192
|
+
- Guardfile
|
193
|
+
- README.md
|
194
|
+
- Rakefile
|
195
|
+
- ecs-solo.gemspec
|
196
|
+
- exe/ecs-solo
|
197
|
+
- lib/ecs-solo.rb
|
198
|
+
- lib/ecs_solo.rb
|
199
|
+
- lib/ecs_solo/abstract_base.rb
|
200
|
+
- lib/ecs_solo/autoloader.rb
|
201
|
+
- lib/ecs_solo/aws_services.rb
|
202
|
+
- lib/ecs_solo/cli.rb
|
203
|
+
- lib/ecs_solo/command.rb
|
204
|
+
- lib/ecs_solo/completer.rb
|
205
|
+
- lib/ecs_solo/completer/script.rb
|
206
|
+
- lib/ecs_solo/completer/script.sh
|
207
|
+
- lib/ecs_solo/deploy.rb
|
208
|
+
- lib/ecs_solo/docker.rb
|
209
|
+
- lib/ecs_solo/help.rb
|
210
|
+
- lib/ecs_solo/help/completion.md
|
211
|
+
- lib/ecs_solo/help/completion_script.md
|
212
|
+
- lib/ecs_solo/help/deploy.md
|
213
|
+
- lib/ecs_solo/version.rb
|
214
|
+
- pkg/ecs-solo-0.1.0.gem
|
215
|
+
- pkg/solo-0.1.0.gem
|
216
|
+
- spec/cli_spec.rb
|
217
|
+
- spec/spec_helper.rb
|
218
|
+
homepage: https://github.com/tongueroo/ecs-solo
|
219
|
+
licenses:
|
220
|
+
- MIT
|
221
|
+
metadata:
|
222
|
+
homepage_uri: https://github.com/tongueroo/ecs-solo
|
223
|
+
source_code_uri: https://github.com/tongueroo/ecs-deploy
|
224
|
+
changelog_uri: https://github.com/tongueroo/ecs-deploy/blob/master/CHANGELOG.md
|
225
|
+
post_install_message:
|
226
|
+
rdoc_options: []
|
227
|
+
require_paths:
|
228
|
+
- lib
|
229
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
230
|
+
requirements:
|
231
|
+
- - ">="
|
232
|
+
- !ruby/object:Gem::Version
|
233
|
+
version: '0'
|
234
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
235
|
+
requirements:
|
236
|
+
- - ">="
|
237
|
+
- !ruby/object:Gem::Version
|
238
|
+
version: '0'
|
239
|
+
requirements: []
|
240
|
+
rubygems_version: 3.1.2
|
241
|
+
signing_key:
|
242
|
+
specification_version: 4
|
243
|
+
summary: Deploy Docker image from ECS service to current instance
|
244
|
+
test_files:
|
245
|
+
- spec/cli_spec.rb
|
246
|
+
- spec/spec_helper.rb
|