stackster 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/bin/stackster +6 -0
- data/lib/stackster/aws/cloud_formation.rb +46 -0
- data/lib/stackster/aws/ec2.rb +24 -0
- data/lib/stackster/aws/simpledb.rb +36 -0
- data/lib/stackster/aws.rb +3 -0
- data/lib/stackster/cli.rb +62 -0
- data/lib/stackster/config.rb +20 -0
- data/lib/stackster/entry/entry_lister.rb +15 -0
- data/lib/stackster/entry.rb +57 -0
- data/lib/stackster/instance/instance_reader.rb +27 -0
- data/lib/stackster/instance.rb +1 -0
- data/lib/stackster/stack/stack_creater.rb +38 -0
- data/lib/stackster/stack/stack_destroyer.rb +14 -0
- data/lib/stackster/stack/stack_formater.rb +39 -0
- data/lib/stackster/stack/stack_lister.rb +15 -0
- data/lib/stackster/stack/stack_reader.rb +42 -0
- data/lib/stackster/stack.rb +42 -0
- data/lib/stackster/version.rb +3 -0
- data/lib/stackster.rb +6 -0
- data/stackster.gemspec +25 -0
- metadata +97 -0
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/stackster
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'fog'
|
2
|
+
|
3
|
+
module Stackster
|
4
|
+
class AWS
|
5
|
+
class CloudFormation
|
6
|
+
|
7
|
+
def initialize(environment)
|
8
|
+
c = Config.environment environment
|
9
|
+
@connect = Fog::AWS::CloudFormation.new :aws_access_key_id => c['access_key'],
|
10
|
+
:aws_secret_access_key => c['secret_key'],
|
11
|
+
:region => c['region']
|
12
|
+
end
|
13
|
+
|
14
|
+
def create(args)
|
15
|
+
data = { 'Capabilities' => ['CAPABILITY_IAM'],
|
16
|
+
'TemplateBody' => args[:template] }.merge( { 'Parameters' => args[:parameters] } )
|
17
|
+
@connect.create_stack(args[:name], data)
|
18
|
+
end
|
19
|
+
|
20
|
+
def destroy(name)
|
21
|
+
@connect.delete_stack name
|
22
|
+
end
|
23
|
+
|
24
|
+
def describe_stack(name)
|
25
|
+
@connect.describe_stacks('StackName' => name).body['Stacks']
|
26
|
+
end
|
27
|
+
|
28
|
+
def stack_resources(name)
|
29
|
+
@connect.describe_stack_resources('StackName' => name).body['StackResources']
|
30
|
+
end
|
31
|
+
|
32
|
+
def stack_events(name)
|
33
|
+
@connect.describe_stack_events(name).body['StackEvents']
|
34
|
+
end
|
35
|
+
|
36
|
+
def stack_status(name)
|
37
|
+
describe_stack(name).first['StackStatus']
|
38
|
+
end
|
39
|
+
|
40
|
+
def stack_outputs(name)
|
41
|
+
describe_stack(name).last['Outputs']
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'fog'
|
2
|
+
|
3
|
+
module Stackster
|
4
|
+
class AWS
|
5
|
+
class EC2
|
6
|
+
|
7
|
+
def initialize(environment)
|
8
|
+
c = Config.environment environment
|
9
|
+
@connect = Fog::Compute::AWS.new :aws_access_key_id => c['access_key'],
|
10
|
+
:aws_secret_access_key => c['secret_key'],
|
11
|
+
:region => c['region']
|
12
|
+
end
|
13
|
+
|
14
|
+
def describe_instances
|
15
|
+
i = []
|
16
|
+
@connect.describe_instances.body['reservationSet'].each do |instance|
|
17
|
+
i << instance
|
18
|
+
end
|
19
|
+
i
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'fog'
|
2
|
+
|
3
|
+
module Stackster
|
4
|
+
class AWS
|
5
|
+
class SimpleDB
|
6
|
+
|
7
|
+
def initialize(environment)
|
8
|
+
c = Config.environment environment
|
9
|
+
@connect = Fog::AWS::SimpleDB.new :aws_access_key_id => c['access_key'],
|
10
|
+
:aws_secret_access_key => c['secret_key'],
|
11
|
+
:region => c['region']
|
12
|
+
end
|
13
|
+
|
14
|
+
def domains
|
15
|
+
@connect.list_domains.body['Domains']
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_domain(domain)
|
19
|
+
@connect.create_domain(domain) unless domains.include? domain
|
20
|
+
end
|
21
|
+
|
22
|
+
def put_attributes(domain, key, attributes, options)
|
23
|
+
@connect.put_attributes domain, key, attributes, options
|
24
|
+
end
|
25
|
+
|
26
|
+
def select(query)
|
27
|
+
@connect.select(query).body['Items']
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(domain, key)
|
31
|
+
@connect.delete_attributes domain, key
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
|
3
|
+
module Stackster
|
4
|
+
module CLI
|
5
|
+
def self.start
|
6
|
+
@opts = Trollop::options do
|
7
|
+
banner <<-EOS
|
8
|
+
build and manage stacks
|
9
|
+
EOS
|
10
|
+
opt :help, "Display Help"
|
11
|
+
opt :attributes, "CSV list of = seperated attributes to set", :type => :string
|
12
|
+
opt :environment, "Set the target environment", :type => :string
|
13
|
+
opt :name, "Stack name to manage", :type => :string
|
14
|
+
opt :template, "Path to the template file", :type => :string
|
15
|
+
end
|
16
|
+
|
17
|
+
@cmd = ARGV.shift
|
18
|
+
|
19
|
+
unless Config.environments.include? @opts[:environment]
|
20
|
+
puts "Please specify a valid environment: #{Config.environments.to_s}"
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
s = Stack.new :environment => @opts[:environment],
|
25
|
+
:name => @opts[:name]
|
26
|
+
read_attributes
|
27
|
+
|
28
|
+
case @cmd
|
29
|
+
when 'create'
|
30
|
+
s.create :attributes => attributes,
|
31
|
+
:template => @opts[:template]
|
32
|
+
puts "#{@opts[:name]} created."
|
33
|
+
when 'destroy', 'delete'
|
34
|
+
s.destroy
|
35
|
+
puts "#{@opts[:name]} destroyed."
|
36
|
+
when 'show'
|
37
|
+
sf = StackFormater.new :environment => @opts[:environment],
|
38
|
+
:name => @opts[:name]
|
39
|
+
puts sf.display_short.to_yaml
|
40
|
+
when 'list'
|
41
|
+
s = StackLister.new @opts[:environment]
|
42
|
+
puts s.all
|
43
|
+
else
|
44
|
+
puts "Unkown command '#{@cmd}'"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.attributes
|
49
|
+
attrs = []
|
50
|
+
read_attributes.each do |attribs|
|
51
|
+
a = attribs.split('=')
|
52
|
+
attrs << { a.first => a.last }
|
53
|
+
end
|
54
|
+
attrs
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.read_attributes
|
58
|
+
@opts[:attributes].nil? ? [] : @opts[:attributes].split(',')
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Stackster
|
2
|
+
class Config
|
3
|
+
def self.load_config_file(config_file = "#{ENV['HOME']}/.stackster.yml")
|
4
|
+
YAML::load File.open( config_file )
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.environment(environment)
|
8
|
+
load_config_file[environment]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.region(environment)
|
12
|
+
load_config_file[environment]['region']
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.environments
|
16
|
+
load_config_file.keys
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Stackster
|
2
|
+
class EntryLister
|
3
|
+
|
4
|
+
def initialize(environment)
|
5
|
+
@environment = environment
|
6
|
+
@domain = 'stacks'
|
7
|
+
@sdb_connect = AWS::SimpleDB.new @environment
|
8
|
+
end
|
9
|
+
|
10
|
+
def all
|
11
|
+
@sdb_connect.select "select * from #{@domain}"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'stackster/entry/entry_lister'
|
2
|
+
|
3
|
+
module Stackster
|
4
|
+
class Entry
|
5
|
+
attr_accessor :name, :attributes
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@environment = args[:environment]
|
9
|
+
@domain = 'stacks'
|
10
|
+
@sdb_connect = AWS::SimpleDB.new @environment
|
11
|
+
self.name = "#{args[:name]}-#{Config.region @environment}"
|
12
|
+
create_domain
|
13
|
+
get_attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.find(args)
|
17
|
+
e = Entry.new :name => args[:name],
|
18
|
+
:environment => args[:environment]
|
19
|
+
e.get_attributes
|
20
|
+
e
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_domain
|
24
|
+
@sdb_connect.create_domain(@domain)
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_attributes
|
28
|
+
u = {}
|
29
|
+
attrs = @sdb_connect.select("select * from stacks where itemName() = '#{name}'")
|
30
|
+
if attrs[name]
|
31
|
+
attrs[name].each_pair do |k, v|
|
32
|
+
u[k] = v.first
|
33
|
+
end
|
34
|
+
end
|
35
|
+
self.attributes = u
|
36
|
+
end
|
37
|
+
|
38
|
+
def save
|
39
|
+
attributes.each_pair do |k,v|
|
40
|
+
@sdb_connect.put_attributes('stacks', name, { k => v }, { :replace => k })
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_attributes(a)
|
45
|
+
a.each { |attribute| set_attribute(attribute) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_attribute(attribute)
|
49
|
+
self.attributes = attributes.merge(attribute)
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete_attributes
|
53
|
+
@sdb_connect.delete('stacks', name)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Stackster
|
2
|
+
class InstanceReader
|
3
|
+
|
4
|
+
def initialize(environment)
|
5
|
+
@environment = environment
|
6
|
+
@ec2 = AWS::EC2.new @environment
|
7
|
+
end
|
8
|
+
|
9
|
+
# Data structure is return deffernelty for class
|
10
|
+
# Currently only supports VPC
|
11
|
+
def list_stack_instances(stack_name)
|
12
|
+
h = []
|
13
|
+
describe_instances.each do |instance|
|
14
|
+
tag_set = instance['instancesSet'].first['tagSet']
|
15
|
+
if tag_set['aws:cloudformation:stack-name']
|
16
|
+
h << instance if tag_set['aws:cloudformation:stack-name'] == stack_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
h
|
20
|
+
end
|
21
|
+
|
22
|
+
def describe_instances
|
23
|
+
@ec2.describe_instances
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'stackster/instance/instance_reader'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Stackster
|
4
|
+
class StackCreater
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@cf = AWS::CloudFormation.new args[:environment]
|
8
|
+
@entry = args[:entry]
|
9
|
+
@name = args[:name]
|
10
|
+
@template = read_template_from_file args[:template]
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_template_from_file(template_file)
|
14
|
+
file = File.open template_file
|
15
|
+
file.read
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_parameters_from_template
|
19
|
+
t = JSON.parse(@template)
|
20
|
+
t['Paramaters'].nil? ? t['Parameters'].keys : []
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_parameters_from_entry
|
24
|
+
h = {}
|
25
|
+
read_parameters_from_template.each do |p|
|
26
|
+
h[p] = @entry.attributes[p] if @entry.attributes[p]
|
27
|
+
end
|
28
|
+
h
|
29
|
+
end
|
30
|
+
|
31
|
+
def create
|
32
|
+
@cf.create :name => @name,
|
33
|
+
:parameters => read_parameters_from_entry,
|
34
|
+
:template => @template
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Stackster
|
2
|
+
class StackFormater
|
3
|
+
|
4
|
+
def initialize(args)
|
5
|
+
@sr = StackReader.new :name => args[:name],
|
6
|
+
:environment => args[:environment]
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_short
|
10
|
+
{
|
11
|
+
'attributes' => @sr.attributes,
|
12
|
+
'status' => @sr.status,
|
13
|
+
'outputs' => @sr.outputs,
|
14
|
+
'last_event' => events_summary.first,
|
15
|
+
'resources' => resources_summary,
|
16
|
+
'instances' => instances_public_ip_addresses
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def instances_public_ip_addresses
|
21
|
+
@sr.instances.map { |i| i['instancesSet'].first['ipAddress'] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def resources_summary
|
25
|
+
@sr.resources.map do |i|
|
26
|
+
{ 'LogicalResourceId' => i['LogicalResourceId'],
|
27
|
+
'PhysicalResourceId' => i['PhysicalResourceId'] }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def events_summary
|
32
|
+
@sr.events.map do |i|
|
33
|
+
{ 'ResourceStatus' => i['ResourceStatus'],
|
34
|
+
'LogicalResourceId' => i['LogicalResourceId'] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Stackster
|
2
|
+
class StackReader
|
3
|
+
|
4
|
+
def initialize(args)
|
5
|
+
@name = args[:name]
|
6
|
+
@environment = args[:environment]
|
7
|
+
@cf = AWS::CloudFormation.new @environment
|
8
|
+
@entry = Entry.find :name => @name,
|
9
|
+
:environment => @environment
|
10
|
+
end
|
11
|
+
|
12
|
+
def attributes
|
13
|
+
@entry.attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
def outputs
|
17
|
+
@cf.stack_outputs @name
|
18
|
+
end
|
19
|
+
|
20
|
+
def status
|
21
|
+
@cf.stack_status @name
|
22
|
+
end
|
23
|
+
|
24
|
+
def events
|
25
|
+
@cf.stack_events @name
|
26
|
+
end
|
27
|
+
|
28
|
+
def resources
|
29
|
+
@cf.stack_resources @name
|
30
|
+
end
|
31
|
+
|
32
|
+
def last_event
|
33
|
+
events.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def instances
|
37
|
+
ir = InstanceReader.new @environment
|
38
|
+
ir.list_stack_instances @name
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'stackster/stack/stack_creater'
|
2
|
+
require 'stackster/stack/stack_destroyer'
|
3
|
+
require 'stackster/stack/stack_reader'
|
4
|
+
require 'stackster/stack/stack_formater'
|
5
|
+
require 'stackster/stack/stack_lister'
|
6
|
+
|
7
|
+
module Stackster
|
8
|
+
class Stack
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
@name = args[:name]
|
12
|
+
@environment = args[:environment]
|
13
|
+
@entry = Entry.new :name => @name,
|
14
|
+
:environment => @environment
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(args)
|
18
|
+
@entry.set_attributes args[:attributes]
|
19
|
+
@entry.save
|
20
|
+
|
21
|
+
s = StackCreater.new :name => @name,
|
22
|
+
:environment => @environment,
|
23
|
+
:entry => @entry,
|
24
|
+
:template => args[:template]
|
25
|
+
s.create
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroy
|
29
|
+
s = StackDestroyer.new :name => @name,
|
30
|
+
:environment => @environment
|
31
|
+
s.destroy
|
32
|
+
@entry.delete_attributes
|
33
|
+
end
|
34
|
+
|
35
|
+
def show
|
36
|
+
s = StackReader.new :name => @name,
|
37
|
+
:environment => @environment
|
38
|
+
s.show
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/stackster.rb
ADDED
data/stackster.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "stackster/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "stackster"
|
7
|
+
s.version = Stackster::VERSION
|
8
|
+
s.authors = ["Brett Weaver"]
|
9
|
+
s.email = ["brett@weav.net"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{I make deployments easier}
|
12
|
+
s.description = %q{Thats what I do}
|
13
|
+
|
14
|
+
s.rubyforge_project = "stackster"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
s.add_runtime_dependency "fog"
|
24
|
+
s.add_runtime_dependency "trollop"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stackster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brett Weaver
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fog
|
16
|
+
requirement: &70312936224120 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70312936224120
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: trollop
|
27
|
+
requirement: &70312936223700 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70312936223700
|
36
|
+
description: Thats what I do
|
37
|
+
email:
|
38
|
+
- brett@weav.net
|
39
|
+
executables:
|
40
|
+
- stackster
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files: []
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- Gemfile
|
46
|
+
- Rakefile
|
47
|
+
- bin/stackster
|
48
|
+
- lib/stackster.rb
|
49
|
+
- lib/stackster/aws.rb
|
50
|
+
- lib/stackster/aws/cloud_formation.rb
|
51
|
+
- lib/stackster/aws/ec2.rb
|
52
|
+
- lib/stackster/aws/simpledb.rb
|
53
|
+
- lib/stackster/cli.rb
|
54
|
+
- lib/stackster/config.rb
|
55
|
+
- lib/stackster/entry.rb
|
56
|
+
- lib/stackster/entry/entry_lister.rb
|
57
|
+
- lib/stackster/instance.rb
|
58
|
+
- lib/stackster/instance/instance_reader.rb
|
59
|
+
- lib/stackster/stack.rb
|
60
|
+
- lib/stackster/stack/stack_creater.rb
|
61
|
+
- lib/stackster/stack/stack_destroyer.rb
|
62
|
+
- lib/stackster/stack/stack_formater.rb
|
63
|
+
- lib/stackster/stack/stack_lister.rb
|
64
|
+
- lib/stackster/stack/stack_reader.rb
|
65
|
+
- lib/stackster/version.rb
|
66
|
+
- stackster.gemspec
|
67
|
+
homepage: ''
|
68
|
+
licenses: []
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
hash: -4027145731470457093
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
hash: -4027145731470457093
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project: stackster
|
93
|
+
rubygems_version: 1.8.16
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: I make deployments easier
|
97
|
+
test_files: []
|