ezpaas-cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 10fb16fdc72f68849bb66c48cdf1d4525fad2da4
4
+ data.tar.gz: c84bb12b4670417135b8502f6a6e91bcda9f81fa
5
+ SHA512:
6
+ metadata.gz: 29d4ba4348233ba5a1e6094960bb3f9b38e488bdd8a44be3b9e117af07cece9593f70b06b1a54d3d55c1710398f7595f2c00d96df12e81cdf571dbfc5bab2217
7
+ data.tar.gz: d45f763a0d9ec8ce649f4ab7db8c86f9a6d94c2db7569f389b145d9d8a7a4dfefcd86f462cb73eed67eb674de7dc0c57a618fb1173e09ffbea9d36eb729da4f9
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Nick Lee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,73 @@
1
+ # EzPaaS CLI
2
+ ### A miniature Heroku clone for easy in-house deployments, powered by Docker.
3
+
4
+ ## What Is It?
5
+
6
+ At [Tendigi](http://www.tendigi.com), we build applications for a variety of clients, often simultaneously, and those applications usually require server-side infrastructure. We also build [random things internally](https://blog.tendigi.com/people-who-are-really-serious-about-software-should-make-their-own-hardware-6983007e7427) from time to time, and these often depend on services that have to live *somewhere*.
7
+
8
+ For production deployments, we love [Heroku](https://heroku.com) (when it makes financial sense) as well as systems like [Deis](https://deis.com/) which can be deployed on AWS / DigitalOcean / etc.
9
+
10
+ We longed for a simple, on-site [PaaS](https://en.wikipedia.org/wiki/Platform_as_a_service) solution that we could hack on as our needs evolved. [Dokku](https://github.com/dokku/dokku) is a great project, but we ran into some issues with it (problems updating to newer versions, discrepancies in application behavior compared to our other Deis deployments, a little annoying to work on because it's a collection of shell scripts, etc). As a result, we built EzPaaS: a mini Heroku clone, built in Ruby, powered by Deis images running on Docker.
11
+
12
+ ## Prerequsites
13
+
14
+ #### EzPaaS Server
15
+
16
+ **Important!** To use this CLI utility, you need to have [EzPaaS Server](https://github.com/TENDIGI/ezpaas-server) deployed somewhere. This can be on your local machine (the CLI will connect to `localhost` on port 3000 by default) or a remote server by passing the `--server` option.
17
+
18
+ #### Ruby
19
+
20
+ EzPaaS also requires [Ruby 2.2 or newer](https://www.ruby-lang.org/en/downloads/). It may work with older versions, but they have not been tested.
21
+
22
+ ## Installation
23
+
24
+ Install the gem. The easiest way is to install it for all users with `sudo`:
25
+
26
+ `$ sudo gem install ezpaas-cli`
27
+
28
+ ## Usage
29
+
30
+ ### Create an app
31
+
32
+ `$ ezpaas apps create`
33
+
34
+ ![](./assets/create-app.gif)
35
+
36
+ ### List all apps on the server
37
+
38
+ `$ ezpaas apps list`
39
+
40
+ ![](./assets/list-apps.gif)
41
+
42
+ ### Deploy an app
43
+
44
+ `$ ezpaas deployments push --app=<app name>`
45
+
46
+ ![](./assets/deploy-app.gif)
47
+
48
+ ### Scale an app
49
+
50
+ `$ ezpaas deployments scale [<process=count>...] --app=<app name>`
51
+
52
+ ![](./assets/scale-app.gif)
53
+
54
+ ### Take an app down
55
+
56
+ `$ ezpaas deployments destroy --app=<app name>`
57
+
58
+ ![](./assets/take-app-down.gif)
59
+
60
+ ### Delete an app
61
+
62
+ `$ ezpaas apps destroy`
63
+
64
+ ![](./assets/destroy-app.gif)
65
+
66
+
67
+ ## Contributing
68
+
69
+ Bug reports and pull requests are welcome on [GitHub]().
70
+
71
+ ## License
72
+
73
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ezpaas/cli"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ezpaas/cli'
4
+ require 'tty'
5
+
6
+ begin
7
+ EzPaaS::CLI::Main.start(ARGV)
8
+ rescue Exception => e
9
+ pastel = Pastel.new
10
+ puts
11
+ puts pastel.red.bold('Error:') + ' ' + e.message
12
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,19 @@
1
+ require 'Thor'
2
+
3
+ require 'ezpaas/cli/version'
4
+ require 'ezpaas/cli/commands/apps'
5
+ require 'ezpaas/cli/commands/deployments'
6
+
7
+ module EzPaaS
8
+ module CLI
9
+ class Main < Thor
10
+
11
+ desc 'apps', 'Manage apps registered with the EzPaaS server'
12
+ subcommand 'apps', Commands::Apps
13
+
14
+ desc 'deployments', 'Manage code deployments for your apps'
15
+ subcommand 'deployments', Commands::Deployments
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,102 @@
1
+ require 'thor'
2
+ require 'tty'
3
+ require 'random-word'
4
+ require 'uri'
5
+ require 'ezpaas/cli/commands/server_commands'
6
+ require 'ezpaas/http/rest_client'
7
+
8
+ module EzPaaS
9
+ module CLI
10
+ module Commands
11
+ class Apps < ServerCommands
12
+
13
+ desc 'list', 'Lists all apps registered with the EzPaaS server'
14
+ def list
15
+ apps = client.apps
16
+
17
+ pastel = Pastel.new
18
+ not_deployed = pastel.red('(not deployed)')
19
+
20
+ table = TTY::Table.new(header: ['Name', 'Slug', 'Scale', 'URL']) do |t|
21
+ apps.each do |app|
22
+
23
+ app_name = app['name']
24
+
25
+ app_slug = app['slug']
26
+
27
+ url_string = app_slug ? URI::join(options[:server], "proxy/#{app_name}/") : not_deployed
28
+
29
+ slug_string = app_slug || not_deployed
30
+
31
+ if scale = app['scale']
32
+ scale_string = app['scale'].map{ |k, v| "#{k}=#{v}" }.join('\n')
33
+ else
34
+ scale_string = not_deployed
35
+ end
36
+
37
+ t << [app_name, slug_string, scale_string, url_string]
38
+ end
39
+ end
40
+
41
+ puts
42
+ puts table.render(:unicode, multiline: true)
43
+ end
44
+
45
+ desc 'create', 'Creates a new app on the EzPaaS server'
46
+ option :name, :type => :string, :banner => '<app name>'
47
+ def create
48
+ app_name = options[:name]
49
+
50
+ unless app_name
51
+ prompt = TTY::Prompt.new
52
+
53
+ default = loop do
54
+ default = "#{RandomWord.adjs(not_longer_than: 10).next}-#{RandomWord.nouns(not_longer_than: 10).next}"
55
+ break default unless default.include?('_')
56
+ end
57
+
58
+ app_name = prompt.ask('What would you like to call your new app?', required: true, default: default)
59
+ end
60
+
61
+ pastel = Pastel.new
62
+
63
+ app = client.create_app(app_name)['app']
64
+ app_name = pastel.blue(app['name'])
65
+
66
+ puts
67
+ puts "App #{app_name} created successfully!"
68
+ end
69
+
70
+ desc 'destroy', 'Destroys an app on the EzPaaS server'
71
+ option :app, :type => :string, :banner => '<app name>'
72
+ def destroy
73
+ pastel = Pastel.new
74
+
75
+ puts
76
+ puts '🚨 🚨 🚨 ' + pastel.red('WARNING: Destroying an app is ') + pastel.red.bold.underline('not') + pastel.red(' reversible!') + ' 🚨 🚨 🚨'
77
+ puts
78
+
79
+ app_name = options[:app]
80
+
81
+ prompt = TTY::Prompt.new
82
+
83
+ unless app_name
84
+ apps = client.apps
85
+ app_names = apps.map { |e| e['name'] }
86
+ app_name = prompt.select('Which app would you like to destroy?', app_names)
87
+ end
88
+
89
+ if prompt.yes?('Are you sure?', default: false)
90
+ client.destroy_app(app_name)
91
+ puts
92
+ puts "App #{app_name} destroyed successfully!"
93
+ else
94
+ puts
95
+ puts pastel.blue('Phew!') + ' App deletion aborted.'
96
+ end
97
+ end
98
+
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,131 @@
1
+ require 'thor'
2
+ require 'git'
3
+ require 'tmpdir'
4
+ require 'tty'
5
+ require 'uri'
6
+ require 'ezpaas/cli/commands/server_commands'
7
+ require 'ezpaas/http/sse_client'
8
+
9
+ module EzPaaS
10
+ module CLI
11
+ module Commands
12
+ class Deployments < ServerCommands
13
+
14
+ desc 'push', 'Pushes the current git repository'
15
+ option :app, :type => :string, :required => true
16
+ option :dir, :type => :string, :default => Dir.pwd
17
+ option :branch, :type => :string, :default => 'master'
18
+ def push
19
+ pastel = Pastel.new
20
+
21
+ app = options[:app]
22
+ dir = options[:dir]
23
+ branch = options[:branch]
24
+
25
+ puts 'Opening git repository at ' + pastel.blue(dir)
26
+ git = Git.open(dir)
27
+ branch = git.branches[branch]
28
+
29
+ begin
30
+ path = Dir::Tmpname.create('ezpaas') do |file|
31
+ puts 'Archiving ' + pastel.blue(branch) + ' branch'
32
+ branch.archive(file, {format: 'tar'})
33
+ end
34
+
35
+ url_str = URI::join(options[:server], "proxy/#{app}/").to_s
36
+ success_msg = pastel.green('Application deployment completed')
37
+ success_msg += "\n" + 'Access your application at ' + pastel.blue(url_str)
38
+
39
+ server_comm_wrap(success_msg) do
40
+ sse_client.deploy(app, path) do |message|
41
+ puts message
42
+ end
43
+ end
44
+
45
+ ensure
46
+ File.delete(path)
47
+ end
48
+ end
49
+
50
+ desc 'destroy', 'Scales all processes of an EzPaas app to zero'
51
+ option :app, :type => :string, :required => true
52
+ def destroy
53
+ pastel = Pastel.new
54
+ prompt = TTY::Prompt.new
55
+
56
+ app = options[:app]
57
+
58
+ puts
59
+ puts '🚨 🚨 🚨 ' + pastel.red("WARNING: You're about to take this app down. People won't be able to access it until you scale it up again.") + ' 🚨 🚨 🚨'
60
+ puts
61
+
62
+ if prompt.yes?('Are you sure?', default: false)
63
+ success_msg = pastel.green('Application scaling completed')
64
+ server_comm_wrap(success_msg) do
65
+ sse_client.destroy(app) do |message|
66
+ puts message
67
+ end
68
+ end
69
+ else
70
+ puts
71
+ puts pastel.blue('Phew!') + ' App scaling aborted.'
72
+ end
73
+
74
+
75
+ end
76
+
77
+ desc 'scale [<process=count>...]', 'Scales the processes of an EzPaas app'
78
+ option :app, :type => :string, :required => true
79
+ def scale(*scales)
80
+ pastel = Pastel.new
81
+
82
+ app = options[:app]
83
+
84
+ if scales.empty?
85
+ raise 'You must provide scaling arguments'
86
+ end
87
+
88
+ new_scale = {}
89
+
90
+ scales.each do |s|
91
+ components = s.split '='
92
+ raise 'Invalid scale format' unless components.count == 2 and components.first.is_a? String and (components.last.to_i.to_s == components.last) and components.last.to_i >= 0
93
+ new_scale[components.first] = components.last.to_i
94
+ end
95
+
96
+ puts
97
+
98
+ success_msg = pastel.green('Application scaling completed')
99
+
100
+ server_comm_wrap(success_msg) do
101
+ sse_client.scale(app, new_scale) do |message|
102
+ puts message
103
+ end
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ no_commands do
110
+ def sse_client
111
+ HTTP::SSEClient.new(options[:server])
112
+ end
113
+
114
+ def server_comm_wrap(end_msg)
115
+ screen = TTY::Screen.new
116
+ msg = 'Opening connection to slug compilation container'
117
+ puts msg
118
+ puts '─' * [msg.length, screen.width].min
119
+ puts
120
+ yield
121
+ puts
122
+ puts '─' * [end_msg.length, screen.width].min
123
+ puts end_msg
124
+ end
125
+ end
126
+
127
+
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,19 @@
1
+ require 'thor'
2
+
3
+ module EzPaaS
4
+ module CLI
5
+ module Commands
6
+ class ServerCommands < Thor
7
+
8
+ class_option :server, :type => :string, :banner => '<server url>', :default => 'http://127.0.0.1:3000'
9
+
10
+ no_commands do
11
+ def client
12
+ HTTP::RESTClient.new(options[:server])
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module EzPaaS
2
+ module CLI
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ require 'uri'
2
+
3
+ module EzPaaS
4
+ module HTTP
5
+ class HTTPClient
6
+
7
+ class HTTPError < StandardError
8
+ end
9
+
10
+ attr_reader :url
11
+
12
+ def initialize(url)
13
+ @url = url
14
+ end
15
+
16
+ protected
17
+
18
+ def url_for(path)
19
+ URI::join(url, path).to_s
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ require 'httparty'
2
+ require 'ezpaas/http/http_client'
3
+
4
+ module EzPaaS
5
+ module HTTP
6
+ class RESTClient < HTTPClient
7
+
8
+ include HTTParty
9
+
10
+ # Apps
11
+
12
+ def apps
13
+ (handle self.class.get(url_for('/apps')))['apps']
14
+ end
15
+
16
+ def create_app(name)
17
+ options = {
18
+ body: {
19
+ name: name
20
+ }
21
+ }
22
+ handle self.class.post(url_for('/apps'), options)
23
+ end
24
+
25
+ def destroy_app(name)
26
+ options = {
27
+ query: {
28
+ name: name
29
+ }
30
+ }
31
+ handle self.class.delete(url_for('/apps'), options)
32
+ end
33
+
34
+ private
35
+
36
+ def handle(response)
37
+ if response.code >= 400
38
+ raise HTTPError, (response['error'] || 'An unknown error occurred.')
39
+ else
40
+ response
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,94 @@
1
+ require "json"
2
+
3
+ module EzPaaS
4
+ module HTTP
5
+ module SSE
6
+ # An EventSource instance is used to parse a stream in the format given by
7
+ # the Server Sent Events standard: http://www.whatwg.org/specs/web-apps/current-work/#server-sent-events
8
+ class EventSource
9
+ class Event < Struct.new(:data, :name, :id);
10
+ end
11
+
12
+ # @!attribute retry [r]
13
+ # @return [Fixnum] the last retry field received, or nil if no retry fields
14
+ # have been received.
15
+ attr_reader :retry
16
+
17
+ # @!attribute last_event_id [r]
18
+ # @return [String] the id of the last fully received event, or nil if no
19
+ # events have been received containing an id field.
20
+ attr_reader :last_event_id
21
+
22
+ # @!attribute json [rw]
23
+ # @return [Boolean] (true) Whether to parse event's data field as JSON.
24
+ attr_accessor :json
25
+ alias json? json
26
+
27
+ def initialize
28
+ @json = true
29
+ @on = {}
30
+ @all = []
31
+ @buffer = ""
32
+ end
33
+
34
+ # Feed a chunk of data to the EventSource and dispatch any fully receieved
35
+ # events.
36
+ # @param [String] chunk the data to parse
37
+ def feed chunk
38
+ @buffer << chunk
39
+
40
+ while i = @buffer.index("\n\n")
41
+ process_event @buffer.slice!(0..i)
42
+ end
43
+ end
44
+
45
+ # Adds a listener for :name:
46
+ def on name, &block
47
+ (@on[name.to_sym] ||= []) << block
48
+ end
49
+
50
+ # Listens to all messages
51
+ def message &block
52
+ @all << block
53
+ end
54
+
55
+ protected
56
+ def process_event s
57
+ data, id, name = [], nil, nil
58
+ s.lines.map(&:chomp).each do |line|
59
+ field, value = case line
60
+ when /^:/ then
61
+ next # comment, do nothing
62
+ when /^(.*?):(.*)$/ then
63
+ [$1, $2]
64
+ else
65
+ [line, ''] # this is what the spec says, I swear!
66
+ end
67
+ # spec allows one optional space after the colon
68
+ value = value[1..-1] if value.start_with? ' '
69
+ case field
70
+ when 'data' then
71
+ data << value
72
+ when 'id' then
73
+ id = value
74
+ when 'event' then
75
+ name = value.to_sym
76
+ when 'retry' then
77
+ @retry = value.to_i
78
+ end
79
+ end
80
+ @last_event_id = id
81
+ dispatch_event data.join("\n"), id, name
82
+ end
83
+
84
+ def dispatch_event data, id, name
85
+ data = JSON.parse(data) if json?
86
+ name = (name || :message).to_sym
87
+ event = Event.new(data, name, id)
88
+ ((@on[name] || []) + @all).each { |p| p.call event }
89
+ end
90
+
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,70 @@
1
+ require 'excon'
2
+ require 'json'
3
+ require 'ezpaas/http/http_client'
4
+ require 'ezpaas/http/sse/event_source'
5
+
6
+ module EzPaaS
7
+ module HTTP
8
+ class SSEClient < HTTPClient
9
+
10
+ # Deployments
11
+
12
+ def deploy(app, file_name)
13
+ begin
14
+ file = File.open(file_name, 'rb')
15
+
16
+ path = "/deployments?app=#{app}"
17
+
18
+ options = {
19
+ body: file,
20
+ headers: {'Content-Type': 'application/tar'}
21
+ }
22
+
23
+ streaming_request(:post, path, options) do |message|
24
+ yield message.data if block_given?
25
+ end
26
+ ensure
27
+ file.close
28
+ end
29
+ end
30
+
31
+ def scale(app, scale)
32
+ scale_qs = scale.map { |k, v| "scale[#{k}]=#{v}" }.join('&')
33
+ path = "/deployments?app=#{app}&" + scale_qs
34
+ streaming_request(:patch, path) do |message|
35
+ yield message.data if block_given?
36
+ end
37
+ end
38
+
39
+ def destroy(app)
40
+ path = "/deployments?app=#{app}"
41
+ streaming_request(:delete, path) do |message|
42
+ yield message.data if block_given?
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def streaming_request(type, path, options = {})
49
+ url = url_for(path)
50
+
51
+ source = SSE::EventSource.new
52
+
53
+ source.json = false
54
+
55
+ streamer = lambda do |chunk, remaining_bytes, total_bytes|
56
+ source.feed chunk
57
+ end
58
+
59
+ source.on :message do |event|
60
+ yield event if block_given?
61
+ end
62
+
63
+ merged_options = options.merge({ :response_block => streamer })
64
+
65
+ Excon.send type, url, merged_options
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,58 @@
1
+ require 'httparty'
2
+ require 'uri'
3
+
4
+ module EzPaaS
5
+ class RESTClient
6
+
7
+ class RestError < StandardError
8
+ end
9
+
10
+ include HTTParty
11
+
12
+ attr_reader :url
13
+
14
+ def initialize(url)
15
+ @url = url
16
+ end
17
+
18
+ # Apps
19
+
20
+ def apps
21
+ (handle self.class.get(url_for('/apps')))['apps']
22
+ end
23
+
24
+ def create_app(name)
25
+ options = {
26
+ body: {
27
+ name: name
28
+ }
29
+ }
30
+ handle self.class.post(url_for('/apps'), options)
31
+ end
32
+
33
+ def destroy_app(name)
34
+ options = {
35
+ query: {
36
+ name: name
37
+ }
38
+ }
39
+ handle self.class.delete(url_for('/apps'), options)
40
+ end
41
+
42
+ private
43
+
44
+ def url_for(path)
45
+ URI::join(url, path)
46
+ end
47
+
48
+ def handle(response)
49
+ if response.code >= 400
50
+ raise ClientError, (response['error'] || 'An unknown error occurred.')
51
+ else
52
+ response
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ezpaas-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Lee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.19.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.19.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: tty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.7.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.7.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.15.6
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.15.6
55
+ - !ruby/object:Gem::Dependency
56
+ name: random-word
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: git
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: excon
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.58.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.58.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.15'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.15'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ description:
126
+ email:
127
+ - nick@tendigi.com
128
+ executables:
129
+ - ezpaas
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - LICENSE.txt
134
+ - README.md
135
+ - bin/console
136
+ - bin/ezpaas
137
+ - bin/setup
138
+ - lib/ezpaas/cli.rb
139
+ - lib/ezpaas/cli/commands/apps.rb
140
+ - lib/ezpaas/cli/commands/deployments.rb
141
+ - lib/ezpaas/cli/commands/server_commands.rb
142
+ - lib/ezpaas/cli/version.rb
143
+ - lib/ezpaas/http/http_client.rb
144
+ - lib/ezpaas/http/rest_client.rb
145
+ - lib/ezpaas/http/sse/event_source.rb
146
+ - lib/ezpaas/http/sse_client.rb
147
+ - lib/ezpaas/rest_client.rb
148
+ homepage: https://github.com/TENDIGI/ezpaas-cli
149
+ licenses:
150
+ - MIT
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubyforge_project:
168
+ rubygems_version: 2.4.8
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: A miniature Heroku clone for easy in-house deployments, powered by Docker
172
+ test_files: []