artofmission-heroku 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/README.md +66 -0
  2. data/Rakefile +107 -0
  3. data/bin/heroku +15 -0
  4. data/lib/heroku.rb +5 -0
  5. data/lib/heroku/client.rb +487 -0
  6. data/lib/heroku/command.rb +96 -0
  7. data/lib/heroku/commands/account.rb +13 -0
  8. data/lib/heroku/commands/addons.rb +109 -0
  9. data/lib/heroku/commands/app.rb +239 -0
  10. data/lib/heroku/commands/auth.rb +137 -0
  11. data/lib/heroku/commands/base.rb +133 -0
  12. data/lib/heroku/commands/bundles.rb +51 -0
  13. data/lib/heroku/commands/config.rb +55 -0
  14. data/lib/heroku/commands/db.rb +129 -0
  15. data/lib/heroku/commands/domains.rb +31 -0
  16. data/lib/heroku/commands/help.rb +148 -0
  17. data/lib/heroku/commands/keys.rb +49 -0
  18. data/lib/heroku/commands/logs.rb +11 -0
  19. data/lib/heroku/commands/maintenance.rb +13 -0
  20. data/lib/heroku/commands/plugins.rb +25 -0
  21. data/lib/heroku/commands/ps.rb +37 -0
  22. data/lib/heroku/commands/service.rb +23 -0
  23. data/lib/heroku/commands/sharing.rb +29 -0
  24. data/lib/heroku/commands/ssl.rb +33 -0
  25. data/lib/heroku/commands/version.rb +7 -0
  26. data/lib/heroku/helpers.rb +23 -0
  27. data/lib/heroku/plugin.rb +65 -0
  28. data/spec/base.rb +23 -0
  29. data/spec/client_spec.rb +366 -0
  30. data/spec/command_spec.rb +15 -0
  31. data/spec/commands/addons_spec.rb +47 -0
  32. data/spec/commands/app_spec.rb +175 -0
  33. data/spec/commands/auth_spec.rb +104 -0
  34. data/spec/commands/base_spec.rb +114 -0
  35. data/spec/commands/bundles_spec.rb +48 -0
  36. data/spec/commands/config_spec.rb +45 -0
  37. data/spec/commands/db_spec.rb +53 -0
  38. data/spec/commands/domains_spec.rb +31 -0
  39. data/spec/commands/keys_spec.rb +60 -0
  40. data/spec/commands/logs_spec.rb +21 -0
  41. data/spec/commands/maintenance_spec.rb +21 -0
  42. data/spec/commands/plugins_spec.rb +26 -0
  43. data/spec/commands/ps_spec.rb +16 -0
  44. data/spec/commands/sharing_spec.rb +32 -0
  45. data/spec/commands/ssl_spec.rb +25 -0
  46. data/spec/plugin_spec.rb +64 -0
  47. metadata +150 -0
@@ -0,0 +1,31 @@
1
+ module Heroku::Command
2
+ class Domains < BaseWithApp
3
+ def list
4
+ domains = heroku.list_domains(app)
5
+ if domains.empty?
6
+ display "No domain names for #{app}.#{heroku.host}"
7
+ else
8
+ display "Domain names for #{app}.#{heroku.host}:"
9
+ display domains.map { |d| d[:domain] }.join("\n")
10
+ end
11
+ end
12
+ alias :index :list
13
+
14
+ def add
15
+ domain = args.shift.downcase rescue nil
16
+ heroku.add_domain(app, domain)
17
+ display "Added #{domain} as a custom domain name to #{app}.#{heroku.host}"
18
+ end
19
+
20
+ def remove
21
+ domain = args.shift.downcase rescue nil
22
+ heroku.remove_domain(app, domain)
23
+ display "Removed #{domain} as a custom domain name to #{app}.#{heroku.host}"
24
+ end
25
+
26
+ def clear
27
+ heroku.remove_domains(app)
28
+ display "Removed all domain names for #{app}.#{heroku.host}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,148 @@
1
+ module Heroku::Command
2
+ class Help < Base
3
+ class HelpGroup < Array
4
+ attr_reader :title
5
+
6
+ def initialize(title)
7
+ @title = title
8
+ end
9
+
10
+ def command(name, description)
11
+ self << [name, description]
12
+ end
13
+
14
+ def space
15
+ self << ['', '']
16
+ end
17
+ end
18
+
19
+ def self.groups
20
+ @groups ||= []
21
+ end
22
+
23
+ def self.group(title, &block)
24
+ groups << begin
25
+ group = HelpGroup.new(title)
26
+ group.instance_eval(&block)
27
+ group
28
+ end
29
+ end
30
+
31
+ def self.create_default_groups!
32
+ group('General Commands') do
33
+ command 'help', 'show this usage'
34
+ command 'version', 'show the gem version'
35
+ space
36
+ command 'list', 'list your apps'
37
+ command 'create [<name>]', 'create a new app'
38
+ space
39
+ command 'keys', 'show your user\'s public keys'
40
+ command 'keys:add [<path to keyfile>]', 'add a public key'
41
+ command 'keys:remove <keyname> ', 'remove a key by name (user@host)'
42
+ command 'keys:clear', 'remove all keys'
43
+ space
44
+ command 'info', 'show app info, like web url and git repo'
45
+ command 'open', 'open the app in a web browser'
46
+ command 'rename <newname>', 'rename the app'
47
+ space
48
+ command 'dynos <qty>', 'scale to qty web processes'
49
+ command 'workers <qty>', 'scale to qty background processes'
50
+ space
51
+ command 'sharing:add <email>', 'add a collaborator'
52
+ command 'sharing:remove <email>', 'remove a collaborator'
53
+ command 'sharing:transfer <email>', 'transfers the app ownership'
54
+ space
55
+ command 'domains:add <domain>', 'add a custom domain name'
56
+ command 'domains:remove <domain>', 'remove a custom domain name'
57
+ command 'domains:clear', 'remove all custom domains'
58
+ space
59
+ command 'ssl:add <pem> <key>', 'add SSL cert to the app'
60
+ command 'ssl:remove <domain>', 'removes SSL cert from the app domain'
61
+ space
62
+ command 'rake <command>', 'remotely execute a rake command'
63
+ command 'console <command>', 'remotely execute a single console command'
64
+ command 'console', 'start an interactive console to the remote app'
65
+ space
66
+ command 'restart', 'restart app servers'
67
+ command 'logs', 'fetch recent log output for debugging'
68
+ command 'logs:cron', 'fetch cron log output'
69
+ space
70
+ command 'maintenance:on', 'put the app into maintenance mode'
71
+ command 'maintenance:off', 'take the app out of maintenance mode'
72
+ space
73
+ command 'config', 'display the app\'s config vars (environment)'
74
+ command 'config:add key=val [...]', 'add one or more config vars'
75
+ command 'config:remove key [...]', 'remove one or more config vars'
76
+ command 'config:clear', 'clear user-set vars and reset to default'
77
+ space
78
+ command 'db:pull [<database_url>]', 'pull the app\'s database into a local database'
79
+ command 'db:push [<database_url>]', 'push a local database into the app\'s remote database'
80
+ command 'db:reset', 'reset the database for the app'
81
+ space
82
+ command 'bundles', 'list bundles for the app'
83
+ command 'bundles:capture [<bundle>]', 'capture a bundle of the app\'s code and data'
84
+ command 'bundles:download', 'download most recent app bundle as a tarball'
85
+ command 'bundles:download <bundle>', 'download the named bundle'
86
+ command 'bundles:animate <bundle>', 'animate a bundle into a new app'
87
+ command 'bundles:destroy <bundle>', 'destroy the named bundle'
88
+ space
89
+ command 'addons', 'list installed addons'
90
+ command 'addons:info', 'list all available addons'
91
+ command 'addons:add name [key=value]', 'install addon (with zero or more config vars)'
92
+ command 'addons:remove name', 'uninstall an addons'
93
+ command 'addons:clear', 'uninstall all addons'
94
+ space
95
+ command 'destroy', 'destroy the app permanently'
96
+ end
97
+
98
+ group('Plugins') do
99
+ command 'plugins', 'list installed plugins'
100
+ command 'plugins:install <url>', 'install the plugin from the specified git url'
101
+ command 'plugins:uninstall <url/name>', 'remove the specified plugin'
102
+ end
103
+ end
104
+
105
+ def index
106
+ display usage
107
+ end
108
+
109
+ def version
110
+ display Heroku::Client.version
111
+ end
112
+
113
+ def usage
114
+ longest_command_length = self.class.groups.map do |group|
115
+ group.map { |g| g.first.length }
116
+ end.flatten.max
117
+
118
+ self.class.groups.inject(StringIO.new) do |output, group|
119
+ output.puts "=== %s" % group.title
120
+ output.puts
121
+
122
+ group.each do |command, description|
123
+ if command.empty?
124
+ output.puts
125
+ else
126
+ output.puts "%-*s # %s" % [longest_command_length, command, description]
127
+ end
128
+ end
129
+
130
+ output.puts
131
+ output
132
+ end.string + <<-EOTXT
133
+ === Example:
134
+
135
+ rails myapp
136
+ cd myapp
137
+ git init
138
+ git add .
139
+ git commit -m "my new app"
140
+ heroku create
141
+ git push heroku master
142
+
143
+ EOTXT
144
+ end
145
+ end
146
+ end
147
+
148
+ Heroku::Command::Help.create_default_groups!
@@ -0,0 +1,49 @@
1
+ module Heroku::Command
2
+ class Keys < Base
3
+ def list
4
+ long = args.any? { |a| a == '--long' }
5
+ keys = heroku.keys
6
+ if keys.empty?
7
+ display "No keys for #{heroku.user}"
8
+ else
9
+ display "=== #{keys.size} key#{'s' if keys.size > 1} for #{heroku.user}"
10
+ keys.each do |key|
11
+ display long ? key.strip : format_key_for_display(key)
12
+ end
13
+ end
14
+ end
15
+ alias :index :list
16
+
17
+ def add
18
+ keyfile = args.first || find_key
19
+ key = File.read(keyfile)
20
+
21
+ display "Uploading ssh public key #{keyfile}"
22
+ heroku.add_key(key)
23
+ end
24
+
25
+ def remove
26
+ heroku.remove_key(args.first)
27
+ display "Key #{args.first} removed."
28
+ end
29
+
30
+ def clear
31
+ heroku.remove_all_keys
32
+ display "All keys removed."
33
+ end
34
+
35
+ protected
36
+ def find_key
37
+ %w(rsa dsa).each do |key_type|
38
+ keyfile = "#{home_directory}/.ssh/id_#{key_type}.pub"
39
+ return keyfile if File.exists? keyfile
40
+ end
41
+ raise CommandFailed, "No ssh public key found in #{home_directory}/.ssh/id_[rd]sa.pub. You may want to specify the full path to the keyfile."
42
+ end
43
+
44
+ def format_key_for_display(key)
45
+ type, hex, local = key.strip.split(/\s/)
46
+ [type, hex[0,10] + '...' + hex[-10,10], local].join(' ')
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ module Heroku::Command
2
+ class Logs < BaseWithApp
3
+ def index
4
+ display heroku.logs(app)
5
+ end
6
+
7
+ def cron
8
+ display heroku.cron_logs(app)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Heroku::Command
2
+ class Maintenance < BaseWithApp
3
+ def on
4
+ heroku.maintenance(app, :on)
5
+ display "Maintenance mode enabled."
6
+ end
7
+
8
+ def off
9
+ heroku.maintenance(app, :off)
10
+ display "Maintenance mode disabled."
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Heroku::Command
2
+ class Plugins < Base
3
+ def list
4
+ ::Heroku::Plugin.list.each do |plugin|
5
+ display plugin
6
+ end
7
+ end
8
+ alias :index :list
9
+
10
+ def install
11
+ plugin = Heroku::Plugin.new(args.shift)
12
+ if plugin.install
13
+ display "#{plugin} installed"
14
+ else
15
+ error "Could not install #{plugin}. Please check the URL and try again"
16
+ end
17
+ end
18
+
19
+ def uninstall
20
+ plugin = Heroku::Plugin.new(args.shift)
21
+ plugin.uninstall
22
+ display "#{plugin} uninstalled"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ module Heroku::Command
2
+ class Ps < Base
3
+ def index
4
+ ps = heroku.ps(extract_app)
5
+
6
+ output = []
7
+ output << "UPID Slug Command State Since"
8
+ output << "------- ------------ -------------------------- ---------- ---------"
9
+
10
+ ps.each do |p|
11
+ output << "%-7s %-12s %-26s %-10s %-9s" %
12
+ [p['upid'], p['slug'], truncate(p['command'], 22), p['state'], time_ago(p['elapsed'])]
13
+ end
14
+
15
+ display output.join("\n")
16
+ end
17
+
18
+ private
19
+ def time_ago(elapsed)
20
+ if elapsed < 60
21
+ "#{elapsed.floor}s ago"
22
+ elsif elapsed < (60 * 60)
23
+ "#{(elapsed / 60).floor}m ago"
24
+ else
25
+ "#{(elapsed / 60 / 60).floor}h ago"
26
+ end
27
+ end
28
+
29
+ def truncate(text, length)
30
+ if text.size > length
31
+ text[0, length - 2] + '..'
32
+ else
33
+ text
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Heroku::Command
2
+ class Service < Base
3
+ def start
4
+ error "heroku service:start is defunct. Use heroku workers +1 instead."
5
+ end
6
+
7
+ def up
8
+ error "heroku service:up is defunct. Use heroku workers +1 instead."
9
+ end
10
+
11
+ def down
12
+ error "heroku service:down is defunct. Use heroku workers -1 instead."
13
+ end
14
+
15
+ def bounce
16
+ error "heroku service:bounce is defunct. Use heroku restart instead."
17
+ end
18
+
19
+ def status
20
+ error "heroku service:status is defunct. Use heroku ps instead."
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ module Heroku::Command
2
+ class Sharing < BaseWithApp
3
+ def list
4
+ list = heroku.list_collaborators(app)
5
+ display list.map { |c| c[:email] }.join("\n")
6
+ end
7
+ alias :index :list
8
+
9
+ def add
10
+ email = args.shift.downcase rescue ''
11
+ raise(CommandFailed, "Specify an email address to share the app with.") if email == ''
12
+ display heroku.add_collaborator(app, email)
13
+ end
14
+
15
+ def remove
16
+ email = args.shift.downcase rescue ''
17
+ raise(CommandFailed, "Specify an email address to remove from the app.") if email == ''
18
+ heroku.remove_collaborator(app, email)
19
+ display "Collaborator removed."
20
+ end
21
+
22
+ def transfer
23
+ email = args.shift.downcase rescue ''
24
+ raise(CommandFailed, "Specify the email address of the new owner") if email == ''
25
+ heroku.update(app, :transfer_owner => email)
26
+ display "App ownership transfered. New owner is #{email}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ module Heroku::Command
2
+ class Ssl < BaseWithApp
3
+ def list
4
+ heroku.list_domains(app).each do |d|
5
+ if cert = d[:cert]
6
+ display "#{d[:domain]} has a SSL certificate registered to #{cert[:subject]} which expires on #{cert[:expires_at].strftime("%b %d, %Y")}"
7
+ else
8
+ display "#{d[:domain]} has no certificate"
9
+ end
10
+ end
11
+ end
12
+ alias :index :list
13
+
14
+ def add
15
+ usage = 'heroku ssl:add <pem> <key>'
16
+ raise CommandFailed, "Missing pem file. Usage:\n#{usage}" unless pem_file = args.shift
17
+ raise CommandFailed, "Missing key file. Usage:\n#{usage}" unless key_file = args.shift
18
+ raise CommandFailed, "Could not find pem in #{pem_file}" unless File.exists?(pem_file)
19
+ raise CommandFailed, "Could not find key in #{key_file}" unless File.exists?(key_file)
20
+
21
+ pem = File.read(pem_file)
22
+ key = File.read(key_file)
23
+ info = heroku.add_ssl(app, pem, key)
24
+ display "Added certificate to #{info['domain']}, expiring in #{info['expires_at']}"
25
+ end
26
+
27
+ def remove
28
+ raise CommandFailed, "Missing domain. Usage:\nheroku ssl:remove <domain>" unless domain = args.shift
29
+ heroku.remove_ssl(app, domain)
30
+ display "Removed certificate from #{domain}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module Heroku::Command
2
+ class Version < Base
3
+ def index
4
+ display Heroku::Client.gem_version_string
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ module Heroku
2
+ module Helpers
3
+ def home_directory
4
+ running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
5
+ end
6
+
7
+ def running_on_windows?
8
+ RUBY_PLATFORM =~ /mswin32|mingw32/
9
+ end
10
+
11
+ def running_on_a_mac?
12
+ RUBY_PLATFORM =~ /-darwin\d/
13
+ end
14
+ end
15
+ end
16
+
17
+ unless String.method_defined?(:shellescape)
18
+ class String
19
+ def shellescape
20
+ empty? ? "''" : gsub(/([^A-Za-z0-9_\-.,:\/@\n])/n, '\\\\\\1').gsub(/\n/, "'\n'")
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,65 @@
1
+ # based on the Rails Plugin
2
+
3
+ module Heroku
4
+ class Plugin
5
+ class << self
6
+ include Heroku::Helpers
7
+ end
8
+
9
+ attr_reader :name, :uri
10
+
11
+ def self.directory
12
+ "#{home_directory}/.heroku/plugins"
13
+ end
14
+
15
+ def self.list
16
+ Dir["#{directory}/*"].map do |folder|
17
+ File.basename(folder)
18
+ end
19
+ end
20
+
21
+ def self.load!
22
+ list.each do |plugin|
23
+ folder = "#{self.directory}/#{plugin}"
24
+ $: << "#{folder}/lib" if File.directory? "#{folder}/lib"
25
+ load "#{folder}/init.rb" if File.exists? "#{folder}/init.rb"
26
+ end
27
+ end
28
+
29
+ def initialize(uri)
30
+ @uri = uri
31
+ guess_name(uri)
32
+ end
33
+
34
+ def to_s
35
+ name
36
+ end
37
+
38
+ def path
39
+ "#{self.class.directory}/#{name}"
40
+ end
41
+
42
+ def install
43
+ FileUtils.mkdir_p(path)
44
+ Dir.chdir(path) do
45
+ system("git init > /dev/null 2>&1")
46
+ if !system("git pull --depth 1 #{uri} > /dev/null 2>&1")
47
+ FileUtils.rm_rf path
48
+ return false
49
+ end
50
+ end
51
+ true
52
+ end
53
+
54
+ def uninstall
55
+ FileUtils.rm_r path if File.directory?(path)
56
+ end
57
+
58
+ private
59
+ def guess_name(url)
60
+ @name = File.basename(url)
61
+ @name = File.basename(File.dirname(url)) if @name.empty?
62
+ @name.gsub!(/\.git$/, '') if @name =~ /\.git$/
63
+ end
64
+ end
65
+ end