appli 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/appli +3 -0
- data/lib/appli/command.rb +126 -0
- data/lib/appli.rb +166 -0
- data/lib/commands/gems.rb +57 -0
- data/lib/commands/system.rb +40 -0
- metadata +68 -0
data/bin/appli
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
module Appli
|
2
|
+
class Command
|
3
|
+
|
4
|
+
def initialize(block)
|
5
|
+
(class << self;self end).send :define_method, :command, &block
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(options, *args)
|
9
|
+
@options = options
|
10
|
+
arity = method(:command).arity
|
11
|
+
args << nil while args.size < arity
|
12
|
+
send :command, *args
|
13
|
+
rescue Error => e
|
14
|
+
puts "An error occured while excuting your command...\n#{e.message}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def use_hirb
|
18
|
+
begin
|
19
|
+
require 'hirb'
|
20
|
+
extend Hirb::Console
|
21
|
+
rescue LoadError
|
22
|
+
puts "Hirb is not installed. Install hirb using '[sudo] gem install hirb' to get cool ASCII tables"
|
23
|
+
Process.exit(1)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def application
|
28
|
+
return nil if @options[:application].nil?
|
29
|
+
@application ||= get("applications/#{@options[:application]}")['application']
|
30
|
+
rescue
|
31
|
+
puts "Application not found with identifier '#{@options[:application]}'"
|
32
|
+
Process.exit(1)
|
33
|
+
end
|
34
|
+
|
35
|
+
def options
|
36
|
+
@options || Hash.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def execute_commands(array)
|
40
|
+
for command in array
|
41
|
+
puts "\e[44;33m" + command + "\e[0m"
|
42
|
+
exit_code = 0
|
43
|
+
IO.popen(command) do |f|
|
44
|
+
output = f.read
|
45
|
+
exit_code = Process.waitpid2(f.pid)[1]
|
46
|
+
end
|
47
|
+
if exit_code != 0
|
48
|
+
$stderr.puts "An error occured running: #{command}"
|
49
|
+
Process.exit(1)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def git_config_variable(name)
|
55
|
+
if name.is_a?(Symbol)
|
56
|
+
r = `git config appli.#{name.to_s}`.chomp
|
57
|
+
else
|
58
|
+
r = `git config #{name.to_s}`.chomp
|
59
|
+
end
|
60
|
+
r.empty? ? nil : r
|
61
|
+
end
|
62
|
+
|
63
|
+
def api_request(url, username, password, data = nil)
|
64
|
+
require 'uri'
|
65
|
+
require 'net/http'
|
66
|
+
require 'net/https'
|
67
|
+
uri = URI.parse(url)
|
68
|
+
if data
|
69
|
+
req = Net::HTTP::Post.new(uri.path)
|
70
|
+
else
|
71
|
+
req = Net::HTTP::Get.new(uri.path)
|
72
|
+
end
|
73
|
+
req.basic_auth(username, password)
|
74
|
+
req.add_field("Accept", "application/json")
|
75
|
+
req.add_field("Content-type", "application/json")
|
76
|
+
res = Net::HTTP.new(uri.host, uri.port)
|
77
|
+
if url.include?('https://')
|
78
|
+
res.use_ssl = true
|
79
|
+
res.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
80
|
+
end
|
81
|
+
res = res.request(req, data)
|
82
|
+
case res
|
83
|
+
when Net::HTTPSuccess
|
84
|
+
return res.body
|
85
|
+
when Net::HTTPServiceUnavailable
|
86
|
+
puts "The API is currently unavailable. Please check your codebase account has been enabled for API access."
|
87
|
+
Process.exit(1)
|
88
|
+
when Net::HTTPForbidden, Net::HTTPUnauthorized
|
89
|
+
puts "Access Denied. Ensure you have correctly configured your local Gem installation using the 'cb setup' command."
|
90
|
+
Process.exit(1)
|
91
|
+
else
|
92
|
+
@errors = res.body
|
93
|
+
return false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def errors
|
98
|
+
@errors || nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def get(path)
|
102
|
+
JSON.parse(api(path))
|
103
|
+
end
|
104
|
+
|
105
|
+
def api(path, data = nil)
|
106
|
+
api_request("http://#{domain}/#{path}", git_config_variable(:username), git_config_variable(:apikey), data)
|
107
|
+
end
|
108
|
+
|
109
|
+
def api_on_api(path, data = nil)
|
110
|
+
api("applications/#{git_config_variable(:app)}/#{path}", data)
|
111
|
+
end
|
112
|
+
|
113
|
+
def domain
|
114
|
+
git_config_variable(:domain) || 'applihq.com'
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_from_ssh(command)
|
118
|
+
`ssh -p #{application['ssh_port']} app@mercury.uk.applihq.com \"#{command}\"`
|
119
|
+
end
|
120
|
+
|
121
|
+
def ssh(command, filter = //)
|
122
|
+
puts get_from_ssh(command).gsub(filter, '')
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
data/lib/appli.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
require 'appli/command'
|
7
|
+
|
8
|
+
trap("INT") { puts; exit }
|
9
|
+
|
10
|
+
module Appli
|
11
|
+
extend self
|
12
|
+
|
13
|
+
class Error < RuntimeError; end
|
14
|
+
class NotConfiguredError < StandardError; end
|
15
|
+
class MustBeInRepositoryError < StandardError; end
|
16
|
+
|
17
|
+
VERSION = "4.0.0"
|
18
|
+
|
19
|
+
def run(command, args = [])
|
20
|
+
load_commands
|
21
|
+
command = 'help' if command.nil?
|
22
|
+
application = nil
|
23
|
+
commands_array = @global_commands
|
24
|
+
|
25
|
+
if @global_commands[command]
|
26
|
+
## nothing to do...
|
27
|
+
elsif @application_commands[args.first]
|
28
|
+
application = command
|
29
|
+
command = args.shift
|
30
|
+
commands_array = @application_commands
|
31
|
+
elsif !command.empty?
|
32
|
+
puts "usage: appli #{command} {command}\n\n"
|
33
|
+
command = 'help'
|
34
|
+
else
|
35
|
+
puts "Command not found. Check 'cb help' for full information."
|
36
|
+
Process.exit(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
options = parse_options(args)
|
40
|
+
if args.size < commands_array[command][:required_args].to_i
|
41
|
+
puts "error: #{commands_array[command][:usage]}"
|
42
|
+
puts "See 'cb help #{command}' for usage."
|
43
|
+
Process.exit(1)
|
44
|
+
end
|
45
|
+
options.merge!({:application => application})
|
46
|
+
commands_array[command][:block].call(options, *args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def command(command, options = {}, &block)
|
50
|
+
hash = Hash.new
|
51
|
+
hash[:description] = @next_description
|
52
|
+
hash[:usage] = @next_usage
|
53
|
+
hash[:flags] = @next_flags
|
54
|
+
hash[:required_args] = (options[:required_args] || 0)
|
55
|
+
hash[:block] = Command.new(block)
|
56
|
+
|
57
|
+
@global_commands = Hash.new if @global_commands.nil?
|
58
|
+
@application_commands = Hash.new if @application_commands.nil?
|
59
|
+
|
60
|
+
if options[:global]
|
61
|
+
@global_commands[command] = hash
|
62
|
+
else
|
63
|
+
@application_commands[command] = hash
|
64
|
+
end
|
65
|
+
|
66
|
+
@next_usage, @next_description, @next_flags = nil, nil, nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def global_commands
|
70
|
+
@global_commands
|
71
|
+
end
|
72
|
+
|
73
|
+
def application_commands
|
74
|
+
@application_commands
|
75
|
+
end
|
76
|
+
|
77
|
+
def desc(value)
|
78
|
+
@next_description = Array.new if @next_description.nil?
|
79
|
+
@next_description << value
|
80
|
+
end
|
81
|
+
|
82
|
+
def usage(value)
|
83
|
+
@next_usage = value
|
84
|
+
end
|
85
|
+
|
86
|
+
def flags(key, value)
|
87
|
+
@next_flags = Hash.new if @next_flags.nil?
|
88
|
+
@next_flags[key] = value
|
89
|
+
end
|
90
|
+
|
91
|
+
def load_commands
|
92
|
+
Dir[File.join(File.dirname(__FILE__), 'commands', '*.rb')].each do |path|
|
93
|
+
Appli.module_eval File.read(path), path
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_options(args)
|
98
|
+
idx = 0
|
99
|
+
args.clone.inject({}) do |memo, arg|
|
100
|
+
case arg
|
101
|
+
when /^--(.+?)=(.*)/
|
102
|
+
args.delete_at(idx)
|
103
|
+
memo.merge($1.to_sym => $2)
|
104
|
+
when /^--(.+)/
|
105
|
+
args.delete_at(idx)
|
106
|
+
memo.merge($1.to_sym => true)
|
107
|
+
when "--"
|
108
|
+
args.delete_at(idx)
|
109
|
+
return memo
|
110
|
+
else
|
111
|
+
idx += 1
|
112
|
+
memo
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
Appli.desc "Displays this help message"
|
119
|
+
Appli.usage "cb help [command]"
|
120
|
+
Appli.command "help", :global => true do |command|
|
121
|
+
if command.nil?
|
122
|
+
puts "The Appli Gem allows you to easily access your Appli account functions from the"
|
123
|
+
puts "command line. The functions below outline the options which are currently available."
|
124
|
+
puts
|
125
|
+
puts "Global Commands"
|
126
|
+
puts
|
127
|
+
for key, command in Appli.global_commands.sort_by{|k,v| k}
|
128
|
+
puts " #{key.ljust(15)} #{command[:description].first}"
|
129
|
+
end
|
130
|
+
puts
|
131
|
+
puts "Application Specific Commands"
|
132
|
+
puts "The following commands can be executed by running 'appli [app identifier] {command}'"
|
133
|
+
puts
|
134
|
+
for key, command in Appli.application_commands.sort_by{|k,v| k}
|
135
|
+
puts " #{key.ljust(15)} #{command[:description].first}"
|
136
|
+
end
|
137
|
+
|
138
|
+
puts
|
139
|
+
puts "For more information see http://www.applihq.com/gem"
|
140
|
+
puts "See 'appli help [command]' for usage information."
|
141
|
+
else
|
142
|
+
if c = Appli.global_commands[command]
|
143
|
+
puts c[:description]
|
144
|
+
if c[:usage]
|
145
|
+
puts
|
146
|
+
puts "Usage:"
|
147
|
+
puts " #{c[:usage]}"
|
148
|
+
end
|
149
|
+
if c[:flags]
|
150
|
+
puts
|
151
|
+
puts "Options:"
|
152
|
+
for key, value in c[:flags]
|
153
|
+
puts " #{key.ljust(15)} #{value}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
else
|
157
|
+
puts "Command Not Found. Check 'appli help' for a full list of commands available to you."
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
Appli.desc "Displays the current version number"
|
163
|
+
Appli.usage "cb version"
|
164
|
+
Appli.command "version", :global => true do
|
165
|
+
puts "Appli Gem Version #{Appli::VERSION}"
|
166
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class FakeGemfile
|
2
|
+
attr_reader :gems, :sources
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@sources, @gems = [], []
|
6
|
+
end
|
7
|
+
|
8
|
+
def gem(name, version = nil, options = {})
|
9
|
+
@gems << {:name => name, :version => version, :options => options}
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(name, *values, &block)
|
13
|
+
yield block if block_given?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Read in the specified Gemfile and install all the required gems on your application"
|
18
|
+
usage "appli gems:import"
|
19
|
+
command "gems:import" do |file|
|
20
|
+
file_to_import = File.expand_path(file || 'Gemfile')
|
21
|
+
unless File.exist?(file_to_import)
|
22
|
+
raise Error, "Gemfile was not found at #{file_to_import}"
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "Importing gems from #{file_to_import}"
|
26
|
+
gem_details = FakeGemfile.new
|
27
|
+
gem_details.instance_eval(File.read(file_to_import), file_to_import)
|
28
|
+
|
29
|
+
puts "There are #{gem_details.gems.size} gem(s) to install on your application."
|
30
|
+
for g in gem_details.gems[0,4]
|
31
|
+
puts "Installing #{g[:name]} (#{g[:version] || 'latest'})"
|
32
|
+
data = {:ruby_gem => {:name => g[:name], :version => g[:version]}}.to_json
|
33
|
+
if api_on_api "ruby_gems", data
|
34
|
+
puts "=> Installed successfully"
|
35
|
+
else
|
36
|
+
puts "=> Installation failed"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Install the named gem"
|
42
|
+
usage "appli gems:install [gem name] [version]"
|
43
|
+
command "gems:install" do |name, version|
|
44
|
+
puts "Installing #{name} (#{version || 'latest'})"
|
45
|
+
data = {:ruby_gem => {:name => name, :version => version || ''}}.to_json
|
46
|
+
if a = api_on_api("ruby_gems", data)
|
47
|
+
puts "=> Installed"
|
48
|
+
else
|
49
|
+
puts "=> Install failed"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "List all installed gems on your application"
|
54
|
+
usage "appli gems"
|
55
|
+
command "gems" do
|
56
|
+
ssh "gem list"
|
57
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
desc "Display system memory usage"
|
2
|
+
usage "appli mem:system"
|
3
|
+
command "mem:system" do
|
4
|
+
puts get_from_ssh("free -m").split("\n")[0,2].join("\n")
|
5
|
+
end
|
6
|
+
|
7
|
+
desc 'Display passenger memory statistics'
|
8
|
+
usage "appli mem:passenger"
|
9
|
+
command "mem:passenger" do
|
10
|
+
ssh "passenger-memory-stats", /^\*\*\* WARNING\:(.*)$/
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Display your current disk space usage'
|
14
|
+
usage "appli diskusage"
|
15
|
+
command "diskusage" do
|
16
|
+
output = get_from_ssh("df -h").split("\n")[1]
|
17
|
+
output = output.split(/\s+/)
|
18
|
+
fs, size, usage, available, usage_percent, mountpoint = output
|
19
|
+
puts "You are using #{usage} (including system files)."
|
20
|
+
puts
|
21
|
+
puts "You are not billed for system file usage - only files & gems which"
|
22
|
+
puts "you place within your hosting area."
|
23
|
+
puts
|
24
|
+
puts "Your system is limited to #{size} however if you need additional"
|
25
|
+
puts "space, please just contact support."
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Display the current settings"
|
29
|
+
command "settings", :global => true do
|
30
|
+
puts "Username........: #{git_config_variable(:username) || 'unknown'}"
|
31
|
+
puts "API Key.........: #{git_config_variable(:apikey) || 'unknown'}"
|
32
|
+
puts "Domain..........: #{git_config_variable(:domain) || 'unknown'}"
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
desc 'SSH to the named server'
|
37
|
+
usage "appli [application] ssh"
|
38
|
+
command "ssh" do
|
39
|
+
exec "ssh -p #{application['ssh_port']} app@mercury.uk.applihq.com"
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: appli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Cooke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-04 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.1.5
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: adam@atechmedia.com
|
27
|
+
executables:
|
28
|
+
- appli
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- bin/appli
|
35
|
+
- lib/appli/command.rb
|
36
|
+
- lib/appli.rb
|
37
|
+
- lib/commands/gems.rb
|
38
|
+
- lib/commands/system.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://www.atechmedia.com
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.5
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Gem for Appli shared Ruby hosting management
|
67
|
+
test_files: []
|
68
|
+
|