kibo 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/kibo/config.rb CHANGED
@@ -1,134 +1,119 @@
1
1
  require "yaml"
2
+ require "mash"
2
3
 
3
- class Kibo::Configfile < Hash
4
- attr :path
4
+ class Kibo::Config < Mash
5
+ DEFAULTS = {
6
+ "heroku" => {
7
+ "mode" => "freemium"
8
+ },
9
+ "deployment" => {},
10
+ "collaborations" => {},
11
+ "source" => {},
12
+ "collaborators" => []
13
+ }
14
+
15
+ attr :environment, :kibofile
5
16
 
6
- def initialize(path)
7
- @path = path
17
+ def initialize(kibofile, environment)
18
+ @kibofile, @environment = kibofile, environment
19
+ @kibofile = File.expand_path @kibofile
8
20
 
9
- File.read(path).
10
- split("\n").
11
- each_with_index do |line, lineno|
12
- next if line =~ /^\s*#/
13
- next if line =~ /^\s*$/
14
- die(lineno, "Can't parse line: #{line.inspect}") unless line =~ /\s*([a-z]+):\s*(.*)$/
15
- key, value = $1, $2.gsub(/\s+$/, "")
16
-
17
- die(lineno, "Multiple entries for #{key.inspect}") if key?(key)
21
+ kibo = begin
22
+ YAML.load File.read(kibofile)
23
+ rescue Errno::ENOENT
24
+ E "No such file", File.expand_path(@kibofile)
25
+ end
18
26
 
19
- update key => value
20
- end
21
- rescue
22
- E "No such file", path
27
+ build_config(kibo)
28
+ verify_config
23
29
  end
24
-
25
- def die(lineno, msg)
26
- E "#{path}:#{lineno}", msg
30
+
31
+ def processes
32
+ return unless processes = super
33
+ processes.reject { |k,v| v.to_s.to_i <= 0 }
27
34
  end
28
- end
29
35
 
30
- class Kibo::Config
31
- attr :procfile
32
-
33
- DEFAULTS = {
34
- "procfile" => "Procfile"
35
- }
36
-
37
- def [](key)
38
- @data[key]
36
+ def freemium?
37
+ heroku.mode == "freemium"
39
38
  end
40
39
 
41
- def environment
42
- Kibo.environment
40
+ def namespace
41
+ heroku.namespace
43
42
  end
44
43
 
45
- def initialize(path)
46
- super()
47
-
48
- @data = DEFAULTS.dup
44
+ private
45
+
46
+ def build_config(kibo)
47
+ config = DEFAULTS.dup
49
48
 
50
- begin
51
- kibo = YAML.load File.read(path)
52
- @data.update(kibo)
53
- @data.update(kibo["defaults"] || {})
54
- @data.update(kibo[environment] || {})
55
- rescue Errno::ENOENT
56
- W "No such file", path
49
+ [ kibo, kibo["defaults"], kibo[environment] ].each do |hash|
50
+ next unless hash
51
+ config = config.deep_merge(hash)
57
52
  end
58
53
 
59
- verify_version!
60
-
61
- @procfile = Kibo::Configfile.new(self["procfile"])
54
+ self.update config
62
55
  end
63
56
 
64
- # processes are defined in the Procfile. The scaling, however, is defined in
65
- # the Kibofile.
66
- def processes
67
- @processes ||= procfile.keys.inject({}) do |hash, key|
68
- hash.update key => (self[key] || 1)
69
- end
57
+ def verify_config
58
+ Kibo::Config.verify_version!(self.version)
59
+
60
+ # verify required entries
61
+ E("#{@kibofile}: Please set the heroku namespace.") unless heroku.namespace?
62
+ E("#{@kibofile}: Please set the heroku account email") unless heroku.account?
63
+ E("#{@kibofile}: Missing 'processes' settings") unless self.processes.is_a?(Hash)
70
64
  end
65
+
66
+ def self.verify_version!(version)
67
+ return unless version
71
68
 
72
- def verify_version!
73
- return unless self["version"]
69
+ kibofile_version = version.split(".").map(&:to_i)
70
+ kibo_version = Kibo::VERSION.split(".").map(&:to_i)
74
71
 
75
- files_version = self["version"].split(".").map(&:to_i)
76
- kibos_version = Kibo::VERSION.split(".").map(&:to_i)
72
+ return if kibo_version >= kibofile_version
77
73
 
78
- files_version.zip(kibos_version).each do |files, kibos|
79
- next if kibos == files
80
- if kibos > files
81
- W "The Kibofile requires kibo version #{self["version"]}. You have #{Kibo::VERSION}... this might work."
82
- return
83
- end
84
-
85
- E "The Kibofile requires kibo version #{self["version"]}. You have #{Kibo::VERSION}."
86
- end
74
+ E "The Kibofile requires kibo version #{version}. You have #{Kibo::VERSION}."
87
75
  end
76
+ end
88
77
 
89
- #
90
- # we need namespace-ENVIRONMENT-process<1>
78
+ class Kibo::Instance < String
79
+ attr :count, :role
91
80
 
92
- # returns the heroku configuration
93
- def heroku
94
- self["heroku"] || {}
95
- end
96
-
97
- # returns deployment specific configuration
98
- def deployment
99
- self["deployment"] || {}
81
+ def initialize(config, role, count)
82
+ @role, @count = role, count
83
+
84
+ super "#{config.namespace}-#{config.environment}-#{role}"
100
85
  end
101
86
 
102
- def collaborations
103
- self["collaborations"] || {}
87
+ def addons
88
+ []
104
89
  end
105
90
 
106
- # returns source specific configuration
107
- def source
108
- self["source"] || {}
109
- end
110
-
111
- # returns the heroku namespace
112
- def namespace
113
- heroku["namespace"] || E("Please set the heroku namespace in your Kibofile.")
91
+ class Freemium < self
92
+ def initialize(config, role, number)
93
+ super config, role, 1
94
+ @number = number
95
+
96
+ concat "#{@number}"
97
+ end
114
98
  end
99
+ end
115
100
 
116
- # returns the heroku account email. This is the account that
117
- # you should be logged in
118
- def account
119
- heroku["account"] || E("Please set the heroku account email in your Kibofile")
120
- end
101
+ class Kibo::Config
121
102
 
122
- def remotes_by_process
123
- remotes = Kibo.git("remote", :quiet).split("\n")
124
-
125
- @remotes_by_process ||= begin
126
- r = processes.map do |process, count|
127
- prefix = "#{namespace}-#{environment}-#{process}"
128
- remotes = 1.upto(count).map { |idx| "#{prefix}#{idx}" }
129
- [ process, remotes ]
103
+ # return an array of instances.
104
+ def instances
105
+ instances = if freemium?
106
+ processes.map do |process, count|
107
+ 1.upto(count).map { |idx|
108
+ Kibo::Instance::Freemium.new self, process, idx
109
+ }
110
+ end.flatten
111
+ else
112
+ processes.map do |process, count|
113
+ Kibo::Instance.new self, process, count
130
114
  end
131
- Hash[r]
132
115
  end
116
+
117
+ instances.sort_by(&:to_s)
133
118
  end
134
119
  end
@@ -0,0 +1,30 @@
1
+ class Array
2
+ def self.diff(one, other)
3
+ one.zip(other).each do |ones, others|
4
+ return 0 if ones.nil? && others.nil?
5
+
6
+ return -1 if ones.nil?
7
+ return 1 if others.nil?
8
+
9
+ return -1 if ones < others
10
+ return 1 if others < ones
11
+ end
12
+
13
+ return 0
14
+ end
15
+
16
+ def < (other); Array.diff(self, other) < 0; end
17
+ def > (other); Array.diff(self, other) > 0; end
18
+ def <= (other); Array.diff(self, other) <= 0; end
19
+ def >= (other); Array.diff(self, other) >= 0; end
20
+ end
21
+
22
+ class Hash
23
+ def deep_merge(other_hash)
24
+ self.merge(other_hash) do |key, oldval, newval|
25
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
26
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
27
+ oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,5 @@
1
+ module Kibo::Helpers; end
2
+
1
3
  class Kibo::Helpers::Info < Array
2
4
  def self.print(out = STDOUT, &block)
3
5
  out.puts build(&block).to_s
@@ -12,9 +12,11 @@
12
12
  # The heroku gem is licensed under the terms of the MIT license,
13
13
  # see the file License.MIT.
14
14
  #
15
- # The heroku has been created by Adam Wiggins, and is currently maintained
15
+ # The heroku gem has been created by Adam Wiggins, and is currently maintained
16
16
  # by Wesley Beary.
17
- module Kibo::Helpers::Heroku
17
+ module Kibo::Heroku
18
+ extend self
19
+
18
20
  require "netrc"
19
21
 
20
22
  def whoami
@@ -72,4 +74,16 @@ module Kibo::Helpers::Heroku
72
74
  end
73
75
  end
74
76
  end
77
+
78
+ # --- helpers for kibo ----------------------------------------------
79
+
80
+ public
81
+
82
+ # returns names of all apps for the current user on heroku
83
+ def apps
84
+ @apps ||= Kibo::System.heroku("apps").
85
+ split(/\n/).
86
+ reject { |line| line.empty? || line =~ /=== / }.
87
+ map { |line| line.split(" ").first }
88
+ end
75
89
  end
data/lib/kibo/log.rb CHANGED
@@ -13,12 +13,14 @@ def D(*args)
13
13
  end
14
14
 
15
15
  def W(*args)
16
- UI.warn log_message(*args)
16
+ UI.warn log_message(*args).to_s
17
+ end
18
+
19
+ class FatalError < RuntimeError
17
20
  end
18
21
 
19
22
  def E(*args)
20
- UI.error log_message(*args)
21
- exit 1
23
+ raise FatalError, log_message(*args)
22
24
  end
23
25
 
24
26
  def B(*args, &block)
data/lib/kibo/system.rb CHANGED
@@ -10,9 +10,11 @@ module Kibo::System
10
10
  end
11
11
 
12
12
  def sys(*args)
13
- cmd = build_command(*args)
13
+ quiet = args.pop if args.last == :quiet
14
+
15
+ cmd = build_command(quiet, *args)
14
16
  result = Kernel.send "`", "bash -c \"#{cmd}\""
15
- if command_succeeded?(cmd)
17
+ if command_succeeded?(quiet, cmd)
16
18
  result.chomp
17
19
  end
18
20
  end
@@ -21,30 +23,19 @@ module Kibo::System
21
23
  sys(*args) || exit(1)
22
24
  end
23
25
 
24
- def sh(*args)
25
- cmd = build_command(*args)
26
- system(cmd)
27
- command_succeeded?(cmd)
28
- end
29
-
30
- def sh!(*args)
31
- sh(*args) || exit(1)
32
- end
33
-
34
26
  private
35
-
36
- def build_command(*args)
37
- quiet = args.pop if args.last == :quiet
27
+
28
+ def build_command(quiet, *args)
38
29
  args[0].sub!(/^kibo\b/, $0)
39
30
  cmd = args.map(&:to_s).join(" ")
40
31
  W cmd unless quiet
41
32
  cmd
42
33
  end
43
34
 
44
- def command_succeeded?(cmd)
35
+ def command_succeeded?(quiet, cmd)
45
36
  return true if $?.exitstatus == 0
46
37
 
47
- UI.error("Command failed: #{cmd}")
38
+ UI.error("Command failed: #{cmd}") unless quiet
48
39
  false
49
40
  end
50
41
  end
data/lib/kibo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kibo
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/kibo.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Kibo
2
2
  end
3
3
 
4
+ require_relative "kibo/ext/ruby_ext.rb"
4
5
  require_relative "kibo/version"
5
6
  require_relative "kibo/log"
6
7
  require_relative "kibo/system"
@@ -12,22 +13,25 @@ module Kibo
12
13
  extend self
13
14
 
14
15
  def config
15
- @config ||= Config.new(kibofile)
16
+ @config ||= Config.new(CommandLine.config, CommandLine.environment)
16
17
  end
17
18
 
18
19
  def environment
19
- CommandLine.environment
20
+ Kibo.config.environment
21
+ end
22
+
23
+ def namespace
24
+ Kibo.config.heroku.namespace
20
25
  end
21
26
 
22
27
  def run
23
28
  Commands.send CommandLine.subcommand
29
+ rescue RuntimeError
30
+ UI.error $!.to_s
31
+ exit 1
24
32
  end
25
-
26
- def command_line
27
- CommandLine
28
- end
29
-
30
- def kibofile
31
- command_line.kibofile
33
+
34
+ def binary
35
+ File.join(File.dirname(__FILE__), "..", "bin", "kibo")
32
36
  end
33
37
  end
data/man/kibo.1 CHANGED
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "KIBO" "1" "September 2012" "Kibo 0.3.5" "Kibo Manual"
4
+ .TH "KIBO" "1" "October 2012" "Kibo 0.4.0" "Kibo Manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBkibo\fR \- manage heroku applications
data/man/kibo.1.html CHANGED
@@ -172,8 +172,8 @@ kibo -e production spindown
172
172
 
173
173
 
174
174
  <ol class='man-decor man-foot man foot'>
175
- <li class='tl'>Kibo 0.3.5</li>
176
- <li class='tc'>September 2012</li>
175
+ <li class='tl'>Kibo 0.4.0</li>
176
+ <li class='tc'>October 2012</li>
177
177
  <li class='tr'>kibo(1)</li>
178
178
  </ol>
179
179
 
data/test/Kibofile ADDED
@@ -0,0 +1,33 @@
1
+ # This is an example Kibofile. Use with kibo(1) to configure
2
+ # remote instances.
3
+ heroku:
4
+ # The email of the heroku account to create app instances on heroku.
5
+ account: kibo@kibo.local
6
+
7
+ # Namespace to use when generating names for git remotes and heroku app
8
+ # instances. With a "kibo" namespace our instances will be called
9
+ # 'kibo-staging-clerk1', 'kibo-production-twirl1', etc.
10
+ namespace: kibo
11
+
12
+ # How many instances to spin up?
13
+ defaults:
14
+ processes:
15
+ stats: 0
16
+ clerk: 1
17
+ twirl: 1
18
+
19
+ # Live mode will have 1 clerk and 3 twirls.
20
+ live:
21
+ heroku:
22
+ mode: pro
23
+ processes:
24
+ clerk: 1
25
+ twirl: 3
26
+
27
+ source:
28
+ pre:
29
+ - rake bountybase:release
30
+ success:
31
+ - git tag $ENVIRONMENT-$(date "+%Y.%m.%d-%H.%M")
32
+
33
+ #arena:
data/test/kibo_test.rb ADDED
@@ -0,0 +1,59 @@
1
+ require_relative 'test_helper'
2
+
3
+
4
+ module Kibo::CommandLine
5
+ extend self
6
+
7
+ def parse
8
+ raise "CommandLine::parse"
9
+ end
10
+ end
11
+
12
+ class KiboTest < Test::Unit::TestCase
13
+ def config(environment = "staging")
14
+ Kibo::Config.new(File.dirname(__FILE__) + "/Kibofile", environment)
15
+ end
16
+
17
+ def test_missing_config
18
+ assert_raise(FatalError) {
19
+ Kibo::Config.new(File.dirname(__FILE__) + "/Kibofile.missing", "environment")
20
+ }
21
+ end
22
+
23
+ def test_verify_version
24
+ assert(Kibo::VERSION =~ /^0\.4/)
25
+
26
+ assert_raise(FatalError) {
27
+ Kibo::Config.verify_version!("1.0")
28
+ }
29
+
30
+ assert_raise(FatalError) {
31
+ Kibo::Config.verify_version!("0.5")
32
+ }
33
+
34
+ assert_nothing_raised(FatalError) {
35
+ Kibo::Config.verify_version!(Kibo::VERSION)
36
+ Kibo::Config.verify_version!("0.1")
37
+ }
38
+ end
39
+
40
+ def test_config
41
+ config = self.config
42
+
43
+ assert_equal({"clerk"=>1, "twirl"=>1}, config.processes)
44
+
45
+ assert_equal config.namespace, "kibo"
46
+ assert config.freemium?
47
+ assert_equal ["kibo-staging-clerk1", "kibo-staging-twirl1"], config.instances
48
+ end
49
+
50
+ def test_live_config
51
+ config = self.config("live")
52
+
53
+ assert_equal({"clerk"=>1, "twirl"=>3}, config.processes)
54
+
55
+ assert_equal config.namespace, "kibo"
56
+ assert !config.freemium?
57
+ assert_equal ["kibo-live-clerk", "kibo-live-twirl"], config.instances
58
+ end
59
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ ENV["RACK_ENV"] ||= "test"
5
+
6
+ require 'ruby-debug'
7
+ require 'simplecov'
8
+ require 'test/unit'
9
+ require 'test/unit/ui/console/testrunner'
10
+
11
+ class Test::Unit::UI::Console::TestRunner
12
+ def guess_color_availability; true; end
13
+ end
14
+
15
+ require 'mocha'
16
+ require 'awesome_print'
17
+
18
+ SimpleCov.start do
19
+ add_filter "test/*.rb"
20
+ end
21
+
22
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
23
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
24
+ require 'kibo'
25
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kibo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-05 00:00:00.000000000 Z
12
+ date: 2012-10-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
@@ -75,6 +75,54 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: mash
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: foreman
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: heroku
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
78
126
  description: Manage heroku instances with ease
79
127
  email:
80
128
  - eno@radiospiel.org
@@ -88,6 +136,7 @@ files:
88
136
  - Gemfile.lock
89
137
  - README.md
90
138
  - Rakefile
139
+ - YUILICENSE.md
91
140
  - bin/kibo
92
141
  - kibo.gemspec
93
142
  - lib/kibo.rb
@@ -95,16 +144,17 @@ files:
95
144
  - lib/kibo/commandline.rb
96
145
  - lib/kibo/commands.rb
97
146
  - lib/kibo/commands/compress.rb
98
- - lib/kibo/commands/create.rb
99
147
  - lib/kibo/commands/deploy.rb
100
148
  - lib/kibo/commands/generate.rb
149
+ - lib/kibo/commands/helpers.rb
101
150
  - lib/kibo/commands/info.rb
102
- - lib/kibo/commands/reconfigure.rb
151
+ - lib/kibo/commands/logs.rb
152
+ - lib/kibo/commands/setup.rb
103
153
  - lib/kibo/commands/spin.rb
104
154
  - lib/kibo/config.rb
105
- - lib/kibo/helpers.rb
106
- - lib/kibo/helpers/heroku.rb
155
+ - lib/kibo/ext/ruby_ext.rb
107
156
  - lib/kibo/helpers/info.rb
157
+ - lib/kibo/heroku.rb
108
158
  - lib/kibo/log.rb
109
159
  - lib/kibo/system.rb
110
160
  - lib/kibo/version.rb
@@ -113,6 +163,9 @@ files:
113
163
  - man/kibo.1.markdown
114
164
  - man/kibo.1.ronn
115
165
  - tasks/doc.rake
166
+ - test/Kibofile
167
+ - test/kibo_test.rb
168
+ - test/test_helper.rb
116
169
  homepage: http://github.com/radiospiel/kibo
117
170
  licenses: []
118
171
  post_install_message:
@@ -127,7 +180,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
127
180
  version: '0'
128
181
  segments:
129
182
  - 0
130
- hash: 173262939670169996
183
+ hash: -3844677239198829294
131
184
  required_rubygems_version: !ruby/object:Gem::Requirement
132
185
  none: false
133
186
  requirements:
@@ -140,4 +193,7 @@ rubygems_version: 1.8.24
140
193
  signing_key:
141
194
  specification_version: 3
142
195
  summary: Manage heroku instances with ease
143
- test_files: []
196
+ test_files:
197
+ - test/Kibofile
198
+ - test/kibo_test.rb
199
+ - test/test_helper.rb