artofmission-heroku 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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