helm 0.0.1

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: de942658d9f770530a4b5ef00b7481cc03dbf4e3
4
+ data.tar.gz: 7418c354fc2f5122d2e8f86d56d05f6123a26811
5
+ SHA512:
6
+ metadata.gz: d5611ba2985c413ea5b0964ff9b36243ee061b1627a076c54362770ea0b94874a1b81e86c1ef4eec40e51b7c7e98d262b8534efbcc65c0529b7f0a40f77dacbe
7
+ data.tar.gz: 73550c907b1a97555e299981057ba0eb4c21987bf0765ef8cffa210ca3c7ec9f416a3460c05e9868449300168edb196e3d0b7fc1fbffe899a8b5473089c963c6
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'helm/cli'
4
+
5
+ Helm.load_commands
6
+ Helm::CLI::Main.start(ARGV)
@@ -0,0 +1,60 @@
1
+ require 'valise'
2
+ require 'sequel'
3
+ require 'logger'
4
+ require 'helm/application'
5
+ require 'helm/cli'
6
+ require 'helm/server-command'
7
+
8
+ module Helm
9
+ class << self
10
+ def logger
11
+ @logger ||= Logger.new($stderr).tap do |logger|
12
+ logger.level = Logger::ERROR
13
+ end
14
+ end
15
+
16
+ def databases
17
+ @databases ||= Hash.new do |hash, conn_string|
18
+ hash[conn_string] = Sequel::connect(conn_string, :logger => logger).tap do |db|
19
+ db.extension(:pretty_table)
20
+ end
21
+ end
22
+ end
23
+
24
+ def log_at(level)
25
+ begin
26
+ old_level, logger.level = logger.level, level
27
+ yield
28
+ ensure
29
+ logger.level = old_level
30
+ end
31
+ end
32
+
33
+ def check_db(db_string, directory)
34
+ require 'sequel/extensions/migration'
35
+
36
+ unless Sequel::Migrator.is_current?(databases[db_string], directory)
37
+ warn "Your servers database is out of date. You should run #$0 db update."
38
+ end
39
+ end
40
+
41
+ def valise
42
+ @valise ||= Valise.define do
43
+ ro from_here(%w{.. default_configuration}, up_to("lib"))
44
+ end
45
+ end
46
+
47
+ def commands_valise
48
+ @commands_valise ||= valise.sub_set("commands")
49
+ end
50
+
51
+ def load_commands
52
+ commands_valise.glob("*.rb") do |file|
53
+ require file.full_path
54
+ end
55
+ ServerCommand.valid_commands.each do |command_class|
56
+ CLI::Main.define_from(command_class.new)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,163 @@
1
+ require 'helm/command-runner'
2
+ require 'helm/queries/server'
3
+
4
+ module Helm
5
+ module Commands
6
+ class Command
7
+ def initialize(app_config, options)
8
+ @app_config = app_config
9
+ @options = options
10
+ end
11
+ attr_accessor :app_config, :options
12
+
13
+ def if_option(name)
14
+ if options.has_key?(name.to_s)
15
+ yield(options[name])
16
+ end
17
+ end
18
+ end
19
+
20
+ module Main
21
+ class Defined < Command
22
+ def initialize(app_config, options, config)
23
+ @config = config
24
+ super(app_config, options)
25
+ end
26
+ attr_reader :config
27
+
28
+ def execute
29
+ config.query_options = options || {}
30
+
31
+ query = Helm::Queries::Server.new(app_config.connstring)
32
+ if_option(:client){|client| query.client = client }
33
+ if_option(:role){|role| query.role = role }
34
+ if_option(:name){|name| query.name = name }
35
+ if_option(:platform){|platform| query.platform = platform }
36
+
37
+ runner = Helm::CommandRunner.new(query, config)
38
+ runner.run
39
+ end
40
+ end
41
+ end
42
+
43
+ module Database
44
+ class Migrate < Command
45
+ def initialize(app_config, options)
46
+ @directory = "migrations"
47
+ super
48
+ end
49
+ attr_accessor :db_string, :directory
50
+
51
+ def execute
52
+ require 'sequel/extensions/migration'
53
+
54
+ Helm.log_at(Logger::DEBUG) do
55
+ Sequel::Migrator.run(Helm.databases[app_config.connstring], directory)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ module Servers
62
+ class Add < Command
63
+ def initialize(app_config, options, source_io)
64
+ @source_io = source_io
65
+ super(app_config, options)
66
+ end
67
+
68
+ def execute
69
+ require 'yaml'
70
+ require 'helm/persisters/server'
71
+ serverlist = Array(YAML.load(source_io.read))
72
+
73
+ store = Helm::Persisters::Server.new(app_config.connstring)
74
+
75
+ defaults = {:client => options[:client]}
76
+
77
+ serverlist.each do |server|
78
+ store.insert_or_update(defaults.merge(server))
79
+ end
80
+ end
81
+ end
82
+
83
+ class List < Command
84
+ def execute
85
+ require 'helm/queries/server'
86
+ Helm::Queries::Server.new(app_config.connstring).print
87
+ end
88
+ end
89
+
90
+ class Edit < Command
91
+ def edit_tempfile(file)
92
+ require 'io/console'
93
+
94
+ edit_shell = Caliph::Shell.new
95
+ edit_shell.output_stream = File.new("/dev/null", "a")
96
+ edit_shell.run(ENV['EDITOR'] || app_config.editor, file.path) do |cmd|
97
+ cmd.redirect_stdin(IO.console.path)
98
+ cmd.redirect_stdout(IO.console.path)
99
+ end
100
+ ensure
101
+ edit_shell.output_stream.close
102
+ end
103
+
104
+ def server_query
105
+ query = Helm::Queries::Server.new(app_config.connstring)
106
+
107
+ if_option(:id){|server_id| query.server_id = server_id }
108
+ if_option(:client){|client| query.client = client }
109
+ if_option(:role){|role| query.role = role }
110
+ if_option(:name){|name| query.name = name }
111
+ if_option(:platform){|platform| query.platform = platform }
112
+
113
+ query
114
+ end
115
+
116
+ def put_server_record_in_file(query, file)
117
+ server = query.first
118
+ server_hash = server.to_hash
119
+
120
+ server_id = server_hash.delete(:server_id)
121
+
122
+ file.write("# Edit this file to update the server\n")
123
+ file.write(server_hash.to_yaml)
124
+ file.close
125
+
126
+ return server_id
127
+ end
128
+
129
+ def save_server_record_to_database(server_id, file)
130
+ file.open
131
+ new_hash = YAML.load(file.read).merge(:server_id => server_id)
132
+
133
+ store = Helm::Persisters::Server.new(app_config.connstring)
134
+ store.insert_or_update(new_hash)
135
+ end
136
+
137
+ def execute
138
+ require 'yaml'
139
+ require 'helm/persisters/server'
140
+
141
+ query = server_query
142
+
143
+ unless query.to_a.length == 1
144
+ puts "#{query.to_a.length} servers match #{query.constraint.inspect}"
145
+ exit 1
146
+ end
147
+
148
+ begin
149
+ file = Tempfile.new('server', app_config.tempdir)
150
+
151
+ server_id = put_server_record_in_file(query, file)
152
+
153
+ edit_tempfile(file)
154
+
155
+ save_server_record_to_database(server_id, file)
156
+ ensure
157
+ file.unlink if file
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,75 @@
1
+ require 'helm'
2
+ require 'thor'
3
+
4
+ module Helm
5
+ Config = Struct.new(:connstring, :editor, :tempdir)
6
+
7
+ def self.config
8
+ Config.new("sqlite://servers.sql", "vim", ".")
9
+ end
10
+
11
+ module CLI
12
+ class DB < Thor
13
+ include Commands::Database
14
+
15
+ desc "update", "Update the servers database schema"
16
+ def update
17
+ Migrate.new(Helm.config, options).execute
18
+ end
19
+ end
20
+
21
+ class Server < Thor
22
+ include Commands::Servers
23
+
24
+ desc "add", "Add a list of servers described in YAML from STDIN"
25
+ option :client, :banner => "Client name"
26
+ def add
27
+ Add.new(Helm.config, options, $stdin).execute
28
+ end
29
+
30
+ desc "list", "List servers that Helm knows about"
31
+ def list
32
+ puts List.new(Helm.config, options).execute
33
+ end
34
+
35
+ desc "edit", "Edit the details of particular servers"
36
+ method_option :id
37
+ def edit
38
+ Edit.new(Helm.config, options).execute
39
+ end
40
+ end
41
+
42
+ class Main < Thor
43
+ desc "db SUBCOMMAND", "server database maintenance tasks"
44
+ subcommand "db", DB
45
+
46
+ desc "server SUBCOMMAND", "server listing tasks"
47
+ subcommand "server", Server
48
+
49
+ class_option :client
50
+ class_option :name
51
+ class_option :role
52
+
53
+ class << self
54
+ def define_from(command_config)
55
+ desc command_config.name.to_s, command_config.description
56
+ command_config.scope_options.each do |name|
57
+ option name, :required => true
58
+ end
59
+
60
+ define_method command_config.name do |*args|
61
+ command = Commands::Main::Defined.new(Helm.config, options, command_config)
62
+ command.execute(*args)
63
+ end
64
+ end
65
+ end
66
+
67
+ def help(task = nil, subcommand=false)
68
+ super
69
+
70
+ shell.say "Commands are loaded from:"
71
+ shell.say Helm.commands_valise.to_s
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,56 @@
1
+ require 'caliph'
2
+
3
+ module Helm
4
+ class CommandRunner
5
+ CommandExecution = Struct.new(:server, :result)
6
+
7
+ def initialize(server_query, command_definition)
8
+ @server_query, @command_definition = server_query, command_definition
9
+ @completed = []
10
+ @ran_any = false
11
+ end
12
+
13
+ attr_accessor :server_query, :command_definition, :shell
14
+
15
+ def shell
16
+ @shell ||= Caliph::Shell.new
17
+ end
18
+
19
+ def run
20
+ start_run
21
+ catch :fail do
22
+ configured_query.each do |server|
23
+ result = run_one(command_definition.command(server))
24
+ execution = CommandExecution.new(server, result)
25
+ collect(execution)
26
+ end
27
+ end
28
+ finish_run
29
+ end
30
+
31
+ def configured_query
32
+ command_definition.configure_query(server_query)
33
+ server_query
34
+ end
35
+
36
+ def run_one(single)
37
+ @ran_any = true
38
+ shell.execute(single)
39
+ end
40
+
41
+ def start_run
42
+ @ran_any = false
43
+ end
44
+
45
+ def finish_run
46
+ warn "No servers found for #{server_query}!" unless @ran_any
47
+ command_definition.all_results(@completed)
48
+ return true
49
+ end
50
+
51
+ def collect(execution)
52
+ @completed << execution
53
+ command_definition.each_result(execution)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,27 @@
1
+ require 'helm'
2
+
3
+ module Helm
4
+ class Persister
5
+ def initialize(db_string)
6
+ @db = Helm.databases[db_string]
7
+ end
8
+ attr_accessor :db
9
+
10
+ def slice(hash)
11
+ Hash[
12
+ columns.zip(
13
+ hash.values_at(*columns).zip(
14
+ hash.values_at(*(columns.map(&:to_s)))
15
+ ).map{|sym_val, str_val| sym_val || str_val })
16
+ ]
17
+ end
18
+
19
+ def dataset
20
+ @db[table_name]
21
+ end
22
+
23
+ def insert_or_update(row)
24
+ dataset.replace(slice(row))
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ require 'helm/persister'
2
+ require 'helm/records/server'
3
+
4
+ module Helm
5
+ module Persisters
6
+ class Server < Persister
7
+ def columns
8
+ Records::Server.columns
9
+ end
10
+
11
+ def table_name
12
+ :servers
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ require 'helm/query'
2
+ require 'helm/records/server'
3
+
4
+ module Helm
5
+ module Queries
6
+ class Server < Query
7
+ attr_accessor :server_id, :client, :name, :role, :platform, :id_from_platform
8
+
9
+ def to_s
10
+ nil_attrs = constraint_attrs.reject{|attr| constraint_hash.has_key?(attr) }
11
+ "Servers query: #{constraint_hash.inspect} (not set: #{nil_attrs.inspect})"
12
+ end
13
+
14
+ def inspect
15
+ "<#{self.class.name}:#{"%0x" % object_id} #{constraint.select_sql rescue "<?sql?>"}>"
16
+ end
17
+
18
+ def constraint_attrs
19
+ [:server_id, :client, :name, :role, :platform, :id_from_plaform]
20
+ end
21
+
22
+ def constraint_hash
23
+ @constraint_hash ||= constraint_attrs.each_with_object({}) do |attr, hash|
24
+ val = instance_variable_get("@#{attr}")
25
+ hash[attr] = val unless val.nil?
26
+ end
27
+ end
28
+
29
+ def constraint
30
+ @constraint ||= @db[:servers].where(constraint_hash)
31
+ end
32
+
33
+ def result_class
34
+ Records::Server
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ require 'helm'
2
+
3
+ module Helm
4
+ class Query
5
+ include Enumerable
6
+
7
+ def initialize(conn_string)
8
+ @db = Helm.databases[conn_string]
9
+ end
10
+
11
+ attr_reader :db
12
+ attr_accessor :result_class
13
+
14
+ def constraint
15
+ @constraint ||= @db
16
+ end
17
+
18
+ def print
19
+ constraint.print
20
+ end
21
+
22
+ def each
23
+ constraint.select(*result_class.columns).each do |row|
24
+ yield result_class.new(row)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,72 @@
1
+ module Helm
2
+ class Record
3
+ class << self
4
+ attr_accessor :columns
5
+
6
+ def types
7
+ @types ||= {}
8
+ end
9
+
10
+ def with_columns(*columns)
11
+ types[columns.sort] || subclass(*columns)
12
+ end
13
+
14
+ def subclass(*columns, &block)
15
+ klass = Class.new(self, &block)
16
+ columns = columns.sort
17
+ klass.columns = columns
18
+
19
+ columns.each do |column|
20
+ klass.column_accessor(column)
21
+ end
22
+ types[columns] ||= klass
23
+ end
24
+
25
+ def column_accessor(name)
26
+ unless instance_methods(false).include?(name)
27
+ define_method name do
28
+ @values[name]
29
+ end
30
+ end
31
+
32
+ write_method = "#{name}="
33
+ unless instance_methods(false).include?(write_method)
34
+ define_method write_method do |value|
35
+ @values[name] = value
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def [](key)
42
+ @values[key]
43
+ end
44
+
45
+ def []=(key, value)
46
+ @values[key] = value
47
+ end
48
+
49
+ def keys
50
+ @row.keys | @values.keys
51
+ end
52
+
53
+ def initialize(row)
54
+ @row = row
55
+ @values = Hash.new do |h, k|
56
+ @row[k]
57
+ end
58
+ end
59
+
60
+ def changed
61
+ @values.keys
62
+ end
63
+
64
+ def to_hash
65
+ Hash[ keys.map{|key| [key, self[key]]}]
66
+ end
67
+
68
+ def to_yaml
69
+ to_hash.to_yaml
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,24 @@
1
+ require 'helm/record'
2
+
3
+ module Helm
4
+ module Records
5
+ Server = Record.subclass(
6
+ :server_id,
7
+ :platform,
8
+ :id_from_platform,
9
+ :name,
10
+ :client,
11
+ :private_dns_name,
12
+ :public_dns_name,
13
+ :private_ip_address,
14
+ :public_ip_address,
15
+ :ssh_key_name,
16
+ :architecture,
17
+ :launch_time
18
+ ) do
19
+ def name
20
+ self[:name] || [self[:platform], self[:id_from_platform]].join(":")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,116 @@
1
+ require 'caliph/command-line-dsl'
2
+
3
+ module Helm
4
+ class ServerCommand
5
+
6
+ class << self
7
+ def subclasses
8
+ @subclasses ||= []
9
+ end
10
+
11
+ def inherited(sub)
12
+ ServerCommand.subclasses << sub
13
+ end
14
+
15
+ def abstract
16
+ @abstract = true
17
+ end
18
+
19
+ def abstract?
20
+ !!@abstract
21
+ end
22
+
23
+ def valid_commands(quietly = false)
24
+ valid = []
25
+ ServerCommand.subclasses.each do |sub|
26
+ next if sub.abstract?
27
+
28
+ methods = sub.instance_methods(false)
29
+
30
+ unless methods.include?(:name) and methods.include?(:command)
31
+ unless quietly
32
+ warn "ServerCommand subclass #{sub.name} isn't abstract and doesn't define :name and :command"
33
+ a_method = sub.instance_method(methods.first)
34
+ warn " (appears to be defined in #{a_method.source_location.first})"
35
+ end
36
+ next
37
+ end
38
+
39
+ valid << sub
40
+ end
41
+ valid
42
+ end
43
+ end
44
+
45
+ def valid?
46
+ true
47
+ end
48
+
49
+ def desc
50
+ ""
51
+ end
52
+ alias description desc
53
+
54
+ def required_options
55
+ []
56
+ end
57
+ alias scope_options required_options
58
+
59
+ attr_accessor :query_options
60
+
61
+ def configure_query(query)
62
+ end
63
+
64
+ def each_result(execution)
65
+ format_result(execution)
66
+ if execution.result.exit_code != 0
67
+ throw :fail
68
+ end
69
+ end
70
+
71
+ def all_results(results)
72
+ end
73
+
74
+ def format_result(execution)
75
+ result = execution.result
76
+ server = execution.server
77
+
78
+ puts "#{server.name} (#{server.public_ip_address})"
79
+ puts "#{result.command.string_format} => #{result.exit_code}"
80
+ puts result.format_streams
81
+ puts
82
+ end
83
+
84
+ include Caliph::CommandLineDSL
85
+
86
+ def ssh(server, *command, &block)
87
+ cmd("ssh") do |command|
88
+ command.options << "-o BatchMode=yes"
89
+ command.options << "-i " + server.ssh_key_name unless server.ssh_key_name.nil?
90
+ #command.options << "-l " + server.connect_as unless
91
+ #server.connect_as.nil?
92
+ # XXX need to add 'connect_as'
93
+ #command.options << "-p " + server.port unless server.ssh_port.nil? ##ok
94
+ # XXX need to add "ssh_port"
95
+ command.options << server.public_dns_name || server.public_ip_address
96
+ end - escaped_command(*command, &block)
97
+ end
98
+ end
99
+
100
+ module Persistent
101
+ def each_result(result)
102
+ format_results(result)
103
+ end
104
+ end
105
+
106
+ module Picky
107
+ def all_results(results)
108
+ if results.any?{|execution| execution.result.exit_code != 0}
109
+ raise "Command failed!\n #{
110
+ results.select{|execution| execution.result.exit_code != 0
111
+ }.map{|exec| exec.result.inspect}.join(" \n")
112
+ }"
113
+ end
114
+ end
115
+ end
116
+ end
File without changes
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: helm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Judson Lester
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-17 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.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 10.1.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 10.1.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.5
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.3.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: sequel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 4.6.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 4.6.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: caliph
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.3.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.3.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: valise
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.1.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 1.1.1
97
+ description: ''
98
+ email:
99
+ - nyarly@gmail.com
100
+ executables:
101
+ - helm
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - lib/helm/records/server.rb
106
+ - lib/helm/persister.rb
107
+ - lib/helm/queries/server.rb
108
+ - lib/helm/record.rb
109
+ - lib/helm/cli.rb
110
+ - lib/helm/persisters/server.rb
111
+ - lib/helm/command-runner.rb
112
+ - lib/helm/server-command.rb
113
+ - lib/helm/query.rb
114
+ - lib/helm/application.rb
115
+ - lib/helm.rb
116
+ - bin/helm
117
+ - spec_help/gem_test_suite.rb
118
+ homepage: http://nyarly.github.com/helm
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options:
124
+ - --inline-source
125
+ - --main
126
+ - doc/README
127
+ - --title
128
+ - helm-0.0.1 Documentation
129
+ require_paths:
130
+ - lib/
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project: helm
143
+ rubygems_version: 2.0.14
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: ''
147
+ test_files:
148
+ - spec_help/gem_test_suite.rb